All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.activiti.engine.impl.pvm.runtime.ExecutionImpl Maven / Gradle / Ivy

The newest version!
/* Licensed under the Apache License, ersion 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.activiti.engine.impl.pvm.runtime;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.activiti.engine.EngineServices;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.pvm.PvmActivity;
import org.activiti.engine.impl.pvm.PvmException;
import org.activiti.engine.impl.pvm.PvmExecution;
import org.activiti.engine.impl.pvm.PvmProcessDefinition;
import org.activiti.engine.impl.pvm.PvmProcessElement;
import org.activiti.engine.impl.pvm.PvmProcessInstance;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.delegate.ActivityExecution;
import org.activiti.engine.impl.pvm.delegate.ExecutionListenerExecution;
import org.activiti.engine.impl.pvm.delegate.SignallableActivityBehavior;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.process.ProcessDefinitionImpl;
import org.activiti.engine.impl.pvm.process.TransitionImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Tom Baeyens
 * @author Joram Barrez
 * @author Daniel Meyer
 * @author Falko Menge
 */
public class ExecutionImpl implements
        Serializable,
        ActivityExecution, 
        ExecutionListenerExecution, 
        PvmExecution,
        InterpretableExecution {
  
  private static final long serialVersionUID = 1L;
  
  private static Logger log = LoggerFactory.getLogger(ExecutionImpl.class);
  
  // current position /////////////////////////////////////////////////////////
  
  protected ProcessDefinitionImpl processDefinition;

  /** current activity */
  protected ActivityImpl activity;
  
  /** current transition.  is null when there is no transition being taken. */
  protected TransitionImpl transition = null;

  /** the process instance.  this is the root of the execution tree.  
   * the processInstance of a process instance is a self reference. */
  protected ExecutionImpl processInstance;
  
  /** the parent execution */
  protected ExecutionImpl parent;
  
  /** nested executions representing scopes or concurrent paths */
  protected List executions;
  
  /** super execution, not-null if this execution is part of a subprocess */
  protected ExecutionImpl superExecution;
  
  /** reference to a subprocessinstance, not-null if currently subprocess is started from this execution */
  protected ExecutionImpl subProcessInstance;
  
  /** only available until the process instance is started */
  protected StartingExecution startingExecution;
  
  // state/type of execution ////////////////////////////////////////////////// 
  
  /** indicates if this execution represents an active path of execution.
   * Executions are made inactive in the following situations:
   * 
    *
  • an execution enters a nested scope
  • *
  • an execution is split up into multiple concurrent executions, then the parent is made inactive.
  • *
  • an execution has arrived in a parallel gateway or join and that join has not yet activated/fired.
  • *
  • an execution is ended.
  • *
*/ protected boolean isActive = true; protected boolean isScope = true; protected boolean isConcurrent = false; protected boolean isEnded = false; protected boolean isEventScope = false; protected Map variables = null; // events /////////////////////////////////////////////////////////////////// protected String eventName; protected PvmProcessElement eventSource; protected int executionListenerIndex = 0; // cascade deletion //////////////////////////////////////////////////////// protected boolean deleteRoot; protected String deleteReason; // replaced by ////////////////////////////////////////////////////////////// /** when execution structure is pruned during a takeAll, then * the original execution has to be resolved to the replaced execution. * @see {@link #takeAll(List, List)} {@link OutgoingExecution} */ protected ExecutionImpl replacedBy; // atomic operations //////////////////////////////////////////////////////// /** next operation. process execution is in fact runtime interpretation of the process model. * each operation is a logical unit of interpretation of the process. so sequentially processing * the operations drives the interpretation or execution of a process. * @see AtomicOperation * @see #performOperation(AtomicOperation) */ protected AtomicOperation nextOperation; protected boolean isOperating = false; /* Default constructor for ibatis/jpa/etc. */ public ExecutionImpl() { } public ExecutionImpl(ActivityImpl initial) { startingExecution = new StartingExecution(initial); } // lifecycle methods //////////////////////////////////////////////////////// /** creates a new execution. properties processDefinition, processInstance and activity will be initialized. */ public ExecutionImpl createExecution() { // create the new child execution ExecutionImpl createdExecution = newExecution(); // manage the bidirectional parent-child relation ensureExecutionsInitialized(); executions.add(createdExecution); createdExecution.setParent(this); // initialize the new execution createdExecution.setProcessDefinition(getProcessDefinition()); createdExecution.setProcessInstance(getProcessInstance()); createdExecution.setActivity(getActivity()); return createdExecution; } /** instantiates a new execution. can be overridden by subclasses */ protected ExecutionImpl newExecution() { return new ExecutionImpl(); } public PvmProcessInstance createSubProcessInstance(PvmProcessDefinition processDefinition) { ExecutionImpl subProcessInstance = newExecution(); // manage bidirectional super-subprocess relation subProcessInstance.setSuperExecution(this); this.setSubProcessInstance(subProcessInstance); // Initialize the new execution subProcessInstance.setProcessDefinition((ProcessDefinitionImpl) processDefinition); subProcessInstance.setProcessInstance(subProcessInstance); return subProcessInstance; } public void initialize() { } public void destroy() { setScope(false); } public void remove() { ensureParentInitialized(); if (parent!=null) { parent.ensureExecutionsInitialized(); parent.executions.remove(this); } // remove event scopes: List childExecutions = new ArrayList(getExecutions()); for (InterpretableExecution childExecution : childExecutions) { if(childExecution.isEventScope()) { log.debug("removing eventScope {}", childExecution); childExecution.destroy(); childExecution.remove(); } } } @Override public void destroyScope(String reason) { log.debug("performing destroy scope behavior for execution {}", this); // remove all child executions and sub process instances: List executions = new ArrayList(getExecutions()); for (InterpretableExecution childExecution : executions) { if (childExecution.getSubProcessInstance()!=null) { childExecution.getSubProcessInstance().deleteCascade(reason); } childExecution.deleteCascade(reason); } } // parent /////////////////////////////////////////////////////////////////// /** ensures initialization and returns the parent */ public ExecutionImpl getParent() { ensureParentInitialized(); return parent; } @Override public String getSuperExecutionId() { ensureActivityInitialized(); if (superExecution != null) { return superExecution.getId(); } return null; } public String getParentId() { ensureActivityInitialized(); if(parent != null) { return parent.getId(); } return null; } /** all updates need to go through this setter as subclasses can override this method */ public void setParent(InterpretableExecution parent) { this.parent = (ExecutionImpl) parent; } /** must be called before memberfield parent is used. * can be used by subclasses to provide parent member field initialization. */ protected void ensureParentInitialized() { } // executions /////////////////////////////////////////////////////////////// /** ensures initialization and returns the non-null executions list */ public List getExecutions() { ensureExecutionsInitialized(); return executions; } public ExecutionImpl getSuperExecution() { ensureSuperExecutionInitialized(); return superExecution; } public void setSuperExecution(ExecutionImpl superExecution) { this.superExecution = superExecution; if (superExecution != null) { superExecution.setSubProcessInstance(null); } } // Meant to be overridden by persistent subclasseses protected void ensureSuperExecutionInitialized() { } public ExecutionImpl getSubProcessInstance() { ensureSubProcessInstanceInitialized(); return subProcessInstance; } public void setSubProcessInstance(InterpretableExecution subProcessInstance) { this.subProcessInstance = (ExecutionImpl) subProcessInstance; } // Meant to be overridden by persistent subclasses protected void ensureSubProcessInstanceInitialized() { } public void deleteCascade(String deleteReason) { this.deleteReason = deleteReason; this.deleteRoot = true; performOperation(AtomicOperation.DELETE_CASCADE); } /** removes an execution. if there are nested executions, those will be ended recursively. * if there is a parent, this method removes the bidirectional relation * between parent and this execution. */ public void end() { isActive = false; isEnded = true; performOperation(AtomicOperation.ACTIVITY_END); } /** searches for an execution positioned in the given activity */ public ExecutionImpl findExecution(String activityId) { if ( (getActivity()!=null) && (getActivity().getId().equals(activityId)) ) { return this; } for (ExecutionImpl nestedExecution : getExecutions()) { ExecutionImpl result = nestedExecution.findExecution(activityId); if (result != null) { return result; } } return null; } public List findActiveActivityIds() { List activeActivityIds = new ArrayList(); collectActiveActivityIds(activeActivityIds); return activeActivityIds; } protected void collectActiveActivityIds(List activeActivityIds) { ensureActivityInitialized(); if (isActive && activity!=null) { activeActivityIds.add(activity.getId()); } ensureExecutionsInitialized(); for (ExecutionImpl execution: executions) { execution.collectActiveActivityIds(activeActivityIds); } } /** must be called before memberfield executions is used. * can be used by subclasses to provide executions member field initialization. */ protected void ensureExecutionsInitialized() { if (executions==null) { executions = new ArrayList(); } } // process definition /////////////////////////////////////////////////////// /** ensures initialization and returns the process definition. */ public ProcessDefinitionImpl getProcessDefinition() { ensureProcessDefinitionInitialized(); return processDefinition; } public String getProcessDefinitionId() { return getProcessDefinition().getId(); } /** for setting the process definition, this setter must be used as subclasses can override */ /** must be called before memberfield processDefinition is used. * can be used by subclasses to provide processDefinition member field initialization. */ protected void ensureProcessDefinitionInitialized() { } // process instance ///////////////////////////////////////////////////////// /** ensures initialization and returns the process instance. */ public ExecutionImpl getProcessInstance() { ensureProcessInstanceInitialized(); return processInstance; } public String getProcessInstanceId() { return getProcessInstance().getId(); } public String getBusinessKey() { return getProcessInstance().getBusinessKey(); } public String getProcessBusinessKey() { return getProcessInstance().getBusinessKey(); } /** for setting the process instance, this setter must be used as subclasses can override */ public void setProcessInstance(InterpretableExecution processInstance) { this.processInstance = (ExecutionImpl) processInstance; } /** must be called before memberfield processInstance is used. * can be used by subclasses to provide processInstance member field initialization. */ protected void ensureProcessInstanceInitialized() { } // activity ///////////////////////////////////////////////////////////////// /** ensures initialization and returns the activity */ public ActivityImpl getActivity() { ensureActivityInitialized(); return activity; } /** sets the current activity. can be overridden by subclasses. doesn't * require initialization. */ public void setActivity(ActivityImpl activity) { this.activity = activity; } /** must be called before the activity member field or getActivity() is called */ protected void ensureActivityInitialized() { } // scopes /////////////////////////////////////////////////////////////////// protected void ensureScopeInitialized() { } public boolean isScope() { return isScope; } public void setScope(boolean isScope) { this.isScope = isScope; } // process instance start implementation //////////////////////////////////// public void start() { if(startingExecution == null && isProcessInstanceType()) { startingExecution = new StartingExecution(processDefinition.getInitial()); } performOperation(AtomicOperation.PROCESS_START); } // methods that translate to operations ///////////////////////////////////// public void signal(String signalName, Object signalData) { ensureActivityInitialized(); SignallableActivityBehavior activityBehavior = (SignallableActivityBehavior) activity.getActivityBehavior(); try { activityBehavior.signal(this, signalName, signalData); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new PvmException("couldn't process signal '"+signalName+"' on activity '"+activity.getId()+"': "+e.getMessage(), e); } } @Override public void take(PvmTransition transition, boolean fireActivityCompletedEvent) { // No event firing on executionlevel impl take(transition); } public void take(PvmTransition transition) { if (this.transition!=null) { throw new PvmException("already taking a transition"); } if (transition==null) { throw new PvmException("transition is null"); } setTransition((TransitionImpl) transition); performOperation(AtomicOperation.TRANSITION_NOTIFY_LISTENER_END); } public void executeActivity(PvmActivity activity) { setActivity((ActivityImpl) activity); performOperation(AtomicOperation.ACTIVITY_START); } public List findInactiveConcurrentExecutions(PvmActivity activity) { List inactiveConcurrentExecutionsInActivity = new ArrayList(); List otherConcurrentExecutions = new ArrayList(); if (isConcurrent()) { List< ? extends ActivityExecution> concurrentExecutions = getParent().getExecutions(); for (ActivityExecution concurrentExecution: concurrentExecutions) { if (concurrentExecution.getActivity()==activity) { if (concurrentExecution.isActive()) { throw new PvmException("didn't expect active execution in "+activity+". bug?"); } inactiveConcurrentExecutionsInActivity.add(concurrentExecution); } else { otherConcurrentExecutions.add(concurrentExecution); } } } else { if (!isActive()) { inactiveConcurrentExecutionsInActivity.add(this); } else { otherConcurrentExecutions.add(this); } } if (log.isDebugEnabled()) { log.debug("inactive concurrent executions in '{}': {}", activity, inactiveConcurrentExecutionsInActivity); log.debug("other concurrent executions: {}", otherConcurrentExecutions); } return inactiveConcurrentExecutionsInActivity; } @SuppressWarnings("unchecked") public void takeAll(List transitions, List recyclableExecutions) { transitions = new ArrayList(transitions); recyclableExecutions = (recyclableExecutions!=null ? new ArrayList(recyclableExecutions) : new ArrayList()); if (recyclableExecutions.size()>1) { for (ActivityExecution recyclableExecution: recyclableExecutions) { if (((ExecutionImpl)recyclableExecution).isScope()) { throw new PvmException("joining scope executions is not allowed"); } } } ExecutionImpl concurrentRoot = ((isConcurrent && !isScope) ? getParent() : this); List concurrentActiveExecutions = new ArrayList(); for (ExecutionImpl execution: concurrentRoot.getExecutions()) { if (execution.isActive()) { concurrentActiveExecutions.add(execution); } } if (log.isDebugEnabled()) { log.debug("transitions to take concurrent: {}", transitions); log.debug("active concurrent executions: {}", concurrentActiveExecutions); } if ( (transitions.size()==1) && (concurrentActiveExecutions.isEmpty()) ) { @SuppressWarnings("rawtypes") List recyclableExecutionImpls = (List) recyclableExecutions; for (ExecutionImpl prunedExecution: recyclableExecutionImpls) { // End the pruned executions if necessary. // Some recyclable executions are inactivated (joined executions) // Others are already ended (end activities) if (!prunedExecution.isEnded()) { log.debug("pruning execution {}", prunedExecution); prunedExecution.remove(); } } log.debug("activating the concurrent root {} as the single path of execution going forward", concurrentRoot); concurrentRoot.setActive(true); concurrentRoot.setActivity(activity); concurrentRoot.setConcurrent(false); concurrentRoot.take(transitions.get(0)); } else { List outgoingExecutions = new ArrayList(); recyclableExecutions.remove(concurrentRoot); log.debug("recyclable executions for reused: {}", recyclableExecutions); // first create the concurrent executions while (!transitions.isEmpty()) { PvmTransition outgoingTransition = transitions.remove(0); ExecutionImpl outgoingExecution = null; if (recyclableExecutions.isEmpty()) { outgoingExecution = concurrentRoot.createExecution(); log.debug("new {} created to take transition {}", outgoingExecution, outgoingTransition); } else { outgoingExecution = (ExecutionImpl) recyclableExecutions.remove(0); log.debug("recycled {} to take transition {}",outgoingExecution, outgoingTransition); } outgoingExecution.setActive(true); outgoingExecution.setScope(false); outgoingExecution.setConcurrent(true); outgoingExecutions.add(new OutgoingExecution(outgoingExecution, outgoingTransition, true)); } // prune the executions that are not recycled for (ActivityExecution prunedExecution: recyclableExecutions) { log.debug("pruning execution {}", prunedExecution); prunedExecution.end(); } // then launch all the concurrent executions for (OutgoingExecution outgoingExecution: outgoingExecutions) { outgoingExecution.take(); } } } public void performOperation(AtomicOperation executionOperation) { this.nextOperation = executionOperation; if (!isOperating) { isOperating = true; while (nextOperation!=null) { AtomicOperation currentOperation = this.nextOperation; this.nextOperation = null; if (log.isTraceEnabled()) { log.trace("AtomicOperation: {} on {}", currentOperation, this); } currentOperation.execute(this); } isOperating = false; } } public boolean isActive(String activityId) { return findExecution(activityId)!=null; } // variables //////////////////////////////////////////////////////////////// public Object getVariable(String variableName) { ensureVariablesInitialized(); // If value is found in this scope, return it if (variables.containsKey(variableName)) { return variables.get(variableName); } // If value not found in this scope, check the parent scope ensureParentInitialized(); if (parent != null) { return parent.getVariable(variableName); } // Variable is nowhere to be found return null; } @Override public Object getVariable(String variableName, boolean fetchAllVariables) { return getVariable(variableName); // No support for fetchAllVariables on ExecutionImpl } public Map getVariables() { Map collectedVariables = new HashMap(); collectVariables(collectedVariables); return collectedVariables; } @Override public Map getVariables(Collection variableNames) { Map allVariables = getVariables(); Map filteredVariables = new HashMap(); for (String variableName : variableNames) { filteredVariables.put(variableName, allVariables.get(variableName)); } return filteredVariables; } @Override public Map getVariables(Collection variableNames, boolean fetchAllVariables) { return getVariables(variableNames); // No support for the boolean param } protected void collectVariables(Map collectedVariables) { ensureParentInitialized(); if (parent!=null) { parent.collectVariables(collectedVariables); } ensureVariablesInitialized(); for (String variableName: variables.keySet()) { collectedVariables.put(variableName, variables.get(variableName)); } } public void setVariables(Map variables) { ensureVariablesInitialized(); if (variables!=null) { for (String variableName: variables.keySet()) { setVariable(variableName, variables.get(variableName)); } } } public void setVariable(String variableName, Object value) { ensureVariablesInitialized(); if (variables.containsKey(variableName)) { setVariableLocally(variableName, value); } else { ensureParentInitialized(); if (parent!=null) { parent.setVariable(variableName, value); } else { setVariableLocally(variableName, value); } } } @Override public void setVariable(String variableName, Object value, boolean fetchAllVariables) { setVariable(variableName, value); } public void setVariableLocally(String variableName, Object value) { log.debug("setting variable '{}' to value '{}' on {}", variableName, value, this); variables.put(variableName, value); } @Override public Object setVariableLocal(String variableName, Object value, boolean fetchAllVariables) { return setVariableLocal(variableName, value); } public boolean hasVariable(String variableName) { ensureVariablesInitialized(); if (variables.containsKey(variableName)) { return true; } ensureParentInitialized(); if (parent!=null) { return parent.hasVariable(variableName); } return false; } protected void ensureVariablesInitialized() { if (variables==null) { variables = new HashMap(); } } // process engine convience access ///////////////////////////////////////////////////////////////// public EngineServices getEngineServices() { return Context.getProcessEngineConfiguration(); } // toString ///////////////////////////////////////////////////////////////// public String toString() { if (isProcessInstanceType()) { return "ProcessInstance["+getToStringIdentity()+"]"; } else { return (isEventScope? "EventScope":"")+(isConcurrent? "Concurrent" : "")+(isScope() ? "Scope" : "")+"Execution["+getToStringIdentity()+"]"; } } protected String getToStringIdentity() { return Integer.toString(System.identityHashCode(this)); } // customized getters and setters /////////////////////////////////////////// public boolean isProcessInstanceType() { ensureParentInitialized(); return parent==null; } public void inactivate() { this.isActive = false; } // allow for subclasses to expose a real id ///////////////////////////////// public String getId() { return null; } // getters and setters ////////////////////////////////////////////////////// public TransitionImpl getTransition() { return transition; } public void setTransition(TransitionImpl transition) { this.transition = transition; } public Integer getExecutionListenerIndex() { return executionListenerIndex; } public void setExecutionListenerIndex(Integer executionListenerIndex) { this.executionListenerIndex = executionListenerIndex; } public boolean isConcurrent() { return isConcurrent; } public void setConcurrent(boolean isConcurrent) { this.isConcurrent = isConcurrent; } public boolean isActive() { return isActive; } public void setActive(boolean isActive) { this.isActive = isActive; } public boolean isEnded() { return isEnded; } public void setProcessDefinition(ProcessDefinitionImpl processDefinition) { this.processDefinition = processDefinition; } public String getEventName() { return eventName; } public void setEventName(String eventName) { this.eventName = eventName; } public PvmProcessElement getEventSource() { return eventSource; } public void setEventSource(PvmProcessElement eventSource) { this.eventSource = eventSource; } public String getDeleteReason() { return deleteReason; } public void setDeleteReason(String deleteReason) { this.deleteReason = deleteReason; } public ExecutionImpl getReplacedBy() { return replacedBy; } public void setReplacedBy(InterpretableExecution replacedBy) { this.replacedBy = (ExecutionImpl) replacedBy; } public void setExecutions(List executions) { this.executions = executions; } public boolean isDeleteRoot() { return deleteRoot; } public String getCurrentActivityId() { String currentActivityId = null; if (this.activity != null) { currentActivityId = activity.getId(); } return currentActivityId; } public String getCurrentActivityName() { String currentActivityName = null; if (this.activity != null) { currentActivityName = (String) activity.getProperty("name"); } return currentActivityName; } public void createVariableLocal(String variableName, Object value) { } public void createVariablesLocal(Map variables) { } public Object getVariableLocal(String variableName) { return null; } @Override public Object getVariableLocal(String variableName, boolean fetchAllVariables) { return getVariableLocal(variableName); // No support for fetchAllVariables } @Override public T getVariable(String variableName, Class variableClass) { return variableClass.cast(getVariable(variableName)); } @Override public T getVariableLocal(String variableName, Class variableClass) { return variableClass.cast(getVariableLocal(variableName)); } public Set getVariableNames() { return null; } public Set getVariableNamesLocal() { return null; } public Map getVariablesLocal() { return null; } @Override public Map getVariablesLocal(Collection variableNames) { return null; } @Override public Map getVariablesLocal(Collection variableNames, boolean fetchAllVariables) { return null; } public boolean hasVariableLocal(String variableName) { return false; } public boolean hasVariables() { return false; } public boolean hasVariablesLocal() { return false; } public void removeVariable(String variableName) { } public void removeVariableLocal(String variableName) { } public void removeVariables(Collection variableNames) { } public void removeVariablesLocal(Collection variableNames) { } public void removeVariables() { } public void removeVariablesLocal() { } public void deleteVariablesLocal() { } public Object setVariableLocal(String variableName, Object value) { return null; } public void setVariablesLocal(Map variables) { } public boolean isEventScope() { return isEventScope; } public void setEventScope(boolean isEventScope) { this.isEventScope = isEventScope; } public StartingExecution getStartingExecution() { return startingExecution; } public void disposeStartingExecution() { startingExecution = null; } public String updateProcessBusinessKey(String bzKey) { return getProcessInstance().updateProcessBusinessKey(bzKey); } public String getTenantId() { return null; // Not implemented } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy