
org.xwiki.extension.job.internal.AbstractExtensionJob Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xwiki-commons-extension-api Show documentation
Show all versions of xwiki-commons-extension-api Show documentation
XWiki Commons - Extension - API
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.extension.job.internal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import org.xwiki.extension.Extension;
import org.xwiki.extension.ExtensionDependency;
import org.xwiki.extension.ExtensionException;
import org.xwiki.extension.ExtensionId;
import org.xwiki.extension.InstallException;
import org.xwiki.extension.InstalledExtension;
import org.xwiki.extension.LocalExtension;
import org.xwiki.extension.UninstallException;
import org.xwiki.extension.event.ExtensionInstalledEvent;
import org.xwiki.extension.event.ExtensionUninstalledEvent;
import org.xwiki.extension.event.ExtensionUpgradedEvent;
import org.xwiki.extension.handler.ExtensionHandlerManager;
import org.xwiki.extension.job.ExtensionRequest;
import org.xwiki.extension.job.InstallRequest;
import org.xwiki.extension.job.plan.ExtensionPlanAction;
import org.xwiki.extension.job.plan.ExtensionPlanAction.Action;
import org.xwiki.extension.repository.InstalledExtensionRepository;
import org.xwiki.extension.repository.LocalExtensionRepository;
import org.xwiki.job.AbstractJob;
import org.xwiki.job.GroupedJob;
import org.xwiki.job.JobGroupPath;
import org.xwiki.job.Request;
import org.xwiki.job.event.status.JobStatus;
import org.xwiki.logging.marker.BeginTranslationMarker;
import org.xwiki.logging.marker.EndTranslationMarker;
import org.xwiki.logging.marker.TranslationMarker;
/**
* Base class for any Job dealing with extensions.
*
* @param the type of the request
* @param the type of the {@link org.xwiki.job.event.status.JobStatus}
* @version $Id: ae347526184f0856a0dce0f6ef538a60839a8759 $
* @since 4.0M1
*/
public abstract class AbstractExtensionJob extends AbstractJob
implements GroupedJob
{
/**
* The key to use to access the context extension plan.
*/
public static final String CONTEXTKEY_PLAN = "job.extension.plan";
/**
* The root group of all extension related jobs.
*/
public static final JobGroupPath ROOT_GROUP = new JobGroupPath(Arrays.asList("extension"));
/**
* Used to manipulate local extension repository.
*/
@Inject
protected LocalExtensionRepository localExtensionRepository;
/**
* Used to install the extension itself depending of its type.
*/
@Inject
protected ExtensionHandlerManager extensionHandlerManager;
/**
* Used to manipulate installed extension repository.
*/
@Inject
protected InstalledExtensionRepository installedExtensionRepository;
protected JobGroupPath groupPath;
@Override
public void initialize(Request request)
{
super.initialize(request);
// Build the group path
if (getRequest().getNamespaces() != null && getRequest().getNamespaces().size() == 1) {
this.groupPath = new JobGroupPath(getRequest().getNamespaces().iterator().next(), ROOT_GROUP);
} else {
this.groupPath = ROOT_GROUP;
}
}
@Override
public JobGroupPath getGroupPath()
{
return this.groupPath;
}
private static TranslationMarker getTranslationMarker(ExtensionPlanAction action, String extension, boolean begin)
{
StringBuilder str = new StringBuilder("extension.log.job.");
str.append(action.getAction().toString().toLowerCase());
if (extension != null) {
str.append('.');
str.append(extension);
}
str.append('.');
str.append(begin ? "begin" : "end");
if (action.getNamespace() != null) {
str.append("OnNamespace");
}
return begin ? new BeginTranslationMarker(str.toString()) : new EndTranslationMarker(str.toString());
}
/**
* @param actions the actions to apply
* @throws ExtensionException failed to apply action
*/
protected void applyActions(Collection actions) throws ExtensionException
{
this.progressManager.pushLevelProgress(actions.size(), this);
try {
for (ExtensionPlanAction action : actions) {
this.progressManager.startStep(this);
if (action.getAction() != Action.NONE) {
applyAction(action);
}
}
} finally {
this.progressManager.popLevelProgress(this);
}
}
/**
* @param action the action to perform
* @throws ExtensionException failed to apply action
*/
protected void applyAction(ExtensionPlanAction action) throws ExtensionException
{
Collection previousExtensions = action.getPreviousExtensions();
Extension extension = action.getExtension();
String namespace = action.getNamespace();
List previousExtensionsIds;
if (getRequest().isVerbose()) {
previousExtensionsIds = new ArrayList(previousExtensions.size());
for (InstalledExtension previousExtension : previousExtensions) {
previousExtensionsIds.add(previousExtension.getId());
}
this.logger.info(getTranslationMarker(action, null, true),
"Applying [{}] for extension [{}] on namespace [{}] from previous extension [{}]", action.getAction(),
extension.getId(), namespace, previousExtensionsIds);
} else {
previousExtensionsIds = null;
}
try {
if (action.getAction() == Action.REPAIR) {
InstalledExtension installedExtension = (InstalledExtension) action.getExtension();
// Initialize
repairExtension(installedExtension, namespace);
} else if (action.getAction() == Action.UNINSTALL) {
InstalledExtension installedExtension = (InstalledExtension) action.getExtension();
// Uninstall
uninstallExtension(installedExtension, namespace);
} else {
// Store extension in local repository
LocalExtension localExtension = this.localExtensionRepository.resolve(extension.getId());
// Install
installExtension(localExtension, previousExtensions, namespace, action.isDependency());
}
if (getRequest().isVerbose()) {
this.logger.info(getTranslationMarker(action, "success", false),
"Successfully applied [{}] for extension [{}] on namespace [{}] from previous extension [{}]",
action.getAction(), extension.getId(), namespace, previousExtensionsIds);
}
} catch (ExtensionException e) {
if (getRequest().isVerbose()) {
this.logger.error(getTranslationMarker(action, "failure", false),
"Failed to apply [{}] for extension [{}] on namespace [{}] from previous extension [{}]",
action.getAction(), extension.getId(), namespace, previousExtensionsIds, e);
}
throw e;
}
}
/**
* @param installedExtension the existing extension
* @param namespace the namespace in which to perform the action
* @throws ExtensionException failed to initialize extension
*/
private void repairExtension(InstalledExtension installedExtension, String namespace) throws ExtensionException
{
// Initialize extension (invalid extensions are not initialized at startup)
this.extensionHandlerManager.initialize(installedExtension, namespace);
// Repair the extension
this.installedExtensionRepository.installExtension(installedExtension, namespace,
installedExtension.isDependency(namespace));
this.observationManager.notify(new ExtensionUninstalledEvent(installedExtension.getId(), namespace),
installedExtension);
}
/**
* @param installedExtension the existing extension
* @param namespace the namespace in which to perform the action
* @throws UninstallException failed to uninstall extension
*/
private void uninstallExtension(InstalledExtension installedExtension, String namespace) throws UninstallException
{
// Unload extension
this.extensionHandlerManager.uninstall(installedExtension, namespace, getRequest());
// Uninstall from local repository
this.installedExtensionRepository.uninstallExtension(installedExtension, namespace);
this.observationManager.notify(new ExtensionUninstalledEvent(installedExtension.getId(), namespace),
installedExtension);
}
/**
* @param extension the extension
* @param previousExtensions the previous extensions when upgrading
* @param namespace the namespace in which to perform the action
* @param dependency indicate if the extension has been installed as dependency
* @throws InstallException failed to install extension
*/
private void installExtension(LocalExtension extension, Collection previousExtensions,
String namespace, boolean dependency) throws InstallException
{
Map properties = getRequest().getProperty(InstallRequest.PROPERTY_EXTENSION_PROPERTIES,
Collections.emptyMap());
if (previousExtensions.isEmpty()) {
this.extensionHandlerManager.install(extension, namespace, getRequest());
InstalledExtension installedExtension =
this.installedExtensionRepository.installExtension(extension, namespace, dependency, properties);
this.observationManager.notify(new ExtensionInstalledEvent(extension.getId(), namespace),
installedExtension);
} else {
this.extensionHandlerManager.upgrade(previousExtensions, extension, namespace, getRequest());
for (InstalledExtension previousExtension : previousExtensions) {
try {
this.installedExtensionRepository.uninstallExtension(previousExtension, namespace);
} catch (UninstallException e) {
this.logger.error("Failed to uninstall extension [" + previousExtension.getId() + "]", e);
}
}
InstalledExtension installedExtension =
this.installedExtensionRepository.installExtension(extension, namespace, dependency, properties);
this.observationManager.notify(new ExtensionUpgradedEvent(extension.getId(), namespace), installedExtension,
previousExtensions);
}
}
protected Map append(Map managedDependencies,
Extension extension)
{
Map newManagedDependencies =
managedDependencies != null ? new HashMap<>(managedDependencies) : new HashMap<>();
for (ExtensionDependency dependency : extension.getManagedDependencies()) {
newManagedDependencies.put(dependency.getId(), dependency);
}
return newManagedDependencies;
}
protected ExtensionDependency getDependency(ExtensionDependency dependency,
Map managedDependencies, Extension extension)
{
ExtensionDependency managedDependency = managedDependencies.get(dependency.getId());
// If the dependency does not have any version try to find it in extension managed dependencies
if (managedDependency == null && dependency.getVersionConstraint() == null) {
for (ExtensionDependency extensionManagedDependency : extension.getManagedDependencies()) {
if (extensionManagedDependency.getId().equals(dependency.getId())) {
managedDependency = extensionManagedDependency;
}
}
}
return managedDependency != null ? managedDependency : dependency;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy