
org.ow2.bonita.pvm.internal.model.ExecutionImpl Maven / Gradle / Ivy
/*
* 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.ow2.bonita.pvm.internal.model;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import org.ow2.bonita.env.Environment;
import org.ow2.bonita.pvm.Execution;
import org.ow2.bonita.pvm.activity.ActivityExecution;
import org.ow2.bonita.pvm.client.ClientProcessDefinition;
import org.ow2.bonita.pvm.client.ClientProcessInstance;
import org.ow2.bonita.pvm.internal.job.JobImpl;
import org.ow2.bonita.pvm.internal.job.MessageImpl;
import org.ow2.bonita.pvm.internal.job.TimerImpl;
import org.ow2.bonita.pvm.internal.model.op.AtomicOperation;
import org.ow2.bonita.pvm.internal.model.op.ExecuteNode;
import org.ow2.bonita.pvm.internal.model.op.MoveToChildNode;
import org.ow2.bonita.pvm.internal.model.op.MoveToParentNode;
import org.ow2.bonita.pvm.internal.model.op.ProceedToDestination;
import org.ow2.bonita.pvm.internal.model.op.Signal;
import org.ow2.bonita.pvm.internal.model.op.TakeTransition;
import org.ow2.bonita.pvm.internal.type.Converter;
import org.ow2.bonita.pvm.internal.type.Type;
import org.ow2.bonita.pvm.internal.type.Variable;
import org.ow2.bonita.pvm.internal.type.VariableTypeResolver;
import org.ow2.bonita.pvm.internal.type.variable.NullVariable;
import org.ow2.bonita.pvm.internal.type.variable.UnpersistableVariable;
import org.ow2.bonita.pvm.internal.util.Clock;
import org.ow2.bonita.pvm.internal.util.EqualsUtil;
import org.ow2.bonita.pvm.internal.util.Priority;
import org.ow2.bonita.pvm.job.Job;
import org.ow2.bonita.pvm.job.Timer;
import org.ow2.bonita.pvm.listener.EventListener;
import org.ow2.bonita.pvm.listener.EventListenerExecution;
import org.ow2.bonita.pvm.model.Comment;
import org.ow2.bonita.pvm.model.Event;
import org.ow2.bonita.pvm.model.IdGenerator;
import org.ow2.bonita.pvm.model.Node;
import org.ow2.bonita.pvm.model.ObservableElement;
import org.ow2.bonita.pvm.model.OpenExecution;
import org.ow2.bonita.pvm.model.Transition;
import org.ow2.bonita.pvm.processlog.ProcessLog;
import org.ow2.bonita.pvm.session.LogSession;
import org.ow2.bonita.pvm.session.MessageSession;
import org.ow2.bonita.pvm.session.TimerSession;
import org.ow2.bonita.util.BonitaRuntimeException;
import org.ow2.bonita.util.ExceptionManager;
import org.ow2.bonita.util.Log;
/**
* @author Tom Baeyens
*/
public class ExecutionImpl implements ClientProcessInstance, ActivityExecution,
EventListenerExecution, Serializable {
private static final long serialVersionUID = 1L;
private static final Log log = Log.getLog(Execution.class.getName());
// atomic operations
public static final AtomicOperation EXECUTE_NODE = new ExecuteNode();
public static final AtomicOperation PROCEED_TO_DESTINATION = new ProceedToDestination();
public static final AtomicOperation TAKE_TRANSITION = new TakeTransition();
public static final AtomicOperation PROPAGATE_TO_PARENT = new MoveToParentNode();
protected long dbid;
protected int dbversion;
/**
* 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;
/** @see Execution */
protected String state;
protected PVMProcessDefinitionImpl processDefinition;
// current position /////////////////////////////////////////////////////////
/** current node */
protected NodeImpl node;
/** transition is not to be made persistable by default */
protected TransitionImpl transition;
/**
* the node from which the transition was taken. This can be different from
* the transition source in case a transition of an eclosing node was taken.
* transitionOrigin is not to be made persistable by default
*/
protected NodeImpl transitionOrigin;
protected EventImpl event;
protected ObservableElementImpl eventSource;
/** are concurrent executions that related to this execution. */
protected Collection executions;
/**
* the parent child relation of executions is convenient for some forms of
* concurrency.
*/
protected ExecutionImpl parent = null;
protected ExecutionImpl processInstance;
boolean hasVariables;
protected Map variables;
protected Set> jobs = new HashSet>();
/** 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;
/** the free text comments users make on this execution */
protected Set comments;
protected int priority = Priority.NORMAL;
/**
* maintains the index of the next log record. That way, the logs don't have
* to be loaded to add one. Instead, for each log that is added to this
* execution, the nextLogIndex is used and incremented.
*/
protected int nextLogIndex;
/**
* 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 executionsMap = null;
// transient members
/** the queue of atomic operations to be performed for this execution. */
protected Queue atomicOperations;
public enum Propagation {
UNSPECIFIED, WAIT, EXPLICIT
}
protected Propagation propagation = null;
protected Node previousNode;
protected Transition previousTransition;
protected Exception exception;
// It's important that this refers to a separate entity. This
// execution must do nullpointercheck before accessing the
// process modifications. That way, good performance is guaranteed
// for the most common scenario: a persistent execution without
// process modifications.
protected ProcessModificationsImpl processModifications;
// construction /////////////////////////////////////////////////////////////
public void initializeProcessInstance(
final PVMProcessDefinitionImpl processDefinition, final String key) {
this.processDefinition = processDefinition;
this.node = processDefinition.getInitial();
this.processInstance = this;
this.state = STATE_CREATED;
this.key = key;
final IdGenerator keyGenerator = Environment.getFromCurrent(IdGenerator.class,
false);
if (keyGenerator != null) {
this.id = keyGenerator.createId(processDefinition, null, this);
}
}
// execution method : start /////////////////////////////////////////////////
public void begin() {
if (this.state != STATE_CREATED) {
String message = ExceptionManager.getInstance().getFullMessage(
"bp_EI_1 ", toString(), this.state);
throw new BonitaRuntimeException(message);
}
final ExecutionImpl scopedExecution = initializeScopes();
this.state = STATE_ACTIVE;
fire(Event.PROCESS_BEGIN, this.processDefinition);
if (this.node != null) {
scopedExecution.performAtomicOperation(EXECUTE_NODE);
}
}
protected ExecutionImpl initializeScopes() {
final LinkedList enteredNodes = new LinkedList();
final NodeImpl initial = this.processDefinition.getInitial();
ExecutionImpl scopedExecution = null;
if (initial != null) {
enteredNodes.add(initial);
NodeImpl parentNode = initial.getParentNode();
while (parentNode != null) {
enteredNodes.addFirst(parentNode);
parentNode = parentNode.getParentNode();
}
scopedExecution = this;
initializeVariables(this.processDefinition, this);
initializeTimers(this.processDefinition);
for (final NodeImpl enteredNode : enteredNodes) {
if (enteredNode.isLocalScope()) {
scopedExecution.setNode(enteredNode);
scopedExecution = scopedExecution.createScope(enteredNode);
}
}
scopedExecution.setNode(initial);
}
return scopedExecution;
}
public ExecutionImpl createScope(final CompositeElementImpl scope) {
final ExecutionImpl child = createExecution(scope.getName());
// copy the current state from the child execution to the parent execution
child.setNode(getNode());
child.setTransition(getTransition());
child.setPropagation(getPropagation());
child.setTransitionOrigin(getTransitionOrigin());
child.setPreviousTransition(getPreviousTransition());
child.setPreviousNode(getPreviousNode());
child.initializeVariables(scope, this);
child.initializeTimers(scope);
return child;
}
public ExecutionImpl destroyScope(final CompositeElementImpl scope) {
destroyTimers(scope);
destroyVariables(scope, this.parent);
// copy the current state from the child execution to the parent execution
getParent().setNode(getNode());
getParent().setTransition(getTransition());
getParent().setPropagation(getPropagation());
getParent().setTransitionOrigin(getTransitionOrigin());
getParent().setPreviousTransition(getPreviousTransition());
getParent().setPreviousNode(getPreviousNode());
end();
this.parent.removeExecution(this);
return this.parent;
}
// basic object methods /////////////////////////////////////////////////////
@Override
public String toString() {
if (this.name != null) {
return "execution[" + this.name + "]";
}
if (this.parent == null) {
return "process-instance";
}
return "execution";
}
// execution method : end ///////////////////////////////////////////////////
public void end() {
end(Execution.STATE_ENDED);
}
public void end(final String state) {
if (state == null) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_2");
throw new BonitaRuntimeException(message);
}
if (state.equals(STATE_ACTIVE) || state.equals(STATE_CREATED)
|| state.equals(STATE_INACTIVE) || state.equals(STATE_SUSPENDED)
|| state.equals(STATE_ASYNC)) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_3", state);
throw new BonitaRuntimeException(message);
}
if (log.isDebugEnabled()) {
if (state == STATE_ENDED) {
log.debug(toString() + " ends");
} else {
log.debug(toString() + " ends with state " + state);
}
}
// end all child executions
if (this.executions != null) {
for (final ExecutionImpl child : this.executions) {
child.end(state);
}
}
lock(state);
this.propagation = Propagation.EXPLICIT;
if (this.parent == null) {
fire(Event.PROCESS_END, this.processDefinition);
if (this.superProcessExecution != null) {
log.trace(toString() + " signals super process execution");
this.superProcessExecution.signal();
}
}
}
public void cancel() {
end(Execution.STATE_CANCELLED);
}
// execution method : suspend and resume ////////////////////////////////////
/** @see Execution#suspend() */
public void suspend() {
if (isSuspended()) {
String message = ExceptionManager.getInstance().getFullMessage(
"bp_EI_4", toString());
throw new BonitaRuntimeException(message);
}
lock(STATE_SUSPENDED);
}
/** @see Execution#resume() */
public void resume() {
if (!isSuspended()) {
String message = ExceptionManager.getInstance().getFullMessage(
"bp_EI_5", toString());
throw new BonitaRuntimeException(message);
}
unlock();
}
// execution method : signal ////////////////////////////////////////////////
@SuppressWarnings("unchecked")
public void signal() {
signal(null, (Map) null);
}
@SuppressWarnings("unchecked")
public void signal(final String signal) {
signal(signal, (Map) null);
}
public void signal(final Map parameters) {
signal(null, parameters);
}
public void signal(final String signal, final Map parameters) {
checkLock();
if (this.node != null) {
performAtomicOperation(new Signal(signal, parameters, this.node));
} else if (this.transition != null) {
performAtomicOperation(ExecutionImpl.PROCEED_TO_DESTINATION);
} else {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_6");
throw new BonitaRuntimeException(message);
}
}
@SuppressWarnings("unchecked")
public void signal(final Execution execution) {
((ExecutionImpl) execution).signal(null, (Map) null);
}
@SuppressWarnings("unchecked")
public void signal(final String signalName, final Execution execution) {
((ExecutionImpl) execution).signal(signalName, (Map) null);
}
public void signal(final Map parameters, final Execution execution) {
((ExecutionImpl) execution).signal(null, parameters);
}
public void signal(final String signalName, final Map parameters,
final Execution execution) {
((ExecutionImpl) execution).signal(signalName, parameters);
}
// execution method : take ////////////////////////////////////////////////
/** @see Execution#takeDefaultTransition() */
public void takeDefaultTransition() {
final TransitionImpl defaultTransition = this.node.getDefaultTransition();
if (defaultTransition == null) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_7", this.node);
throw new BonitaRuntimeException(message);
}
take(defaultTransition);
}
/** @see Execution#take(String) */
public void take(final String transitionName) {
if (this.node == null) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_8", toString());
throw new BonitaRuntimeException(message);
}
final TransitionImpl transition = findTransition(transitionName);
if (transition == null) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_9", transitionName, this.node);
throw new BonitaRuntimeException(message);
}
take(transition);
}
/** @see Execution#takeDefaultTransition() */
public void take(final Transition transition) {
checkLock();
setPropagation(Propagation.EXPLICIT);
setTransition((TransitionImpl) transition);
// copy the current node as the transition origin. the origin can be
// different from
// the transition source in case a transition is taken from an enclosing
// node
setTransitionOrigin(getNode());
setPreviousTransition(null);
performAtomicOperation(TAKE_TRANSITION);
}
public void take(final Transition transition, final Execution execution) {
((ExecutionImpl) execution).take(transition);
}
// execution method : execute ///////////////////////////////////////////////
/** @see Execution#execute(String) */
public void execute(final String nodeName) {
if (this.node == null) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_10");
throw new BonitaRuntimeException(message);
}
final Node nestedNode = this.node.getNode(nodeName);
if (nestedNode == null) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_11", nodeName, this.node);
throw new BonitaRuntimeException(message);
}
execute(nestedNode);
}
/** @see Execution#execute(Node) */
public void execute(final Node node) {
if (node == null) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_12");
throw new BonitaRuntimeException(message);
}
checkLock();
this.propagation = Propagation.EXPLICIT;
performAtomicOperation(new MoveToChildNode((NodeImpl) node));
}
// execution method : waitForSignal /////////////////////////////////////////
public void waitForSignal() {
this.propagation = Propagation.WAIT;
}
// execution method : proceed ///////////////////////////////////////////////
public void proceed() {
checkLock();
// in graph based processDefinition languages we assume that a
// default transition is available
final 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 node of the current node
} else {
final NodeImpl parentNode = this.node.getParentNode();
// if there is a parent node
if (parentNode != null) {
// propagate to the parent
performAtomicOperation(PROPAGATE_TO_PARENT);
} else {
// When we don't know how to proceed, i don't know if it's best to
// throw new BonitaRuntimeException("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 move(final Node destination, final Execution execution) {
((ExecutionImpl) execution).move(destination);
}
public void move(final Node destination) {
checkLock();
setNode((NodeImpl) destination);
}
// execution : internal methods /////////////////////////////////////////////
public void moveTo(final NodeImpl destination) {
// if the parent node needs to know the previous node
if (destination.isPreviousNeeded()) {
setPreviousNode(getNode());
setPreviousTransition(getTransition());
} else {
this.previousNode = null;
this.previousTransition = null;
}
// move the execution to the destination
this.node = destination;
this.transition = null;
this.transitionOrigin = null;
}
public ExecutionImpl beginNode(final NodeImpl node) {
ExecutionImpl propagatingExecution = this;
if (node.isLocalScope()) {
propagatingExecution = createScope(node);
}
fire(Event.NODE_BEGIN, node);
return propagatingExecution;
}
public ExecutionImpl endNode(final NodeImpl node) {
ExecutionImpl propagatingExecution = this;
fire(Event.NODE_END, node);
if (node.isLocalScope()) {
propagatingExecution = destroyScope(node);
}
return propagatingExecution;
}
public synchronized void performAtomicOperation(final AtomicOperation operation) {
if (operation.isAsync(this)) {
sendContinuationMessage(operation);
} else {
performAtomicOperationSync(operation);
}
}
// variables ////////////////////////////////////////////////////////////////
protected void initializeVariables(final CompositeElementImpl scope,
final ExecutionImpl outerExecution) {
// loop over all variable definitions
final List variableDefinitions = scope
.getVariableDefinitions();
if (!variableDefinitions.isEmpty()) {
if (log.isTraceEnabled()) {
log.trace("initializing variables in scope " + scope);
}
this.variables = new HashMap();
for (final VariableDefinitionImpl variableDefinition : variableDefinitions) {
final String key = variableDefinition.getKey();
final Object value = variableDefinition.getSourceValue(outerExecution);
final Type type = variableDefinition.getType();
createVariable(key, value, type);
}
}
}
protected void destroyVariables(final CompositeElementImpl scope,
final ExecutionImpl outerExecution) {
// loop over all variable definitions
final List variableDefinitions = scope
.getVariableDefinitions();
if (variableDefinitions != null) {
if (log.isTraceEnabled()) {
log.trace("destroying var scope " + scope);
}
for (final VariableDefinitionImpl variableDefinition : variableDefinitions) {
final String destination = variableDefinition.getDestination();
if (destination != null) {
final String key = variableDefinition.getKey();
final Object value = variableDefinition.getDestinationValue(this);
outerExecution.setVariable(key, value);
}
}
}
}
public void createVariable(final String key, final Object value) {
createVariable(key, value, null, null);
}
public void createVariable(final String key, final Object value, final String typeName) {
createVariable(key, value, typeName, null);
}
public void createVariable(final String key, final Object value, final Type type) {
createVariable(key, value, null, type);
}
public void createVariable(final String key, final Object value, final String typeName,
Type type) {
if (isFinished()) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_13", key, this, this.state);
throw new BonitaRuntimeException(message);
}
log.debug("create variable '" + key + "' in '" + this + "' with value '"
+ value + "'");
if (type == null) {
final Environment environment = Environment.getCurrent();
if (environment != null) {
final VariableTypeResolver variableTypeResolver = environment
.get(VariableTypeResolver.class);
if (variableTypeResolver != null) {
if (typeName != null) {
type = variableTypeResolver.findTypeByName(typeName);
}
if (type == null) {
type = variableTypeResolver.findTypeByMatch(key, value);
}
}
}
}
Variable variable = null;
if (type != null) {
final Class> variableClass = type.getVariableClass();
try {
log.trace("creating new " + type + " variable " + key);
variable = (Variable) variableClass.newInstance();
} catch (final Exception e) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_14", variableClass.getName());
throw new BonitaRuntimeException(message);
}
final Converter converter = type.getConverter();
variable.setConverter(converter);
} else {
if (value == null) {
log.trace("creating null variable for " + key);
variable = new NullVariable();
} else {
log.trace("creating new unpersistable variable for " + key);
variable = new UnpersistableVariable();
}
}
variable.setKey(key);
variable.setValue(value);
if (this.variables == null) {
this.variables = new HashMap();
}
this.variables.put(variable.getKey(), variable);
this.hasVariables = true;
// TODO add create-variable-log
}
public void setVariable(final String key, final Object value) {
if (isFinished()) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_15", key, this, this.state);
throw new BonitaRuntimeException(message);
/*throw new BonitaRuntimeException("can't update variable '" + key + "' on " + this
+ ": " + this.state);*/
}
Variable variable = getVariableObject(key);
// if there is already a variable instance and it doesn't support the
// current type...
if ((variable != null) && (!variable.supports(value))) {
// delete the old variable instance
log.debug("variable type change. deleting '" + key + "' from '" + this
+ "'");
removeVariable(key);
variable = null;
}
if (variable != null) {
log.debug("updating variable '" + key + "' in '" + this + "' to value '"
+ value + "'");
variable.setValue(value);
} else if (this.parent == null) {
createVariable(key, value, null, null);
} else {
this.parent.setVariable(key, value);
}
}
public void setVariables(final Map variables) {
if (variables != null) {
for (final String key : variables.keySet()) {
final Object value = variables.get(key);
setVariable(key, value);
}
}
}
public Object getVariable(final String key) {
final Variable variable = getVariableObject(key);
if (variable != null) {
return variable.getValue();
}
if (this.parent != null) {
return this.parent.getVariable(key);
}
return null;
}
public Variable getVariableObject(final String key) {
return (this.hasVariables ? (Variable) this.variables.get(key) : null);
}
public boolean hasVariable(final String key) {
return ((this.hasVariables && this.variables.containsKey(key)) || (this.parent != null && this.parent
.hasVariable(key)));
}
public Set getVariableKeys() {
Set variableKeys = null;
if (this.parent != null) {
variableKeys = this.parent.getVariableKeys();
} else {
variableKeys = new HashSet();
}
if (this.hasVariables) {
variableKeys.addAll(this.variables.keySet());
}
return variableKeys;
}
public Map getVariablesMap() {
return this.variables;
}
public Map getVariables() {
Map values = null;
if (this.parent != null) {
values = this.parent.getVariables();
} else {
values = new HashMap();
}
if (this.hasVariables) {
for (final Map.Entry entry : this.variables.entrySet()) {
final String name = entry.getKey();
final Variable variable = entry.getValue();
final Object value = variable.getValue();
values.put(name, value);
}
}
return values;
}
public boolean hasVariables() {
return (this.hasVariables || (this.parent != null && this.parent.hasVariables()));
}
public boolean removeVariable(final String key) {
if (isFinished()) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_16", key, this, this.state);
throw new BonitaRuntimeException(message);
}
Variable variable = null;
if (this.hasVariables) {
variable = this.variables.remove(key);
if (this.variables.isEmpty()) {
this.hasVariables = false;
}
if (variable != null) {
return true;
}
}
if (this.parent != null) {
return this.parent.removeVariable(key);
}
// the actual value is not returned to prevent that an object
// has to be fetched from the db for it to be deleted
return false;
}
public void removeVariables() {
if (this.hasVariables) {
this.variables.clear();
}
this.hasVariables = false;
}
// timers ///////////////////////////////////////////////////////////////////
protected void initializeTimers(final CompositeElementImpl scope) {
// initialize the timers
final Set timerDefinitions = scope.getTimerDefinitions();
if (!timerDefinitions.isEmpty()) {
this.jobs = new HashSet>();
for (final TimerDefinitionImpl timerDefinition : timerDefinitions) {
createTimer(timerDefinition.getEventName(), timerDefinition
.getSignalName(), timerDefinition.getDueDateDescription(),
timerDefinition.getDueDate(), timerDefinition.getRepeat(),
timerDefinition.isExclusive(), timerDefinition.getRetries());
}
}
}
protected void destroyTimers(final CompositeElementImpl scope) {
log.debug("destroying timers of " + toString());
if (this.jobs != null && !this.jobs.isEmpty()) {
// get the TimerSession from the environment
final Environment environment = Environment.getCurrent();
if (environment == null) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_17");
throw new BonitaRuntimeException(message);
}
final TimerSession timerSession = environment.get(TimerSession.class);
if (timerSession == null) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_18");
throw new BonitaRuntimeException(message);
}
final Set jobsSnapshot = new HashSet(this.jobs);
for (final Job job : jobsSnapshot) {
if (job instanceof Timer) {
timerSession.cancel((Timer) job);
this.jobs.remove(job);
}
}
}
}
public void createTimer(final String eventName, final String signalName,
final String dueDateDescription) {
createTimer(eventName, signalName, dueDateDescription, null, null, null,
null);
}
public void createTimer(final String eventName, final String signalName,
final String dueDateDescription, final String repeat) {
createTimer(eventName, signalName, dueDateDescription, null, repeat, null,
null);
}
public void createTimer(final String eventName, final String signalName,
final String dueDateDescription, final Date dueDate, final String repeat,
final Boolean isExclusive, final Integer retries) {
if ((eventName == null) && (signalName == null)) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_19");
throw new BonitaRuntimeException(message);
}
if (log.isDebugEnabled()) {
log.debug("creating timer on " + this.toString());
}
// instantiate the timer
final TimerImpl timerImpl = instantiateTimer();
// create the bidirectional reference
timerImpl.setExecution(this);
this.jobs.add(timerImpl);
// setInverseReference(timerImpl);
// initialise all the timer properties
timerImpl.setEventName(eventName);
timerImpl.setSignalName(signalName);
if (dueDate != null) {
timerImpl.setDueDate(dueDate);
} else {
timerImpl.setDueDateDescription(dueDateDescription);
}
// the if is added to keep the original default
if (isExclusive != null) {
timerImpl.setExclusive(isExclusive);
}
// the if is added to keep the original default
if (retries != null) {
timerImpl.setRetries(retries);
}
// the if is added to keep the original default
if (repeat != null) {
timerImpl.setRepeat(repeat);
}
// get the TimerSession from the environment
final Environment environment = Environment.getCurrent();
if (environment == null) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_20");
throw new BonitaRuntimeException(message);
}
final TimerSession timerSession = environment.get(TimerSession.class);
if (timerSession == null) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_21");
throw new BonitaRuntimeException(message);
}
// schedule the timer with the TimerSession
timerSession.schedule(timerImpl);
}
@SuppressWarnings("unchecked")
public Set getJobs() {
return (Set) this.jobs;
}
protected TimerImpl instantiateTimer() {
return new TimerImpl();
}
// state ////////////////////////////////////////////////////////////////////
/** @see Execution#getState() */
public String getState() {
return this.state;
}
/** @see Execution#lock(String) */
public void lock(final String state) {
if (state == null) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_22");
throw new BonitaRuntimeException(message);
}
checkLock();
log.trace("locking " + this);
this.state = state;
}
/** @see Execution#unlock() */
public void unlock() {
if (STATE_ACTIVE.equals(this.state)) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_23");
throw new BonitaRuntimeException(message);
}
log.trace("unlocking " + this);
this.state = STATE_ACTIVE;
}
/** @see Execution#isActive() */
public boolean isActive() {
return STATE_ACTIVE.equals(this.state);
}
/** @see Execution#isLocked() */
public boolean isLocked() {
return !isActive();
}
/** @see Execution#isSuspended() */
public boolean isSuspended() {
return STATE_SUSPENDED.equals(this.state);
}
/** @see Execution#isEnded() */
public boolean isEnded() {
return STATE_ENDED.equals(this.state);
}
/** @see Execution#isFinished() */
public boolean isFinished() {
return STATE_ENDED.equals(this.state) || STATE_CANCELLED.equals(this.state);
}
// state : internal methods /////////////////////////////////////////////////
protected void checkLock() {
if (!STATE_ACTIVE.equals(this.state)) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_24", toString(), this.state);
throw new BonitaRuntimeException(message);
}
}
// asynchronous continuations ////////////////////////////////////////////////
public void sendContinuationMessage(final AtomicOperation operation) {
final Environment environment = Environment.getCurrent();
final MessageSession messageSession = environment.get(MessageSession.class);
if (messageSession == null) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_25");
throw new BonitaRuntimeException(message);
}
final MessageImpl> asyncMessage = operation.createAsyncMessage(this);
lock("async continuation message " + asyncMessage);
messageSession.send(asyncMessage);
}
public void performAtomicOperationSync(final AtomicOperation operation) {
if (this.atomicOperations == null) {
// initialise the fifo queue of atomic operations
this.atomicOperations = new LinkedList();
this.atomicOperations.offer(operation);
try {
while (!this.atomicOperations.isEmpty()) {
final AtomicOperation atomicOperation = this.atomicOperations.poll();
atomicOperation.perform(this);
}
} catch (final RuntimeException e) {
throw e;
} finally {
this.atomicOperations = null;
}
} else {
this.atomicOperations.offer(operation);
}
}
// events ///////////////////////////////////////////////////////////////////
/** @see Execution#fire(String, ObservableElement) */
public void fire(final String eventName, final ObservableElement eventSource) {
fire(eventName, eventSource, (ObservableElementImpl) eventSource);
}
/**
* fires the event on the given *processElement* and then propagates the event
* up to the *processElement* parent chain.
*/
void fire(final String eventName, final ObservableElement eventSource,
final ObservableElementImpl observableElement) {
if (observableElement != null) {
final EventImpl event = observableElement.getEvent(eventName);
if (event != null) {
if (log.isTraceEnabled()) {
if (observableElement == eventSource) {
log.trace("firing " + event + " on " + eventSource);
} else {
log.trace("firing " + event + " on " + observableElement
+ ", propagated from source " + eventSource);
}
}
fire(event, eventSource, observableElement);
}
propagateEvent(eventName, eventSource, observableElement);
}
}
/**
* this method enables specific process languages to overwrite the event
* propagation behaviour
*/
protected void propagateEvent(final String eventName,
final ObservableElement eventSource, final ObservableElementImpl observableElement) {
fire(eventName, eventSource, observableElement.getParent());
}
/** fires the given event without propagation */
void fire(final EventImpl event, final ObservableElement eventSource,
final ObservableElement observableElement) {
try {
this.event = event;
this.eventSource = (ObservableElementImpl) eventSource;
final List eventListenerReferences = event
.getListenerReferences();
if (eventListenerReferences != null) {
for (final EventListenerReference eventListenerReference : eventListenerReferences) {
if ((observableElement.equals(eventSource)) // this event is not
// propagated
|| (eventListenerReference.isPropagationEnabled()) // propagation
// is allowed
) {
final EventListener eventListener = eventListenerReference.get();
log.trace("executing " + eventListener + " for " + event);
try {
// TODO can/should this invocation be unified with the exception
// handler invocation of the event notification method?
eventListener.notify(this);
} catch (final Exception e) {
log.trace("exception during action: " + e);
handleException((ObservableElementImpl) observableElement, event,
eventListenerReference, e, "couldn't run action "
+ eventListener);
}
}
}
}
} finally {
this.eventSource = null;
this.event = null;
}
}
public void handleException(ObservableElementImpl observableElement,
final EventImpl event, final EventListenerReference eventListenerReference,
Exception exception, final String rethrowMessage) {
final List processElements = new ArrayList();
if (eventListenerReference != null) {
processElements.add(eventListenerReference);
}
if (event != null) {
processElements.add(event);
}
while (observableElement != null) {
processElements.add(observableElement);
observableElement = observableElement.getParent();
}
for (final ProcessElementImpl processElement : processElements) {
final List exceptionHandlers = processElement
.getExceptionHandlers();
if (exceptionHandlers != null) {
for (final ExceptionHandlerImpl exceptionHandler : exceptionHandlers) {
if (exceptionHandler.matches(exception)) {
try {
exceptionHandler.handle(this, exception);
return;
} catch (final Exception rethrowException) {
if (!exceptionHandler.isRethrowMasked()) {
exception = rethrowException;
}
}
break;
}
}
}
}
log.trace("rethrowing exception cause no exception handler for "
+ exception);
ExceptionHandlerImpl.rethrow(exception, rethrowMessage + ": "
+ exception.getMessage());
}
/**
* searches for an event up the process element parent hierarchy starting from
* the given process element and returns an event or null if no such event
* exists.
*/
EventImpl findEvent(final String eventName, ObservableElementImpl observableElement) {
EventImpl event = null;
while ((event == null) && (observableElement != null)) {
event = observableElement.getEvent(eventName);
if (event == null) {
observableElement = observableElement.getParent();
}
}
return event;
}
// comments /////////////////////////////////////////////////////////////////
public Comment createComment(final String message) {
if (message == null) {
String msg = ExceptionManager.getInstance().getFullMessage("bp_EI_26");
throw new BonitaRuntimeException(msg);
}
final CommentImpl comment = new CommentImpl(message);
addComment(comment);
return comment;
}
public void removeComment(final Comment comment) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_27");
throw new UnsupportedOperationException(message);
}
public void addComment(final CommentImpl comment) {
if (comment == null) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_28");
throw new BonitaRuntimeException(message);
}
if (this.comments == null) {
this.comments = new LinkedHashSet();
}
this.comments.add(comment);
}
// child executions /////////////////////////////////////////////////////////
/** @see Execution#createProcessInstance() */
public ExecutionImpl createExecution() {
return createExecution((String) null);
}
public Execution createExecution(final Execution parent) {
return ((ExecutionImpl) parent).createExecution();
}
public Execution createExecution(final String name, final Execution parent) {
return ((ExecutionImpl) parent).createExecution(name);
}
/** @see Execution#createProcessInstance(String) */
public ExecutionImpl createExecution(final String name) {
// creating a child execution implies that this execution
// is not a leave any more and therefore, it is inactivated
if (isActive()) {
lock(STATE_INACTIVE);
this.propagation = Propagation.EXPLICIT;
}
// create child execution
final ExecutionImpl childExecution = newChildExecution();
childExecution.processDefinition = this.processDefinition;
childExecution.processInstance = this.processInstance;
childExecution.node = this.node;
childExecution.state = STATE_ACTIVE;
childExecution.name = name;
log.debug("creating " + childExecution);
// add it to this execution
addExecution(childExecution);
// invalidate the cached executionsMap
this.executionsMap = null;
final IdGenerator keyGenerator = Environment.getFromCurrent(IdGenerator.class,
false);
if (keyGenerator != null) {
childExecution.id = keyGenerator.createId(this.processDefinition, this,
childExecution);
}
return childExecution;
}
protected ExecutionImpl newChildExecution() {
return new ExecutionImpl();
}
public void addExecution(final Execution execution) {
final ExecutionImpl executionImpl = (ExecutionImpl) execution;
executionImpl.parent = this;
if (this.executions == null) {
this.executions = new ArrayList();
}
this.executions.add(executionImpl);
}
/** @see Execution#getExecution(String) */
public ExecutionImpl getExecution(final String name) {
final Map executionsMap = getExecutionsMap();
return (ExecutionImpl) (executionsMap != null ? executionsMap.get(name)
: null);
}
public void removeExecution(final Execution child) {
if (this.executions != null) {
if (this.executions.remove(child)) {
if (this.state.equals(STATE_INACTIVE) && (this.executions.isEmpty())) {
if (log.isTraceEnabled()) {
log.trace("last child execution was removed; unlocking");
}
this.state = STATE_ACTIVE;
} else if (log.isTraceEnabled()) {
log.trace("removed " + child + " from " + this);
}
// invalidate the executionsMap cache
this.executionsMap = null;
} else {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_29", child, this);
throw new BonitaRuntimeException(message);
}
}
}
public void removeExecution(final Execution child, final Execution parent) {
((ExecutionImpl) parent).removeExecution(child);
}
public Map getExecutionsMap() {
if ((this.executionsMap == null) && (this.executions != null)) {
// initialize executionsMap cache
this.executionsMap = new HashMap();
for (final ExecutionImpl execution : this.executions) {
final 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 (!this.executionsMap.containsKey(executionName)) {
this.executionsMap.put(executionName, execution);
}
}
}
return this.executionsMap;
}
public boolean hasExecution(final String name) {
return ((getExecutionsMap() != null) && this.executionsMap.containsKey(name));
}
// sub process creation /////////////////////////////////////////////////////
public ClientProcessInstance createSubProcessInstance(
final ClientProcessDefinition processDefinition) {
return createSubProcessInstance(processDefinition, null);
}
public ClientProcessInstance createSubProcessInstance(
final ClientProcessDefinition processDefinition, final String key) {
if (this.subProcessInstance != null) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_30", toString(), this.subProcessInstance);
throw new BonitaRuntimeException(message);
}
this.subProcessInstance = (ExecutionImpl) processDefinition
.createProcessInstance(key);
this.subProcessInstance.setSuperProcessExecution(this);
return this.subProcessInstance;
}
public ClientProcessInstance beginSubProcessInstance(
final ClientProcessDefinition processDefinition) {
return beginSubProcessInstance(processDefinition, null);
}
public ClientProcessInstance beginSubProcessInstance(
final ClientProcessDefinition processDefinition, final String key) {
createSubProcessInstance(processDefinition, key);
this.subProcessInstance.begin();
return this.subProcessInstance;
}
// node name ////////////////////////////////////////////////////////////////
public String getNodeName() {
if (this.node == null) {
return null;
}
return this.node.getName();
}
// //////////////////////////////////////////////////////////////////////////////
public void addLog(final ProcessLog processLog) {
final Environment environment = Environment.getCurrent();
if (environment != null) {
final LogSession logSession = environment.get(LogSession.class);
if (logSession != null) {
processLog.setExecution(this);
processLog.setTime(Clock.getCurrentTime());
logSession.add(processLog);
}
}
}
public int nextLogIndex() {
return this.nextLogIndex++;
}
// overridable by process languages /////////////////////////////////////////
/**
* by default this will use {@link NodeImpl#findOutgoingTransition(String)} to
* search for the outgoing transition, which includes a search over the parent
* chain of the current node. This method allows process languages to
* overwrite this default implementation of the transition lookup by
* transitionName.
*/
protected TransitionImpl findTransition(final String transitionName) {
return this.node.findOutgoingTransition(transitionName);
}
protected TransitionImpl findDefaultTransition() {
return this.node.findDefaultTransition();
}
// extensions ///////////////////////////////////////////////////////////////
public T getExtension(final Class extensionClass) {
if (extensionClass == null) {
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_31");
throw new BonitaRuntimeException(message);
}
String message = ExceptionManager.getInstance().getFullMessage("bp_EI_32", extensionClass.getName());
throw new BonitaRuntimeException(message);
}
// 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
@Override
public boolean equals(final Object o) {
return EqualsUtil.equals(this, o);
}
// getters and setters
// /////////////////////////////////////////////////////////
public List getComments() {
if (this.comments == null) {
return Collections.emptyList();
}
return new ArrayList(this.comments);
}
public Event getEvent() {
return this.event;
}
public ObservableElement getEventSource() {
return this.eventSource;
}
@SuppressWarnings("unchecked")
public Collection getExecutions() {
return (Collection) this.executions;
}
public String getName() {
return this.name;
}
public ExecutionImpl getParent() {
return this.parent;
}
public int getPriority() {
return this.priority;
}
public PVMProcessDefinitionImpl getProcessDefinition() {
return this.processDefinition;
}
public TransitionImpl getTransition() {
return this.transition;
}
public void setEvent(final EventImpl event) {
this.event = event;
}
public void setEventSource(final ObservableElementImpl eventSource) {
this.eventSource = eventSource;
}
public void setPriority(final int priority) {
this.priority = priority;
}
public void setTransition(final TransitionImpl transition) {
this.transition = transition;
}
public Node getPreviousNode() {
return this.previousNode;
}
public Transition getPreviousTransition() {
return this.previousTransition;
}
public ExecutionImpl getProcessInstance() {
return this.processInstance;
}
public void setProcessInstance(final ExecutionImpl processInstance) {
this.processInstance = processInstance;
}
public void setComments(final Set comments) {
this.comments = comments;
}
public NodeImpl getTransitionOrigin() {
return this.transitionOrigin;
}
public void setTransitionOrigin(final NodeImpl transitionOrigin) {
this.transitionOrigin = transitionOrigin;
}
public Exception getException() {
return this.exception;
}
public void setException(final Exception exception) {
this.exception = exception;
}
public ProcessModificationsImpl getProcessModifications() {
return this.processModifications;
}
public void setProcessModifications(
final ProcessModificationsImpl processModifications) {
this.processModifications = processModifications;
}
public String getKey() {
return this.key;
}
public Propagation getPropagation() {
return this.propagation;
}
public void setPropagation(final Propagation propagation) {
this.propagation = propagation;
}
public void setName(final String name) {
this.name = name;
}
public void setState(final String state) {
this.state = state;
}
public void setExecutions(final Collection executions) {
this.executions = executions;
}
public void setParent(final ExecutionImpl parent) {
this.parent = parent;
}
public void setPreviousNode(final Node previousNode) {
this.previousNode = previousNode;
}
public void setPreviousTransition(final Transition previousTransition) {
this.previousTransition = previousTransition;
}
public void setProcessDefinition(final PVMProcessDefinitionImpl processDefinition) {
this.processDefinition = processDefinition;
}
public ExecutionImpl getSuperProcessExecution() {
return this.superProcessExecution;
}
public void setSuperProcessExecution(final ExecutionImpl superProcessExecution) {
this.superProcessExecution = superProcessExecution;
}
public ExecutionImpl getSubProcessInstance() {
return this.subProcessInstance;
}
public void setSubProcessInstance(final ExecutionImpl subProcessExecution) {
this.subProcessInstance = subProcessExecution;
}
public NodeImpl getNode() {
return this.node;
}
public void setNode(final NodeImpl node) {
this.node = node;
}
public long getDbid() {
return this.dbid;
}
public void setKey(final String key) {
this.key = key;
}
public String getId() {
return this.id;
}
public void setId(final String id) {
this.id = id;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy