
org.ow2.bonita.deployment.PVMProcessBuilder Maven / Gradle / Ivy
/**
* Copyright (C) 2006 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.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.ow2.bonita.definition.InternalProcess;
import org.ow2.bonita.definition.activity.AbstractActivity;
import org.ow2.bonita.definition.activity.AutomaticActivity;
import org.ow2.bonita.definition.activity.ConditionDefinition;
import org.ow2.bonita.definition.activity.EndingNodeBehaviour;
import org.ow2.bonita.definition.activity.InitialNodeBehaviour;
import org.ow2.bonita.definition.activity.SubFlow;
import org.ow2.bonita.definition.activity.Task;
import org.ow2.bonita.facade.def.element.DeadlineDefinition;
import org.ow2.bonita.facade.def.element.MultiInstantiationDefinition;
import org.ow2.bonita.facade.def.majorElement.ActivityDefinition;
import org.ow2.bonita.facade.def.majorElement.DataFieldDefinition;
import org.ow2.bonita.facade.def.majorElement.ProcessDefinition;
import org.ow2.bonita.facade.def.majorElement.TransitionDefinition;
import org.ow2.bonita.facade.def.majorElement.ActivityDefinition.JoinType;
import org.ow2.bonita.facade.def.majorElement.impl.ActivityDefinitionImpl;
import org.ow2.bonita.pvm.activity.ExternalActivity;
import org.ow2.bonita.pvm.internal.model.CompositeElementImpl;
import org.ow2.bonita.pvm.internal.model.NodeImpl;
import org.ow2.bonita.pvm.internal.model.TimerDefinitionImpl;
import org.ow2.bonita.pvm.internal.model.TransitionImpl;
import org.ow2.bonita.pvm.internal.model.VariableDefinitionImpl;
import org.ow2.bonita.pvm.internal.wire.Descriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.ArgDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.BooleanHelper;
import org.ow2.bonita.pvm.internal.wire.descriptor.CharacterDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.DoubleDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.FloatDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.IntegerDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.LongDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.NullDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.ObjectDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.ProvidedObjectDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.ShortDescriptor;
import org.ow2.bonita.pvm.internal.wire.descriptor.StringDescriptor;
import org.ow2.bonita.pvm.model.Condition;
import org.ow2.bonita.pvm.model.Node;
import org.ow2.bonita.pvm.model.Transition;
import org.ow2.bonita.util.DateUtil;
import org.ow2.bonita.util.EnvTool;
/**
*
* @author Marc Blachon, Guillaume Porcher, Charles Souillard, Miguel Valdes, Pierre Vigneras
*/
public final class PVMProcessBuilder {
private static final Logger LOG = Logger.getLogger(PVMProcessBuilder.class.getName());
public static final String BONITA_INIT = "BonitaInit";
public static final String BONITA_END = "BonitaEnd";
private PVMProcessBuilder() { }
public static InternalProcess createProcess(final ProcessDefinition processDef) {
final InternalProcess process = new InternalProcess(processDef.getUUID());
createNodes(processDef, process);
initializeProcessVariables(processDef, process);
IterationDetection.findIterations(process);
return process;
}
protected static void createNodes(final ProcessDefinition processDef, final InternalProcess process) {
final Set activities = getAllActivities(processDef);
//map of created activities : key = activity.id and value = corresponding node
final Map createdNodes = new HashMap();
for (final ActivityDefinition activity : activities) {
final NodeImpl node = createNode(activity, processDef, process);
createdNodes.put(activity.getName(), node);
}
createTransitions(processDef, createdNodes);
// INITIAL / ENDING node creation
String initialEndCreationError = null;
//create initial split
NodeImpl initialNode = null;
if (createdNodes.containsKey(BONITA_INIT)) {
initialNode = createdNodes.get(BONITA_INIT);
} else {
initialNode = process.createNode(BONITA_INIT);
final Set initialActivities = getInitialActivities(processDef, activities);
if (!initialActivities.isEmpty()) {
//normal case, this process has at least one activity
//create a node for each initialActivity and create a transition from initialNode to initialActivities
for (final ActivityDefinition initialActivity : initialActivities) {
final NodeImpl initialActivityNode = createdNodes.get(initialActivity.getName());
initialNode.createOutgoingTransition(initialActivityNode, "initialSplit_"
+ initialNode.getName() + "_to_" + initialActivityNode.getName());
}
} else {
if (!createdNodes.isEmpty()) {
//there is a problem, there are activities, but none of them are initial activities
//maybe a cycle ?
initialEndCreationError = "no initial";
}
}
}
ActivityDefinitionImpl initialActivity = new ActivityDefinitionImpl(processDef.getUUID(), BONITA_INIT, null, null);
final ExternalActivity initialRoute = new InitialNodeBehaviour(initialActivity);
initialNode.setBehaviour(initialRoute);
initialNode.setPreviousNeeded(true);
process.setInitial(initialNode);
//create ending join
NodeImpl endingNode = null;
if (createdNodes.containsKey(BONITA_END)) {
endingNode = createdNodes.get(BONITA_END);
} else {
endingNode = process.createNode(BONITA_END);
final Set endingActivities = getEndingActivities(processDef, activities);
if (!endingActivities.isEmpty()) {
//normal case, this process has at least one activity
//create node for each endingActivity and create a transition from ending activities to endingNode
for (final ActivityDefinition endingActivity : endingActivities) {
final NodeImpl endingActivityNode = createdNodes.get(endingActivity.getName());
endingActivityNode.createOutgoingTransition(endingNode, "endingJoin_"
+ endingActivityNode.getName() + "_to_" + endingNode.getName());
}
} else {
if (!createdNodes.isEmpty()) {
//there is a problem, there are activities, but none of them are ending activities
//maybe a cycle ?
if (initialEndCreationError != null) {
initialEndCreationError += " and no ending";
} else {
initialEndCreationError = "no ending";
}
throw new DeploymentRuntimeException("Error in process definition: " + initialEndCreationError
+ " node found. There is maybe a cycle in activities definition");
}
//empty process (no activity)
initialNode.createOutgoingTransition(endingNode, "initialSplit_" + initialNode.getName() + "_to_" + endingNode.getName());
}
}
if (initialEndCreationError != null) {
throw new DeploymentRuntimeException("Error in process definition: " + initialEndCreationError
+ " node found. There is maybe a cycle in activities definition");
}
ActivityDefinitionImpl endActivity = new ActivityDefinitionImpl(processDef.getUUID(), BONITA_END, null, null);
endActivity.setJoinType(JoinType.XOR);
final ExternalActivity endingRoute = new EndingNodeBehaviour(endActivity);
endingNode.setPreviousNeeded(true);
endingNode.setBehaviour(endingRoute);
// Check path from init to end
final Set activityNames = new HashSet();
activityNames.add(BONITA_END);
for (final ActivityDefinition act : activities) {
activityNames.add(act.getName());
}
activityNames.remove(BONITA_INIT);
if (!checkReachable(activityNames, initialNode)) {
throw new DeploymentRuntimeException("There is no path from " + BONITA_INIT + " to " + BONITA_END);
}
if (!activityNames.isEmpty()) {
throw new DeploymentRuntimeException("These activities " + activityNames + " cannot be executed."
+ " There is no path from " + BONITA_INIT + " to them.");
}
}
private static boolean checkReachable(final Set nodeNames, final Node currentNode) {
boolean endReached = currentNode.getName().equals(BONITA_END);
final List trs = currentNode.getOutgoingTransitions();
if (trs != null) {
for (final Transition t : trs) {
if (nodeNames.remove(t.getDestination().getName())) {
endReached = checkReachable(nodeNames, t.getDestination()) || endReached;
}
}
}
return endReached;
}
protected static void createTransitions(final ProcessDefinition processDef,
final Map createdNodes) {
final Set transitions = processDef.getTransitions();
if (transitions != null) {
for (final TransitionDefinition transition : transitions) {
final String toActivityId = transition.getTo();
final String fromActivityId = transition.getFrom();
String condition = transition.getCondition();
if (!activityWithIdExist(processDef, toActivityId)) {
throw new DeploymentRuntimeException("Transition " + transition.getName() + " is using activity "
+ toActivityId + " in to attribute. There is no activity with this uuid in the defined workflowprocess.");
}
if (!activityWithIdExist(processDef, fromActivityId)) {
throw new DeploymentRuntimeException("Transition " + transition.getName() + " is using activity "
+ fromActivityId + " in from attribute. There is no activity with this uuid in the defined workflowprocess.");
}
final NodeImpl fromNode = createdNodes.get(fromActivityId);
final NodeImpl toNode = createdNodes.get(toActivityId);
final String transitionName = fromActivityId + "_" + toActivityId;
final TransitionImpl pvmTransition = fromNode.createOutgoingTransition(toNode, transitionName);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("**** created transition = " + transitionName);
}
// check/set if condition on the transition
if (condition != null) {
final Condition guardCondition = new ConditionDefinition(condition);
pvmTransition.setConditionDescriptor(new ProvidedObjectDescriptor(guardCondition));
}
}
}
}
protected static NodeImpl createNode(final ActivityDefinition activity, final ProcessDefinition processDef,
final InternalProcess process) {
final NodeImpl node = process.createNode(activity.getName());
if (activity.isAsynchronous()) {
node.setExecutionAsync(true);
}
AbstractActivity abstractActivity = null;
//createVariableDefinitionsFromDataFields(node, activity.getDataFields());
if (activity.isAutomatic() && activity.isSubflow()) {
final String subProcessId = activity.getSubflowProcessName();
final InternalProcess externalSubProcess = EnvTool.getRepository().findLatestProcessByName(subProcessId);
if (externalSubProcess == null) {
throw new DeploymentRuntimeException("Unable to find process \"" + subProcessId + "\" used in subFlow. "
+ "If the subFlow process is in an external package, "
+ "please check that this package has already been deployed");
}
abstractActivity = new SubFlow(activity);
} else if (activity.isAutomatic()) {
abstractActivity = new AutomaticActivity(activity);
} else if (!activity.isAutomatic()) {
abstractActivity = new Task(activity);
}
createTimerDefinitions(activity, node);
final MultiInstantiationDefinition multiInstantiationDefinition = activity.getMultiInstantiationDefinition();
if (multiInstantiationDefinition != null) {
final String variableId = multiInstantiationDefinition.getVariableName();
boolean variableExists = false;
if (activity.getDataFields() != null) {
for (final DataFieldDefinition var : activity.getDataFields()) {
if (var.getName().equals(variableId)) {
variableExists = true;
break;
}
}
}
if (!variableExists) {
throw new DeploymentRuntimeException("MultiInstantiation variable " + variableId
+ " must be a local variable of activity " + activity.getName());
}
}
node.setBehaviour(abstractActivity);
node.setPreviousNeeded(true);
return node;
}
protected static ProcessDefinition getProcess(final Set processes, final String processId) {
if (processes == null) {
return null;
}
for (final ProcessDefinition process : processes) {
if (process.getName().equals(processId)) {
return process;
}
}
return null;
}
protected static Set getAllActivities(final ProcessDefinition processDef) {
final Set activities = new HashSet();
final Set processActivities = processDef.getActivities();
if (processActivities != null) {
activities.addAll(processActivities);
}
return activities;
}
protected static Set getInitialActivities(final ProcessDefinition processDef,
final Set activitiesToAnalyse) {
final Set initialActivities = new HashSet();
for (final ActivityDefinition activity : activitiesToAnalyse) {
if (getIncomingTransitionNb(processDef, activity.getName()) == 0) {
initialActivities.add(activity);
}
}
return initialActivities;
}
protected static Set getEndingActivities(final ProcessDefinition processDef,
final Set activitiesToAnalyse) {
final Set endingActivities = new HashSet();
for (final ActivityDefinition activity : activitiesToAnalyse) {
if (getOutgoingTransitionNb(processDef, activity.getName()) == 0) {
endingActivities.add(activity);
}
}
return endingActivities;
}
protected static int getIncomingTransitionNb(final ProcessDefinition processDef, final String activityId) {
int incomingTransitionNb = 0;
if (processDef.getTransitions() != null) {
for (final TransitionDefinition transition : processDef.getTransitions()) {
if (transition.getTo().equals(activityId)) {
incomingTransitionNb++;
}
}
}
return incomingTransitionNb;
}
protected static int getOutgoingTransitionNb(final ProcessDefinition processDef, final String activityId) {
int outgoingTransitionNb = 0;
if (processDef.getTransitions() != null) {
for (final TransitionDefinition transition : processDef.getTransitions()) {
if (transition.getFrom().equals(activityId)) {
outgoingTransitionNb++;
}
}
}
return outgoingTransitionNb;
}
protected static void createVariableDefinitionsFromDataFields(final CompositeElementImpl element,
final Collection dataFields) {
if (dataFields != null && !dataFields.isEmpty()) {
for (final DataFieldDefinition dataField : dataFields) {
final VariableDefinitionImpl variable = element.createVariableDefinition();
variable.setKey(dataField.getName());
variable.setSourceDescriptor(getInitialValueDescriptor(dataField.getInitialValue(), dataField.getDataTypeClassName()));
}
}
}
protected static void initializeProcessVariables(final ProcessDefinition processDef, final InternalProcess process) {
Set processDatafields = processDef.getDataFields();
final Map dataFields = new HashMap();
if (processDatafields != null) {
for (final DataFieldDefinition datafield : processDatafields) {
dataFields.put(datafield.getName(), datafield);
}
}
//createVariableDefinitionsFromDataFields(process, dataFields.values());
}
/**
* create a descriptor for the initial value of a BasicType INTEGER element
*/
private static Descriptor getIntegerInitialValueDescriptor(final Integer value) {
Integer i = null;
if (value != null) {
i = Integer.valueOf(value);
}
final IntegerDescriptor descriptor = new IntegerDescriptor();
descriptor.setValue(i);
return descriptor;
}
/**
* create a descriptor for the initial value of a BasicType FLOAT element
*/
private static Descriptor getFloatInitialValueDescriptor(final Float value) {
Float flt = null;
if (value != null) {
flt = Float.valueOf(value);
}
final FloatDescriptor descriptor = new FloatDescriptor();
descriptor.setValue(flt);
return descriptor;
}
private static Descriptor getShortInitialValueDescriptor(final Short value) {
Short flt = null;
if (value != null) {
flt = Short.valueOf(value);
}
final ShortDescriptor descriptor = new ShortDescriptor();
descriptor.setValue(flt);
return descriptor;
}
private static Descriptor getCharInitialValueDescriptor(final Character value) {
Character flt = null;
if (value != null) {
flt = Character.valueOf(value);
}
final CharacterDescriptor descriptor = new CharacterDescriptor();
descriptor.setValue(flt);
return descriptor;
}
private static Descriptor getLongInitialValueDescriptor(final Long value) {
Long flt = null;
if (value != null) {
flt = Long.valueOf(value);
}
final LongDescriptor descriptor = new LongDescriptor();
descriptor.setValue(flt);
return descriptor;
}
private static Descriptor getDoubleInitialValueDescriptor(final Double value) {
Double flt = null;
if (value != null) {
flt = Double.valueOf(value);
}
final DoubleDescriptor descriptor = new DoubleDescriptor();
descriptor.setValue(flt);
return descriptor;
}
/**
* create a descriptor for the initial value of a BasicType DATETIME element
*/
private static Descriptor getDateTimeInitialValueDescriptor(final Date value) {
if (value == null) {
return new NullDescriptor();
}
// creation of the descriptor
final ObjectDescriptor descriptor = new ObjectDescriptor();
descriptor.setClassName(DateUtil.class.getName());
descriptor.setMethodName("parseDate");
final ArgDescriptor argDescriptor = new ArgDescriptor();
argDescriptor.setTypeName(String.class.getName());
argDescriptor.setDescriptor(new StringDescriptor(DateUtil.format(value)));
descriptor.addArgDescriptor(argDescriptor);
return descriptor;
}
/**
* create a descriptor for the initial value of a BasicType PERFORMER element
*/
/*
private static Descriptor getPerformerInitialValueDescriptor(final String value,
final Set participantIds, final String dataFieldName) {
Descriptor descriptor;
if (value == null || value.length() == 0) {
descriptor = new NullDescriptor();
} else {
if (participantIds != null && participantIds.contains(value)) {
descriptor = new StringDescriptor(value);
} else {
throw new DeploymentRuntimeException("Invalid value: Participant " + value
+ " does not exist in the naming context of datafield " + dataFieldName);
}
}
return descriptor;
}*/
/**
* create a descriptor for the initialValue of a dataField
* @param processDef: the process definition; used for a PERFORMER dataField
* @param dataField: the dataField definition
* @return a descriptor of the initial value
*/
public static Descriptor getInitialValueDescriptor(final Object initialValue, final String typeClassName) {
if (Boolean.class.getName().equals(typeClassName)) {
return BooleanHelper.createDescriptor((Boolean) initialValue);
} else if (String.class.getName().equals(typeClassName)) {
return new StringDescriptor((String) initialValue);
} else if (Integer.class.getName().equals(typeClassName)) {
return getIntegerInitialValueDescriptor((Integer) initialValue);
} else if (Float.class.getName().equals(typeClassName)) {
return getFloatInitialValueDescriptor((Float) initialValue);
} else if (Date.class.getName().equals(typeClassName)) {
return getDateTimeInitialValueDescriptor((Date) initialValue);
} else if (Character.class.getName().equals(typeClassName)) {
return getCharInitialValueDescriptor((Character) initialValue);
} else if (Short.class.getName().equals(typeClassName)) {
return getShortInitialValueDescriptor((Short) initialValue);
} else if (Long.class.getName().equals(typeClassName)) {
return getLongInitialValueDescriptor((Long) initialValue);
} else if (Double.class.getName().equals(typeClassName)) {
return getDoubleInitialValueDescriptor((Double) initialValue);
} else {
throw new DeploymentRuntimeException("Unknow basic type: " + typeClassName);
}
}
protected static boolean activityWithIdExist(final ProcessDefinition process, final String activityId) {
final Set activities = process.getActivities();
if (activities != null) {
for (final ActivityDefinition activity : activities) {
if (activity.getName().equals(activityId)) {
return true;
}
}
}
//scope of from and to is the surrounding scope (see spec p40)
return false;
}
protected static Set createTimerDefinitions(final ActivityDefinition activity, final NodeImpl node) {
if (activity.getDeadlines() != null) {
final Set deadlineHooks = new HashSet();
for (final DeadlineDefinition deadline : activity.getDeadlines()) {
deadlineHooks.add(deadline.getClassName());
createTimerDefinition(node, deadline);
}
return deadlineHooks;
}
return null;
}
protected static void createTimerDefinition(final NodeImpl node, final DeadlineDefinition deadline) {
final TimerDefinitionImpl timerDefinition = node.createTimerDefinition();
timerDefinition.setSignalName(deadline.getClassName());
final String condition = deadline.getCondition();
try {
final long duration = Long.parseLong(condition);
timerDefinition.setDueDateDescription(duration + " millis");
} catch (final NumberFormatException e1) {
try {
timerDefinition.setDueDate(DateUtil.parseDate(condition));
} catch (final IllegalArgumentException e2) {
throw new DeploymentRuntimeException("deadline condition '" + deadline.getCondition()
+ "' is neither a Long nor a formatted date", e2);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy