
org.ow2.bonita.deployment.Deployer Maven / Gradle / Ivy
/**
* Copyright (C) 2007 Bull S. A. S.
* Bull, Rue Jean Jaures, B.P.68, 78340, Les Clayes-sous-Bois
* This library 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
* version 2.1 of the License.
* This library 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
* program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301, USA.
**/
package org.ow2.bonita.deployment;
import java.io.ByteArrayInputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.ow2.bonita.pvm.internal.xml.Parse;
import org.ow2.bonita.definition.Hook;
import org.ow2.bonita.definition.PackageClassData;
import org.ow2.bonita.definition.TxHook;
import org.ow2.bonita.definition.XpdlProcess;
import org.ow2.bonita.facade.def.element.BusinessArchive;
import org.ow2.bonita.facade.def.element.HookDefinition;
import org.ow2.bonita.facade.def.element.Resource;
import org.ow2.bonita.facade.def.majorElement.ActivityDefinition;
import org.ow2.bonita.facade.def.majorElement.PackageFullDefinition;
import org.ow2.bonita.facade.def.majorElement.ProcessDefinition;
import org.ow2.bonita.facade.def.majorElement.ProcessFullDefinition;
import org.ow2.bonita.facade.def.majorElement.impl.ProcessDefinitionImpl;
import org.ow2.bonita.facade.exception.DeploymentException;
import org.ow2.bonita.facade.uuid.PackageDefinitionUUID;
import org.ow2.bonita.parsing.XpdlParser;
import org.ow2.bonita.runtime.ClassDataLoader;
import org.ow2.bonita.runtime.XpdlInstance;
import org.ow2.bonita.services.Querier;
import org.ow2.bonita.services.Recorder;
import org.ow2.bonita.services.Repository;
import org.ow2.bonita.services.handlers.UndeployedPackageHandler;
import org.ow2.bonita.util.ClassDataTool;
import org.ow2.bonita.util.EngineEnvTool;
import org.ow2.bonita.util.Misc;
/**
* takes {@link BusinessArchiveImpl}s as input, parses the contents into a
* {@link WorkflowProcess}, stores the {@link WorkflowProcess} into the
* {@link Repository}
*
*
* This class is thread safe and can be shared between multiple threads.
*
*
* @author Marc Blachon, Guillaume Porcher, Charles Souillard, Miguel Valdes, Pierre Vigneras
*/
public final class Deployer {
private static final Logger LOG = Logger.getLogger(Deployer.class.getName());
private Deployer() { }
public static Map deploy(final BusinessArchive businessArchive,
final String userId) throws DeploymentException {
PackageFullDefinition packageDef = null;
try {
final Parse parse = new XpdlParser().createParse();
parse.pushObject(businessArchive);
// now the xpdl process will be parsed.
Resource xpdlResource = businessArchive.getXpdlFile();
if (xpdlResource == null) {
throw new DeploymentException("The given bar archive does not contain any '.xpdl' file");
}
final byte[] xpdl = xpdlResource.getData();
final Collection classes = businessArchive.getClasses();
final Repository repository = EngineEnvTool.getRepository();
// make the xpdl package is available to the xpdl parser by pushing it
// on the object stack in the parse.
parse.setInputStream(new ByteArrayInputStream(xpdl));
packageDef = (PackageFullDefinition) parse.execute().getDocumentObject();
//CHECK EVERYTING IS OK
parse.checkProblems("xpdl file");
parse.popObject();
Misc.badStateIfNull(packageDef, "Ouch! The returned PackageFullDefinition is null!");
final Set processes = packageDef.getProcesses();
//check package and processes versions are OK
final String packageId = packageDef.getPackageId();
final String packageVersion = packageDef.getVersion();
final String lastPackageVersion = repository.getLastPackageVersion(packageId);
if (lastPackageVersion != null && lastPackageVersion.compareTo(packageVersion) >= 0) {
throw new DeploymentRuntimeException("A package with id = " + packageId + " has already been deployed with version = "
+ lastPackageVersion + ". The given package version (" + packageVersion
+ ") is not lexicographically upper. Please change the version of the package to follow "
+ lastPackageVersion);
}
for (final ProcessFullDefinition process : processes) {
final String processId = process.getProcessId();
final String processVersion = process.getVersion();
final String lastProcessVersion = repository.getLastProcessVersion(processId, packageId);
if (lastProcessVersion != null && lastProcessVersion.compareTo(processVersion) >= 0) {
throw new DeploymentRuntimeException("A process with id = " + processId
+ " has already been deployed with version = "
+ lastProcessVersion + ". The given process version (" + processVersion
+ ") is not lexicographically upper. Please change the version of the process to follow "
+ lastProcessVersion);
}
}
final Map map = new HashMap();
if (classes != null) {
try {
for (final Resource resource : classes) {
final byte[] data = resource.getData();
final ClassDataTool.MyVisitor mv = ClassDataTool.visitClass(data);
final String className = mv.getClassName();
map.put(className, data);
}
} catch (final ArrayIndexOutOfBoundsException e) {
throw new DeploymentRuntimeException("Error when getting class names from classes (Set of byte[]).");
}
}
final PackageClassData packageClassData = new PackageClassData(packageDef.getUUID(), map);
repository.storePackageClassData(packageClassData);
for (final String className : map.keySet()) {
Class< ? > clazz = null;
try {
clazz = ClassDataLoader.getClass(packageDef.getUUID(), className);
} catch (final ClassNotFoundException e) {
throw new DeploymentRuntimeException("Problem while deploying package: " + packageId
+ ". No Class available with classname: " + className);
}
if (TxHook.class.isAssignableFrom(clazz)) {
for (final ProcessFullDefinition process : processes) {
final Set activities = process.getActivities();
if (activities != null) {
for (final ActivityDefinition activity : activities) {
final Set hooks = activity.getHooks();
if (hooks != null) {
for (final HookDefinition hookDef : hooks) {
if (hookDef.getClassName().equals(className) && !hookDef.isThrowingException()) {
throw new DeploymentRuntimeException("Hook : " + hookDef.getClassName() + " defined on activity "
+ activity.getActivityId() + " in process " + process.getProcessId()
+ " is defined as a 'non rollback' hook but the given class (" + className
+ ") is a " + TxHook.class + ". This is not possible. Please, give a class that only implements "
+ Hook.class + " or change the process to define a rollback hook.");
}
}
}
}
}
}
}
}
if (packageDef.getClassDependencies() != null) {
for (final String className : packageDef.getClassDependencies()) {
try {
ClassDataLoader.getClass(packageDef.getUUID(), className);
} catch (final ClassNotFoundException e) {
throw new DeploymentRuntimeException("Problem while deploying package: " + packageId
+ ". No Class available with classname: " + className);
}
}
}
repository.addPackageDependencies(packageDef.getUUID(), packageDef.getClassDependencies());
repository.storePackageVersion(packageId, packageVersion);
for (final ProcessFullDefinition workflowProcess : packageDef.getProcesses()) {
final XpdlProcess xpdlProcess = XpdlProcessBuilder.createXpdlProcess(packageDef,
workflowProcess);
Misc.badStateIfNull(xpdlProcess, "XpdlProcessBuilder returned null!");
repository.storeXpdlProcess(xpdlProcess);
repository.storeProcessVersion(workflowProcess.getProcessId(), workflowProcess.getVersion(),
packageDef.getPackageId());
}
final Recorder recorder = EngineEnvTool.getRecorder();
recorder.recordPackageDeployed(packageDef);
final Map result = new HashMap();
for (final ProcessDefinition process : processes) {
result.put(process.getProcessId(), new ProcessDefinitionImpl(process));
}
if (LOG.isLoggable(Level.INFO)) {
final StringBuilder logMsg = new StringBuilder();
logMsg.append("Package ").append(packageDef.getPackageId())
.append(" deployed (UUID: ")
.append(packageDef.getUUID())
.append(").");
LOG.info(logMsg.toString());
}
if (LOG.isLoggable(Level.FINE)) {
if (!processes.isEmpty()) {
final StringBuilder logMsg = new StringBuilder();
logMsg.append("Package ").append(packageDef.getPackageId()).append(" contains following processes : ");
boolean first = true;
for (final ProcessDefinition process : processes) {
if (!first) {
logMsg.append(", ");
} else {
first = false;
}
logMsg.append(process.getProcessId()).append("(UUID: ").append(process.getUUID()).append(")");
}
LOG.fine(logMsg.toString());
}
}
return result;
} catch (final RuntimeException re) {
if (packageDef != null) {
ClassDataLoader.removePackageClassLoader(packageDef.getUUID());
}
throw re;
}
}
public static boolean undeployPackage(final PackageDefinitionUUID packageUUID, final String userId) {
Misc.badStateIfNull(packageUUID, "Impossible to undeploy a package from a null uuid");
final Querier journal = EngineEnvTool.getJournalQueriers();
final PackageFullDefinition packageDef = journal.getPackage(packageUUID);
if (packageDef == null) {
throw new DeploymentRuntimeException("Error during undeployment: package " + packageUUID + " not found in journal!");
}
removePackageDependencies(packageDef);
final Recorder recorder = EngineEnvTool.getRecorder();
recorder.recordPackageUndeployed(packageDef, userId);
final UndeployedPackageHandler handler = EngineEnvTool.getUndeployedPackageHandler();
handler.handleUndeployedPackage(packageDef);
if (LOG.isLoggable(Level.INFO)) {
final StringBuilder logMsg = new StringBuilder();
logMsg.append("Package ").append(packageDef.getPackageId())
.append(" undeployed (UUID: ")
.append(packageDef.getUUID())
.append(").");
LOG.info(logMsg.toString());
}
return true;
}
public static void removePackageDependencies(final PackageFullDefinition packageDef) {
final Querier journal = EngineEnvTool.getJournalQueriers();
final Repository repository = EngineEnvTool.getRepository();
final PackageDefinitionUUID packageUUID = packageDef.getUUID();
if (packageDef.getProcesses() != null) {
for (final ProcessFullDefinition process : packageDef.getProcesses()) {
final Set instances = repository.getXpdlInstances(process.getUUID());
if (instances != null && !instances.isEmpty()) {
throw new DeploymentRuntimeException("Error during undeployment: there are still running instances");
}
}
}
// check that this package is not used by another package
// (e.g. external reference, subflow ...)
final Set dependentPackages = journal.getDependentPackages(packageDef.getUUID());
if (dependentPackages != null && !dependentPackages.isEmpty()) {
throw new DeploymentRuntimeException("Error during undeployment: these packages depends on "
+ "package '" + packageUUID + "': " + dependentPackages);
}
// remove from processRepo
if (packageDef.getProcesses() != null) {
for (final ProcessFullDefinition process : packageDef.getProcesses()) {
repository.removeXpdlProcess(process.getUUID());
}
}
//remove from classRepo
repository.removePackageClassData(packageUUID);
ClassDataLoader.removePackageClassLoader(packageDef.getUUID());
repository.removePackageDependencies(packageDef.getUUID());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy