Package org.jbpm.pvm.internal.model

Source Code of org.jbpm.pvm.internal.model.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.internal.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.StringTokenizer;

import org.jbpm.api.Execution;
import org.jbpm.api.JbpmException;
import org.jbpm.api.activity.ActivityExecution;
import org.jbpm.api.job.Job;
import org.jbpm.api.job.Timer;
import org.jbpm.api.listener.EventListenerExecution;
import org.jbpm.api.model.Event;
import org.jbpm.api.model.OpenExecution;
import org.jbpm.api.task.Assignable;
import org.jbpm.api.task.AssignmentHandler;
import org.jbpm.internal.log.Log;
import org.jbpm.pvm.internal.client.ClientProcessDefinition;
import org.jbpm.pvm.internal.client.ClientProcessInstance;
import org.jbpm.pvm.internal.env.Context;
import org.jbpm.pvm.internal.env.Environment;
import org.jbpm.pvm.internal.env.ExecutionContext;
import org.jbpm.pvm.internal.history.HistoryEvent;
import org.jbpm.pvm.internal.history.events.ActivityEnd;
import org.jbpm.pvm.internal.history.events.ActivityStart;
import org.jbpm.pvm.internal.history.events.AutomaticEnd;
import org.jbpm.pvm.internal.history.events.DecisionEnd;
import org.jbpm.pvm.internal.history.events.ProcessInstanceCreate;
import org.jbpm.pvm.internal.history.events.ProcessInstanceEnd;
import org.jbpm.pvm.internal.job.JobImpl;
import org.jbpm.pvm.internal.job.MessageImpl;
import org.jbpm.pvm.internal.model.op.AtomicOperation;
import org.jbpm.pvm.internal.model.op.MoveToChildActivity;
import org.jbpm.pvm.internal.model.op.Signal;
import org.jbpm.pvm.internal.script.ScriptManager;
import org.jbpm.pvm.internal.session.DbSession;
import org.jbpm.pvm.internal.session.MessageSession;
import org.jbpm.pvm.internal.session.RepositorySession;
import org.jbpm.pvm.internal.session.TimerSession;
import org.jbpm.pvm.internal.task.AssignableDefinitionImpl;
import org.jbpm.pvm.internal.task.SwimlaneDefinitionImpl;
import org.jbpm.pvm.internal.task.SwimlaneImpl;
import org.jbpm.pvm.internal.type.Variable;
import org.jbpm.pvm.internal.util.EqualsUtil;
import org.jbpm.pvm.internal.util.Priority;

/**
* @author Tom Baeyens
*/
public class ExecutionImpl extends ScopeInstanceImpl
                           implements ClientProcessInstance,
                                      ActivityExecution,
                                      EventListenerExecution,
                                      Serializable {

  private static final long serialVersionUID = 1L;

  private static final Log log = Log.getLog(ExecutionImpl.class.getName());
 
  /** an optional name for this execution.  can be used to
   * differentiate concurrent paths of execution like e.g.
   * the 'shipping' and 'billing' paths. */
  protected String name;

  /** a key for this execution. typically this is an externally provided reference
   * that is unique within the scope of the process definition.  */
  protected String key;

  /** a unique id for this execution. */
  protected String id;

  /** are concurrent executions that related to this execution. */
  protected Collection<ExecutionImpl> executions = new ArrayList<ExecutionImpl>();

  /** the parent child relation of executions is convenient for some forms of
   * concurrency. */
  protected ExecutionImpl parent = null;
  protected ExecutionImpl processInstance;
 
  /** the super process link in case this is a sub process execution */ 
  protected ExecutionImpl superProcessExecution;
 
  /** the sub process link in case of sub process execution */
  protected ExecutionImpl subProcessInstance;

  /** swimlanes */
  protected Map<String, SwimlaneImpl> swimlanes = new HashMap<String, SwimlaneImpl>();

  /** reference to the current activity instance history record */
  protected Long historyActivityInstanceDbid;
 
  /** start time of the activity for history purposes (not persisted) */
  protected Date historyActivityStart;

  protected int priority = Priority.NORMAL;

  protected Map<String, Variable> systemVariables = new HashMap<String, Variable>();

  // persistent indicators of the current position ////////////////////////////
 
  /** persistent process definition reference */
  protected String processDefinitionId;
 
  /** persistent activity reference */
  protected String activityName;

  // transient cached indicators of the current position //////////////////////

  /** transient cached process definition.  persistence is managed in {@link #processDefinitionId} */
  protected ProcessDefinitionImpl processDefinition;
 
  /** transient cached current activity pointer.  persistence is managed in {@link #activityName} */
  private ActivityImpl activity;

  /** transition is not to be made persistable by default */
  protected TransitionImpl transition;

  protected EventImpl event;

  protected AtomicOperation eventCompletedOperation;

  protected int eventListenerIndex;

  protected ObservableElementImpl eventSource;

  // cached named executions //////////////////////////////////////////////////
 
  /** caches the child executions by execution name.  This member might be
   * null and is only created from the executions in case its needed.  Note
   * that not all executions are forced to have a name and duplicates are allowed.
   * In case the {@link #executions} change, the executionsMap can be nulled or
   * also updated (but a check needs to be added whether it exists). */
  protected transient Map<String, Execution> executionsMap = null;

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

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

  // construction /////////////////////////////////////////////////////////////
 
  public void initializeProcessInstance(ProcessDefinitionImpl processDefinition, String key) {
    setProcessDefinition(processDefinition);
    setActivity ( (ActivityImpl) processDefinition.getInitial() );
    this.processInstance = this;
    this.state = STATE_CREATED;
    this.key = key;

    IdGenerator idGenerator = processDefinition.getIdGenerator();
    if (idGenerator!=null) {
      this.id = idGenerator.createId(processDefinition, null, this);
    }
   
    HistoryEvent.fire(new ProcessInstanceCreate(), this);
  }

  // execution method : start /////////////////////////////////////////////////

  public void start() {
    if (!STATE_CREATED.equals(state)) {
      throw new JbpmException(toString()+" is already begun: "+state);
    }
    this.state = STATE_ACTIVE_ROOT;
    ExecutionImpl scopedExecution = initializeScopes();
   
    fire(Event.START, getProcessDefinition());
    if (getActivity()!=null) {
      scopedExecution.performAtomicOperation(AtomicOperation.EXECUTE_ACTIVITY);
    }
  }

  protected ExecutionImpl initializeScopes() {
    LinkedList<ActivityImpl> enteredActivities = new LinkedList<ActivityImpl>();

    ActivityImpl initial = getProcessDefinition().getInitial();
    ExecutionImpl scopedExecution = null;
   
    if (initial!=null) {
      enteredActivities.add(initial);
      ActivityImpl parentActivity = initial.getParentActivity();
      while (parentActivity!=null) {
        enteredActivities.addFirst(parentActivity);
        parentActivity = parentActivity.getParentActivity();
      }
     
      scopedExecution = this;

      initializeVariables(getProcessDefinition(), this);
      initializeTimers(getProcessDefinition());
     
      for (ActivityImpl enteredActivity: enteredActivities) {
        if (enteredActivity.isLocalScope()) {
          scopedExecution.setActivity(enteredActivity);
          scopedExecution = scopedExecution.createScope(enteredActivity);
        }
      }
     
      scopedExecution.setActivity(initial);
    }
    return scopedExecution;
  }

  public ExecutionImpl createScope(ScopeElementImpl scope) {
    ExecutionImpl child = createExecution(scope.getName());
   
    setState(STATE_INACTIVE_SCOPE);
    child.setState(STATE_ACTIVE_ROOT);
   
    // copy the current state from the child execution to the parent execution
    child.setActivity(getActivity());
    child.setTransition(getTransition());
    child.setPropagation(getPropagation());
   
    child.initializeVariables(scope, this);
    child.initializeTimers(scope);
   
    return child;
  }
 
  public ExecutionImpl destroyScope(CompositeElementImpl scope) {
    destroyTimers(scope);
   
    // copy the current state from the child execution to the parent execution
    parent.setActivity(getActivity());
    parent.setTransition(getTransition());
    parent.setPropagation(getPropagation());
   
    ExecutionImpl parentsParent = parent.getParent();
    if ( (parentsParent!=null)
         && (STATE_INACTIVE_CONCURRENT_ROOT.equals(parentsParent.getState()))
       ) {
      parent.setState(STATE_ACTIVE_CONCURRENT);
    } else {
      parent.setState(STATE_ACTIVE_ROOT);
    }
   
    // capture the parent execution cause the
    // subsequent invocation of end() will set the parent to null
    ExecutionImpl parent = this.parent;
   
    end();

    return parent;
  }
 
 
  protected void destroyTimers(CompositeElementImpl scope) {
    TimerSession timerSession = Environment.getFromCurrent(TimerSession.class, false);
    if (timerSession!=null) {
      log.debug("destroying timers of "+this);
      List<Timer> timers = timerSession.findTimersByExecution(this);
      for (Timer timer: timers) {
       
        Job job = Environment.getFromCurrent(JobImpl.class, false);
        if (timer!=job) {
          timerSession.cancel(timer);
        }
      }
    }
  }

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

  public String toString() {
    if (getId()!=null) {
      return "execution["+id+"]";
    }
    if (parent==null) {
      return "process-instance";
    }
    return "execution";
  }
 
  // execution method : end ///////////////////////////////////////////////////

  public void end() {
    end(Execution.STATE_ENDED);
  }

  public void end(String state) {
    if (state==null) {
      throw new JbpmException("state is null");
    }

    if (state.equals(STATE_CREATED)
        || state.equals(STATE_ACTIVE_ROOT)
        || state.equals(STATE_ACTIVE_CONCURRENT)
        || state.equals(STATE_INACTIVE_CONCURRENT_ROOT)
        || state.equals(STATE_INACTIVE_SCOPE)
        || state.equals(STATE_INACTIVE_JOIN)
        || state.equals(STATE_SUSPENDED)
        || state.equals(STATE_ASYNC)) {
      throw new JbpmException("invalid end state: "+state);
    }
     
    if (log.isDebugEnabled()) {
      if (state==STATE_ENDED) {
        log.debug(toString()+" ends");
      } else {
        log.debug(toString()+" ends with state "+state);
      }
    }
   
    // end all child executions
   // making a copy of the executions to prevent ConcurrentMoidificationException
    List<ExecutionImpl> executionsToEnd = new ArrayList<ExecutionImpl>(executions);
    for (ExecutionImpl child: executionsToEnd) {
      child.end(state);
    }
   
    setState(state);

    this.propagation = Propagation.EXPLICIT;
   
    if (parent!=null) {
      parent.removeExecution(this);
     
    } else { // this is a process instance
      HistoryEvent.fire(new ProcessInstanceEnd(), this);
      fire(Event.END, getProcessDefinition());

      if (superProcessExecution!=null) {
        log.trace(toString()+" signals super process execution");
        superProcessExecution.signal();
      }
     
      DbSession dbSession = Environment.getFromCurrent(DbSession.class, false);
      if (dbSession!=null) {
        dbSession.deleteProcessInstance(id, false);
      }
    }
  }
 
  public void end(OpenExecution executionToEnd) {
    ((ExecutionImpl)executionToEnd).end();
  }

  public void end(OpenExecution executionToEnd, String state) {
    ((ExecutionImpl)executionToEnd).end(state);
  }


  // execution method : signal ////////////////////////////////////////////////

  public void signal() {
    signal(null, (Map<String,?>)null);
  }

  public void signal(String signal) {
    signal(signal, (Map<String,?>)null);
  }
 
  public void signal(Map<String, ?> parameters) {
    signal(null, parameters);
  }

  public void signal(String signal, Map<String, ?> parameters) {
    checkActive();
    if (getProcessDefinition().isSuspended()) {
      throw new JbpmException("process definition "+getProcessDefinition().getId()+" is suspended");
    }
    propagation = Propagation.EXPLICIT;
    if (getActivity()!=null) {
      performAtomicOperation(new Signal(signal, parameters));
    } else if (transition!=null) {
      performAtomicOperation(AtomicOperation.TRANSITION_START_ACTIVITY);
    } else {
      throw new JbpmException("execution is not in a activity or in a transition");
    }
  }
 
  public void signal(Execution execution) {
    ((ExecutionImpl)execution).signal(null, (Map<String,?>)null);
  }

  public void signal(String signalName, Execution execution) {
    ((ExecutionImpl)execution).signal(signalName, (Map<String,?>)null);
  }

  public void signal(Map<String, ?> parameters, Execution execution) {
    ((ExecutionImpl)execution).signal(null, parameters);
  }

  public void signal(String signalName, Map<String, ?> parameters, Execution execution) {
    ((ExecutionImpl)execution).signal(signalName, parameters);
  }

  // execution method : take ////////////////////////////////////////////////
 
  /** @see Execution#takeDefaultTransition() */
  public void takeDefaultTransition() {
    TransitionImpl defaultTransition = getActivity().getDefaultOutgoingTransition();
    if (defaultTransition==null) {
      throw new JbpmException("there is no default transition in "+getActivity());
    }
    take(defaultTransition);
  }

  /** @see Execution#take(String) */
  public void take(String transitionName) {
    if (getActivity()==null) {
      throw new JbpmException(toString()+" is not positioned in activity");
    }
    TransitionImpl transition = findTransition(transitionName);
    if (transition==null) {
      throw new JbpmException("there is no transition "+transitionName+" in "+getActivity());
    }
    take(transition);
  }

  /** @see Execution#takeDefaultTransition() */
  public void take(Transition transition) {
    checkActive();

    setPropagation(Propagation.EXPLICIT);

    setTransition((TransitionImpl) transition);
   
    fire(Event.END, getActivity(), AtomicOperation.TRANSITION_END_ACTIVITY);
  }

  public void take(Transition transition, Execution execution) {
    ((ExecutionImpl)execution).take(transition);
  }

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

  /** @see Execution#execute(String) */
  public void execute(String activityName) {
    if (getActivity()==null) {
      throw new JbpmException("activity is null");
    }
    Activity nestedActivity = getActivity().getActivity(activityName);
    if (nestedActivity==null) {
      throw new JbpmException("activity "+activityName+" doesn't exist in "+getActivity());
    }
    execute(nestedActivity);
  }
 
  /** @see Execution#execute(Activity) */
  public void execute(Activity activity) {
    if (activity==null) {
      throw new JbpmException("activity is null");
    }
    checkActive();
   
    this.propagation = Propagation.EXPLICIT;
    performAtomicOperation(new MoveToChildActivity((ActivityImpl) activity));
  }
 
  // execution method : waitForSignal /////////////////////////////////////////
 
  public void waitForSignal() {
    propagation = Propagation.WAIT;
  }
 
  // execution method : proceed ///////////////////////////////////////////////

  public void proceed() {
    checkActive();

    // in graph based processDefinition languages we assume that a
    // default transition is available
    TransitionImpl defaultTransition = findDefaultTransition();
    if (defaultTransition!=null) {
      take(defaultTransition);
     
    // in block structured processDefinition languages we assume that
    // there is no default transition and that there is a
    // parent activity of the current activity
    } else {
      ActivityImpl parentActivity = getActivity().getParentActivity();

      // if there is a parent activity
      if (parentActivity!=null) {
        // propagate to the parent
        performAtomicOperation(AtomicOperation.PROPAGATE_TO_PARENT);
       
      else {
        // When we don't know how to proceed, i don't know if it's best to
        // throw new PvmException("don't know how to proceed");
        // or to end the execution.  Because of convenience for testing,
        // I opted to end the execution.
        end();
      }
    }
  }

  public void setActivity(Activity activity, Execution execution) {
    ((ExecutionImpl)execution).setActivity(activity);
  }

  public void setActivity(Activity activity) {
    setActivity((ActivityImpl) activity);
  }

  // events ///////////////////////////////////////////////////////////////////
 
  public void fire(String eventName, ObservableElement eventSource) {
    fire(eventName, (ObservableElementImpl) eventSource, null);
  }

  public void fire(String eventName, ObservableElementImpl observableElement, AtomicOperation eventCompletedOperation) {
    EventImpl event = findEvent(observableElement, eventName);
    if (event!=null) {
      setEvent(event);
      setEventSource(observableElement);
      setEventListenerIndex(0);
      setEventCompletedOperation(eventCompletedOperation);
      performAtomicOperation(AtomicOperation.EXECUTE_EVENT_LISTENER);
    } else {
      if (eventCompletedOperation!=null) {
        performAtomicOperationSync(eventCompletedOperation);
      }
    }
  }
 
  public static EventImpl findEvent(ObservableElementImpl observableElement, String eventName) {
    if (observableElement==null) {
      return null;
    }
   
    EventImpl event = observableElement.getEvent(eventName);
    if (event!=null) {
      return event;
    }
   
    return findEvent(observableElement.getParent(), eventName);
  }

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

  public void moveTo(ActivityImpl destination) {
    // move the execution to the destination
    setActivity(destination);
    transition = null;
  }

  public ExecutionImpl startActivity(ActivityImpl activity) {
    ExecutionImpl propagatingExecution = this;
    if (activity.isLocalScope()) {
      propagatingExecution = createScope(activity);
    }
    fire(Event.START, activity);
    return propagatingExecution;
  }

  public ExecutionImpl endActivity(ActivityImpl activity) {
    ExecutionImpl propagatingExecution = this;
    fire(Event.END, activity);
    if (activity.isLocalScope()) {
      propagatingExecution = destroyScope(activity);
    }
    return propagatingExecution;
  }

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

  public synchronized void performAtomicOperation(AtomicOperation operation) {
    if (operation.isAsync(this)) {
      sendContinuationMessage(operation);
    } else {
      performAtomicOperationSync(operation);
    }
  }
 
  public void sendContinuationMessage(AtomicOperation operation) {
    Environment environment = Environment.getCurrent();
    MessageSession messageSession = environment.get(MessageSession.class);
    if (messageSession==null) {
      throw new JbpmException("no message-session configured to send asynchronous continuation message");
    }
    MessageImpl<?> asyncMessage = operation.createAsyncMessage(this);
    setState(Execution.STATE_ASYNC);
    messageSession.send(asyncMessage);
  }

  public void performAtomicOperationSync(AtomicOperation operation) {
    if (atomicOperations==null) {
     
      // initialise the fifo queue of atomic operations
      atomicOperations = new LinkedList<AtomicOperation>();
      atomicOperations.offer(operation);
     
      ExecutionContext originalExecutionContext = null;
      ExecutionContext executionContext = null;
      Environment environment = Environment.getCurrent();
      if (environment!=null) {
        originalExecutionContext = (ExecutionContext) environment.getContext(Context.CONTEXTNAME_EXECUTION);
        if ( (originalExecutionContext!=null)
             && (originalExecutionContext.getExecution()==this)
           ) {
          originalExecutionContext = null;
        } else {
          executionContext = new ExecutionContext(this);
          environment.setContext(executionContext);
        }
      }
     
      try {
        while (! atomicOperations.isEmpty()) {
          AtomicOperation atomicOperation = atomicOperations.poll();
          atomicOperation.perform(this);
        }

      } catch (RuntimeException e ) {
        throw e;
      } finally {
        atomicOperations = null;
       
        if (executionContext!=null) {
          environment.removeContext(executionContext);
        }
        if (originalExecutionContext!=null) {
          environment.setContext(originalExecutionContext);
        }
      }
    } else {
      atomicOperations.offer(operation);
    }
  }

  public void handleException(ObservableElementImpl observableElement,
                              EventImpl event,
                              EventListenerReference eventListenerReference,
                              Exception exception,
                              String rethrowMessage) {
   
    List<ProcessElementImpl> processElements = new ArrayList<ProcessElementImpl>();
    if (eventListenerReference!=null) {
      processElements.add(eventListenerReference);
    }
    if (event!=null) {
      processElements.add(event);
    }
    while (observableElement!=null) {
      processElements.add(observableElement);
      observableElement = observableElement.getParent();
    }
   
    for (ProcessElementImpl processElement: processElements) {
      List<ExceptionHandlerImpl> exceptionHandlers = processElement.getExceptionHandlers();
      if (exceptionHandlers!=null) {
        for (ExceptionHandlerImpl exceptionHandler: exceptionHandlers) {
          if (exceptionHandler.matches(exception)) {
            try {
              exceptionHandler.handle(this, exception);
              return;
            } catch (Exception rethrowException) {
              if (!exceptionHandler.isRethrowMasked()) {
                exception = rethrowException;
              }
            }
            break;
          }
        }
      }
    }

    log.trace("rethrowing exception cause no exception handler for "+exception);
    ExceptionHandlerImpl.rethrow(exception, rethrowMessage+": "+exception.getMessage());
  }
 
 
  // tasks ////////////////////////////////////////////////////////////////////

  /** tasks and swimlane assignment.
   * SwimlaneDefinitionImpl is base class for TaskDefinitionImpl.
   * Both Task and Swimlane implement Assignable. */
  public void initializeAssignments(AssignableDefinitionImpl assignableDefinition, Assignable assignable) {
    String assigneeExpression = assignableDefinition.getAssigneeExpression();
    if (assigneeExpression!=null) {
      String assignee = resolveAssignmentExpression(assigneeExpression,
                                                    assignableDefinition.getAssigneeExpressionLanguage());
      assignable.setAssignee(assignee);
     
      if (log.isTraceEnabled()) log.trace("task "+name+" assigned to "+assignee+" using expression "+assigneeExpression);
    }
   
    String candidateUsersExpression = assignableDefinition.getCandidateUsersExpression();
    if (candidateUsersExpression!=null) {
      String candidateUsers =
          resolveAssignmentExpression(candidateUsersExpression,
                                      assignableDefinition.getCandidateUsersExpressionLanguage());
      StringTokenizer tokenizer = new StringTokenizer(candidateUsers, ",");
      while (tokenizer.hasMoreTokens()) {
        String candidateUser = tokenizer.nextToken().trim();
        assignable.addCandidateUser(candidateUser);
      }
    }
 
    String candidateGroupsExpression = assignableDefinition.getCandidateGroupsExpression();
    if (candidateGroupsExpression!=null) {
      String candidateGroups =
            resolveAssignmentExpression(candidateGroupsExpression,
                                        assignableDefinition.getCandidateGroupsExpressionLanguage());
      StringTokenizer tokenizer = new StringTokenizer(candidateGroups, ",");
      while (tokenizer.hasMoreTokens()) {
        String candidateGroup = tokenizer.nextToken();
        assignable.addCandidateGroup(candidateGroup);
      }
    }
   
    AssignmentHandler assignmentHandler = assignableDefinition.getAssignmentHandler();
    if (assignmentHandler!=null) {
      try {
        assignmentHandler.assign(assignable, this);
      } catch (Exception e) {
        throw new JbpmException("assignment handler threw exception: " + e, e);
      }
    }
  }

  protected String resolveAssignmentExpression(String expression, String expressionLanguage) {
    ScriptManager scriptManager = Environment.getFromCurrent(ScriptManager.class);
    Object result = scriptManager.evaluateExpression(expression, expressionLanguage);
    if ( (result ==null)
         || (result instanceof String)
       ) {
      return (String) result;
    }
    throw new JbpmException("result of assignment expression "+expression+" is "+result+" ("+result.getClass().getName()+") instead of String");
  }
 
  // swimlanes ////////////////////////////////////////////////////////////////
 
  public void addSwimlane(SwimlaneImpl swimlane) {
    swimlanes.put(swimlane.getName(), swimlane);
    swimlane.setExecution(this);
  }
 
  public SwimlaneImpl getSwimlane(String swimlaneName) {
    return swimlanes.get(swimlaneName);
  }
 
  public void removeSwimlane(SwimlaneImpl swimlane) {
      swimlanes.remove(swimlane.getName());
      swimlane.setExecution(null);
  }

  public SwimlaneImpl getInitializedSwimlane(SwimlaneDefinitionImpl swimlaneDefinition) {
    String swimlaneName = swimlaneDefinition.getName();
    SwimlaneImpl swimlane = swimlanes.get(swimlaneName);
    if (swimlane==null) {
      swimlane = createSwimlane(swimlaneName);
      initializeAssignments(swimlaneDefinition, swimlane);
    }

    return swimlane;
  }

  public SwimlaneImpl createSwimlane(String swimlaneName) {
    SwimlaneImpl swimlane = new SwimlaneImpl();
    swimlane.setName(swimlaneName);
    swimlane.setExecution(this);
    swimlanes.put(swimlaneName, swimlane);
    return swimlane;
  }
 
  // child executions /////////////////////////////////////////////////////////

  public ExecutionImpl createExecution() {
    return createExecution(null);
  }

  public ExecutionImpl createExecution(String name) {
    // ?!
    propagation = Propagation.EXPLICIT;

    // create child execution
    ExecutionImpl childExecution = newChildExecution();
    addExecution(childExecution);

    // initialize new concurrent execution
    childExecution.setProcessDefinition(getProcessDefinition());
    childExecution.processInstance = this.processInstance;
    childExecution.name = name;

    // id generation (after initialization)
    IdGenerator keyGenerator = Environment.getFromCurrent(IdGenerator.class, false);
    if (keyGenerator!=null) {
      childExecution.id = keyGenerator.createId(getProcessDefinition(), this, childExecution);
    }

    log.debug("created "+childExecution);

    return childExecution;
  }

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

  public void addExecution(ExecutionImpl execution) {
    execution.setParent(this);
    executions.add(execution);
    executionsMap = null;
  }

  /** @see Execution#getExecution(String) */
  public ExecutionImpl getExecution(String name) {
    Map<String, Execution> executionsMap = getExecutionsMap();
    return (ExecutionImpl) (executionsMap!=null ? executionsMap.get(name) : null);
  }

  public void removeExecution(ExecutionImpl child) {
    if (executions.contains(child)) {
      if (executions.remove(child)) {
        child.setParent(null);

        // invalidate the executionsMap cache
        executionsMap = null;
      } else {
        throw new JbpmException(child+" is not a child execution of "+this);
      }
    }
  }

  public Map<String, Execution> getExecutionsMap() {
    if ((executionsMap==null)) {
      // initialize executionsMap cache
      executionsMap = new HashMap<String, Execution>();
      for(ExecutionImpl execution: executions) {
        String executionName = execution.getName();
        // the next test makes sure that the first execution wins
        // in case there are multiple executions with the same name
        if (! executionsMap.containsKey(executionName)) {
          executionsMap.put(executionName, execution);
        }
      }
    }
    return executionsMap;
  }
 
  public boolean hasExecution(String name) {
    return ( (getExecutionsMap()!=null)
             && executionsMap.containsKey(name)
           );
  }

  public boolean isActive(String activityName) {
    return findActiveActivityNames().contains(activityName);
  }

  public Set<String> findActiveActivityNames() {
    return addActiveActivityNames(new HashSet<String>());
  }

  protected Set<String> addActiveActivityNames(Set<String> activityNames) {
    if ( ( (state.equals(STATE_ACTIVE_ROOT)) || (state.equals(STATE_ACTIVE_CONCURRENT)) )
         &&
         (activityName!=null)
       ) {
      activityNames.add(activityName);
    }
 
    for (ExecutionImpl childExecution: executions) {
      childExecution.addActiveActivityNames(activityNames);
    }
 
    return activityNames;
  }

  public ExecutionImpl findActiveExecutionIn(String activityName) {
    if ( activityName.equals(this.activityName)
         && isActive()) {
      return this;
    }

    for (ExecutionImpl childExecution: executions) {
      ExecutionImpl found = childExecution.findActiveExecutionIn(activityName);
      if (found!=null) {
        return found;
      }
    }

    return null;
  }
 
  // system variables /////////////////////////////////////////////////////////
 
  public void createSystemVariable(String key, Object value) {
    createSystemVariable(key, value, null);
  }

  public void createSystemVariable(String key, Object value, String typeName) {
    Variable variable = createVariableObject(key, value, typeName, false);
    systemVariables.put(variable.getKey(), variable);
  }

  public void setSystemVariable(String key, Object value) {
    Variable variable = systemVariables.get(key);
    if (variable!=null) {
      log.debug("setting system variable '"+key+"' in '"+this+"' to value '"+value+"'");
      variable.setValue(value);
    } else {
      log.debug("creating system variable '"+key+"' in '"+this+"' to value '"+value+"'");
      createSystemVariable(key, value, null);
    }
  }
 
  public Object getSystemVariable(String key) {
    Variable variable = systemVariables.get(key);
    if (variable!=null) {
      return variable.getValue();
    }
    return null;
  }
 
  public boolean removeSystemVariable(String key) {
    if (systemVariables.containsKey(key)) {
      return (systemVariables.remove(key)!=null);
    }
    return false;
  }

  // sub process creation /////////////////////////////////////////////////////

  public ClientProcessInstance createSubProcessInstance(ClientProcessDefinition processDefinition) {
    return createSubProcessInstance(processDefinition, null);
  }
 
  public ClientProcessInstance createSubProcessInstance(ClientProcessDefinition processDefinition, String key) {
    if (subProcessInstance!=null) {
      throw new JbpmException(toString()+" already has a sub process instance: "+subProcessInstance);
    }
    subProcessInstance = (ExecutionImpl) processDefinition.createProcessInstance(key);
    subProcessInstance.setSuperProcessExecution(this);
    return subProcessInstance;
  }
 
  public ClientProcessInstance startSubProcessInstance(ClientProcessDefinition processDefinition) {
    return startSubProcessInstance(processDefinition, null);
  }
 
  public ClientProcessInstance startSubProcessInstance(ClientProcessDefinition processDefinition, String key) {
    createSubProcessInstance(processDefinition, key);
    subProcessInstance.start();
    return subProcessInstance;
  }

  // state mgmt ///////////////////////////////////////////////////////////////

  /** @see Execution#suspend() */
  public void suspend() {
    super.suspend();
    this.propagation = Propagation.EXPLICIT;
    DbSession dbSession = Environment.getFromCurrent(DbSession.class, false);
    if (dbSession!=null) {
      dbSession.cascadeExecutionSuspend(this);
    }
  }

  /** @see Execution#resume() */
  public void resume() {
    super.resume();
    DbSession hibernatePvmDbSession = Environment.getFromCurrent(DbSession.class, false);
    if (hibernatePvmDbSession!=null) {
      hibernatePvmDbSession.cascadeExecutionResume(this);
    }
  }

  protected void checkActive() {
    if (!isActive()) {
      throw new JbpmException(toString()+" is not active: "+state);
    }
  }
 
  public boolean isEnded() {
    if (Execution.STATE_ENDED.equals(state)) {
      return true;
    }
    if (Execution.STATE_CREATED.equals(state)) {
      return false;
    }
    if (Execution.STATE_ACTIVE_ROOT.equals(state)) {
      return false;
    }
    if (Execution.STATE_ACTIVE_CONCURRENT.equals(state)) {
      return false;
    }
    if (Execution.STATE_INACTIVE_CONCURRENT_ROOT.equals(state)) {
      return false;
    }
    if (Execution.STATE_INACTIVE_SCOPE.equals(state)) {
      return false;
    }
    if (Execution.STATE_SUSPENDED.equals(state)) {
      return false;
    }
    if (Execution.STATE_ASYNC.equals(state)) {
      return false;
    }
    return true;
  }

  ////////////////////////////////////////////////////////////////////////////////

  // overriding the ScopeInstanceImpl methods /////////////////////////////////
 
  public ScopeInstanceImpl getParentVariableScope() {
    return parent;
  }

  public ExecutionImpl getTimerExecution() {
    return this;
  }

  // overridable by process languages /////////////////////////////////////////
 
  /** by default this will use {@link ActivityImpl#findOutgoingTransition(String)} to
   * search for the outgoing transition, which includes a search over the parent chain
   * of the current activity.  This method allows process languages to overwrite this default
   * implementation of the transition lookup by transitionName.*/
  protected TransitionImpl findTransition(String transitionName) {
    return getActivity().findOutgoingTransition(transitionName);
  }

  protected TransitionImpl findDefaultTransition() {
    return getActivity().findDefaultTransition();
  }
 
  // history //////////////////////////////////////////////////////////////////

  public void historyAutomatic() {
    HistoryEvent.fire(new AutomaticEnd(), this);
  }

  public void historyDecision(String transitionName) {
    HistoryEvent.fire(new DecisionEnd(transitionName), this);
  }
 
  public void historyActivityStart() {
    HistoryEvent.fire(new ActivityStart(), this);
  }

  public void historyActivityEnd() {
    HistoryEvent.fire(new ActivityEnd(), this);
  }

  public void historyActivityEnd(String transitionName) {
    HistoryEvent.fire(new ActivityEnd(transitionName), this);
  }

  // equals ///////////////////////////////////////////////////////////////////
  // hack to support comparing hibernate proxies against the real objects
  // since this always falls back to ==, we don't need to overwrite the hashcode
  public boolean equals(Object o) {
    return EqualsUtil.equals(this, o);
  }
 
  // process definition getter and setter /////////////////////////////////////
  // this getter and setter is special because persistence is based on the   //
  // process definition id.                                                  //
  /////////////////////////////////////////////////////////////////////////////
 
 
  public ProcessDefinitionImpl getProcessDefinition() {
    if ( (processDefinition==null)
         && (processDefinitionId!=null)
       ) {
      RepositorySession repositorySession = Environment.getFromCurrent(RepositorySession.class);
      processDefinition = (ProcessDefinitionImpl) repositorySession.findProcessDefinitionById(processDefinitionId);
      if (processDefinition==null) {
        throw new JbpmException("couldn't find process definition "+processDefinitionId+" in the repository");
      }
    }
    return processDefinition;
  }
  public void setProcessDefinition(ProcessDefinitionImpl processDefinition) {
    this.processDefinition = processDefinition;
    this.processDefinitionId = processDefinition.getId();
  }
 
  // activity getter and setter ///////////////////////////////////////////////
  // this getter and setter is special because persistence is based on the   //
  // activity name.                                                          //
  /////////////////////////////////////////////////////////////////////////////
 
  public ActivityImpl getActivity() {
    if ( (activity==null)
         && (activityName!=null)
       ) {
      activity = getProcessDefinition().findActivity(activityName);
    }
    return activity;
  }
 
  public void setActivity(ActivityImpl activity) {
    this.activity = activity;
    if (activity!=null) {
      this.activityName = activity.getName();
    } else {
      this.activityName = null;
    }
  }

  public String getActivityName() {
    return activityName;
  }

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

  public boolean hasAsyncEndEvent(List<ActivityImpl> leftActivities) {
    for (ActivityImpl leftActivity : leftActivities) {
      EventImpl endEvent = leftActivity.getEvent(Event.END);
      if ( (endEvent!=null)
           && (endEvent.isAsync())
         ) {
        return true;
      }
    }
    return false;
  }

  public boolean isProcessInstance() {
    return parent==null;
  }

  // getters and setters for scope instance //////////////////////////////////////
 
  public ExecutionImpl getExecution() {
    return this;
  }

  // getters and setters /////////////////////////////////////////////////////////
 
  public TransitionImpl getTransition() {
    return transition;
  }
  public void setTransition(TransitionImpl transition) {
    this.transition = transition;
  }
  public EventImpl getEvent() {
    return event;
  }
  public ObservableElementImpl getEventSource() {
    return eventSource;
  }
  public Collection<ExecutionImpl> getExecutions() {
    return (Collection) executions;
  }
  public String getName() {
    return name;
  }
  public ExecutionImpl getParent() {
    return parent;
  }
  public int getPriority() {
    return priority;
  }
  public void setEvent(EventImpl event) {
    this.event = event;
  }
  public void setEventSource(ObservableElementImpl eventSource) {
    this.eventSource = eventSource;
  }
  public void setPriority(int priority) {
    this.priority = priority;
  }
  public ExecutionImpl getProcessInstance() {
    return processInstance;
  }
  public void setProcessInstance(ExecutionImpl processInstance) {
    this.processInstance = processInstance;
  }
  public String getKey() {
    return key;
  }
  public Propagation getPropagation() {
    return propagation;
  }
  public void setPropagation(Propagation propagation) {
    this.propagation = propagation;
  }
  public void setName(String name) {
    this.name = name;
  }
  public void setExecutions(Collection<ExecutionImpl> executions) {
    this.executions = executions;
  }
  public void setParent(ExecutionImpl parent) {
    this.parent = parent;
  }
  public ExecutionImpl getSuperProcessExecution() {
    return superProcessExecution;
  }
  public void setSuperProcessExecution(ExecutionImpl superProcessExecution) {
    this.superProcessExecution = superProcessExecution;
  }
  public ExecutionImpl getSubProcessInstance() {
    return subProcessInstance;
  }
  public void setSubProcessInstance(ExecutionImpl subProcessExecution) {
    this.subProcessInstance = subProcessExecution;
  }
  public void setKey(String key) {
    this.key = key;
  }
  public String getId() {
    return id;
  }
  public void setId(String id) {
    this.id = id;
  }
  public Long getHistoryActivityInstanceDbid() {
    return historyActivityInstanceDbid;
  }
  public void setHistoryActivityInstanceDbid(Long historyActivityInstanceDbid) {
    this.historyActivityInstanceDbid = historyActivityInstanceDbid;
  }
  public Date getHistoryActivityStart() {
    return historyActivityStart;
  }
  public void setHistoryActivityStart(Date historyActivityStart) {
    this.historyActivityStart = historyActivityStart;
  }
  public String getProcessDefinitionId() {
    return processDefinitionId;
  }
  public int getEventListenerIndex() {
    return eventListenerIndex;
  }
  public void setEventListenerIndex(int eventListenerIndex) {
    this.eventListenerIndex = eventListenerIndex;
  }
  public AtomicOperation getEventCompletedOperation() {
    return eventCompletedOperation;
  }
  public void setEventCompletedOperation(AtomicOperation eventCompletedOperation) {
    this.eventCompletedOperation = eventCompletedOperation;
  }
}
TOP

Related Classes of org.jbpm.pvm.internal.model.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.