Package org.jbpm.pvm.impl

Source Code of org.jbpm.pvm.impl.ExecutionImpl

/*
* 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.impl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jbpm.PvmException;
import org.jbpm.env.Environment;
import org.jbpm.env.session.Message;
import org.jbpm.env.session.MessageSession;
import org.jbpm.pvm.Activity;
import org.jbpm.pvm.Comment;
import org.jbpm.pvm.Event;
import org.jbpm.pvm.Execution;
import org.jbpm.pvm.Node;
import org.jbpm.pvm.ObservableElement;
import org.jbpm.pvm.ProcessDefinition;
import org.jbpm.pvm.ProcessElement;
import org.jbpm.pvm.Transition;
import org.jbpm.pvm.VariableDefinition;
import org.jbpm.pvm.VariableScope;
import org.jbpm.pvm.log.ProcessLog;
import org.jbpm.util.Priority;

/**
* @author Tom Baeyens
*/
public class ExecutionImpl implements Execution, Serializable {

  private static final long serialVersionUID = 1L;

  private static Logger log = Logger.getLogger(Execution.class.getName());

  // atomic operations
  static final AtomicOperation EXECUTE_NODE = new ExecuteNodeOp();
  static final AtomicOperation PROCEED_TO_DESTINATION = new ProceedToDestinationOp();
  static final AtomicOperation TAKE_TRANSITION = new TakeTransitionOp();
  static final AtomicOperation PROPAGATE_TO_PARENT = new MoveToParentNodeOp();
  static final AtomicOperation DEFAULT_SIGNAL = new SignalOp(null, null);
 
  static enum UserCodeType {
    EVENT_ACTIVITY, NODE_BEHAVIOUR, CONDITION, EXCEPTION_HANDLER,CLIENTAPII
  }

  protected long dbid;
  protected int dbversion;

  protected String key;
  protected String name;
 
  /** @see Execution */ 
  protected String state = Execution.STATE_ACTIVE;

  protected ProcessDefinitionImpl processDefinition;
 
  // current position /////////////////////////////////////////////////////////
 
  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<ExecutionImpl> executions;

  /** the parent child relation of executions is convenient for some forms of
   * concurrency. */
  protected ExecutionImpl parent = null;
  protected ExecutionImpl processInstance;
  
  protected Set<CommentImpl> 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 wether it exists). */
  protected transient Map<String, Execution> executionsMap = null;

  /** stack of runtime variable scopes.  An execution scope can maintain
   * runtime information related to a local processDefinition scope.  Scopes of
   * parent executions should be considered as appended to this stack.
   * Execution scopes are ordered from inner to outer.  So when searching
   * for a scope, it should be done in the normal iteration order.
   * New execution scopes should always be added as the first element. */
  protected List<VariableScope> variableScopes;
 
  // transient members

  /** the queue of atomic operations to be performed for this execution. */
  protected Queue<AtomicOperation> atomicOperations;

  enum Propagation {
    UNSPECIFIED, WAIT, EXPLICIT
  }
  protected Propagation propagation = null;

  protected Node previousNode;
  protected Transition previousTransition;
  protected UserCodeType userCodeType;
  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 /////////////////////////////////////////////////////////////

  /** invoked as part of {@link ProcessDefinitionImpl#startExecution()}.  Specific processDefinition
   * languages can customize the start behaviour by subclassing and overriding this method. */
  public void start() {
    fire(ProcessDefinition.EVENT_PROCESS_START, processDefinition);
    if (node!=null) {
      performAtomicOperation(EXECUTE_NODE);
    }
  }

  // basic object methods /////////////////////////////////////////////////////

  public String toString() {
    if (name!=null) {
      return "execution["+name+"]";
    }
    return "execution";
  }
 
  // execution method : signal ////////////////////////////////////////////////

  /** @see Execution#signal() */
  public void signal() {
    signal(null, null);
  }

  /** @see Execution#signal(String) */
  public void signal(String signal) {
    signal(signal, null);
  }
 
  /** @see Execution#signal(Map) */
  public void signal(Map<String, Object> parameters) {
    signal(null, parameters);
  }
 
  /** @see Execution#signal(String, Map) */
  public void signal(String signal, Map<String, Object> parameters) {
    if (userCodeType==UserCodeType.EVENT_ACTIVITY) {
      throw new PvmException("invocation of signal is not allowed inside an event. only node behaviour activities can control the propagation of execution");
    }
    checkLock();
    if (node!=null) {
      performAtomicOperation(new SignalOp(signal, parameters));
    } else if (transition!=null) {
      performAtomicOperation(PROCEED_TO_DESTINATION);
    } else {
      throw new PvmException("execution is not in a node or in a transition");
    }
  }

  // execution method : take ////////////////////////////////////////////////
 
  /** @see Execution#takeDefaultTransition() */
  public void takeDefaultTransition() {
    if (userCodeType==UserCodeType.EVENT_ACTIVITY) {
      throw new PvmException("invocation of takeDefaultTransition is not allowed inside an event. only node behaviour activities can control the propagation of execution");
    }
    TransitionImpl defaultTransition = node.getDefaultTransition();
    if (defaultTransition==null) {
      throw new PvmException("there is no default transition in "+node);
    }
    take(defaultTransition);
  }

  /** @see Execution#take(String) */
  public void take(String transitionName) {
    if (userCodeType==UserCodeType.EVENT_ACTIVITY) {
      throw new PvmException("invocation of take is not allowed inside an event. only node behaviour activities can control the propagation of execution");
    }
    if (node==null) {
      throw new PvmException(toString()+" is not positioned in node");
    }
    TransitionImpl transition = findTransition(transitionName);
    if (transition==null) {
      throw new PvmException("there is no transition "+transitionName+" in "+node);
    }
    take(transition);
  }

  /** @see Execution#takeDefaultTransition() */
  public void take(Transition transition) {
    if (userCodeType==UserCodeType.EVENT_ACTIVITY) {
      throw new PvmException("invocation of take is not allowed inside an event. only node behaviour activities can control the propagation of execution");
    }
    checkLock();

    this.propagation = Propagation.EXPLICIT;
    this.transition = (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
    this.transitionOrigin = node;
    this.previousTransition = null;

    performAtomicOperation(TAKE_TRANSITION);
  }

  // execution method : execute ///////////////////////////////////////////////

  /** @see Execution#execute(String) */
  public void execute(String nodeName) {
    if (node==null) {
      throw new PvmException("node is null");
    }
    Node nestedNode = node.getNode(nodeName);
    if (nestedNode==null) {
      throw new PvmException("node "+nodeName+" doesn't exist in "+node);
    }
    execute(nestedNode);
  }
 
  /** @see Execution#execute(Node) */
  public void execute(Node node) {
    if (userCodeType==UserCodeType.EVENT_ACTIVITY) {
      throw new PvmException("invocation of execute is not allowed inside an event. only node behaviour activities can control the propagation of execution");
    }
    if (node==null) {
      throw new PvmException("node is null");
    }
    checkLock();
   
    this.propagation = Propagation.EXPLICIT;
    performAtomicOperation(new MoveToChildNodeOp((NodeImpl) node));
  }
 
  // execution method : waitForSignal /////////////////////////////////////////
 
  public void waitForSignal() {
    if (userCodeType==UserCodeType.EVENT_ACTIVITY) {
      throw new PvmException("invocation of waitForSignal is not allowed inside an event. only node behaviour activities can control the propagation of execution");
    }
    propagation = Propagation.WAIT;
  }
 
  // execution method : proceed ///////////////////////////////////////////////

  public void proceed() {
    if (userCodeType==UserCodeType.EVENT_ACTIVITY) {
      throw new PvmException("invocation of proceed is not allowed inside an event. only node behaviour activities can control the propagation of execution");
    }
    checkLock();

    // in graph based processDefinition langauges 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 node of the current node
    } else {
      NodeImpl parentNode = 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 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 move(Node destination) {
    if (userCodeType==UserCodeType.EVENT_ACTIVITY) {
      throw new PvmException("invocation of move is not allowed inside an event. only node behaviour activities can control the propagation of execution");
    }
    checkLock();

    setNode((NodeImpl) destination);
  }

  // execution : internal methods /////////////////////////////////////////////

  protected void moveTo(NodeImpl destination) {
    // if the parent node needs to know the previous node
    if (destination.isPreviousNeeded()) {
      this.previousNode = node;
      this.previousTransition = transition;
    } else {
      this.previousNode = null;
      this.previousTransition = null;
    }

    // move the execution to the destination
    node = destination;
    transition = null;
    transitionOrigin = null;
  }
 
  public synchronized void performAtomicOperation(AtomicOperation operation) {
    if (operation.isAsync(this)) {
      sendContinuationMessage(operation);
    } else {
      performAtomicOperationSync(operation);
    }
  }
 
  // state ////////////////////////////////////////////////////////////////////

  /** @see Execution#getState() */
  public String getState() {
    return state;
  }

  /** @see Execution#lock(String) */
  public void lock(String state) {
    if (userCodeType==UserCodeType.EVENT_ACTIVITY) {
      throw new PvmException("lock is not allowed inside an event. only node behaviour activities can control the propagation of execution");
    }
    if (state==null) {
      throw new PvmException("given state is null");
    }
    checkLock();
    log.finest("locking "+this);
    this.state = state;
  }
 
  /** @see Execution#unlock() */
  public void unlock() {
    if (STATE_ACTIVE.equals(state)) {
      throw new PvmException("state is already active");
    }
    log.finest("unlocking "+this);
    this.state = STATE_ACTIVE;
  }

  /** @see Execution#isActive() */
  public boolean isActive() {
    return STATE_ACTIVE.equals(state);
  }

  /** @see Execution#isLocked() */
  public boolean isLocked() {
    return ! isActive();
  }
 
  /** @see Execution#isSuspended() */
  public boolean isSuspended() {
    return STATE_SUSPENDED.equals(state);
  }

  /** @see Execution#isEnded() */
  public boolean isEnded() {
    return STATE_ENDED.equals(state);
  }

  /** @see Execution#isFinished() */
  public boolean isFinished() {
    return STATE_ENDED.equals(state)
           || STATE_CANCELLED.equals(state);
  }

  /** @see Execution#end() */
  public void end() {
    end(Execution.STATE_ENDED, true);
  }

  /** @see Execution#end(String) */
  public void end(String state) {
    end(state, true);
  }
 
  /** @see Execution#end(String, boolean) */
  public void end(String state, boolean remove) {
    if (state==null) {
      throw new PvmException("state is null");
    }
    if (userCodeType==UserCodeType.EVENT_ACTIVITY) {
      throw new PvmException("end is not allowed inside an event. only node behaviour activities can control the propagation of execution");
    }
    log.finest(toString()+" ends with state "+state);
    lock(state);
    this.propagation = Propagation.EXPLICIT;
    fire(ProcessDefinition.EVENT_PROCESS_END, processDefinition);
    if ( (parent!=null)
         && (remove)
       ) {
      parent.removeExecution(this);
    }
  }

  /** @see Execution#cancel() */
  public void cancel() {
    if (userCodeType==UserCodeType.EVENT_ACTIVITY) {
      throw new PvmException("cancel is not allowed inside an event. only node behaviour activities can control the propagation of execution");
    }
    end(Execution.STATE_CANCELLED, true);
  }

  /** @see Execution#suspend() */
  public void suspend() {
    if ( userCodeType==UserCodeType.EVENT_ACTIVITY
         || userCodeType==UserCodeType.NODE_BEHAVIOUR) {
      throw new PvmException("suspend is not allowed inside an activity.");
    }
    if (isSuspended()) {
      throw new PvmException(toString()+" is suspended");
    }
    lock(STATE_SUSPENDED);
  }

  /** @see Execution#resume() */
  public void resume() {
    if (userCodeType==UserCodeType.EVENT_ACTIVITY
        || userCodeType==UserCodeType.NODE_BEHAVIOUR) {
      throw new PvmException("resume is not allowed inside an activity.");
    }
    if (! isSuspended()) {
      throw new PvmException(toString()+" is not suspended");
    }
    unlock();
  }

  // state : internal methods /////////////////////////////////////////////////

  protected void checkLock() {
    if (!STATE_ACTIVE.equals(state)) {
      throw new PvmException(toString()+" is not active: "+state);
    }
  }

  // asynchronous continuations //////////////////////////////////////////////// 

  public void sendContinuationMessage(AtomicOperation operation) {
    Environment environment = Environment.getCurrent();
    MessageSession messageSession = environment.get(MessageSession.class);
    Message asyncMessage = operation.createAsyncMessage(this);
    lock("async continuation message "+asyncMessage);
    messageSession.send(asyncMessage);
  }

  public void performAtomicOperationSync(AtomicOperation operation) {
    if (atomicOperations==null) {
      atomicOperations = new LinkedList<AtomicOperation>();
      atomicOperations.offer(operation);

      try {
        while (! atomicOperations.isEmpty()) {
          AtomicOperation atomicOperation = atomicOperations.poll();
          atomicOperation.perform(this);
        }

      } catch (RuntimeException e ) {
        throw e;
      } finally {
        atomicOperations = null;
      }
    } else {
      atomicOperations.offer(operation);
    }
  }

  // execution scope /////////////////////////////////////////////////////////////

  public void pushVariableScope(VariableScope executionScope) {
    if (variableScopes==null) {
      variableScopes = new ArrayList<VariableScope>();
    }
    variableScopes.add(0, executionScope);
  }

  public void popVariableScope() {
    if ( (variableScopes!=null)
         && (!variableScopes.isEmpty())
       ) {
      variableScopes.remove(0);
    }
  }

  public List<VariableScope> getVariableScopes() {
    return variableScopes;
  }

  // variables ////////////////////////////////////////////////////////////////

  /** @see Execution#setVariable(String, Object) */
  public void setVariable(String key, Object value) {
    // first search for the most inner variable scope that has a variable for
    // the given key
    VariableScope variableScope = findVariableScope(key);
   
    // if no variable scope exists for the given variable
    if (variableScope==null) {
      // then take the most outer scope of the process instance
      List<VariableScope> processInstanceVariableScopes = processInstance.getVariableScopes();
      if ( (processInstanceVariableScopes!=null)
           && (! processInstanceVariableScopes.isEmpty())
         ) {
        int lastIndex = processInstanceVariableScopes.size()-1;
        variableScope = processInstanceVariableScopes.get(lastIndex);

      } else { // means the process instance doesn't have a variable scope yet
        // create the most outer variable scope
        variableScope = processInstance.createVariableScope();
      }
    }
    variableScope.set(key, value);
  }

  /** @see Execution#setVariables(Map) */
  public void setVariables(Map<String, Object> variables) {
    if (variables!=null) {
      for (String key: variables.keySet()) {
        setVariable(key, variables.get(key));
      }
    }
  }

  public Object getVariable(String key) {
    VariableScope variableScope = findVariableScope(key);
    if (variableScope!=null) {
      return variableScope.get(key);
    }
    return null;
  }

  public boolean hasVariable(String key) {
    return (findVariableScope(key)!=null);
  }

  public Set<String> getVariableKeys() {
    Set<String> variableKeys = new HashSet<String>();
    VariableScopeIterator iter = new VariableScopeIterator(this);
    while (iter.hasNext()) {
      VariableScope variableScope = iter.next();
      Set<String> keys = variableScope.keys();
      if (keys!=null) {
        variableKeys.addAll(keys);
      }
    }
    return variableKeys;
  }
 
  public Map<String, Object> getVariables() {
    Map<String, Object> allVariables = new HashMap<String, Object>();
    VariableScopeIterator iter = new VariableScopeIterator(this);
    while (iter.hasNext()) {
      VariableScope variableScope = iter.next();
      Map<String, Object> localVariables = variableScope.getAll();
      if (localVariables!=null) {
        for(String key: localVariables.keySet()) {
          if (! allVariables.containsKey(key)) {
            allVariables.put(key, localVariables.get(key));
          }
        }
      }
    }
    return allVariables;
  }

  public VariableScope createVariableScope() {
    return createVariableScope(null);
  }
 
  public VariableScope createVariableScope(List<VariableDefinition> variableDefinitions) {
    log.finest("creating variable scope on "+this);
    VariableScope variableScope = newVariableScope();
    pushVariableScope(variableScope);
    if (variableDefinitions!=null) {
      for (VariableDefinition variableDefinition: variableDefinitions) {
        String variableName = variableDefinition.getName();
        Object initialValue = variableDefinition.getInitialValue(this);
        variableScope.set(variableName, initialValue);
      }
    }
    return variableScope;
  }

  protected VariableScope newVariableScope() {
    return new VariableMap(this);
  }
 
  public void destroyVariableScope() {
    if ( (variableScopes!=null)
         && (! variableScopes.isEmpty())
       ) {
      variableScopes.remove(0);
    } else if (parent!=null) {
      parent.destroyVariableScope();
    } else {
      throw new PvmException("no variable scope to destroy");
    }
  }

  public Iterator<VariableScope> getVariableScopeIterator() {
    return new VariableScopeIterator(this);
  }
 

  public void removeVariable(String key) {
    VariableScope variableScope = findVariableScope(key);
    if (variableScope!=null) {
      variableScope.remove(key);
    }
  }
 
  // protected variable helper methods ////////////////////////////////////////

  protected VariableScope getVariableScope() {
    VariableScope variableScope = findVariableScope();
    if (variableScope!=null) {
      return variableScope;
    }
    return processInstance.createVariableScope();
  }
 
  protected VariableScope findVariableScope() {
    VariableScopeIterator iter = new VariableScopeIterator(this);
    if (iter.hasNext()) {
      return iter.next();
    }
    return null;
  }

  protected VariableScope findVariableScope(String key) {
    VariableScopeIterator iter = new VariableScopeIterator(this, key);
    if (iter.hasNext()) {
      return iter.next();
    }
    return null;
  }

  // events ///////////////////////////////////////////////////////////////////

  /** @see Execution#fire(String, ObservableElement) */
  public void fire(String eventName, ObservableElement eventSource) {
    fire(eventName, eventSource, eventSource);
  }

  /** fires the event on the given *processElement* and then propagates the event
   * up to the *processElement* parent chain. */
  void fire(String eventName, ObservableElement eventSource, ObservableElement observableElement) {
    if (observableElement!=null) {
      EventImpl event = (EventImpl) observableElement.getEvent(eventName);
      if (event!=null) {
        if (log.isLoggable(Level.FINEST)) {
          if (observableElement==eventSource) {
            log.finest("firing "+event+" on "+eventSource);
          } else {
            log.finest("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(String eventName, ObservableElement eventSource, ObservableElement observableElement) {
    fire(eventName, eventSource, observableElement.getParent());
  }

  /** fires the given event without propagation */
  void fire(EventImpl event, ObservableElement eventSource, ObservableElement observableElement) {
    try {
      this.event = event;
      this.eventSource = (ObservableElementImpl) eventSource;
     
      List<EventListenerReference> eventListenerReferences = event.getListenerReferences();
     
      if (eventListenerReferences!=null) {
        for (EventListenerReference eventListenerReference: eventListenerReferences) {
         
          if ( (observableElement.equals(eventSource)) // this event is not propagated
               || (eventListenerReference.isPropagationEnabled()) // propagation is allowed
             ) {
            Activity activity = eventListenerReference.get();
           
            log.finest("executing "+activity+" for "+event);
            try {
              this.userCodeType = UserCodeType.EVENT_ACTIVITY;
              activity.execute(this);
            } catch (Exception e) {
              log.finest("exception during action: "+e);
              handleException((ObservableElementImpl) observableElement, event, eventListenerReference, e, "couldn't run action "+activity);
            } finally {
              this.userCodeType = null;
            }
          }
        }
      }

    } finally {
      this.eventSource = null;
      this.event = null;
    }
  }
 
  public void handleException(ObservableElementImpl observableElement,
                              EventImpl actionEvent,
                              EventListenerReference eventListenerReference,
                              Exception exception,
                              String rethrowMessage) {
   
    List<ProcessElementImpl> processElements = new ArrayList<ProcessElementImpl>();
    if (eventListenerReference!=null) {
      processElements.add(eventListenerReference);
    }
    if (actionEvent!=null) {
      processElements.add(actionEvent);
    }
    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.finest("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(String eventName, ObservableElement observableElement) {
    EventImpl event = null;
    while ( (event==null)
            && (observableElement!=null)
          ) {
      event = (EventImpl) observableElement.getEvent(eventName);
      if (event==null) {
        observableElement = observableElement.getParent();
      }
    }
    return event;
  }

  // comments /////////////////////////////////////////////////////////////////
 
  public void addComment(String message) {
    addComment(new CommentImpl(message, this));
  }

  public void addComment(CommentImpl comment) {
    if (comments==null) {
      comments = new HashSet<CommentImpl>();
    }
    comments.add(comment);
  }

  // child executions /////////////////////////////////////////////////////////

  /** @see Execution#createExecution() */
  public ExecutionImpl createExecution() {
    return createExecution(null);
  }

  /** @see Execution#createExecution(String) */
  public ExecutionImpl createExecution(String name) {
    // creating a child execution implies that this execution
    // is not a leave any more and therefore, it is inactivated
    if (userCodeType==UserCodeType.EVENT_ACTIVITY) {
      throw new PvmException("createExecution is not allowed inside an event. only node behaviour activities can control the propagation of execution");
    }
    if (isActive()) {
      lock(STATE_INACTIVE);
      propagation = Propagation.EXPLICIT;
    }

    // create child execution
    ExecutionImpl childExecution = newChildExecution();
    childExecution.processDefinition = this.processDefinition;
    childExecution.processInstance = this.processInstance;
    childExecution.node = this.node;
    childExecution.state = STATE_ACTIVE;
    childExecution.name = name;
    log.fine("creating "+childExecution);
    // add it to this execution
    addExecution(childExecution);
    // invalidate the cached executionsMap
    executionsMap = null;
    return childExecution;
  }

  protected ExecutionImpl newChildExecution() {
    return new ExecutionImpl();
  }

  public void addExecution(Execution execution) {
    ExecutionImpl executionImpl = (ExecutionImpl) execution;
    executionImpl.parent = this;
    if (executions==null) {
      executions = new ArrayList<ExecutionImpl>();
    }
    executions.add(executionImpl);
  }

  /** @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(Execution execution) {
    if (executions!=null) {
      executions.remove(execution);
      // invalidate the executionsMap cache
      executionsMap = null;
    }
  }

  public void removeExecutions() {
    executions = null;
    executionsMap = null;
  }

  /** @see Execution#getExecutionsMap() */
  public Map<String, Execution> getExecutionsMap() {
    if ( (executionsMap==null)
         && (executions!=null)
        ) {
      // initialize executionsMap cache
      executionsMap = new HashMap<String, Execution>();
      for(Execution 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 void addLog(ProcessLog processLog) {
    processLog.setExecution(this);
    // TODO send log to the logging service if it is available in the environment
  }

  public int nextLogIndex() {
    return 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(String transitionName) {
    return node.findOutgoingTransition(transitionName);
  }

  protected TransitionImpl findDefaultTransition() {
    return node.findDefaultTransition();
  }

 
  // extensions ///////////////////////////////////////////////////////////////

  public <T> T getExtension(Class<T> extensionClass) {
    if (extensionClass==null) {
      throw new PvmException("extensionClass is null");
    }
    throw new PvmException("unsuppported extension "+extensionClass.getName());
  }

  // getters and setters /////////////////////////////////////////////////////////

  public NodeImpl getNode() {
    return node;
  }
  public long getDbid() {
    return dbid;
  }
  public Event getEvent() {
    return event;
  }
  public ObservableElement getEventSource() {
    return eventSource;
  }
  public Collection<Execution> getExecutions() {
    return (Collection) executions;
  }
  public String getName() {
    return name;
  }
  public Execution getParent() {
    return parent;
  }
  public int getPriority() {
    return priority;
  }
  public ProcessDefinitionImpl getProcessDefinition() {
    return processDefinition;
  }
  public TransitionImpl getTransition() {
    return transition;
  }
  public void setEvent(EventImpl event) {
    this.event = event;
  }
  public void setEventSource(ObservableElementImpl eventSource) {
    this.eventSource = eventSource;
  }
  public void setNode(NodeImpl node) {
    this.node = node;
  }
  public void setPriority(int priority) {
    this.priority = priority;
  }
  public void setTransition(TransitionImpl transition) {
    this.transition = transition;
  }
  public Node getPreviousNode() {
    return previousNode;
  }
  public Transition getPreviousTransition() {
    return previousTransition;
  }
  public ExecutionImpl getProcessInstance() {
    return processInstance;
  }
  public void setProcess(ProcessDefinitionImpl processDefinition) {
    this.processDefinition = processDefinition;
  }
  public void setProcessInstance(ExecutionImpl processInstance) {
    this.processInstance = processInstance;
  }
  public Set<Comment> getComments() {
    return (Set) comments;
  }
  public void setComments(Set<CommentImpl> comments) {
    this.comments = comments;
  }
  public NodeImpl getTransitionOrigin() {
    return transitionOrigin;
  }
  public void setTransitionOrigin(NodeImpl transitionOrigin) {
    this.transitionOrigin = transitionOrigin;
  }
  public Exception getException() {
    return exception;
  }
  public void setException(Exception exception) {
    this.exception = exception;
  }
  public ProcessModificationsImpl getProcessModifications() {
    return processModifications;
  }
  public void setProcessModifications(ProcessModificationsImpl processModifications) {
    this.processModifications = processModifications;
  }
  public String getKey() {
    return key;
  }
  public void setKey(String key) {
    this.key = key;
  }
  public Propagation getPropagation() {
    return propagation;
  }
  public void setPropagation(Propagation propagation) {
    this.propagation = propagation;
  }
  public UserCodeType getUserCodeType() {
    return userCodeType;
  }
  public void setUserCodeType(UserCodeType userCodeType) {
    this.userCodeType = userCodeType;
  }
  public void setName(String name) {
    this.name = name;
  }
  public void setState(String state) {
    this.state = state;
  }
  public void setProcessDefinition(ProcessDefinitionImpl processDefinition) {
    this.processDefinition = processDefinition;
  }
  public void setExecutions(Collection<ExecutionImpl> executions) {
    this.executions = executions;
  }
  public void setParent(ExecutionImpl parent) {
    this.parent = parent;
  }
  public void setVariableScopes(List<VariableScope> variableScopes) {
    this.variableScopes = variableScopes;
  }
  public void setPreviousNode(Node previousNode) {
    this.previousNode = previousNode;
  }
  public void setPreviousTransition(Transition previousTransition) {
    this.previousTransition = previousTransition;
  }
}
TOP

Related Classes of org.jbpm.pvm.impl.ExecutionImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.