/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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.jbpm.pvm.internal.model;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.StringTokenizer;
import org.jbpm.api.Execution;
import org.jbpm.api.JbpmException;
import org.jbpm.api.activity.ActivityExecution;
import org.jbpm.api.job.Job;
import org.jbpm.api.job.Timer;
import org.jbpm.api.listener.EventListenerExecution;
import org.jbpm.api.model.Event;
import org.jbpm.api.model.OpenExecution;
import org.jbpm.api.task.Assignable;
import org.jbpm.api.task.AssignmentHandler;
import org.jbpm.internal.log.Log;
import org.jbpm.pvm.internal.client.ClientProcessDefinition;
import org.jbpm.pvm.internal.client.ClientProcessInstance;
import org.jbpm.pvm.internal.env.Context;
import org.jbpm.pvm.internal.env.Environment;
import org.jbpm.pvm.internal.env.ExecutionContext;
import org.jbpm.pvm.internal.history.HistoryEvent;
import org.jbpm.pvm.internal.history.events.ActivityEnd;
import org.jbpm.pvm.internal.history.events.ActivityStart;
import org.jbpm.pvm.internal.history.events.AutomaticEnd;
import org.jbpm.pvm.internal.history.events.DecisionEnd;
import org.jbpm.pvm.internal.history.events.ProcessInstanceCreate;
import org.jbpm.pvm.internal.history.events.ProcessInstanceEnd;
import org.jbpm.pvm.internal.job.JobImpl;
import org.jbpm.pvm.internal.job.MessageImpl;
import org.jbpm.pvm.internal.model.op.AtomicOperation;
import org.jbpm.pvm.internal.model.op.MoveToChildActivity;
import org.jbpm.pvm.internal.model.op.Signal;
import org.jbpm.pvm.internal.script.ScriptManager;
import org.jbpm.pvm.internal.session.DbSession;
import org.jbpm.pvm.internal.session.MessageSession;
import org.jbpm.pvm.internal.session.RepositorySession;
import org.jbpm.pvm.internal.session.TimerSession;
import org.jbpm.pvm.internal.task.AssignableDefinitionImpl;
import org.jbpm.pvm.internal.task.SwimlaneDefinitionImpl;
import org.jbpm.pvm.internal.task.SwimlaneImpl;
import org.jbpm.pvm.internal.type.Variable;
import org.jbpm.pvm.internal.util.EqualsUtil;
import org.jbpm.pvm.internal.util.Priority;
/**
* @author Tom Baeyens
*/
public class ExecutionImpl extends ScopeInstanceImpl
implements ClientProcessInstance,
ActivityExecution,
EventListenerExecution,
Serializable {
private static final long serialVersionUID = 1L;
private static final Log log = Log.getLog(ExecutionImpl.class.getName());
/** an optional name for this execution. can be used to
* differentiate concurrent paths of execution like e.g.
* the 'shipping' and 'billing' paths. */
protected String name;
/** a key for this execution. typically this is an externally provided reference
* that is unique within the scope of the process definition. */
protected String key;
/** a unique id for this execution. */
protected String id;
/** are concurrent executions that related to this execution. */
protected Collection<ExecutionImpl> executions = new ArrayList<ExecutionImpl>();
/** the parent child relation of executions is convenient for some forms of
* concurrency. */
protected ExecutionImpl parent = null;
protected ExecutionImpl processInstance;
/** the super process link in case this is a sub process execution */
protected ExecutionImpl superProcessExecution;
/** the sub process link in case of sub process execution */
protected ExecutionImpl subProcessInstance;
/** swimlanes */
protected Map<String, SwimlaneImpl> swimlanes = new HashMap<String, SwimlaneImpl>();
/** reference to the current activity instance history record */
protected Long historyActivityInstanceDbid;
/** start time of the activity for history purposes (not persisted) */
protected Date historyActivityStart;
protected int priority = Priority.NORMAL;
protected Map<String, Variable> systemVariables = new HashMap<String, Variable>();
// persistent indicators of the current position ////////////////////////////
/** persistent process definition reference */
protected String processDefinitionId;
/** persistent activity reference */
protected String activityName;
// transient cached indicators of the current position //////////////////////
/** transient cached process definition. persistence is managed in {@link #processDefinitionId} */
protected ProcessDefinitionImpl processDefinition;
/** transient cached current activity pointer. persistence is managed in {@link #activityName} */
private ActivityImpl activity;
/** transition is not to be made persistable by default */
protected TransitionImpl transition;
protected EventImpl event;
protected AtomicOperation eventCompletedOperation;
protected int eventListenerIndex;
protected ObservableElementImpl eventSource;
// cached named executions //////////////////////////////////////////////////
/** caches the child executions by execution name. This member might be
* null and is only created from the executions in case its needed. Note
* that not all executions are forced to have a name and duplicates are allowed.
* In case the {@link #executions} change, the executionsMap can be nulled or
* also updated (but a check needs to be added whether it exists). */
protected transient Map<String, Execution> executionsMap = null;
/** the queue of atomic operations to be performed for this execution. */
protected Queue<AtomicOperation> atomicOperations;
public enum Propagation {
UNSPECIFIED, WAIT, EXPLICIT
}
protected Propagation propagation = null;
// construction /////////////////////////////////////////////////////////////
public void initializeProcessInstance(ProcessDefinitionImpl processDefinition, String key) {
setProcessDefinition(processDefinition);
setActivity ( (ActivityImpl) processDefinition.getInitial() );
this.processInstance = this;
this.state = STATE_CREATED;
this.key = key;
IdGenerator idGenerator = processDefinition.getIdGenerator();
if (idGenerator!=null) {
this.id = idGenerator.createId(processDefinition, null, this);
}
HistoryEvent.fire(new ProcessInstanceCreate(), this);
}
// execution method : start /////////////////////////////////////////////////
public void start() {
if (!STATE_CREATED.equals(state)) {
throw new JbpmException(toString()+" is already begun: "+state);
}
this.state = STATE_ACTIVE_ROOT;
ExecutionImpl scopedExecution = initializeScopes();
fire(Event.START, getProcessDefinition());
if (getActivity()!=null) {
scopedExecution.performAtomicOperation(AtomicOperation.EXECUTE_ACTIVITY);
}
}
protected ExecutionImpl initializeScopes() {
LinkedList<ActivityImpl> enteredActivities = new LinkedList<ActivityImpl>();
ActivityImpl initial = getProcessDefinition().getInitial();
ExecutionImpl scopedExecution = null;
if (initial!=null) {
enteredActivities.add(initial);
ActivityImpl parentActivity = initial.getParentActivity();
while (parentActivity!=null) {
enteredActivities.addFirst(parentActivity);
parentActivity = parentActivity.getParentActivity();
}
scopedExecution = this;
initializeVariables(getProcessDefinition(), this);
initializeTimers(getProcessDefinition());
for (ActivityImpl enteredActivity: enteredActivities) {
if (enteredActivity.isLocalScope()) {
scopedExecution.setActivity(enteredActivity);
scopedExecution = scopedExecution.createScope(enteredActivity);
}
}
scopedExecution.setActivity(initial);
}
return scopedExecution;
}
public ExecutionImpl createScope(ScopeElementImpl scope) {
ExecutionImpl child = createExecution(scope.getName());
setState(STATE_INACTIVE_SCOPE);
child.setState(STATE_ACTIVE_ROOT);
// copy the current state from the child execution to the parent execution
child.setActivity(getActivity());
child.setTransition(getTransition());
child.setPropagation(getPropagation());
child.initializeVariables(scope, this);
child.initializeTimers(scope);
return child;
}
public ExecutionImpl destroyScope(CompositeElementImpl scope) {
destroyTimers(scope);
// copy the current state from the child execution to the parent execution
parent.setActivity(getActivity());
parent.setTransition(getTransition());
parent.setPropagation(getPropagation());
ExecutionImpl parentsParent = parent.getParent();
if ( (parentsParent!=null)
&& (STATE_INACTIVE_CONCURRENT_ROOT.equals(parentsParent.getState()))
) {
parent.setState(STATE_ACTIVE_CONCURRENT);
} else {
parent.setState(STATE_ACTIVE_ROOT);
}
// capture the parent execution cause the
// subsequent invocation of end() will set the parent to null
ExecutionImpl parent = this.parent;
end();
return parent;
}
protected void destroyTimers(CompositeElementImpl scope) {
TimerSession timerSession = Environment.getFromCurrent(TimerSession.class, false);
if (timerSession!=null) {
log.debug("destroying timers of "+this);
List<Timer> timers = timerSession.findTimersByExecution(this);
for (Timer timer: timers) {
Job job = Environment.getFromCurrent(JobImpl.class, false);
if (timer!=job) {
timerSession.cancel(timer);
}
}
}
}
// basic object methods /////////////////////////////////////////////////////
public String toString() {
if (getId()!=null) {
return "execution["+id+"]";
}
if (parent==null) {
return "process-instance";
}
return "execution";
}
// execution method : end ///////////////////////////////////////////////////
public void end() {
end(Execution.STATE_ENDED);
}
public void end(String state) {
if (state==null) {
throw new JbpmException("state is null");
}
if (state.equals(STATE_CREATED)
|| state.equals(STATE_ACTIVE_ROOT)
|| state.equals(STATE_ACTIVE_CONCURRENT)
|| state.equals(STATE_INACTIVE_CONCURRENT_ROOT)
|| state.equals(STATE_INACTIVE_SCOPE)
|| state.equals(STATE_INACTIVE_JOIN)
|| state.equals(STATE_SUSPENDED)
|| state.equals(STATE_ASYNC)) {
throw new JbpmException("invalid end state: "+state);
}
if (log.isDebugEnabled()) {
if (state==STATE_ENDED) {
log.debug(toString()+" ends");
} else {
log.debug(toString()+" ends with state "+state);
}
}
// end all child executions
// making a copy of the executions to prevent ConcurrentMoidificationException
List<ExecutionImpl> executionsToEnd = new ArrayList<ExecutionImpl>(executions);
for (ExecutionImpl child: executionsToEnd) {
child.end(state);
}
setState(state);
this.propagation = Propagation.EXPLICIT;
if (parent!=null) {
parent.removeExecution(this);
} else { // this is a process instance
HistoryEvent.fire(new ProcessInstanceEnd(), this);
fire(Event.END, getProcessDefinition());
if (superProcessExecution!=null) {
log.trace(toString()+" signals super process execution");
superProcessExecution.signal();
}
DbSession dbSession = Environment.getFromCurrent(DbSession.class, false);
if (dbSession!=null) {
dbSession.deleteProcessInstance(id, false);
}
}
}
public void end(OpenExecution executionToEnd) {
((ExecutionImpl)executionToEnd).end();
}
public void end(OpenExecution executionToEnd, String state) {
((ExecutionImpl)executionToEnd).end(state);
}
// execution method : signal ////////////////////////////////////////////////
public void signal() {
signal(null, (Map<String,?>)null);
}
public void signal(String signal) {
signal(signal, (Map<String,?>)null);
}
public void signal(Map<String, ?> parameters) {
signal(null, parameters);
}
public void signal(String signal, Map<String, ?> parameters) {
checkActive();
if (getProcessDefinition().isSuspended()) {
throw new JbpmException("process definition "+getProcessDefinition().getId()+" is suspended");
}
propagation = Propagation.EXPLICIT;
if (getActivity()!=null) {
performAtomicOperation(new Signal(signal, parameters));
} else if (transition!=null) {
performAtomicOperation(AtomicOperation.TRANSITION_START_ACTIVITY);
} else {
throw new JbpmException("execution is not in a activity or in a transition");
}
}
public void signal(Execution execution) {
((ExecutionImpl)execution).signal(null, (Map<String,?>)null);
}
public void signal(String signalName, Execution execution) {
((ExecutionImpl)execution).signal(signalName, (Map<String,?>)null);
}
public void signal(Map<String, ?> parameters, Execution execution) {
((ExecutionImpl)execution).signal(null, parameters);
}
public void signal(String signalName, Map<String, ?> parameters, Execution execution) {
((ExecutionImpl)execution).signal(signalName, parameters);
}
// execution method : take ////////////////////////////////////////////////
/** @see Execution#takeDefaultTransition() */
public void takeDefaultTransition() {
TransitionImpl defaultTransition = getActivity().getDefaultOutgoingTransition();
if (defaultTransition==null) {
throw new JbpmException("there is no default transition in "+getActivity());
}
take(defaultTransition);
}
/** @see Execution#take(String) */
public void take(String transitionName) {
if (getActivity()==null) {
throw new JbpmException(toString()+" is not positioned in activity");
}
TransitionImpl transition = findTransition(transitionName);
if (transition==null) {
throw new JbpmException("there is no transition "+transitionName+" in "+getActivity());
}
take(transition);
}
/** @see Execution#takeDefaultTransition() */
public void take(Transition transition) {
checkActive();
setPropagation(Propagation.EXPLICIT);
setTransition((TransitionImpl) transition);
fire(Event.END, getActivity(), AtomicOperation.TRANSITION_END_ACTIVITY);
}
public void take(Transition transition, Execution execution) {
((ExecutionImpl)execution).take(transition);
}
// execution method : execute ///////////////////////////////////////////////
/** @see Execution#execute(String) */
public void execute(String activityName) {
if (getActivity()==null) {
throw new JbpmException("activity is null");
}
Activity nestedActivity = getActivity().getActivity(activityName);
if (nestedActivity==null) {
throw new JbpmException("activity "+activityName+" doesn't exist in "+getActivity());
}
execute(nestedActivity);
}
/** @see Execution#execute(Activity) */
public void execute(Activity activity) {
if (activity==null) {
throw new JbpmException("activity is null");
}
checkActive();
this.propagation = Propagation.EXPLICIT;
performAtomicOperation(new MoveToChildActivity((ActivityImpl) activity));
}
// execution method : waitForSignal /////////////////////////////////////////
public void waitForSignal() {
propagation = Propagation.WAIT;
}
// execution method : proceed ///////////////////////////////////////////////
public void proceed() {
checkActive();
// in graph based processDefinition languages we assume that a
// default transition is available
TransitionImpl defaultTransition = findDefaultTransition();
if (defaultTransition!=null) {
take(defaultTransition);
// in block structured processDefinition languages we assume that
// there is no default transition and that there is a
// parent activity of the current activity
} else {
ActivityImpl parentActivity = getActivity().getParentActivity();
// if there is a parent activity
if (parentActivity!=null) {
// propagate to the parent
performAtomicOperation(AtomicOperation.PROPAGATE_TO_PARENT);
} else {
// When we don't know how to proceed, i don't know if it's best to
// throw new PvmException("don't know how to proceed");
// or to end the execution. Because of convenience for testing,
// I opted to end the execution.
end();
}
}
}
public void setActivity(Activity activity, Execution execution) {
((ExecutionImpl)execution).setActivity(activity);
}
public void setActivity(Activity activity) {
setActivity((ActivityImpl) activity);
}
// events ///////////////////////////////////////////////////////////////////
public void fire(String eventName, ObservableElement eventSource) {
fire(eventName, (ObservableElementImpl) eventSource, null);
}
public void fire(String eventName, ObservableElementImpl observableElement, AtomicOperation eventCompletedOperation) {
EventImpl event = findEvent(observableElement, eventName);
if (event!=null) {
setEvent(event);
setEventSource(observableElement);
setEventListenerIndex(0);
setEventCompletedOperation(eventCompletedOperation);
performAtomicOperation(AtomicOperation.EXECUTE_EVENT_LISTENER);
} else {
if (eventCompletedOperation!=null) {
performAtomicOperationSync(eventCompletedOperation);
}
}
}
public static EventImpl findEvent(ObservableElementImpl observableElement, String eventName) {
if (observableElement==null) {
return null;
}
EventImpl event = observableElement.getEvent(eventName);
if (event!=null) {
return event;
}
return findEvent(observableElement.getParent(), eventName);
}
// execution : internal methods /////////////////////////////////////////////
public void moveTo(ActivityImpl destination) {
// move the execution to the destination
setActivity(destination);
transition = null;
}
public ExecutionImpl startActivity(ActivityImpl activity) {
ExecutionImpl propagatingExecution = this;
if (activity.isLocalScope()) {
propagatingExecution = createScope(activity);
}
fire(Event.START, activity);
return propagatingExecution;
}
public ExecutionImpl endActivity(ActivityImpl activity) {
ExecutionImpl propagatingExecution = this;
fire(Event.END, activity);
if (activity.isLocalScope()) {
propagatingExecution = destroyScope(activity);
}
return propagatingExecution;
}
// asynchronous continuations ////////////////////////////////////////////////
public synchronized void performAtomicOperation(AtomicOperation operation) {
if (operation.isAsync(this)) {
sendContinuationMessage(operation);
} else {
performAtomicOperationSync(operation);
}
}
public void sendContinuationMessage(AtomicOperation operation) {
Environment environment = Environment.getCurrent();
MessageSession messageSession = environment.get(MessageSession.class);
if (messageSession==null) {
throw new JbpmException("no message-session configured to send asynchronous continuation message");
}
MessageImpl<?> asyncMessage = operation.createAsyncMessage(this);
setState(Execution.STATE_ASYNC);
messageSession.send(asyncMessage);
}
public void performAtomicOperationSync(AtomicOperation operation) {
if (atomicOperations==null) {
// initialise the fifo queue of atomic operations
atomicOperations = new LinkedList<AtomicOperation>();
atomicOperations.offer(operation);
ExecutionContext originalExecutionContext = null;
ExecutionContext executionContext = null;
Environment environment = Environment.getCurrent();
if (environment!=null) {
originalExecutionContext = (ExecutionContext) environment.getContext(Context.CONTEXTNAME_EXECUTION);
if ( (originalExecutionContext!=null)
&& (originalExecutionContext.getExecution()==this)
) {
originalExecutionContext = null;
} else {
executionContext = new ExecutionContext(this);
environment.setContext(executionContext);
}
}
try {
while (! atomicOperations.isEmpty()) {
AtomicOperation atomicOperation = atomicOperations.poll();
atomicOperation.perform(this);
}
} catch (RuntimeException e ) {
throw e;
} finally {
atomicOperations = null;
if (executionContext!=null) {
environment.removeContext(executionContext);
}
if (originalExecutionContext!=null) {
environment.setContext(originalExecutionContext);
}
}
} else {
atomicOperations.offer(operation);
}
}
public void handleException(ObservableElementImpl observableElement,
EventImpl event,
EventListenerReference eventListenerReference,
Exception exception,
String rethrowMessage) {
List<ProcessElementImpl> processElements = new ArrayList<ProcessElementImpl>();
if (eventListenerReference!=null) {
processElements.add(eventListenerReference);
}
if (event!=null) {
processElements.add(event);
}
while (observableElement!=null) {
processElements.add(observableElement);
observableElement = observableElement.getParent();
}
for (ProcessElementImpl processElement: processElements) {
List<ExceptionHandlerImpl> exceptionHandlers = processElement.getExceptionHandlers();
if (exceptionHandlers!=null) {
for (ExceptionHandlerImpl exceptionHandler: exceptionHandlers) {
if (exceptionHandler.matches(exception)) {
try {
exceptionHandler.handle(this, exception);
return;
} catch (Exception rethrowException) {
if (!exceptionHandler.isRethrowMasked()) {
exception = rethrowException;
}
}
break;
}
}
}
}
log.trace("rethrowing exception cause no exception handler for "+exception);
ExceptionHandlerImpl.rethrow(exception, rethrowMessage+": "+exception.getMessage());
}
// tasks ////////////////////////////////////////////////////////////////////
/** tasks and swimlane assignment.
* SwimlaneDefinitionImpl is base class for TaskDefinitionImpl.
* Both Task and Swimlane implement Assignable. */
public void initializeAssignments(AssignableDefinitionImpl assignableDefinition, Assignable assignable) {
String assigneeExpression = assignableDefinition.getAssigneeExpression();
if (assigneeExpression!=null) {
String assignee = resolveAssignmentExpression(assigneeExpression,
assignableDefinition.getAssigneeExpressionLanguage());
assignable.setAssignee(assignee);
if (log.isTraceEnabled()) log.trace("task "+name+" assigned to "+assignee+" using expression "+assigneeExpression);
}
String candidateUsersExpression = assignableDefinition.getCandidateUsersExpression();
if (candidateUsersExpression!=null) {
String candidateUsers =
resolveAssignmentExpression(candidateUsersExpression,
assignableDefinition.getCandidateUsersExpressionLanguage());
StringTokenizer tokenizer = new StringTokenizer(candidateUsers, ",");
while (tokenizer.hasMoreTokens()) {
String candidateUser = tokenizer.nextToken().trim();
assignable.addCandidateUser(candidateUser);
}
}
String candidateGroupsExpression = assignableDefinition.getCandidateGroupsExpression();
if (candidateGroupsExpression!=null) {
String candidateGroups =
resolveAssignmentExpression(candidateGroupsExpression,
assignableDefinition.getCandidateGroupsExpressionLanguage());
StringTokenizer tokenizer = new StringTokenizer(candidateGroups, ",");
while (tokenizer.hasMoreTokens()) {
String candidateGroup = tokenizer.nextToken();
assignable.addCandidateGroup(candidateGroup);
}
}
AssignmentHandler assignmentHandler = assignableDefinition.getAssignmentHandler();
if (assignmentHandler!=null) {
try {
assignmentHandler.assign(assignable, this);
} catch (Exception e) {
throw new JbpmException("assignment handler threw exception: " + e, e);
}
}
}
protected String resolveAssignmentExpression(String expression, String expressionLanguage) {
ScriptManager scriptManager = Environment.getFromCurrent(ScriptManager.class);
Object result = scriptManager.evaluateExpression(expression, expressionLanguage);
if ( (result ==null)
|| (result instanceof String)
) {
return (String) result;
}
throw new JbpmException("result of assignment expression "+expression+" is "+result+" ("+result.getClass().getName()+") instead of String");
}
// swimlanes ////////////////////////////////////////////////////////////////
public void addSwimlane(SwimlaneImpl swimlane) {
swimlanes.put(swimlane.getName(), swimlane);
swimlane.setExecution(this);
}
public SwimlaneImpl getSwimlane(String swimlaneName) {
return swimlanes.get(swimlaneName);
}
public void removeSwimlane(SwimlaneImpl swimlane) {
swimlanes.remove(swimlane.getName());
swimlane.setExecution(null);
}
public SwimlaneImpl getInitializedSwimlane(SwimlaneDefinitionImpl swimlaneDefinition) {
String swimlaneName = swimlaneDefinition.getName();
SwimlaneImpl swimlane = swimlanes.get(swimlaneName);
if (swimlane==null) {
swimlane = createSwimlane(swimlaneName);
initializeAssignments(swimlaneDefinition, swimlane);
}
return swimlane;
}
public SwimlaneImpl createSwimlane(String swimlaneName) {
SwimlaneImpl swimlane = new SwimlaneImpl();
swimlane.setName(swimlaneName);
swimlane.setExecution(this);
swimlanes.put(swimlaneName, swimlane);
return swimlane;
}
// child executions /////////////////////////////////////////////////////////
public ExecutionImpl createExecution() {
return createExecution(null);
}
public ExecutionImpl createExecution(String name) {
// ?!
propagation = Propagation.EXPLICIT;
// create child execution
ExecutionImpl childExecution = newChildExecution();
addExecution(childExecution);
// initialize new concurrent execution
childExecution.setProcessDefinition(getProcessDefinition());
childExecution.processInstance = this.processInstance;
childExecution.name = name;
// id generation (after initialization)
IdGenerator keyGenerator = Environment.getFromCurrent(IdGenerator.class, false);
if (keyGenerator!=null) {
childExecution.id = keyGenerator.createId(getProcessDefinition(), this, childExecution);
}
log.debug("created "+childExecution);
return childExecution;
}
protected ExecutionImpl newChildExecution() {
return new ExecutionImpl();
}
public void addExecution(ExecutionImpl execution) {
execution.setParent(this);
executions.add(execution);
executionsMap = null;
}
/** @see Execution#getExecution(String) */
public ExecutionImpl getExecution(String name) {
Map<String, Execution> executionsMap = getExecutionsMap();
return (ExecutionImpl) (executionsMap!=null ? executionsMap.get(name) : null);
}
public void removeExecution(ExecutionImpl child) {
if (executions.contains(child)) {
if (executions.remove(child)) {
child.setParent(null);
// invalidate the executionsMap cache
executionsMap = null;
} else {
throw new JbpmException(child+" is not a child execution of "+this);
}
}
}
public Map<String, Execution> getExecutionsMap() {
if ((executionsMap==null)) {
// initialize executionsMap cache
executionsMap = new HashMap<String, Execution>();
for(ExecutionImpl execution: executions) {
String executionName = execution.getName();
// the next test makes sure that the first execution wins
// in case there are multiple executions with the same name
if (! executionsMap.containsKey(executionName)) {
executionsMap.put(executionName, execution);
}
}
}
return executionsMap;
}
public boolean hasExecution(String name) {
return ( (getExecutionsMap()!=null)
&& executionsMap.containsKey(name)
);
}
public boolean isActive(String activityName) {
return findActiveActivityNames().contains(activityName);
}
public Set<String> findActiveActivityNames() {
return addActiveActivityNames(new HashSet<String>());
}
protected Set<String> addActiveActivityNames(Set<String> activityNames) {
if ( ( (state.equals(STATE_ACTIVE_ROOT)) || (state.equals(STATE_ACTIVE_CONCURRENT)) )
&&
(activityName!=null)
) {
activityNames.add(activityName);
}
for (ExecutionImpl childExecution: executions) {
childExecution.addActiveActivityNames(activityNames);
}
return activityNames;
}
public ExecutionImpl findActiveExecutionIn(String activityName) {
if ( activityName.equals(this.activityName)
&& isActive()) {
return this;
}
for (ExecutionImpl childExecution: executions) {
ExecutionImpl found = childExecution.findActiveExecutionIn(activityName);
if (found!=null) {
return found;
}
}
return null;
}
// system variables /////////////////////////////////////////////////////////
public void createSystemVariable(String key, Object value) {
createSystemVariable(key, value, null);
}
public void createSystemVariable(String key, Object value, String typeName) {
Variable variable = createVariableObject(key, value, typeName, false);
systemVariables.put(variable.getKey(), variable);
}
public void setSystemVariable(String key, Object value) {
Variable variable = systemVariables.get(key);
if (variable!=null) {
log.debug("setting system variable '"+key+"' in '"+this+"' to value '"+value+"'");
variable.setValue(value);
} else {
log.debug("creating system variable '"+key+"' in '"+this+"' to value '"+value+"'");
createSystemVariable(key, value, null);
}
}
public Object getSystemVariable(String key) {
Variable variable = systemVariables.get(key);
if (variable!=null) {
return variable.getValue();
}
return null;
}
public boolean removeSystemVariable(String key) {
if (systemVariables.containsKey(key)) {
return (systemVariables.remove(key)!=null);
}
return false;
}
// sub process creation /////////////////////////////////////////////////////
public ClientProcessInstance createSubProcessInstance(ClientProcessDefinition processDefinition) {
return createSubProcessInstance(processDefinition, null);
}
public ClientProcessInstance createSubProcessInstance(ClientProcessDefinition processDefinition, String key) {
if (subProcessInstance!=null) {
throw new JbpmException(toString()+" already has a sub process instance: "+subProcessInstance);
}
subProcessInstance = (ExecutionImpl) processDefinition.createProcessInstance(key);
subProcessInstance.setSuperProcessExecution(this);
return subProcessInstance;
}
public ClientProcessInstance startSubProcessInstance(ClientProcessDefinition processDefinition) {
return startSubProcessInstance(processDefinition, null);
}
public ClientProcessInstance startSubProcessInstance(ClientProcessDefinition processDefinition, String key) {
createSubProcessInstance(processDefinition, key);
subProcessInstance.start();
return subProcessInstance;
}
// state mgmt ///////////////////////////////////////////////////////////////
/** @see Execution#suspend() */
public void suspend() {
super.suspend();
this.propagation = Propagation.EXPLICIT;
DbSession dbSession = Environment.getFromCurrent(DbSession.class, false);
if (dbSession!=null) {
dbSession.cascadeExecutionSuspend(this);
}
}
/** @see Execution#resume() */
public void resume() {
super.resume();
DbSession hibernatePvmDbSession = Environment.getFromCurrent(DbSession.class, false);
if (hibernatePvmDbSession!=null) {
hibernatePvmDbSession.cascadeExecutionResume(this);
}
}
protected void checkActive() {
if (!isActive()) {
throw new JbpmException(toString()+" is not active: "+state);
}
}
public boolean isEnded() {
if (Execution.STATE_ENDED.equals(state)) {
return true;
}
if (Execution.STATE_CREATED.equals(state)) {
return false;
}
if (Execution.STATE_ACTIVE_ROOT.equals(state)) {
return false;
}
if (Execution.STATE_ACTIVE_CONCURRENT.equals(state)) {
return false;
}
if (Execution.STATE_INACTIVE_CONCURRENT_ROOT.equals(state)) {
return false;
}
if (Execution.STATE_INACTIVE_SCOPE.equals(state)) {
return false;
}
if (Execution.STATE_SUSPENDED.equals(state)) {
return false;
}
if (Execution.STATE_ASYNC.equals(state)) {
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// overriding the ScopeInstanceImpl methods /////////////////////////////////
public ScopeInstanceImpl getParentVariableScope() {
return parent;
}
public ExecutionImpl getTimerExecution() {
return this;
}
// overridable by process languages /////////////////////////////////////////
/** by default this will use {@link ActivityImpl#findOutgoingTransition(String)} to
* search for the outgoing transition, which includes a search over the parent chain
* of the current activity. This method allows process languages to overwrite this default
* implementation of the transition lookup by transitionName.*/
protected TransitionImpl findTransition(String transitionName) {
return getActivity().findOutgoingTransition(transitionName);
}
protected TransitionImpl findDefaultTransition() {
return getActivity().findDefaultTransition();
}
// history //////////////////////////////////////////////////////////////////
public void historyAutomatic() {
HistoryEvent.fire(new AutomaticEnd(), this);
}
public void historyDecision(String transitionName) {
HistoryEvent.fire(new DecisionEnd(transitionName), this);
}
public void historyActivityStart() {
HistoryEvent.fire(new ActivityStart(), this);
}
public void historyActivityEnd() {
HistoryEvent.fire(new ActivityEnd(), this);
}
public void historyActivityEnd(String transitionName) {
HistoryEvent.fire(new ActivityEnd(transitionName), this);
}
// equals ///////////////////////////////////////////////////////////////////
// hack to support comparing hibernate proxies against the real objects
// since this always falls back to ==, we don't need to overwrite the hashcode
public boolean equals(Object o) {
return EqualsUtil.equals(this, o);
}
// process definition getter and setter /////////////////////////////////////
// this getter and setter is special because persistence is based on the //
// process definition id. //
/////////////////////////////////////////////////////////////////////////////
public ProcessDefinitionImpl getProcessDefinition() {
if ( (processDefinition==null)
&& (processDefinitionId!=null)
) {
RepositorySession repositorySession = Environment.getFromCurrent(RepositorySession.class);
processDefinition = (ProcessDefinitionImpl) repositorySession.findProcessDefinitionById(processDefinitionId);
if (processDefinition==null) {
throw new JbpmException("couldn't find process definition "+processDefinitionId+" in the repository");
}
}
return processDefinition;
}
public void setProcessDefinition(ProcessDefinitionImpl processDefinition) {
this.processDefinition = processDefinition;
this.processDefinitionId = processDefinition.getId();
}
// activity getter and setter ///////////////////////////////////////////////
// this getter and setter is special because persistence is based on the //
// activity name. //
/////////////////////////////////////////////////////////////////////////////
public ActivityImpl getActivity() {
if ( (activity==null)
&& (activityName!=null)
) {
activity = getProcessDefinition().findActivity(activityName);
}
return activity;
}
public void setActivity(ActivityImpl activity) {
this.activity = activity;
if (activity!=null) {
this.activityName = activity.getName();
} else {
this.activityName = null;
}
}
public String getActivityName() {
return activityName;
}
// special getters and setters /////////////////////////////////////////////////
public boolean hasAsyncEndEvent(List<ActivityImpl> leftActivities) {
for (ActivityImpl leftActivity : leftActivities) {
EventImpl endEvent = leftActivity.getEvent(Event.END);
if ( (endEvent!=null)
&& (endEvent.isAsync())
) {
return true;
}
}
return false;
}
public boolean isProcessInstance() {
return parent==null;
}
// getters and setters for scope instance //////////////////////////////////////
public ExecutionImpl getExecution() {
return this;
}
// getters and setters /////////////////////////////////////////////////////////
public TransitionImpl getTransition() {
return transition;
}
public void setTransition(TransitionImpl transition) {
this.transition = transition;
}
public EventImpl getEvent() {
return event;
}
public ObservableElementImpl getEventSource() {
return eventSource;
}
public Collection<ExecutionImpl> getExecutions() {
return (Collection) executions;
}
public String getName() {
return name;
}
public ExecutionImpl getParent() {
return parent;
}
public int getPriority() {
return priority;
}
public void setEvent(EventImpl event) {
this.event = event;
}
public void setEventSource(ObservableElementImpl eventSource) {
this.eventSource = eventSource;
}
public void setPriority(int priority) {
this.priority = priority;
}
public ExecutionImpl getProcessInstance() {
return processInstance;
}
public void setProcessInstance(ExecutionImpl processInstance) {
this.processInstance = processInstance;
}
public String getKey() {
return key;
}
public Propagation getPropagation() {
return propagation;
}
public void setPropagation(Propagation propagation) {
this.propagation = propagation;
}
public void setName(String name) {
this.name = name;
}
public void setExecutions(Collection<ExecutionImpl> executions) {
this.executions = executions;
}
public void setParent(ExecutionImpl parent) {
this.parent = parent;
}
public ExecutionImpl getSuperProcessExecution() {
return superProcessExecution;
}
public void setSuperProcessExecution(ExecutionImpl superProcessExecution) {
this.superProcessExecution = superProcessExecution;
}
public ExecutionImpl getSubProcessInstance() {
return subProcessInstance;
}
public void setSubProcessInstance(ExecutionImpl subProcessExecution) {
this.subProcessInstance = subProcessExecution;
}
public void setKey(String key) {
this.key = key;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Long getHistoryActivityInstanceDbid() {
return historyActivityInstanceDbid;
}
public void setHistoryActivityInstanceDbid(Long historyActivityInstanceDbid) {
this.historyActivityInstanceDbid = historyActivityInstanceDbid;
}
public Date getHistoryActivityStart() {
return historyActivityStart;
}
public void setHistoryActivityStart(Date historyActivityStart) {
this.historyActivityStart = historyActivityStart;
}
public String getProcessDefinitionId() {
return processDefinitionId;
}
public int getEventListenerIndex() {
return eventListenerIndex;
}
public void setEventListenerIndex(int eventListenerIndex) {
this.eventListenerIndex = eventListenerIndex;
}
public AtomicOperation getEventCompletedOperation() {
return eventCompletedOperation;
}
public void setEventCompletedOperation(AtomicOperation eventCompletedOperation) {
this.eventCompletedOperation = eventCompletedOperation;
}
}