Index: main/workspace-repository/i18n/messages_en.xml
===================================================================
--- main/workspace-repository/i18n/messages_en.xml (revision 19543)
+++ main/workspace-repository/i18n/messages_en.xml (working copy)
@@ -134,6 +134,23 @@
Properties
+
+
+ Maintenance
+ Maintenance hints<br/>Aide sur la maintenance (TODO)
+ This tab allows you to launch automated maintenance task. Three tasks are available :<ul><li>- Data store garbage collection</li><li>- Re-indexing</li><li>- Consistency check</li>Click on the corresponding task button to launch it. A progress bar and a report will be displayed to easily track the task progress.
+ Progress
+ %
+ Garbage collector
+ Re-indexing
+ Consistency check
+
+ An error occurred.
+ Unable to get response from the server. Cannot get informations if a task is running.
+ Unable to get response from the server. The launch of the task has probably failed.
+ Task failed to launch.
+ Perhaps a task is currently running. Try reloading the page.
+ Unable to get response from the server. The tracking of the task progress is currently not up to date.
Console
Index: main/workspace-repository/i18n/messages_fr.xml
===================================================================
--- main/workspace-repository/i18n/messages_fr.xml (revision 19543)
+++ main/workspace-repository/i18n/messages_fr.xml (working copy)
@@ -134,6 +134,22 @@
Propriétés
+
+
+ Maintenance
+ Cet onglet permet de lancer des tâches de maintenance automatiques. Les tâches suivantes sont disponibles :<ul><li>- Ramasse miettes</li><li>- Ré-indexation</li><li>- Vérification de la cohérence</li>Veuillez cliquez sur le bouton correspondant à la tâche de votre choix pour lancer celle-ci. Une barre de progression, ainsi qu'un rapport s'affichera pour vous permettre de suivre l'avancement du processus.
+ Avancement
+ %
+ Ramasse miettes
+ Ré-indexation
+ Vérification de la cohérence
+
+ Une erreur s'est produite.
+ La récupération des informations serveur a échouée. Impossible de déterminer si une tâche de maintenance est déjà en cours.
+ Impossible de récupérer les informations serveur. Le lancement de la tâche de maintenance a probablement échoué.
+ Le lancement de la tâche de maintenance a échoué.
+ Il est possible qu'une tâche soit déjà en cours d'éxecution. Veuillez essayer de recharger la page.
+ Impossible de récupérer les informations serveur. Les informations relative à la progression de la tâche et le panneau de rapport ne sont plus à jour pour l'instant.
Console
Index: main/workspace-repository/pages/index.xsl
===================================================================
--- main/workspace-repository/pages/index.xsl (revision 19543)
+++ main/workspace-repository/pages/index.xsl (working copy)
@@ -50,6 +50,7 @@
+
Index: main/workspace-repository/src/org/ametys/workspaces/repository/MaintenantIsRunningTaskGenerator.java
===================================================================
--- main/workspace-repository/src/org/ametys/workspaces/repository/MaintenantIsRunningTaskGenerator.java (revision 0)
+++ main/workspace-repository/src/org/ametys/workspaces/repository/MaintenantIsRunningTaskGenerator.java (revision 0)
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2012 Anyware Services
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ametys.workspaces.repository;
+
+import java.io.IOException;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.generation.ServiceableGenerator;
+import org.apache.cocoon.xml.AttributesImpl;
+import org.apache.cocoon.xml.XMLUtils;
+import org.apache.commons.lang.StringUtils;
+import org.xml.sax.SAXException;
+
+import org.ametys.workspaces.repository.tasks.MaintenanceTaskManager;
+
+/**
+ * Tells if a task is running.
+ */
+public class MaintenantIsRunningTaskGenerator extends ServiceableGenerator
+{
+ /** The maintenance task manager */
+ private MaintenanceTaskManager _taskManager;
+
+ @Override
+ public void service(ServiceManager m) throws ServiceException
+ {
+ super.service(m);
+ _taskManager = (MaintenanceTaskManager) m.lookup(MaintenanceTaskManager.ROLE);
+ }
+
+ public void generate() throws IOException, SAXException, ProcessingException
+ {
+ contentHandler.startDocument();
+
+ String task = _taskManager.getRunningTaskType();
+ String isRunning = Boolean.toString(_taskManager.isTaskRunning());
+
+ AttributesImpl attrs = new AttributesImpl();
+ attrs.addCDATAAttribute("running", isRunning);
+ attrs.addCDATAAttribute("repository-state", Boolean.toString(_taskManager.isRepositoryStarted()));
+ XMLUtils.createElement(contentHandler, "task", attrs, task != null ? task : StringUtils.EMPTY);
+
+ contentHandler.endDocument();
+ }
+}
Index: main/workspace-repository/src/org/ametys/workspaces/repository/MaintenantLaunchTaskGenerator.java
===================================================================
--- main/workspace-repository/src/org/ametys/workspaces/repository/MaintenantLaunchTaskGenerator.java (revision 0)
+++ main/workspace-repository/src/org/ametys/workspaces/repository/MaintenantLaunchTaskGenerator.java (revision 0)
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010 Anyware Services
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ametys.workspaces.repository;
+
+import java.io.IOException;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.generation.ServiceableGenerator;
+import org.apache.cocoon.xml.AttributesImpl;
+import org.apache.cocoon.xml.XMLUtils;
+import org.xml.sax.SAXException;
+
+import org.ametys.workspaces.repository.tasks.MaintenanceTaskManager;
+
+/**
+ * Generated information about the task launching process.
+ */
+public class MaintenantLaunchTaskGenerator extends ServiceableGenerator
+{
+ /** The maintenance task manager */
+ private MaintenanceTaskManager _taskManager;
+
+ @Override
+ public void service(ServiceManager m) throws ServiceException
+ {
+ super.service(m);
+ _taskManager = (MaintenanceTaskManager) m.lookup(MaintenanceTaskManager.ROLE);
+ }
+
+ public void generate() throws IOException, SAXException, ProcessingException
+ {
+ Request request = ObjectModelHelper.getRequest(objectModel);
+
+ String task = request.getParameter("task");
+ Boolean launched = parameters.getParameterAsBoolean("launched", false);
+
+ contentHandler.startDocument();
+
+ AttributesImpl attrs = new AttributesImpl();
+ attrs.addCDATAAttribute("launched", Boolean.toString(launched));
+ attrs.addCDATAAttribute("repository-state", Boolean.toString(_taskManager.isRepositoryStarted()));
+ XMLUtils.createElement(contentHandler, "task", attrs, task);
+
+ contentHandler.endDocument();
+ }
+}
Index: main/workspace-repository/src/org/ametys/workspaces/repository/tasks/MaintenanceTaskManager.java
===================================================================
--- main/workspace-repository/src/org/ametys/workspaces/repository/tasks/MaintenanceTaskManager.java (revision 0)
+++ main/workspace-repository/src/org/ametys/workspaces/repository/tasks/MaintenanceTaskManager.java (revision 0)
@@ -0,0 +1,273 @@
+package org.ametys.workspaces.repository.tasks;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.avalon.framework.component.Component;
+import org.apache.avalon.framework.context.ContextException;
+import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.cocoon.Constants;
+import org.apache.cocoon.environment.Context;
+import org.apache.jackrabbit.core.config.ConfigurationException;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+
+import org.ametys.plugins.repositoryapp.RepositoryProvider;
+import org.ametys.runtime.config.Config;
+import org.ametys.workspaces.repository.tasks.core.AbstractMaintenanceTask;
+import org.ametys.workspaces.repository.tasks.core.TaskReport.TaskReportMessage;
+
+/**
+ * The MaintenanceTaskManager Component
+ */
+public class MaintenanceTaskManager extends AbstractLogEnabled implements Component, Serviceable, Contextualizable
+{
+ /** The avalon role. */
+ public static final String ROLE = MaintenanceTaskManager.class.getName();
+
+ /** The maintenance running task */
+ protected AbstractMaintenanceTask _runningTask;
+
+ /** The maintenance running task type */
+ protected MaintenanceTaskType _runningTaskType;
+
+ private Context _context;
+
+ /** The repository provider. */
+ private RepositoryProvider _repositoryProvider;
+
+ private final ExecutorService _executor = Executors.newFixedThreadPool(1);
+ private FutureTask _future;
+
+ private boolean _repositoryShutdown;
+
+ /** Task types */
+ public enum MaintenanceTaskType
+ {
+ /** data store GC */
+ DATA_STORE_GARBAGE_COLLECTOR,
+ /** reindexing task */
+ REINDEXING,
+ /** consistency check */
+ CONSISTENCY_CHECK;
+ }
+
+ @Override
+ public void contextualize(org.apache.avalon.framework.context.Context context) throws ContextException
+ {
+ _context = (Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT);
+ }
+
+ @Override
+ public void service(ServiceManager manager) throws ServiceException
+ {
+ _repositoryProvider = (RepositoryProvider) manager.lookup(RepositoryProvider.ROLE);
+ }
+
+ /**
+ * Launch a maintenance task
+ * @param type
+ * @return true if a task has been launched
+ */
+ public boolean launch(MaintenanceTaskType type)
+ {
+ boolean launched = false;
+ if (!isTaskRunning())
+ {
+ try
+ {
+ run(type);
+ launched = true;
+ }
+ catch (ConfigurationException e)
+ {
+ getLogger().error("Unable to get the configuration of the repository.", e);
+ }
+ catch (RepositoryException e)
+ {
+ getLogger().error("Unable to launch the task.", e);
+ }
+ }
+ return launched;
+ }
+
+ private void run(final MaintenanceTaskType type) throws ConfigurationException, RepositoryException
+ {
+ _runningTaskType = type;
+
+ // Shutdown repository
+ shutdownRepository();
+
+ final Logger logger = getLogger();
+ final RepositoryConfig repositoryConfig = _getRepositoryConfig();
+ _future = new FutureTask(
+ new Callable()
+ {
+ public Void call()
+ {
+ try
+ {
+ _runningTask = _createTask(type);
+ _runningTask.execute(repositoryConfig);
+ }
+ catch (RepositoryException e)
+ {
+ logger.error(e.getMessage(), e);
+ }
+ finally
+ {
+ restartRepository();
+ _runningTaskType = null;
+ }
+
+ return null;
+ }
+ });
+
+ _executor.execute(_future);
+ }
+
+ /**
+ * Retrieve the repository config object
+ * @return RepositoryConfig
+ * @throws ConfigurationException
+ */
+ private RepositoryConfig _getRepositoryConfig() throws ConfigurationException
+ {
+ String home = Config.getInstance().getValueAsString("org.ametys.plugins.repository.home");
+ String config = _context.getRealPath("WEB-INF/param/repository.xml");
+
+ File homeFile = new File(home);
+ if (!homeFile.isAbsolute())
+ {
+ // No : consider it relative to context path
+ homeFile = new File(_context.getRealPath(home));
+ }
+
+ return RepositoryConfig.create(config, homeFile.getAbsolutePath());
+ }
+
+ /**
+ * Shutdown the Ametys repository instance
+ * @throws RepositoryException
+ */
+ protected void shutdownRepository() throws RepositoryException
+ {
+ if (_repositoryProvider.isJndi())
+ {
+ throw new RepositoryException("JNDI Repository instance are currently not allowed.");
+ }
+
+ // FIXME Shutdown the repository
+ _repositoryProvider.disconnect();
+ _repositoryShutdown = true;
+
+ if (getLogger().isInfoEnabled())
+ {
+ getLogger().info("Repository instance has been shutdown in order to run an automated maintenance task.");
+ }
+ }
+
+ /**
+ * Re-create the Ametys repository instance
+ */
+ protected void restartRepository()
+ {
+ // FIXME @see #shutdownRepository()
+ // _repositoryShutdown = true;
+ }
+
+ /**
+ * Initialize the tasks.
+ * @param type MaintenanceTaskType
+ * @return the task.
+ * @throws RepositoryException
+ */
+ protected AbstractMaintenanceTask _createTask(MaintenanceTaskType type) throws RepositoryException
+ {
+ switch(type)
+ {
+ case DATA_STORE_GARBAGE_COLLECTOR:
+ return new DataStoreGarbageCollectorTask();
+ case REINDEXING:
+ return new ReindexingTask();
+ case CONSISTENCY_CHECK:
+ return new ConsistencyCheckTask();
+ default:
+ throw new IllegalArgumentException("This type of maintenance task is not allowed : " + type);
+ }
+ }
+
+ /**
+ * Indicates if a maintenance task is running.
+ * @return true if a task is running.
+ */
+ public boolean isTaskRunning()
+ {
+ return _runningTaskType != null;
+ }
+
+ /**
+ * Get progress information.
+ * @return a map containing informations on the progress status.
+ */
+ public Map getProgressInfo()
+ {
+ if (_runningTask != null)
+ {
+ return _runningTask.getProgressInfo();
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the {@link TaskReportMessage} objects starting from a given part.
+ * @param list the list of {@link TaskReportMessage} to be populated
+ * @param fromPart
+ * @return the last report part number
+ */
+ public Integer getReportMessage(List list, int fromPart)
+ {
+ if (_runningTask != null)
+ {
+ return _runningTask.getReportMessage(list, fromPart);
+ }
+
+ return null;
+ }
+
+ /**
+ * Retrieves the type of the running task
+ * @return the type of the running task or null.
+ */
+ public String getRunningTaskType()
+ {
+ if (_runningTaskType != null)
+ {
+ return _runningTaskType.name();
+ }
+
+ return null;
+ }
+
+ /**
+ * Indicates is the Ametys repository is started or has been shut down.
+ * @return true is the repository is started
+ */
+ public boolean isRepositoryStarted()
+ {
+ return !_repositoryShutdown;
+ }
+}
Index: main/workspace-repository/src/org/ametys/workspaces/repository/tasks/ConsistencyCheckTask.java
===================================================================
--- main/workspace-repository/src/org/ametys/workspaces/repository/tasks/ConsistencyCheckTask.java (revision 0)
+++ main/workspace-repository/src/org/ametys/workspaces/repository/tasks/ConsistencyCheckTask.java (revision 0)
@@ -0,0 +1,188 @@
+package org.ametys.workspaces.repository.tasks;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.apache.jackrabbit.core.RepositoryContext;
+import org.apache.jackrabbit.core.persistence.IterablePersistenceManager;
+import org.apache.jackrabbit.core.persistence.PersistenceManager;
+import org.apache.jackrabbit.core.persistence.bundle.AbstractBundlePersistenceManager;
+import org.apache.jackrabbit.core.persistence.check.ConsistencyCheckListener;
+import org.apache.jackrabbit.core.persistence.check.ConsistencyReport;
+import org.apache.jackrabbit.core.persistence.check.ReportItem;
+import org.apache.jackrabbit.core.version.InternalVersionManagerImpl;
+
+import org.ametys.workspaces.repository.tasks.core.AbstractMaintenanceTask;
+import org.ametys.workspaces.repository.tasks.core.TaskProgress;
+import org.ametys.workspaces.repository.tasks.core.TaskReport.TaskReportMessageType;
+
+/**
+ * DataStoreGarbageCollectorTask
+ */
+public class ConsistencyCheckTask extends AbstractMaintenanceTask implements ConsistencyCheckListener
+{
+ /** The JackRabbit RepositoryImpl Context */
+ protected RepositoryContext _repositoryContext;
+
+ /** The JCR Session bound to this task. */
+ protected Session _session;
+
+ private IterablePersistenceManager[] _pmList;
+
+ @Override
+ protected void initialize() throws RepositoryException
+ {
+ // Create the repository and log in the session.
+ _repositoryContext = RepositoryContext.create(_repositoryConfig);
+ _session = _repositoryContext.getRepository().login(new SimpleCredentials("__MAINTENANCE_TASK__", "".toCharArray()));
+
+ // Workaround to get the list of the PersistenceManager
+ ArrayList pmList = new ArrayList();
+
+ // PM of version manager
+ InternalVersionManagerImpl vm = _repositoryContext.getInternalVersionManager();
+ pmList.add(vm.getPersistenceManager());
+
+ // PMs of workspaces.
+ String[] wspNames = _repositoryContext.getWorkspaceManager().getWorkspaceNames();
+ for (int i = 0; i < wspNames.length; i++)
+ {
+ pmList.add(getPM(wspNames[i]));
+ }
+
+ // Filtering on IterablePersistenceManager
+ _pmList = new IterablePersistenceManager[pmList.size()];
+ for (int i = 0; i < pmList.size(); i++)
+ {
+ PersistenceManager pm = pmList.get(i);
+ if (!(pm instanceof IterablePersistenceManager))
+ {
+ _pmList = null;
+ break;
+ }
+ _pmList[i] = (IterablePersistenceManager) pm;
+ }
+
+ // Initialize the task progress object.
+ int count = 0; // number of item that will be scanned.
+
+ try
+ {
+ for (IterablePersistenceManager pm : _pmList)
+ {
+ count += pm.getAllNodeIds(null, 0).size();
+ }
+ _progress = new TaskProgress(count);
+ }
+ catch (Exception e)
+ {
+ _progress = new TaskProgress(0);
+ _progress.setInErrorState(e);
+ _report.addException(e);
+ }
+ }
+
+ @Override
+ protected void apply() throws RepositoryException
+ {
+ for (PersistenceManager pm : _pmList)
+ {
+ // Do PM consistency check
+ if (pm instanceof AbstractBundlePersistenceManager)
+ {
+ // Perform the check
+ // null -> all uuids, true -> recursive, false -> nofix, null,
+ // lost+found -> null, this -> listener
+ ConsistencyReport report = ((AbstractBundlePersistenceManager) pm).check(null, true, false, null, this);
+
+ _report.add("Consistency check done for persistence manager : '" + pm.toString() + "' in " + (report.getElapsedTimeMs() / 1000f) + " s.");
+ _report.add(report.getNodeCount() + " nodes were checked.");
+ _report.add(report.getItems().isEmpty() ? "No consistency problems where reported." : report.getItems().size() + " consistency problems where reported.");
+ }
+ }
+ }
+
+ @Override
+ protected void close()
+ {
+ if (_session != null)
+ {
+ _session.logout();
+ }
+
+ if (_repositoryContext != null && _repositoryContext.getRepository() != null)
+ {
+ _repositoryContext.getRepository().shutdown();
+ }
+ }
+
+ /**
+ * Retrieves JackRabbit Persistence Manager for currently opened repository. This method uses
+ * Privileged access and will fail with security exception if used in environment with enabled security manager.
+ *
+ * @return Persistence manager used by repository.
+ */
+ private PersistenceManager getPM(String workspaceName)
+ {
+ try
+ {
+ Object workspaceInfo = findAndInvokeMethod(_repositoryContext.getRepository(), "getWorkspaceInfo", new Object[] {workspaceName});
+ return (PersistenceManager) (findAndInvokeMethod(workspaceInfo, "getPersistenceManager", null));
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static Object findAndInvokeMethod(Object obj, String name, Object[] parameters) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+ {
+ Method m = null;
+ Method[] ms = obj.getClass().getDeclaredMethods();
+ for (int i = 0; i < ms.length; i++)
+ {
+ final Method x = ms[i];
+ if (x.getName().equals(name))
+ {
+ m = x;
+ break;
+ }
+ }
+ m.setAccessible(true);
+ return m.invoke(obj, parameters);
+ }
+
+ // Listener methods
+
+ @Override
+ public void startCheck(String id)
+ {
+ if (_progress != null)
+ {
+ _progress.progress();
+ }
+ }
+
+ @Override
+ public void report(ReportItem item)
+ {
+ _report.add(TaskReportMessageType.WARN, item.toString());
+ }
+
+ @Override
+ public void error(String id, String message)
+ {
+ _report.add(TaskReportMessageType.ERROR, "error during the consistency check -> id : [ " + id + "]\n" + message);
+ }
+
+ @Override
+ public void info(String id, String message)
+ {
+ _report.add("error during the consistency check -> id : [ " + id + "]\n" + message);
+ }
+}
Index: main/workspace-repository/src/org/ametys/workspaces/repository/tasks/core/AbstractMaintenanceTask.java
===================================================================
--- main/workspace-repository/src/org/ametys/workspaces/repository/tasks/core/AbstractMaintenanceTask.java (revision 0)
+++ main/workspace-repository/src/org/ametys/workspaces/repository/tasks/core/AbstractMaintenanceTask.java (revision 0)
@@ -0,0 +1,190 @@
+package org.ametys.workspaces.repository.tasks.core;
+
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.joda.time.Duration;
+import org.joda.time.format.PeriodFormatter;
+import org.joda.time.format.PeriodFormatterBuilder;
+
+import org.ametys.plugins.repositoryapp.RepositoryProvider;
+import org.ametys.workspaces.repository.tasks.core.TaskReport.TaskReportMessage;
+
+/**
+ * Jackrabbit maintenance tasks implementations should extends this class.
+ */
+public abstract class AbstractMaintenanceTask
+{
+ /** TaskProgress */
+ protected TaskProgress _progress;
+
+ /** task report */
+ protected TaskReport _report;
+
+ /** The repository config */
+ protected RepositoryConfig _repositoryConfig;
+
+ /** The repository provider. */
+ protected RepositoryProvider _repositoryProvider;
+
+ private boolean _isFinished;
+
+ /**
+ * Execute the task
+ * @param repositoryConfig The RepositoryConfig object, used to create new Repository instance.
+ * @throws RepositoryException
+ */
+ public void execute(RepositoryConfig repositoryConfig) throws RepositoryException
+ {
+ _report = new TaskReport();
+ _repositoryConfig = repositoryConfig;
+
+ long startTime = System.currentTimeMillis();
+
+ _report.add(DateFormat.getTimeInstance().format(new Date()) + " : Executing task");
+
+ try
+ {
+ // task specific initialization.
+ initialize();
+
+ // Mark the task as running.
+ setRunning();
+
+ // Do the task.
+ apply();
+
+ // Mark the task as finished.
+ setFinished();
+ }
+ catch (RepositoryException e)
+ {
+ setInErrorState(e);
+ throw e;
+ }
+ finally
+ {
+ close();
+
+ _report.add(DateFormat.getTimeInstance().format(new Date()) + " : End of the task");
+
+ long elapsedTime = System.currentTimeMillis() - startTime;
+ _report.add("Done in " + _getFormattedDuration(elapsedTime));
+ }
+ }
+
+ /**
+ * Initialize the tasks.
+ * This method can also create the {@link TaskProgress} object bounded to the task.
+ * @throws RepositoryException
+ */
+ protected void initialize() throws RepositoryException
+ {
+ return;
+ }
+
+ /**
+ * Apply the tasks (within the execute method()).
+ * @throws RepositoryException
+ */
+ protected abstract void apply() throws RepositoryException;
+
+ /**
+ * Close the tasks
+ * @throws RepositoryException
+ */
+ protected void close() throws RepositoryException
+ {
+ return;
+ }
+
+ /**
+ * Get progress information.
+ * @return a map containing informations on the progress status.
+ */
+ public Map getProgressInfo()
+ {
+ if (_progress != null)
+ {
+ return _progress.getProgressInfo();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Get the {@link TaskReportMessage} objects starting from a given part.
+ * @param list the list of {@link TaskReportMessage} to be populated
+ * @param fromPart
+ * @return the last report part number
+ */
+ public int getReportMessage(List list, int fromPart)
+ {
+ return _report.getMessages(list, fromPart);
+ }
+
+ /**
+ * Is the execution of the task finished?
+ * @return true if finished
+ */
+ public boolean isFinished()
+ {
+ return _isFinished;
+ }
+
+ private synchronized void setRunning()
+ {
+ if (_progress != null)
+ {
+ _progress.setRunning();
+ }
+ }
+
+ private synchronized void setFinished()
+ {
+ if (_progress != null)
+ {
+ _progress.setFinished();
+ }
+
+ _isFinished = true;
+ }
+
+ private synchronized void setInErrorState(Exception e)
+ {
+ _report.addException(e);
+
+ if (_progress != null)
+ {
+ _progress.setInErrorState(e);
+ }
+
+ _isFinished = true;
+ }
+
+ /**
+ * Transforms millisecond to a formatted string representing a duration.
+ * @param elapsedTime milleseconds corresponding to the duration.
+ * @return Pretty formatted string.
+ */
+ protected String _getFormattedDuration(long elapsedTime)
+ {
+ Duration duration = new Duration(elapsedTime);
+ PeriodFormatter formatter = new PeriodFormatterBuilder()
+ .appendHours()
+ .appendSuffix("h")
+ .appendMinutes()
+ .appendSuffix("m")
+ .appendSeconds()
+ .appendSuffix("s")
+ .toFormatter();
+ return formatter.print(duration.toPeriod());
+ }
+}
Index: main/workspace-repository/src/org/ametys/workspaces/repository/tasks/core/TaskProgress.java
===================================================================
--- main/workspace-repository/src/org/ametys/workspaces/repository/tasks/core/TaskProgress.java (revision 0)
+++ main/workspace-repository/src/org/ametys/workspaces/repository/tasks/core/TaskProgress.java (revision 0)
@@ -0,0 +1,249 @@
+package org.ametys.workspaces.repository.tasks.core;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * TaskProgress
+ */
+public class TaskProgress
+{
+ private final float _total;
+ private float _progress;
+ private State _state;
+
+ private Exception _exception;
+
+ /**
+ * Progress state
+ */
+ public enum State
+ {
+ /** NEW */
+ NEW,
+ /** RUNNING */
+ RUNNING,
+ /** FINISHED */
+ FINISHED,
+ /** ERROR_STATE */
+ ERROR_STATE
+ }
+
+ /**
+ * Ctor
+ * @param total
+ */
+ public TaskProgress(float total)
+ {
+ this._total = total;
+ this._progress = 0f;
+ this._state = State.NEW;
+ }
+
+ /**
+ * Overriden Ctor
+ * @param total
+ * @param start
+ */
+ public TaskProgress(float total, float start)
+ {
+ this(total);
+ this._progress = start;
+ }
+
+ /**
+ * Unit progress
+ */
+ public void progress()
+ {
+ progress(1);
+ }
+
+ /**
+ * Quantitative progress
+ * @param quantity
+ */
+ public synchronized void progress(float quantity)
+ {
+ _progress += quantity;
+ }
+
+ /**
+ * Progress by a percentage relative to the range [current progress; total]
+ * @param percentage must be in the [0; 100] range
+ */
+ public void progressRelativePercentage(int percentage)
+ {
+ if (percentage < 0 || percentage > 100)
+ {
+ throw new IllegalArgumentException("percentage must be in the [0; 100] range.");
+ }
+
+ synchronized (this)
+ {
+ if (percentage == 100)
+ {
+ _progress = _total;
+ }
+ else
+ {
+ _progress += (_total - _progress) * percentage / 100f;
+ }
+ }
+ }
+
+ /**
+ * Progress by a given percentage
+ * @param percentage must be in the [0; 100] range
+ */
+ public void progressTotalPercentage(int percentage)
+ {
+ if (percentage < 0 || percentage > 100)
+ {
+ throw new IllegalArgumentException("percentage must be in the [0; 100] range.");
+ }
+
+ synchronized (this)
+ {
+ if (percentage == 100)
+ {
+ _progress = _total;
+ }
+ else
+ {
+ _progress = _total * percentage / 100f;
+ }
+ }
+ }
+
+ /**
+ * getProgressPercentage
+ * @return int
+ */
+ public synchronized float getProgressPercentage()
+ {
+ return Math.min(1f, _progress / _total);
+ }
+
+ /**
+ * setRunning
+ */
+ public synchronized void setRunning()
+ {
+ _state = State.RUNNING;
+ }
+
+ /**
+ * setFinished
+ */
+ public synchronized void setFinished()
+ {
+ _state = State.FINISHED;
+ }
+
+ /**
+ * setInErrorState
+ */
+ public void setInErrorState()
+ {
+ setInErrorState(null);
+ }
+
+ /**
+ * setInErrorState
+ * @param e
+ */
+ public synchronized void setInErrorState(Exception e)
+ {
+ _exception = e;
+ _state = State.ERROR_STATE;
+ }
+
+ /**
+ * isNotStarted
+ * @return boolean
+ */
+ public boolean isNotStarted()
+ {
+ return State.NEW.equals(_state);
+ }
+
+ /**
+ * isRunning
+ * @return boolean
+ */
+ public boolean isRunning()
+ {
+ return State.RUNNING.equals(_state);
+ }
+
+ /**
+ * isFinished
+ * @return boolean
+ */
+ public boolean isFinished()
+ {
+ return State.FINISHED.equals(_state);
+ }
+
+ /**
+ * isInErrorState
+ * @return boolean
+ */
+ public boolean isInErrorState()
+ {
+ return State.ERROR_STATE.equals(_state);
+ }
+
+ /**
+ * getException
+ * @return Exception
+ */
+ public Exception getException()
+ {
+ return _exception;
+ }
+
+ /**
+ * getState
+ * @return State
+ */
+ public State getState()
+ {
+ return _state;
+ }
+
+
+ /**
+ * Returns informations this progress object.
+ * @return a map containing informations on the progress status
+ */
+ public Map getProgressInfo()
+ {
+ Map infos = new HashMap();
+ synchronized (this)
+ {
+ infos.put("progress", String.valueOf(_progress));
+ infos.put("total", String.valueOf(_total));
+ infos.put("percentage", String.valueOf(getProgressPercentage()));
+ infos.put("state", _state.name());
+ if (_exception != null)
+ {
+ infos.put("exception", _exception.getLocalizedMessage());
+ }
+ }
+ return infos;
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder("Progress : ");
+ synchronized (this)
+ {
+ sb.append(getProgressPercentage()).append(" ");
+ sb.append("[").append(_progress).append("/").append(_total).append("]");
+ }
+ return sb.toString();
+ }
+}
Index: main/workspace-repository/src/org/ametys/workspaces/repository/tasks/core/TaskReport.java
===================================================================
--- main/workspace-repository/src/org/ametys/workspaces/repository/tasks/core/TaskReport.java (revision 0)
+++ main/workspace-repository/src/org/ametys/workspaces/repository/tasks/core/TaskReport.java (revision 0)
@@ -0,0 +1,137 @@
+package org.ametys.workspaces.repository.tasks.core;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * TaskReport
+ */
+public class TaskReport
+{
+ private final List> _reportList = new ArrayList>();
+
+ private final List _currentList = new ArrayList();
+
+ /** Task Report message type */
+ public enum TaskReportMessageType
+ {
+ /** INFO */
+ INFO,
+ /** WARN */
+ WARN,
+ /** ERROR */
+ ERROR;
+ }
+
+ /**
+ * Add an INFO message to the report
+ *
+ * @param msg
+ */
+ public void add(String msg)
+ {
+ add(null, msg);
+ }
+
+ /**
+ * Add an ERROR message to the report, containing the message of the
+ * exception.
+ *
+ * @param e the exception.
+ */
+ public void addException(Exception e)
+ {
+ add(TaskReportMessageType.ERROR, e.getLocalizedMessage());
+ }
+
+ /**
+ * An an message of the specified type to the report
+ *
+ * @param type The message type
+ * @param msg
+ */
+ public void add(TaskReportMessageType type, String msg)
+ {
+ TaskReportMessage trm = new TaskReportMessage(type, msg);
+ synchronized (this)
+ {
+ _currentList.add(trm);
+ }
+ }
+
+ /**
+ * Get the {@link TaskReportMessage} objects starting from a given part.
+ * @param list the list of {@link TaskReportMessage} to be populated
+ * @param fromPart
+ * @return the last report part number
+ */
+ public synchronized int getMessages(List list, int fromPart)
+ {
+ manageReportList();
+ List> subReportList = _reportList.subList(fromPart, _reportList.size());
+ for (List subReportListPart : subReportList)
+ {
+ list.addAll(subReportListPart);
+ }
+
+ return _reportList.size();
+ }
+
+ private synchronized void manageReportList()
+ {
+ if (_currentList.isEmpty())
+ {
+ return;
+ }
+
+ _reportList.add(new ArrayList(_currentList));
+ _currentList.clear();
+ }
+
+ /**
+ * Inner TaskReportMessage class
+ */
+ public class TaskReportMessage
+ {
+ private TaskReportMessageType _type;
+ private String _message;
+
+ /**
+ * Ctor
+ *
+ * @param type
+ * @param message
+ */
+ public TaskReportMessage(TaskReportMessageType type, String message)
+ {
+ _type = type == null ? TaskReportMessageType.INFO : type;
+ _message = message;
+ }
+
+ /**
+ * Retrieves the type of the report message
+ * @return the type of the report message
+ */
+ public TaskReportMessageType getTaskReportMessageType()
+ {
+ return _type;
+ }
+
+ /**
+ * Retrieves the content of the report message
+ * @return the message
+ */
+ public String getMessage()
+ {
+ return _message;
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(_type).append(" ").append(_message);
+ return sb.toString();
+ }
+ }
+}
Index: main/workspace-repository/src/org/ametys/workspaces/repository/tasks/ReindexingTask.java
===================================================================
--- main/workspace-repository/src/org/ametys/workspaces/repository/tasks/ReindexingTask.java (revision 0)
+++ main/workspace-repository/src/org/ametys/workspaces/repository/tasks/ReindexingTask.java (revision 0)
@@ -0,0 +1,116 @@
+package org.ametys.workspaces.repository.tasks;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.core.RepositoryImpl;
+
+import org.ametys.workspaces.repository.tasks.core.AbstractMaintenanceTask;
+import org.ametys.workspaces.repository.tasks.core.TaskProgress;
+
+/**
+ * ReindexingTask
+ */
+public class ReindexingTask extends AbstractMaintenanceTask
+{
+ private static final String _INDEX_FOLDER_RELATIVE_PATH = "repository" + File.separator + "index";
+ private static final String _WORKSPACES_FOLDER_RELATIVE_PATH = "workspaces";
+
+ /** The JackRabbit RepositoryImpl */
+ protected RepositoryImpl _repository;
+
+ private String[] _workspaceFolders;
+
+ @Override
+ protected void initialize() throws RepositoryException
+ {
+ File file = new File(_repositoryConfig.getHomeDir() + File.separator + _WORKSPACES_FOLDER_RELATIVE_PATH);
+ _workspaceFolders = file.list(new FilenameFilter()
+ {
+ @Override
+ public boolean accept(File dir, String name)
+ {
+ return dir.isDirectory();
+ }
+ });
+
+
+ // Initialize the task progress object.
+ // deleting workspace index folders + repository root index folder = 60%
+ // reindexing = 40%
+ _progress = new TaskProgress(5f / 3 * (_workspaceFolders.length + 1));
+ }
+
+ @Override
+ protected void apply() throws RepositoryException
+ {
+ // Deleting repository root index folder
+ try
+ {
+ deleteIndexFolder(_INDEX_FOLDER_RELATIVE_PATH);
+ _report.add("Successfully deleted root repository index folder");
+ }
+ catch (IOException e)
+ {
+ _report.addException(e);
+ }
+
+ // Deleting workspace index folders
+ for (String folder : _workspaceFolders)
+ {
+ try
+ {
+ deleteIndexFolder(_WORKSPACES_FOLDER_RELATIVE_PATH + File.separator + folder + File.separator + "index");
+ _report.add("Successfully deleted index folder of workspace '" + folder + "'");
+ }
+ catch (IOException e)
+ {
+ _report.addException(e);
+ }
+ }
+
+ // Creates a repository instance to perform the re-indexing process.
+ _report.add("Starting repository to launch the re-indexing process");
+ _repository = RepositoryImpl.create(_repositoryConfig);
+ _report.add("Repository restarted successfully, reindexing process has ended.");
+ setProgressTo(90);
+
+ // logout
+ _report.add("Shuting down repository");
+ _repository.shutdown();
+ _repository = null;
+ setProgressTo(100);
+ }
+
+ @Override
+ protected void close()
+ {
+ // shutdown properly if an exception has been thrown
+ if (_repository != null)
+ {
+ _repository.shutdown();
+ }
+ }
+
+ private void deleteIndexFolder(String relativePath) throws IOException
+ {
+ File dir = new File(_repositoryConfig.getHomeDir() + File.separator + relativePath);
+ FileUtils.deleteDirectory(dir);
+ if (_progress != null)
+ {
+ _progress.progress();
+ }
+ }
+
+ private void setProgressTo(int percentage)
+ {
+ if (_progress != null)
+ {
+ _progress.progressTotalPercentage(percentage);
+ }
+ }
+}
Index: main/workspace-repository/src/org/ametys/workspaces/repository/tasks/DataStoreGarbageCollectorTask.java
===================================================================
--- main/workspace-repository/src/org/ametys/workspaces/repository/tasks/DataStoreGarbageCollectorTask.java (revision 0)
+++ main/workspace-repository/src/org/ametys/workspaces/repository/tasks/DataStoreGarbageCollectorTask.java (revision 0)
@@ -0,0 +1,329 @@
+package org.ametys.workspaces.repository.tasks;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.sql.DataSource;
+
+import org.apache.jackrabbit.api.management.MarkEventListener;
+import org.apache.jackrabbit.core.RepositoryContext;
+import org.apache.jackrabbit.core.data.DataIdentifier;
+import org.apache.jackrabbit.core.data.DataStore;
+import org.apache.jackrabbit.core.data.DataStoreException;
+import org.apache.jackrabbit.core.data.FileDataStore;
+import org.apache.jackrabbit.core.data.GarbageCollector;
+import org.apache.jackrabbit.core.data.db.DbDataStore;
+import org.apache.jackrabbit.core.persistence.IterablePersistenceManager;
+import org.apache.jackrabbit.core.persistence.PersistenceManager;
+import org.apache.jackrabbit.core.util.db.ConnectionFactory;
+import org.apache.jackrabbit.core.util.db.ConnectionHelper;
+import org.apache.jackrabbit.core.version.InternalVersionManagerImpl;
+
+import org.ametys.workspaces.repository.tasks.core.AbstractMaintenanceTask;
+import org.ametys.workspaces.repository.tasks.core.TaskProgress;
+import org.ametys.workspaces.repository.tasks.core.TaskReport.TaskReportMessageType;
+
+/**
+ * DataStoreGarbageCollectorTask
+ */
+public class DataStoreGarbageCollectorTask extends AbstractMaintenanceTask implements MarkEventListener
+{
+ private static final int SYSTEM_GC_CALLS = 3;
+
+ /** The JackRabbit RepositoryImpl Context */
+ protected RepositoryContext _repositoryContext;
+
+ /** The JCR Session bound to this task. */
+ protected Session _session;
+
+ private GarbageCollector _garbageCollector;
+ private IterablePersistenceManager[] _pmList;
+
+ /** Internal counter for scanned node by the GC */
+ private int _scannedNodesCount;
+
+ @Override
+ protected void initialize() throws RepositoryException
+ {
+ // Create the repository and log in the session.
+ _repositoryContext = RepositoryContext.create(_repositoryConfig);
+ _session = _repositoryContext.getRepository().login(new SimpleCredentials("__MAINTENANCE_TASK__", "".toCharArray()));
+
+ // Create the GC Object
+ this._garbageCollector = _repositoryContext.getRepository().createDataStoreGarbageCollector();
+
+ // Workaround to get the list of the PersistenceManager
+ ArrayList pmList = new ArrayList();
+ InternalVersionManagerImpl vm = _repositoryContext.getInternalVersionManager();
+ pmList.add(vm.getPersistenceManager());
+
+ String[] wspNames = _repositoryContext.getWorkspaceManager().getWorkspaceNames();
+ for (int i = 0; i < wspNames.length; i++)
+ {
+ pmList.add(getPM(wspNames[i]));
+ }
+
+ _pmList = new IterablePersistenceManager[pmList.size()];
+ for (int i = 0; i < pmList.size(); i++)
+ {
+ PersistenceManager pm = pmList.get(i);
+ if (!(pm instanceof IterablePersistenceManager))
+ {
+ _pmList = null;
+ break;
+ }
+ _pmList[i] = (IterablePersistenceManager) pm;
+ }
+
+ // Initialize the task progress object.
+ int count = 0; // number of item that will be scanned.
+
+ try
+ {
+ for (IterablePersistenceManager pm : _pmList)
+ {
+ count += pm.getAllNodeIds(null, 0).size();
+ }
+
+ // When scan is finished, progress is set at 70%.
+ float total = (10f / 7) * count;
+
+ _progress = new TaskProgress(Math.max(total, 1));
+ }
+ catch (Exception e)
+ {
+ _progress = new TaskProgress(0);
+ _progress.setInErrorState(e);
+ _report.addException(e);
+ }
+ }
+
+ @Override
+ protected void apply() throws RepositoryException
+ {
+ // call System.gc() a few times before running the data store garbage collection.
+ // Please note System.gc() does not guarantee all objects are garbage collected.
+ for (int i = 0; i < SYSTEM_GC_CALLS; i++)
+ {
+ System.gc();
+ }
+
+ // Log some informations about the ds.
+ int startSize = reportDataStoreInfo(_garbageCollector.getDataStore());
+
+ // Sleep
+ if (_garbageCollector.getDataStore() instanceof FileDataStore)
+ {
+ // make sure the file is old (access time resolution is 2 seconds)
+ try
+ {
+ Thread.sleep(2000);
+ }
+ catch (InterruptedException e)
+ {
+ _report.addException(e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ // GC Management
+ _garbageCollector.setMarkEventListener(this);
+ _garbageCollector.setPersistenceManagerScan(true);
+ // gc.setSleepBetweenNodes(0);
+
+ // Run GC
+ try
+ {
+ _scannedNodesCount = 0;
+ _report.add("Scanning the repository nodes...");
+ _garbageCollector.mark();
+ _report.add(_scannedNodesCount + " nodes scanned.");
+
+ // Only for tests purpose (decomment)
+ // _garbageCollector.getDataStore().clearInUse();
+
+ _report.add("Deleting unused items... Please be patient.");
+ int deleted = _garbageCollector.sweep();
+ _report.add(deleted + " unused items deleted.");
+ if (_progress != null)
+ {
+ _progress.progressRelativePercentage(50);
+ }
+
+ _report.add("Finalizing the process...");
+
+ // Compressing derby DATASTORE table.
+ // A Workaround is used to detect that we are using derby for the datastore
+ // because we are in fact using the DbDataStore. In reality, we should simply use _garbageCollector.getDataStore() instanceof DerbyDataStore
+ // See REPOSITORY-209
+ if (_garbageCollector.getDataStore() instanceof DbDataStore && "derby".equals(((DbDataStore) _garbageCollector.getDataStore()).getDatabaseType()))
+ {
+ _report.add("Reclaiming unused space, this may take several minutes depending on the size of the data store.");
+ _report.add("Please be patient.");
+
+ DbDataStore ds = (DbDataStore) _garbageCollector.getDataStore();
+ derbyCompressTable(ds);
+ // TODO try SYSCS_DIAG.SPACE_TABLE to estimate the amount of unused space in a table or index
+ // derby 10.6+ only ?
+ }
+ }
+ finally
+ {
+ _garbageCollector.close();
+ }
+
+ // Log some informations about the ds.
+ int finalSize = reportDataStoreInfo(_garbageCollector.getDataStore());
+ int freedSize = startSize - finalSize;
+ _report.add("Size of cleared data : " + Math.round(freedSize / 1024f) + "Ko");
+ _report.add("The total released space on your disk can be different depending on the type of the data store used by your repository.");
+ }
+
+ @Override
+ protected void close()
+ {
+ if (_session != null)
+ {
+ _session.logout();
+ }
+
+ if (_repositoryContext != null && _repositoryContext.getRepository() != null)
+ {
+ _repositoryContext.getRepository().shutdown();
+ }
+
+ if (_progress != null)
+ {
+ _progress.progressRelativePercentage(100);
+ }
+ }
+
+ private int reportDataStoreInfo(DataStore ds) throws DataStoreException
+ {
+ int count = 0;
+ int total = 0;
+ Iterator it = ds.getAllIdentifiers();
+ while (it.hasNext())
+ {
+ count++;
+ DataIdentifier id = it.next();
+ total += ds.getRecord(id).getLength();
+ }
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("Datastore item count : ").append(count).append(" ");
+ sb.append("[total size : ").append(Math.round(total / 1024f)).append("Ko]");
+ _report.add(sb.toString());
+
+ return total;
+ }
+
+ /**
+ * Reclaiming unused space. This is derby specific.
+ * By default, Derby does not return unused space to the operating system
+ * when updating or deleting data.
+ * @param ds
+ * @throws RepositoryException
+ */
+ private void derbyCompressTable(DbDataStore ds) throws RepositoryException
+ {
+ DataSource dataSource = null;
+
+ try
+ {
+ ConnectionFactory cf = _repositoryConfig.getConnectionFactory();
+ if (ds.getDataSourceName() == null || "".equals(ds.getDataSourceName()))
+ {
+ dataSource = cf.getDataSource(ds.getDriver(), ds.getUrl(), ds.getUser(), ds.getPassword());
+ }
+ else
+ {
+ dataSource = cf.getDataSource(ds.getDataSourceName());
+ }
+ }
+ catch (SQLException e)
+ {
+ _report.addException(e);
+ throw new RuntimeException(e);
+ }
+
+ if (dataSource != null)
+ {
+ ConnectionHelper conHelper = new ConnectionHelper(dataSource, false);
+// String sql = "CALL SYSCS_UTIL.SYSCS_COMPRESS_TABLE(CURRENT SCHEMA, ?, 1)";
+ String sql = "CALL SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE(CURRENT SCHEMA, ?, 1, 1, 1)";
+ String prefix = ds.getTablePrefix();
+ String schemaObjPrefix = ds.getSchemaObjectPrefix();
+ String table = prefix + schemaObjPrefix + "DATASTORE";
+
+ try
+ {
+ conHelper.query(sql, table);
+ }
+ catch (SQLException e)
+ {
+ _report.addException(e);
+ throw new RuntimeException(e);
+ }
+ }
+ else
+ {
+ _report.add(TaskReportMessageType.ERROR, "Unable to compress the Derby datastore, unused space has not been freed up.");
+ }
+
+ }
+
+ @Override
+ public void beforeScanning(Node n) throws RepositoryException
+ {
+ _scannedNodesCount++;
+ if (_progress != null)
+ {
+ _progress.progress();
+ }
+ }
+
+ /**
+ * Retrieves JackRabbit Persistence Manager for currently opened repository. This method uses
+ * Privileged access and will fail with security exception if used in environment with enabled security manager.
+ *
+ * @return Persistence manager used by repository.
+ * @throws RepositoryException
+ */
+ private PersistenceManager getPM(String workspaceName) throws RepositoryException
+ {
+ try
+ {
+ Object workspaceInfo = findAndInvokeMethod(_repositoryContext.getRepository(), "getWorkspaceInfo", new Object[] {workspaceName});
+ return (PersistenceManager) (findAndInvokeMethod(workspaceInfo, "getPersistenceManager", null));
+ }
+ catch (Exception e)
+ {
+ throw new RepositoryException(e);
+ }
+ }
+
+ private static Object findAndInvokeMethod(Object obj, String name, Object[] parameters) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
+ {
+ Method m = null;
+ Method[] ms = obj.getClass().getDeclaredMethods();
+ for (int i = 0; i < ms.length; i++)
+ {
+ final Method x = ms[i];
+ if (x.getName().equals(name))
+ {
+ m = x;
+ break;
+ }
+ }
+ m.setAccessible(true);
+ return m.invoke(obj, parameters);
+ }
+}
Index: main/workspace-repository/src/org/ametys/workspaces/repository/MaintenanceTaskInfoGenerator.java
===================================================================
--- main/workspace-repository/src/org/ametys/workspaces/repository/MaintenanceTaskInfoGenerator.java (revision 0)
+++ main/workspace-repository/src/org/ametys/workspaces/repository/MaintenanceTaskInfoGenerator.java (revision 0)
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2010 Anyware Services
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ametys.workspaces.repository;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.generation.ServiceableGenerator;
+import org.apache.cocoon.xml.AttributesImpl;
+import org.apache.cocoon.xml.XMLUtils;
+import org.xml.sax.SAXException;
+
+import org.ametys.workspaces.repository.tasks.MaintenanceTaskManager;
+import org.ametys.workspaces.repository.tasks.core.TaskReport.TaskReportMessage;
+
+/**
+ * Generate informations about the running maintenance task progress.
+ */
+public class MaintenanceTaskInfoGenerator extends ServiceableGenerator
+{
+ /** The maintenance task manager */
+ private MaintenanceTaskManager _taskManager;
+
+ @Override
+ public void service(ServiceManager m) throws ServiceException
+ {
+ super.service(m);
+ _taskManager = (MaintenanceTaskManager) m.lookup(MaintenanceTaskManager.ROLE);
+ }
+
+ public void generate() throws IOException, SAXException, ProcessingException
+ {
+ Map jsParameters = (Map) objectModel.get(ObjectModelHelper.PARENT_CONTEXT);
+
+ contentHandler.startDocument();
+
+ // Is task finished ?
+ AttributesImpl attrs = new AttributesImpl();
+ attrs.addCDATAAttribute("isRunning", Boolean.toString(_taskManager.isTaskRunning()));
+ XMLUtils.startElement(contentHandler, "task", attrs);
+
+ // Sax report
+ int part = (Integer) jsParameters.get("part");
+ List report = new ArrayList();
+ Integer lastPart = _taskManager.getReportMessage(report, part);
+ if (lastPart != null)
+ {
+ _saxReport(report, lastPart);
+ }
+
+ // Sax progress
+ _saxProgress(_taskManager.getProgressInfo());
+
+ // Sax repository state
+ _saxRepositoryState();
+
+ XMLUtils.endElement(contentHandler, "task");
+ contentHandler.endDocument();
+ }
+
+ private void _saxRepositoryState() throws SAXException
+ {
+ AttributesImpl attrs = new AttributesImpl();
+ attrs.addCDATAAttribute("state", Boolean.toString(_taskManager.isRepositoryStarted()));
+ XMLUtils.createElement(contentHandler, "repository", attrs);
+ }
+
+ private void _saxProgress(Map progress) throws SAXException
+ {
+ if (progress != null && !progress.isEmpty())
+ {
+ AttributesImpl attrs = new AttributesImpl();
+ attrs.addCDATAAttribute("progress", progress.get("progress"));
+ attrs.addCDATAAttribute("total", progress.get("total"));
+ attrs.addCDATAAttribute("percentage", progress.get("percentage"));
+ attrs.addCDATAAttribute("state", progress.get("state"));
+
+ XMLUtils.createElement(contentHandler, "progress", attrs);
+ }
+ }
+
+ private void _saxReport(List report, int lastPart) throws SAXException
+ {
+ AttributesImpl attrs = new AttributesImpl();
+ attrs.addCDATAAttribute("lastPart", String.valueOf(lastPart));
+ XMLUtils.startElement(contentHandler, "report", attrs);
+
+ for (TaskReportMessage reportMessage : report)
+ {
+ AttributesImpl attrs_msg = new AttributesImpl();
+ attrs_msg.addCDATAAttribute("type", reportMessage.getTaskReportMessageType().name().toLowerCase());
+ XMLUtils.createElement(contentHandler, "entry", attrs_msg, reportMessage.getMessage());
+ }
+
+ XMLUtils.endElement(contentHandler, "report");
+ }
+
+}
Index: main/workspace-repository/src/org/ametys/workspaces/repository/MaintenanceLaunchTaskAction.java
===================================================================
--- main/workspace-repository/src/org/ametys/workspaces/repository/MaintenanceLaunchTaskAction.java (revision 0)
+++ main/workspace-repository/src/org/ametys/workspaces/repository/MaintenanceLaunchTaskAction.java (revision 0)
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2010 Anyware Services
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ametys.workspaces.repository;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.thread.ThreadSafe;
+import org.apache.cocoon.acting.ServiceableAction;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.SourceResolver;
+
+import org.ametys.workspaces.repository.tasks.MaintenanceTaskManager;
+import org.ametys.workspaces.repository.tasks.MaintenanceTaskManager.MaintenanceTaskType;
+
+/**
+ * Action that launch a maintenance task.
+ */
+public class MaintenanceLaunchTaskAction extends ServiceableAction implements ThreadSafe
+{
+ /** The maintenance task manager */
+ private MaintenanceTaskManager _taskManager;
+
+ @Override
+ public void service(ServiceManager m) throws ServiceException
+ {
+ super.service(m);
+ _taskManager = (MaintenanceTaskManager) m.lookup(MaintenanceTaskManager.ROLE);
+ }
+
+ public Map act(Redirector redirector, SourceResolver sourceResolver, Map objectModel, String source, Parameters parameters) throws Exception
+ {
+ Map result = new HashMap();
+ Map jsParameters = (Map) objectModel.get(ObjectModelHelper.PARENT_CONTEXT);
+
+ String task = (String) jsParameters.get("task");
+
+ // Launch the task
+ try
+ {
+ boolean launched = _taskManager.launch(MaintenanceTaskType.valueOf(task));
+ if (launched)
+ {
+ result.put("launched", "true");
+ }
+ }
+ catch (Exception e)
+ {
+ getLogger().error(e.getMessage(), e);
+ }
+
+ return result;
+ }
+}
Index: main/workspace-repository/sitemap.xmap
===================================================================
--- main/workspace-repository/sitemap.xmap (revision 19543)
+++ main/workspace-repository/sitemap.xmap (working copy)
@@ -36,6 +36,9 @@
+
+
+
@@ -54,8 +57,13 @@
+
+
+
+
+
-
+
@@ -375,6 +383,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: main/workspace-repository/resources/css/home.css
===================================================================
--- main/workspace-repository/resources/css/home.css (revision 19543)
+++ main/workspace-repository/resources/css/home.css (working copy)
@@ -391,3 +391,77 @@
font-weight: normal;
}
+/* @end Admin */
+
+/** @ Maintenance panel */
+.maintenance-intro-title {
+ color:#15428B;
+ font-family:tahoma,arial,verdana,sans-serif !important;
+ font-size:11px !important;
+ font-weight:bold !important;
+}
+
+.maintenance-hint-panel {
+ background-color:#E8F2FF;
+ border:1px solid #A9BFD3 !important;
+ color:#404040;
+ font-size:0.9em;
+ font-style:italic;
+ padding:5px;
+}
+
+.maintenance-hint-panel .x-panel-body {
+ background-color: #e8f2ff !important;
+ background-image: url('../img/help.png');
+ background-repeat: no-repeat;
+ padding-left: 20px;
+}
+
+#maintenance-progress-panel {
+}
+
+#maintenance-report-panel {
+ padding-top: 1px;
+ padding-left: 5px;
+ font-size:0.9em;
+ font-style: italic;
+}
+#maintenance-report-panel .x-panel {
+ margin: 2px;
+}
+
+#maintenance-report-panel .report-info {
+ color: #404040;
+}
+#maintenance-report-panel .report-warn {
+ color: #ee4000;
+}
+#maintenance-report-panel .report-error {
+ font-weight: bold;
+ font-style: normal;
+ color: #ee4000;
+}
+
+#maintenance-tab .garbage-collector_btn {
+ background-image: url('../img/maintenance/garbage_collector_64.png') !important;
+ width:64px !important;
+ height:64px !important;
+ margin-right: auto !important;
+ margin-left: auto !important;
+}
+#maintenance-tab .reindexing_btn {
+ background-image: url('../img/maintenance/reindexing_64.png') !important;
+ width:64px !important;
+ height:64px !important;
+ margin-right: auto !important;
+ margin-left: auto !important;
+}
+#maintenance-tab .consistency-check_btn {
+ background-image: url('../img/maintenance/consistency_check_64.png') !important;
+ width:64px !important;
+ height:64px !important;
+ margin-right: auto !important;
+ margin-left: auto !important;
+}
+
+/* @end Maintenance */
Index: main/workspace-repository/resources/js/org/ametys/repository/HomePage.i18n.js
===================================================================
--- main/workspace-repository/resources/js/org/ametys/repository/HomePage.i18n.js (revision 19543)
+++ main/workspace-repository/resources/js/org/ametys/repository/HomePage.i18n.js (working copy)
@@ -36,6 +36,7 @@
}
org.ametys.repository.HomePage._tabs.push(new org.ametys.repository.AdminTab());
+ org.ametys.repository.HomePage._tabs.push(new org.ametys.repository.MaintenanceTab());
org.ametys.repository.HomePage._tabs.push(new org.ametys.repository.ConsoleTab());
}
Index: main/workspace-repository/resources/js/org/ametys/repository/MaintenanceTab.i18n.js
===================================================================
--- main/workspace-repository/resources/js/org/ametys/repository/MaintenanceTab.i18n.js (revision 0)
+++ main/workspace-repository/resources/js/org/ametys/repository/MaintenanceTab.i18n.js (revision 0)
@@ -0,0 +1,476 @@
+/*
+ * Copyright 2012 Anyware Services
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+Ext.namespace('org.ametys.repository');
+
+/**
+ * Create the panel
+ */
+org.ametys.repository.MaintenanceTab = function ()
+{
+ var me = this;
+
+ // The task that will be handled by the Ext.TaskMgr
+ this._taskInfoTask = {
+ run: me._getTaskInfo,
+ scope: me,
+ interval: 1000
+ };
+
+ // Contains this task ID of the running task, null if there is no task running.
+ this._runningTask = null;
+ // True if the last server response was a bad response.
+ this._badServerResponse = false;
+ // Indicates the last report part that has been displayed to the client (~ report cursor)
+ this._currentReportPart = 0;
+
+ // 1 - Tool bar = title + repository state
+ this._repositoryStateBtn = new Ext.Button({
+ icon: getWorkspaceResources() + '/img/maintenance/repository_on_16.png',
+ tooltip: "Repo state tootltip"
+ });
+ var tbar = [
+ new Ext.form.Label({
+ text: "Taches de maintenance",
+ cls: "maintenance-intro-title"
+ }),
+ '->',
+ this._repositoryStateBtn
+ ];
+
+ // 2 -Hint panel
+ this._hintPanel = new Ext.Panel ({
+ border: false,
+ cls: 'maintenance-hint-panel',
+ html: ""
+ });
+
+ // 3 - Tasks buttons panel (menu)
+ var taskBtns = [];
+ for (var task in this.Tasks)
+ {
+ taskBtns.push({
+ xtype:'button',
+ tooltip: this.Tasks[task].text,
+ iconCls: this.Tasks[task].buttonCls,
+ disabled: true,
+ scope: me,
+ handler: me._launchTask,
+ itemId: task
+ });
+ }
+
+ this._taskButtonsPanel = new Ext.Toolbar ({
+ layout: 'hbox',
+ layoutConfig: {
+ align: 'stretch'
+ },
+ height: 75,
+
+ items: [
+ {
+ xtype : 'spacer',
+ flex: 0.5
+ },
+ taskBtns[0],
+ {
+ xtype : 'spacer',
+ flex: 1
+ },
+ taskBtns[1],
+ {
+ xtype : 'spacer',
+ flex: 1
+ },
+ taskBtns[2],
+ {
+ xtype : 'spacer',
+ flex: 0.5
+ }
+ ]
+ });
+
+ // 4 - Progress bar
+ this._progressBar = new Ext.ProgressBar();
+ this._progressPanel = new Ext.Panel ({
+ layout: 'fit',
+ border: false,
+ id: 'maintenance-progress-panel',
+ items: this._progressBar
+ });
+ this._progressPanel.hide();
+
+ // 5 - Report panel
+ this._reportPanel = new Ext.Panel ({
+ id: 'maintenance-report-panel',
+ border : false,
+ anchor : '-20'
+ });
+ var reportPanelContainer = new Ext.Panel ({
+ layout: 'anchor',
+ border: false,
+ flex: 1,
+ autoScroll: true,
+ items : [
+ this._reportPanel
+ ]
+ });
+
+ // Task panel = progress + report
+ this._taskPanel = new Ext.Panel ({
+ border: false,
+ layout: 'vbox',
+ layoutConfig: {
+ align: 'stretch'
+ },
+ flex: 1,
+ items: [
+ this._progressPanel,
+ reportPanelContainer
+ ]
+ });
+
+ // Returns the global panel : Toolbar + hint panel + task buttons panel + task panel
+ return new Ext.Panel ({
+ title: "",
+ id: 'maintenance-tab',
+ region: 'center',
+ border: true,
+ layout: 'vbox',
+ layoutConfig: {
+ align: 'stretch'
+ },
+
+ tbar : tbar,
+
+ items: [
+ this._hintPanel,
+ this._taskButtonsPanel,
+ this._taskPanel
+ ],
+
+ listeners: {
+ 'beforeshow': {
+ fn: me._onBeforeShow,
+ scope: me
+ },
+ 'beforehide' : {
+ fn: me._onBeforeHide,
+ scope: me
+ }
+ }
+ });
+};
+
+/**
+ * Enumeration of the available tasks
+ */
+org.ametys.repository.MaintenanceTab.prototype.Tasks = {
+ DATA_STORE_GARBAGE_COLLECTOR : {
+ text: "",
+ buttonCls: "garbage-collector_btn"
+ },
+ REINDEXING : {
+ text: "",
+ buttonCls: "reindexing_btn"
+ },
+ CONSISTENCY_CHECK : {
+ text: "",
+ buttonCls: "consistency-check_btn"
+ }
+}
+
+org.ametys.repository.MaintenanceTab.prototype._onBeforeShow = function()
+{
+ // Disable task btns
+ this._disableTaskBtns(true);
+
+ // Launch task request.
+ var serverMessage = new org.ametys.servercomm.ServerMessage(
+ '_' + context.workspaceName,
+ '/repository/maintenance/is-running-task',
+ null,
+ org.ametys.servercomm.ServerComm.PRIORITY_MAJOR,
+ this._onBeforeShowCb,
+ this,
+ null);
+ org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
+}
+
+org.ametys.repository.MaintenanceTab.prototype._onBeforeShowCb = function(response, args)
+{
+ if (org.ametys.servercomm.ServerComm.handleBadResponse("", response, "org.ametys.repository.MaintenanceTab._onBeforeShow"))
+ {
+ this._addReportErrorLog("");
+ return;
+ }
+
+ // Update repository state
+ var task = response.selectSingleNode("task");
+ this._updateRepositoryState(task.getAttribute("repository-state") === "true");
+
+ // Ajax requests are launched if a task is currently running;
+ // or if a task has finished but a part of the report was already displayed on the screen,
+ // the end of the report must be displayed.
+ if (task.getAttribute("running") === "true" || this._currentReportPart > 0)
+ {
+ if (task.getAttribute("running") === "true")
+ {
+ this._runningTask = task[org.ametys.servercomm.ServerComm.xmlTextContent];
+ this._taskButtonsPanel.getComponent(this._runningTask).toggle(true);
+ }
+
+ if (!this._progressPanel.isVisible())
+ {
+ this._progressPanel.show();
+ this._taskPanel.doLayout();
+ }
+
+ if (this._currentReportPart === 0)
+ {
+ this._reportPanel.removeAll();
+ }
+
+ // Ajax request : get task progress info every {interval} ms.
+ this._stopGetTaskInfo = false;
+ Ext.TaskMgr.start(this._taskInfoTask);
+ }
+ else
+ {
+ this._disableTaskBtns(false);
+ this._runningTask = null;
+ this._currentReportPart = 0;
+ }
+}
+
+org.ametys.repository.MaintenanceTab.prototype._onBeforeHide = function()
+{
+ // This will stop the Ext.TaskMgr
+ // Workaround because the Ext.TaskMgr.stop(this._taskInfoTask); was not working as excepted.
+ this._stopGetTaskInfo = true;
+}
+
+org.ametys.repository.MaintenanceTab.prototype._disableTaskBtns = function (disable)
+{
+ for (var task in this.Tasks)
+ {
+ var taskBtn = this._taskButtonsPanel.getComponent(task);
+ if (disable)
+ {
+ taskBtn.disable();
+ }
+ else
+ {
+ taskBtn.enable();
+ taskBtn.toggle(false);
+ }
+ }
+}
+
+org.ametys.repository.MaintenanceTab.prototype._launchTask = function (btn, e)
+{
+ if (this._runningTask)
+ {
+ return;
+ }
+
+ btn.toggle(true);
+
+ this._runningTask = btn.itemId; // itemId of the btn contains the taskId.
+ this._disableTaskBtns(true);
+ this._reportPanel.removeAll();
+
+ if (!this._progressPanel.isVisible())
+ {
+ this._progressPanel.show();
+ this._taskPanel.doLayout();
+ }
+
+ // Launch task request.
+ var serverMessage = new org.ametys.servercomm.ServerMessage(
+ '_' + context.workspaceName,
+ '/repository/maintenance/launch-task',
+ {task : this._runningTask},
+ org.ametys.servercomm.ServerComm.PRIORITY_MAJOR,
+ this._launchTaskCb,
+ this,
+ null);
+ org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
+};
+
+org.ametys.repository.MaintenanceTab.prototype._launchTaskCb = function (response, args)
+{
+ // Handle error
+ if (org.ametys.servercomm.ServerComm.handleBadResponse("", response, "org.ametys.repository.MaintenanceTab._launchTask"))
+ {
+ this._addReportErrorLog("");
+ return;
+ }
+
+ // Update repository state
+ var task = response.selectSingleNode("task");
+ this._updateRepositoryState(task.getAttribute("repository-state") === "true");
+
+ var isLaunched = task.getAttribute("launched") === "true";
+ if (!isLaunched)
+ {
+ this._onTaskFinished();
+ this._addReportErrorLog("");
+ this._addReportLog("");
+ return;
+ }
+
+ // Ajax request : get task progress info every {interval} ms.
+ this._stopGetTaskInfo = false;
+ Ext.TaskMgr.start(this._taskInfoTask);
+}
+
+org.ametys.repository.MaintenanceTab.prototype._getTaskInfo = function(count)
+{
+ if (this._stopGetTaskInfo === true)
+ {
+ return false; // Ext.TaskMgr will stop the running task.
+ }
+
+ // Get task progress info.
+ var serverMessage = new org.ametys.servercomm.ServerMessage(
+ '_' + context.workspaceName,
+ '/repository/maintenance/get-task-info',
+ {part : this._currentReportPart},
+ org.ametys.servercomm.ServerComm.PRIORITY_MAJOR,
+ this._getTaskInfoCb,
+ this,
+ null);
+ org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
+
+ return true; // Ext.TaskMgr will continue to run the task while this._stopGetTaskInfo is not true.
+}
+
+org.ametys.repository.MaintenanceTab.prototype._onTaskFinished = function ()
+{
+ this._disableTaskBtns(false);
+ this._runningTask = null;
+ this._badServerResponse = false;
+ this._currentReportPart = 0;
+}
+
+org.ametys.repository.MaintenanceTab.prototype._getTaskInfoCb = function(response, args)
+{
+ // Prevents client from being spammed with error dialogs / error logs
+ if (this._badServerResponse && (response == null || response.getAttribute("code") == "500" || response.getAttribute("code") == "404"))
+ {
+ return;
+ }
+
+ // Handle error
+ if (org.ametys.servercomm.ServerComm.handleBadResponse("", response, "org.ametys.repository.MaintenanceTab._getTaskInfo"))
+ {
+ this._addReportWarningLog("");
+ this._badServerResponse = true;
+ return;
+ }
+
+ this._badServerResponse = false;
+ var task = response.selectSingleNode("task");
+
+ // Update progress bar
+ var progress = task.selectSingleNode("progress");
+ this._updateProgressBar(progress);
+
+ // Update report panel
+ var report = task.selectSingleNode("report");
+ this._addReportLogsFromXML(report);
+
+ // Update repository state
+ var repository = task.selectSingleNode("repository");
+ this._updateRepositoryState(repository.getAttribute("state") === "true");
+
+ // End task if needed.
+ if (task.getAttribute("isRunning") !== "true")
+ {
+ this._onTaskFinished();
+ this._stopGetTaskInfo = true;
+ // Ext.TaskMgr.stop(this._taskInfoTask);
+ }
+}
+
+org.ametys.repository.MaintenanceTab.prototype._updateProgressBar = function(progress)
+{
+ if (!progress) return;
+
+ var current = progress.getAttribute("progress");
+ var total = progress.getAttribute("total");
+ var percentage = progress.getAttribute("percentage");
+ var progressLabel = "" + Math.round(100 * percentage) + "";
+ this._progressBar.updateProgress(percentage, progressLabel, false);
+}
+
+org.ametys.repository.MaintenanceTab.prototype._addReportLogsFromXML = function(report)
+{
+ // Update this._currentReportPart
+ var lastPart = Number(report.getAttribute("lastPart"));
+ this._currentReportPart = !!lastPart ? lastPart : this._currentReportPart;
+
+ // Add logs to the report panel.
+ var logs = report.selectNodes("entry");
+ if(logs)
+ {
+ var log;
+ for (var i=0; i < logs.length; i++)
+ {
+ log = logs[i];
+ this._addReportLog(log[org.ametys.servercomm.ServerComm.xmlTextContent], log.getAttribute("type"), true);
+ }
+ this._reportPanel.doLayout();
+ }
+}
+
+org.ametys.repository.MaintenanceTab.prototype._addReportLog = function(log, type, skipDoLayout)
+{
+ this._reportPanel.add({
+ border: false,
+ html: log,
+ cls: 'report-' + (type || "info")
+ });
+
+ if (skipDoLayout !== true)
+ {
+ this._reportPanel.doLayout();
+ }
+}
+
+org.ametys.repository.MaintenanceTab.prototype._addReportWarningLog = function(log)
+{
+ this._addReportLog(log, "warn");
+}
+org.ametys.repository.MaintenanceTab.prototype._addReportErrorLog = function(log)
+{
+ this._addReportLog(log, "error");
+}
+
+org.ametys.repository.MaintenanceTab.prototype._updateRepositoryState = function(on)
+{
+ var state = on ? 'on' : 'off';
+
+ if (this._repositoryState !== state)
+ {
+ this._repositoryState = state;
+ var iconPath = getWorkspaceResources() + '/img/maintenance/repository_' + state + '_16.png'
+ this._repositoryStateBtn.setIcon(iconPath);
+ }
+
+}
+
Index: main/workspace-repository/resources/js/org/ametys/repository/Actions.i18n.js
===================================================================
--- main/workspace-repository/resources/js/org/ametys/repository/Actions.i18n.js (revision 19543)
+++ main/workspace-repository/resources/js/org/ametys/repository/Actions.i18n.js (working copy)
@@ -1281,9 +1281,10 @@
mainPanel.remove(mainPanel.items.get(0));
mainPanel.remove(mainPanel.items.get(0));
mainPanel.remove(mainPanel.items.get(0));
+ mainPanel.remove(mainPanel.items.get(0));
mainPanel.doLayout();
- mainPanel.add([new org.ametys.repository.JCRViewTab(true), new org.ametys.repository.AdminTab(), new org.ametys.repository.ConsoleTab()]);
+ mainPanel.add([new org.ametys.repository.JCRViewTab(true), new org.ametys.repository.AdminTab(), new org.ametys.repository.MaintenanceTab(), new org.ametys.repository.ConsoleTab()]);
mainPanel.setActiveTab(0);
}
\ No newline at end of file
Index: main/plugin-repositoryapp/plugin.xml
===================================================================
--- main/plugin-repositoryapp/plugin.xml (revision 19543)
+++ main/plugin-repositoryapp/plugin.xml (working copy)
@@ -93,4 +93,12 @@
+
+
+
+
+
+