Package de.danet.an.workflow.domain

Source Code of de.danet.an.workflow.domain.AbstractActivity

/*
* This file is part of the WfMOpen project.
* Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* $Id: AbstractActivity.java 3001 2009-03-13 22:15:05Z mlipp $
*
* $Log$
* Revision 1.38  2007/09/21 06:19:35  mlipp
* Fixed problem with NamingException during process deletion.
*
* Revision 1.37  2007/09/14 12:34:42  drmlipp
* Improved map initialization.
*
* Revision 1.36  2007/05/03 21:58:19  mlipp
* Internal refactoring for making better use of local EJBs.
*
*/
package de.danet.an.workflow.domain;

import java.io.Serializable;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import java.rmi.RemoteException;
import java.text.ParseException;

import de.danet.an.workflow.internalapi.ExtActivityLocal;
import de.danet.an.workflow.internalapi.ExtApplication;
import de.danet.an.workflow.internalapi.ExtImplementationLocal;
import de.danet.an.workflow.internalapi.ExtProcessLocal;
import de.danet.an.workflow.internalapi.ThreadInfo;
import de.danet.an.workflow.internalapi.ToolInvocationException;
import de.danet.an.workflow.internalapi.ExtActivityLocal.NotStartedState;
import de.danet.an.workflow.localapi.ActivityLocal;
import de.danet.an.workflow.localapi.ProcessLocal;
import de.danet.an.workflow.localapi.TransitionLocal;
import de.danet.an.workflow.localcoreapi.WfActivityLocal;
import de.danet.an.workflow.localcoreapi.WfProcessLocal;
import de.danet.an.workflow.omgcore.AlreadyRunningException;
import de.danet.an.workflow.omgcore.AlreadySuspendedException;
import de.danet.an.workflow.omgcore.CannotCompleteException;
import de.danet.an.workflow.omgcore.CannotResumeException;
import de.danet.an.workflow.omgcore.CannotStopException;
import de.danet.an.workflow.omgcore.CannotSuspendException;
import de.danet.an.workflow.omgcore.InvalidControlOperationException;
import de.danet.an.workflow.omgcore.InvalidDataException;
import de.danet.an.workflow.omgcore.InvalidPerformerException;
import de.danet.an.workflow.omgcore.InvalidResourceException;
import de.danet.an.workflow.omgcore.InvalidStateException;
import de.danet.an.workflow.omgcore.NotAssignedException;
import de.danet.an.workflow.omgcore.NotRunningException;
import de.danet.an.workflow.omgcore.NotSuspendedException;
import de.danet.an.workflow.omgcore.ProcessData;
import de.danet.an.workflow.omgcore.ResultNotAvailableException;
import de.danet.an.workflow.omgcore.TransitionNotAllowedException;
import de.danet.an.workflow.omgcore.UpdateNotAllowedException;
import de.danet.an.workflow.omgcore.WfAssignment;
import de.danet.an.workflow.omgcore.WfAuditEvent;
import de.danet.an.workflow.omgcore.WfResource;
import de.danet.an.workflow.omgcore.WfStateAuditEvent;
import de.danet.an.workflow.omgcore.WfExecutionObject.ClosedState;
import de.danet.an.workflow.omgcore.WfExecutionObject.NotRunningState;
import de.danet.an.workflow.omgcore.WfExecutionObject.OpenState;
import de.danet.an.workflow.omgcore.WfExecutionObject.State;

import de.danet.an.workflow.api.Activity;
import de.danet.an.workflow.api.ActivityUniqueKey;
import de.danet.an.workflow.api.AlreadyAssignedException;
import de.danet.an.workflow.api.InvalidIdException;
import de.danet.an.workflow.api.InvalidKeyException;
import de.danet.an.workflow.api.Participant;
import de.danet.an.workflow.api.ProcessDefinition;
import de.danet.an.workflow.api.Activity.ClosedCompletedState;
import de.danet.an.workflow.api.Activity.DeadlineInfo;
import de.danet.an.workflow.api.Activity.Implementation;
import de.danet.an.workflow.api.Activity.Info;
import de.danet.an.workflow.api.Activity.JoinAndSplitMode;
import de.danet.an.workflow.api.Activity.StartFinishMode;
import de.danet.an.workflow.api.Activity.SubFlowImplementation;
import de.danet.an.workflow.api.Activity.ToolImplementation;
import de.danet.an.workflow.apix.ExtActivity;

import de.danet.an.workflow.spis.aii.ApplicationNotStoppedException;
import de.danet.an.workflow.spis.aii.ResultProvider.ExceptionResult;
import de.danet.an.workflow.spis.ras.ActivityFinder;

/**
* <code>AbstractActivity</code> represents a simple implementation
* of the interface {@link ActivityLocal <code>ActivityLocal</code>}.
* It has the following features:
* <ul>
* <li>It has no transition restrictions (INLINE, SPLIT, JOIN)
* </li>
* <li>The activity is an implementation activity of the type NO.
* No SubFlows (SubProcess) or Loops can be expressed with this activity.
* </li>
* <li>The activity supports the StartMode and the FinishMode properties.
* </li>
* <li>The activity cannot act as a requester, i.e. can not start a
* sub process.
* </li>
* </ul>
*/
public abstract class AbstractActivity extends AbstractExecutionObject
    implements TimedObject, Serializable {

    private static final org.apache.commons.logging.Log logger
  = org.apache.commons.logging.LogFactory.getLog(AbstractActivity.class);

    // Make sure that loading this class loads the new states
    static {
  State s = NotStartedState.UNKNOWN;
    }

    // A mapping of states to sub-state to save the state during
    // DebugState.FORWARDING_EXCEPTION
    private static final int SUB_STATE_RUNNING = 1;
    private static final int SUB_STATE_ABANDONING = 2;
    private static final int SUB_STATE_ABORTING = 3;
    private static final int SUB_STATE_COMPLETING = 4;
    private static final int SUB_STATE_INVOKING = 5;
    private static final int SUB_STATE_TERMINATING = 6;

    private static final Map STATE_TO_SUB_STATE = new HashMap ();
    private static final Map SUB_STATE_TO_STATE = new HashMap ();

    static {
  STATE_TO_SUB_STATE.put
      (RunningState.RUNNING, new Integer (SUB_STATE_RUNNING));
  STATE_TO_SUB_STATE.put
      (DebugState.ABANDONING, new Integer (SUB_STATE_ABANDONING));
  STATE_TO_SUB_STATE.put
      (DebugState.ABORTING, new Integer (SUB_STATE_ABORTING));
  STATE_TO_SUB_STATE.put
      (DebugState.COMPLETING, new Integer (SUB_STATE_COMPLETING));
  STATE_TO_SUB_STATE.put
      (DebugState.INVOKING, new Integer (SUB_STATE_INVOKING));
  STATE_TO_SUB_STATE.put
      (DebugState.TERMINATING, new Integer (SUB_STATE_TERMINATING));
  for (Iterator i = STATE_TO_SUB_STATE.entrySet().iterator();
       i.hasNext();) {
      Map.Entry e = (Map.Entry)i.next ();
      SUB_STATE_TO_STATE.put (e.getValue (), e.getKey ());
  }
    }

    private static final Map CLOSE_STATE_TO_DEBUG_STATE = new HashMap ();
    private static final Map DEBUG_STATE_TO_CLOSE_STATE = new HashMap ();

    static {
  CLOSE_STATE_TO_DEBUG_STATE.put
      (ClosedState.ABORTED, DebugState.ABORTING);
  CLOSE_STATE_TO_DEBUG_STATE.put
      (ClosedState.TERMINATED, DebugState.TERMINATING);
  CLOSE_STATE_TO_DEBUG_STATE.put
      (ClosedCompletedState.NORMAL, DebugState.COMPLETING);
  for (Iterator i = CLOSE_STATE_TO_DEBUG_STATE.entrySet().iterator();
       i.hasNext();) {
      Map.Entry e = (Map.Entry)i.next ();
      DEBUG_STATE_TO_CLOSE_STATE.put (e.getValue (), e.getKey ());
  }
    }

    // The sorted list of transition id's for the split
    private List splitList = null;

    /**
     * Creates a new <code>AbstractActivity</code>.
     */
    public AbstractActivity () {
    }

    //
    // Persistent attribute accessors and associated methods
    //

    /**
     * The getter method for the persistent attribute <code>actImpl</code>.
     *
     * @return the value of actImpl.
     * @see #setPaActImpl
     */
    protected abstract Implementation[] getPaActImpl();

    /**
     * The setter method for the persistent attribute <code>actImpl</code>.
     *
     * @param newActImpl the new value of actImpl.
     * @see #getPaActImpl
     */
    protected abstract void setPaActImpl(Implementation[] newActImpl);

    /**
     * The getter method for the persistent attribute <code>performer</code>.
     *
     * @return the value of performer.
     * @see #setPaPerformer
     */
    protected abstract String getPaPerformer();

    /**
     * The setter method for the persistent attribute <code>performer</code>.
     *
     * @param newPerformer the new value of performer.
     * @see #getPaPerformer
     */
    protected abstract void setPaPerformer(String newPerformer);

    /**
     * The getter method for the persistent attribute <code>executor</code>.
     *
     * @return the value of executor.
     * @see #setPaExecStat
     */
    protected abstract Integer getPaExecStat();

    /**
     * The setter method for the persistent attribute <code>executor</code>.
     *
     * @param newExecutor the new value of executor.
     * @see #getPaExecStat
     */
    protected abstract void setPaExecStat(Integer newExecutor);

    /**
     * The getter method for the persistent attribute <code>startMode</code>.
     *
     * @return the value of startMode.
     * @see #setPaStartMode
     */
    protected abstract StartFinishMode getPaStartMode();

    /**
     * The setter method for the persistent attribute <code>startMode</code>.
     *
     * @param newStartMode the new value of startMode.
     * @see #getPaStartMode
     */
    protected abstract void setPaStartMode(StartFinishMode newStartMode);

    /**
     * The getter method for the persistent attribute <code>finishMode</code>.
     *
     * @return the value of finishMode.
     * @see #setPaFinishMode
     */
    protected abstract StartFinishMode getPaFinishMode();

    /**
     * The setter method for the persistent attribute <code>finishMode</code>.
     *
     * @param newFinishMode the new value of finishMode.
     * @see #getPaFinishMode
     */
    protected abstract void setPaFinishMode(StartFinishMode newFinishMode);

    /**
     * The getter method for the persistent attribute <code>joinMode</code>.
     *
     * @return the value of joinMode.
     * @see #setPaJoinMode
     */
    protected abstract JoinAndSplitMode getPaJoinMode();

    /**
     * The setter method for the persistent attribute <code>joinMode</code>.
     *
     * @param newJoinMode the new value of joinMode.
     * @see #getPaJoinMode
     */
    protected abstract void setPaJoinMode(JoinAndSplitMode newJoinMode);

    /**
     * The getter method for the persistent attribute <code>splitMode</code>.
     *
     * @return the value of splitMode.
     * @see #setPaSplitMode
     */
    protected abstract JoinAndSplitMode getPaSplitMode();

    /**
     * The setter method for the persistent attribute <code>splitMode</code>.
     *
     * @param newSplitMode the new value of splitMode.
     * @see #getPaSplitMode
     */
    protected abstract void setPaSplitMode(JoinAndSplitMode newSplitMode);

    /**
     * The getter method for the persistent attribute
     * <code>pendingException</code>.
     *
     * @return the value of pendingException.
     * @see #setPaPendingException
     */
    protected abstract String getPaPendingException();

    /**
     * The setter method for the persistent attribute
     * <code>pendingException</code>.
     *
     * @param newPendingException the new value of pendingException.
     * @see #getPaPendingException
     */
    protected abstract void setPaPendingException(String newPendingException);

    /**
     * The getter method for the persistent flag
     * <code>pendingExceptionIsFromBlock</code>.
     *
     * @return the value of the flag.
     * @see #setPaPendingExceptionIsFromBlock
     */
    protected abstract boolean getPaPendingExceptionIsFromBlock();

    /**
     * The setter method for the persistent flags
     * <code>pendingExceptionIsFromBlock</code>.
     *
     * @param newValue the new value of the flag.
     * @see #getPaPendingExceptionIsFromBlock
     */
    protected abstract void setPaPendingExceptionIsFromBlock (boolean newValue);

    /**
     * The getter method implementation for the persistent
     * read-only attribute <code>processKey</code>.
     * May be overridden by the derived class to speed up access
     * in distsributed systems.
     *
     * @return the value of processKey.
     */
    protected String getPaProcessKey() {
        return containerLocal().key();
    }

    /**
     * The getter method implementation for the persistent
     * read-only attribute <code>processName</code>.
     * May be overridden by the derived class to speed up access
     * in distsributed systems.
     *
     * @return the value of processName.
     */
    protected String getPaProcessName() {
        return containerLocal().name();
    }

    /**
     * The getter method implementation for the persistent
     * read-only attribute <code>processMgrName</code>.
     * May be overridden by the derived class to speed up access
     * in distsributed systems.
     *
     * @return the value of processMgrName.
     */
    protected String getPaProcessMgrName() {
        return ((ProcessLocal)containerLocal()).processDefinition().mgrName();
    }

    /**
     * The getter method implementation for the persistent
     * read-only attribute <code>processMgrVersion</code>.
     * May be overridden by the derived class to speed up access
     * in distsributed systems.
     *
     * @return the value of processMgrVersion.
     */
    protected String getPaProcessMgrVersion() {
        return ((ProcessLocal)containerLocal()).processDefinition().version();
    }

    /**
     * The getter method for the persistent attribute <code>threadInfo</code>.
     *
     * @return the value of threadInfo.
     * @see #setPaThreadInfo
     */
    protected abstract ThreadInfo getPaThreadInfo();

    /**
     * The setter method for the persistent attribute <code>threadInfo</code>.
     *
     * @param newThreadInfo the new value of threadInfo.
     * @see #getPaThreadInfo
     */
    protected abstract void setPaThreadInfo(ThreadInfo newThreadInfo);

    /**
     * The getter method for the persistent attribute <code>Subflow</code>.
     *
     * @return the value of Subflow.
     * @see #setPaSubflow
     */
    protected abstract String getPaSubflow();

    /**
     * The setter method for the persistent attribute <code>Subflow</code>.
     *
     * @param newSubflow the new value of Subflow.
     * @see #getPaSubflow
     */
    protected abstract void setPaSubflow(String newSubflow);

    /**
     * The getter method for the persistent attribute <code>deadlines</code>.
     *
     * @return the value of deadlines.
     * @see #setPaDeadlines
     */
    protected abstract List getPaDeadlines();

    /**
     * The setter method for the persistent attribute <code>deadlines</code>.
     *
     * @param newDeadlines the new value of deadlines.
     * @see #getPaDeadlines
     */
    protected abstract void setPaDeadlines(List newDeadlines);

    /**
     * The getter method for the persistent attribute <code>startTime</code>.
     *
     * @return the value of startTime.
     * @see #setPaStartTime
     */
    protected abstract Date getPaStartTime();

    /**
     * The setter method for the persistent attribute <code>startTime</code>.
     *
     * @param newStartTime the new value of startTime.
     * @see #getPaStartTime
     */
    protected abstract void setPaStartTime(Date newStartTime);

    /**
     * The getter method for the persistent attribute <code>suspendStart</code>.
     *
     * @return the value of suspendStart.
     * @see #setPaSuspendStart
     */
    protected abstract Date getPaSuspendStart();

    /**
     * The setter method for the persistent attribute <code>suspendStart</code>.
     *
     * @param newSuspendStart the new value of suspendStart.
     * @see #getPaSuspendStart
     */
    protected abstract void setPaSuspendStart(Date newSuspendStart);

    /**
     * The getter method for the persistent attribute <code>suspendAccum</code>.
     *
     * @return the value of suspendAccum.
     * @see #setPaSuspendAccum
     */
    protected abstract long getPaSuspendAccum();

    /**
     * The setter method for the persistent attribute <code>suspendAccum</code>.
     *
     * @param newSuspendAccum the new value of suspendAccum.
     * @see #getPaSuspendAccum
     */
    protected abstract void setPaSuspendAccum(long newSuspendAccum);

    /**
     * The getter method for the persistent attribute
     * <code>blockActivity</code>.
     *
     * @return the value of blockActivity.
     * @see #setPaBlockActivity
     */
    protected abstract Long getPaBlockActivity();

    /**
     * The setter method for the persistent attribute
     * <code>blockActivity</code>.
     *
     * @param newBlockActivity the new value of blockActivity.
     * @see #getPaBlockActivity
     */
    protected abstract void setPaBlockActivity(Long newBlockActivity);

    /**
     * The getter method for the persistent attribute
     * <code>waitOnProc</code>.
     *
     * @return the value of waitOnProc.
     * @see #setPaWaitOnProc
     */
    protected abstract Long getPaWaitOnProc();

    /**
     * The setter method for the persistent attribute
     * <code>waitOnProc</code>.
     *
     * @param newWaitOnProc the new value of waitOnProc.
     * @see #getPaWaitOnProc
     */
    protected abstract void setPaWaitOnProc(Long newWaitOnProc);

    /**
     * The getter method for the persistent attribute
     * <code>waitOnChan</code>.
     *
     * @return the value of waitOnChan.
     * @see #setPaWaitOnChan
     */
    protected abstract String getPaWaitOnChan();

    /**
     * The setter method for the persistent attribute
     * <code>waitOnChan</code>.
     *
     * @param newWaitOnChan the new value of waitOnChan.
     * @see #getPaWaitOnChan
     */
    protected abstract void setPaWaitOnChan(String newWaitOnChan);

    /**
     * The getter method for the persistent attribute
     * <code>subStateBackup</code>.
     *
     * @return the value of subStateBackup.
     * @see #setPaSubStateBackup
     */
    protected abstract int getPaSubStateBackup();

    /**
     * The setter method for the persistent attribute
     * <code>subStateBackup</code>.
     *
     * @param newSubStateBackup the new value of subStateBackup.
     * @see #getPaSubStateBackup
     */
    protected abstract void setPaSubStateBackup(int newSubStateBackup);

    /**
     * The getter method for the persistent attribute
     * <code>deferChoiceOnSplit</code>.
     *
     * @return the value of deferChoiceOnSplit.
     * @see #setPaDeferChoiceOnSplit
     */
    protected abstract boolean getPaDeferChoiceOnSplit();

    /**
     * The setter method for the persistent attribute
     * <code>deferChoiceOnSplit</code>.
     *
     * @param newDeferChoiceOnSplit the new value of deferChoiceOnSplit.
     * @see #getPaDeferChoiceOnSplit
     */
    protected abstract void
  setPaDeferChoiceOnSplit(boolean newDeferChoiceOnSplit);

    /**
     * The getter method for the persistent attribute
     * <code>electedInChoice</code>.
     *
     * @return the value of electedInChoice.
     * @see #setPaPreliminarilyChosen
     */
    protected abstract boolean getPaPreliminarilyChosen();

    /**
     * The setter method for the persistent attribute
     * <code>electedInChoice</code>.
     *
     * @param newElectedInChoice the new value of electedInChoice.
     * @see #getPaPreliminarilyChosen
     */
    protected abstract void
  setPaPreliminarilyChosen(boolean newElectedInChoice);

    /**
     * The getter method for the persistent attribute
     * <code>noAssignments</code>.
     *
     * @return the value of noAssignments.
     * @see #setPaNoAssignments
     */
    protected abstract boolean getPaNoAssignments();

    /**
     * The setter method for the persistent attribute
     * <code>noAssignments</code>.
     *
     * @param newNoAssignments the new value of noAssignments.
     * @see #getPaNoAssignments
     */
    protected abstract void setPaNoAssignments(boolean newNoAssignments);

    /**
     * Initializes the class, i.e. sets all attributes to the given
     * values. Note that {@link #refresh <code>refresh</code>} will be
     * called subsequently.
     *
     * @param blockActId if the activity is part of a block activity,
     * else <code>null</code>
     * @param priority a <code>Priority</code> value
     * @param name the activity's name
     * @param description activity description
     * @param startMode the start mode
     * @param finishMode the finish mode
     * @param joinMode the join mode
     * @param splitMode the split mode
     * @param implementation the implementation description
     * @param performer the performer
     * @param deadlines the deadlines
     * @param deferChoiceOnSplit if the split is to be made as
     * deferred choice
     * @param auditEventSelection the audit event selection
     * @param storeAuditEvents if true, audit events are stored in the
     * database
     * @see #dispose
     */
    protected void init
  (Long blockActId, Priority priority, String name, String description,
   StartFinishMode startMode, StartFinishMode finishMode,
   JoinAndSplitMode joinMode, JoinAndSplitMode splitMode,
   Implementation[] implementation, String performer, List deadlines,
   boolean deferChoiceOnSplit, int auditEventSelection,
   boolean storeAuditEvents) {
  super.init();
  reset (false, false);
  // (re-)init attributes
  setPaSubflow (null);
  setPaStartTime (null);
  setPaSuspendStart (null);
  setPaSuspendAccum (0);
  setPaWaitOnProc (null);
  setPaWaitOnChan (null);
  setPaNoAssignments (true);
  // set given properties
  setPaBlockActivity (blockActId);
  setPaPriority (priority);
  setPaName (name);
  setPaDescription (description);
  setPaStartMode (startMode);
  setPaFinishMode (finishMode);
  setPaJoinMode (joinMode);
  setPaSplitMode (splitMode);
  setPaActImpl (implementation);
  setPaPerformer (performer);
  setPaDeadlines (deadlines);
  setPaDeferChoiceOnSplit (deferChoiceOnSplit);
  setPaAuditEventSelection (auditEventSelection);
  setPaStoreAuditEvents (storeAuditEvents);
    }

    /**
     * Called after change of persistent attributes. May be used to
     * synchronise state derived from persistent attributes with
     * the new values.
     *
     * @see #init
     */
    protected void refresh () {
  super.refresh ();
    }

    /**
     * Releases all allocated resources. The object will be in an
     * unusable state until resources are reallocated by calling
     * {@link #init <code>init</code>} and
     * {@link #refresh <code>refresh</code>}.
     */
    protected void dispose () {
  super.dispose();
    }

    //
    // Domain methods
    //

    /**
     * Indicates if some other object is equal to this one. <P>
     *
     * Note that <code>obj</code> may be a stub representing the
     * object. Stubs do not in general implement
     * <code>equals</code> correctly, so while
     * <code>this.equals(obj)</code> does indicate the equality as
     * defined for this class, <code>obj.equals(this)</code> generally
     * does not.
     *
     * @param obj the object to compare with.
     * @return <code>true</code> if the other object is equal.
     */
    public boolean equals (Object obj) {
        return (obj instanceof AbstractActivity)
                && getPaKey().equals (((AbstractActivity)obj).key())
                || (obj instanceof WfActivityLocal)
                && getPaKey().equals (((WfActivityLocal)obj).key());
    }

    /**
     * Return the context of this <code>WfExecutionObject</code>.
     * The process data object returned is a copy of the name value
     * association, the values are not copied, however.
     *
     * @return the process relevant data that define the context of the
     * process.
     */
    public ProcessData processContext () {
  return containerLocal().processContext();
    }

    /**
     * Set new process context.
     * @param newValue new value for proces context.
     * @throws InvalidDataException if data is invalid
     * @throws UpdateNotAllowedException if update is not allowed
     */
    public void setProcessContext (ProcessData newValue)
  throws InvalidDataException, UpdateNotAllowedException {
  throw new UpdateNotAllowedException ("Not supported");
    }

    /**
     * The default implementation of <code>result</code> throws a
     * <code>ResultNotAvailableException</code> as this implementation does
     * not provide access to the results of an activity.
     *
     * @return a <code>ProcessData</code> value
     * @exception ResultNotAvailableException if no result is available.
     * @ejb.interface-method view-type="remote"
     */
    public ProcessData result()
  throws ResultNotAvailableException {
  throw new ResultNotAvailableException();
    }
   
    /**
     * The default implementation of <code>setResult</code> maps the
     * formal parameter names to actual process data item names and
     * merges the result data in the process context.
     *
     * @param result the result data.
     * @exception InvalidDataException if the data does not match the
     * parameter list.
     * @ejb.interface-method view-type="remote"
     */
    public void setResult (ProcessData result)
  throws InvalidDataException {
  int curExec = getPaExecStat().intValue();
  if (!workflowState().equals (State.OPEN)) {
            // maybe this activity is already closed by deadline event
            if (typedState().isSameOrSubState(ClosedCompletedState.ABANDONED)) {
                logger.warn("Call to setResult() ignored because " + toString()
                        + " has already been abandoned (probably called "
                        + "by tool that could not be terminated).");
                return;
            }
      throw new InvalidDataException
    ("Cannot set result for " + this + ": state is "
     + state() + " (not open).");
  }
  if (curExec == 0 || curExec == Integer.MAX_VALUE) {
      throw new InvalidDataException
    ("Cannot set result for " + this + ": no tool running.");
  }
  ((ExtImplementationLocal)getPaActImpl()[Math.abs(curExec) - 1])
      .mergeResult (toActivityLocal(), result);
  if (getPaPreliminarilyChosen ()) {
      try {
    choose ();
      } catch (TransitionNotAllowedException e) {
    // cannot happen because state has been checked already
    logger.error ("Unexpected exception: " + e.getMessage (), e);
      }
  }
  if (getPaAuditEventSelection()
      == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS) {
      fireAuditEvent
    (new DefaultDataAuditEvent
     (auditEventBase
      (WfAuditEvent.ACTIVITY_RESULT_CHANGED), null, result));
  }
    }

    /**
     * Check if the given assignment is among the assignments of this
     * activity.
     * @param member the assignment in question.
     * @return true if the assignment is among the assignments of this
     * activity.
     */
    public boolean isMemberOfAssignments (WfAssignment member) {
  throw new UnsupportedOperationException();
    }

    /**
     * Return all performers associated with this requester.
     *
     * @return A {@link java.util.Collection collection} of
     * associated performers.
     */
    public Collection performers() {
  Collection res = new ArrayList ();
        for (Iterator i = performersLocal().iterator(); i.hasNext();) {
            res.add (((ExtProcessLocal)i.next()).toProcess());
        }
  return res;
    }

    /**
     * Return all performers associated with this requester.
     *
     * @return A {@link java.util.Collection collection} of
     * associated performers.
     */
    public Collection performersLocal() {
        Collection res = new ArrayList ();
        if (getPaSubflow() != null) {
            try {
                res.add (lookupProcessLocal (getPaSubflow()));
            } catch (InvalidKeyException e) {
                // process has been deleted, avoid repeated lookup
                setPaSubflow (null);
            }
        }
        return res;
    }

    /**
     * Lookup a process by its key. Can only be implemented by the
     * persistence layer.
     * @param key the primary key
     * @return the process
     * @throws InvalidKeyException if no process with the given key
     * exists
     */
    protected abstract ExtProcessLocal lookupProcessLocal (String key)
        throws InvalidKeyException;

    /**
     * Called by the workflow engine if the activity is a requester.
     *
     * @param e the event.
     * @throws InvalidPerformerException thrown by the derived
     * {@link de.danet.an.workflow.localcoreapi.WfRequesterLocal
     * <code>WfRequesterLocal</code>} if it receives an event from a
     * process that is not among its performers.
     */
    public void receiveEvent (WfAuditEvent e)
  throws InvalidPerformerException {
    }

    /**
     * Return the remote version of this object.
     *
     * @return the client side object.
     */
    public abstract Activity toActivity ();

    /**
     * Return the version of this object to be passed to as local reference.
     *
     * @return the client side object.
     */
    protected abstract ExtActivityLocal toActivityLocal ();

    /**
     * Returns the <code>WfProcessLocal</code> that this activity is a part of.
     * @return the process.
     */
    public abstract WfProcessLocal containerLocal();
   
    /**
     * Return a unique key for the activity. (Note that the OMG
     * interface defines the key returned by the {@link
     * de.danet.an.workflow.localcoreapi.WfExecutionObjectLocal#key
     * <code>key()</code>} method as unique within the scope of the
     * containing process only.)
     * @return value of uniqueKey.
     */
    public ActivityUniqueKey uniqueKey() {
  return new ActivityUniqueKey
      (getPaProcessMgrName(), getPaProcessKey(), getPaKey());
    }

    /**
     * This method returns all available information about the
     * activity in a single operation.
     *
     * @return the resulting <code>Activity.Info</code> value
     */
    public Info activityInfo () {
  WfProcessLocal proc = containerLocal();
  return new Info
      (uniqueKey(),
       name(), description (), priority(), lastStateTime(),
       getPaProcessName(), proc.description());
    }

    /**
     * Returns the key of the "parent" block activity. All activities
     * implicitly created by a block activity share the same block
     * activity key.<P>
     *
     * Note that there need not be an activity with the returned key,
     * as an activity set is actually a template describing how to
     * implement block activities. The information obtained can mainly
     * be used to group all activities that have been instantiated as
     * part of an activity set.<P>
     *
     * @return an identification of the block activity that caused
     * this activity to be instantiated or <code>null</code> if this
     * activity was not instantiated as part of an activity set
     */
    public String blockActivity () {
  Long ba = getPaBlockActivity();
  if (ba == null) {
      return null;
  }
  return ba.toString ();
    }

    /**
     * Returns an <code>WfAuditEvent</code> containing container-related
     * information.
     * @return the audit event
     */
    protected abstract WfAuditEvent containerAuditEventBase ();

    /**
     * List of possible modes for the activity.
     * Handles the mapping between the mode name and the mode object.
     */
    private static Map activityModes = null;
 
    /**
     * Returns the implementation of the activity as {@link
     * de.danet.an.workflow.localapi.ActivityLocal.Implementation
     * <code>Implementation</code>}s.
     *
     * @return an array of <code>Implementation</code>}s or
     * <code>null</code> if no implementation is defined.
     */
    public Implementation[] implementation() {
  return getPaActImpl();
    }

    /**
     * Returns the deadlines defined for this activity.
     *
     * @return the deadlines
     */
    public DeadlineInfo[] deadlines () {
  DeadlineInfo[] dli = new DeadlineInfo[getPaDeadlines().size()];
  boolean closed = typedState().isSameOrSubState (State.CLOSED);
  boolean running = typedState().isSameOrSubState (OpenState.RUNNING);
  int i = 0;
  for (Iterator it = getPaDeadlines().iterator (); it.hasNext (); i++) {
      Deadline dl = (Deadline)it.next ();
      int state = dl.getState();
      if (closed && state == Deadline.STATE_INITIAL) {
    state = DeadlineInfo.STATE_CANCELED;
      } else if (running && state == Deadline.STATE_INITIAL) {
    state = DeadlineInfo.STATE_ACTIVE;
      }
      dli[i] = new DeadlineInfo
    (dl.getExecution(), dl.getExceptionName (),
     dl.getCondition(), state);
  }
  return dli;
    }

    /**
     * Return the start time of the activity.
     * @return result
     * @throws NotRunningException if the activity has not been started yet
     */
    public Date startTime() throws NotRunningException {
        if (getPaStartTime() == null) {
            throw new NotRunningException (state());
        }
        return getPaStartTime();
    }
   
    /**
     * Returns the performer as string.
     * @return performer as string
     */
    public String performer() {
  return getPaPerformer();
    }

    /**
     * Returns the current executor.
     *
     * @return current executor or <code>null</code> if no executor
     * running.
     */
    public Implementation executor() {
   if (getPaExecStat().intValue() <= 0
       || getPaExecStat().intValue() == Integer.MAX_VALUE) {
       return null;
   }
   Implementation impl = getPaActImpl()[getPaExecStat().intValue() - 1];
        if (impl instanceof ProcBasedImpl) {
            ((ProcBasedImpl)impl).setProcessKey(getPaSubflow());
        }
        return impl;
    }
   
    /**
     * Returns the start mode.
     * @return  start mode
     */
    protected StartFinishMode startMode() {
  return getPaStartMode();
    }
   
    /**
     * Returns the finish mode.
     * @return  finish mode
     */
    protected StartFinishMode finishMode() {
  return getPaFinishMode();
    }
   
    /**
     * Set the join mode of the activity.
     * @param joinMode the new join mode
     */
    public void setJoinMode(JoinAndSplitMode joinMode) {
  setPaJoinMode (joinMode);
    }

    /**
     * Returns the join mode.
     * @return  join mode
     */
    public JoinAndSplitMode joinMode() {
  return getPaJoinMode();
    }
   
    /**
     * Set the join mode of the activity.
     * @param splitMode the new split mode
     */
    public void setSplitMode(JoinAndSplitMode splitMode) {
  setPaSplitMode (splitMode);
    }

    /**
     * Returns the split mode.
     * @return  split mode
     */
    public JoinAndSplitMode splitMode() {
  return getPaSplitMode();
    }

    /* Comment copied from Interface. */
    public void doCloseActivity(State closedState) {
  updateState (closedState);
    }

    /**
     * Returns if the activity's split is to be executed as deferred
     * choice.
     * @return <code>true</code> if the activity has been elected
     */
    public boolean deferChoiceOnSplit () {
  return getPaDeferChoiceOnSplit ();
    }

    /**
     * Returns if the activity has been preliminarily chosen in a
     * deferred choice.
     * @return <code>true</code> if the activity has been elected
     */
    public boolean preliminarilyChosen () {
  return getPaPreliminarilyChosen ();
    }

    //
    // State change API implementation
    //

    private static Map vstates = null;

    /**
     * Returns the {@link java.util.Map <code>Map</code>} that maps
     * the activity states to a {@link java.util.Map
     * <code>List</code>} of reachable process states. <P>
     *
     * The map returns the state transitions allowed as parameters of
     * {@link
     * de.danet.an.workflow.localcoreapi.WfExecutionObjectLocal#changeState
     * <code>changeState</code>} only. I.e. the map does not reflect
     * all possible transitions, there may be more, but those are only
     * accessible to the workflow engine itself.
     * @return the resulting map.
     */
    protected Map getStateTransitionMap () {
  // Map must be initialized lazily. Static initialization holds
  // danger of "race conditions" with constant initialization
  // (resulting in null-entries). And new map must be fully initialized
        // before assigning to member variable to avoid concurrent
        // initialization.
  if (vstates == null) {
      Map transMap = new HashMap();
      // Transitions from open.not_running.not_started
      List l = new ArrayList ();
      transMap.put(NotStartedState.UNKNOWN, l);
      transMap.put(NotStartedState.STARTABLE, l);
      l.add (ClosedState.TERMINATED);
      // Transitions from open.running
      l = new ArrayList ();
      transMap.put(RunningState.RUNNING, l);
      l.add (NotRunningState.SUSPENDED);
      l.add (ClosedState.TERMINATED);
      l.add (ClosedState.COMPLETED);
      // Transitions from open.not_running.suspended.suspended
      l = new ArrayList ();
      transMap.put(SuspendedState.SUSPENDED, l);
      l.add (OpenState.RUNNING);
      l.add (ClosedState.ABORTED);
      // i.e. complete may be called, state changes later, of course.
      l.add (ClosedState.COMPLETED);
            // Transitions from open.not_running.suspended.abandoning
            l = new ArrayList ();
            transMap.put(SuspendedState.ABANDONING, l);
            l.add (OpenState.RUNNING);
            l.add (SuspendedState.CLEARING_EXCEPTION);
            // Transitions from open.not_running.suspended.clearing_exception
            l = new ArrayList ();
            transMap.put(SuspendedState.CLEARING_EXCEPTION, l);
            l.add (OpenState.RUNNING);
      // Transitions from debug states
      l = new ArrayList ();
      transMap.put(DebugState.ABORTING, l);
      transMap.put(DebugState.COMPLETING, l);
      transMap.put(DebugState.TERMINATING, l);
      l.add (OpenState.RUNNING);
      l = new ArrayList ();
      transMap.put(DebugState.INVOKING, l);
      l.add (OpenState.RUNNING);
      l.add (DebugState.SKIPPING);
      l.add (NotRunningState.SUSPENDED);
      vstates = Collections.unmodifiableMap(transMap);
  }
  return vstates;
    }

    /**
     * Adds the possibility to change the state to
     * <code>ClosedState.COMPLETED</code>
     * (i.e. "<code>complete()</code>") to the
     * <code>changeState</code> method of the superclass.<P>
     *
     * Adds handling of change from
     * <code>open.not_running.suspended.abandoning</code> to
     * <code>open.not_running.suspended.clearing_exception</code>.
     *
     * @param newState the new state.
     * @throws InvalidStateException If <code>newState</code> is an invalid
     * state for the execution object.
     * @throws TransitionNotAllowedException If the transition from the current
     * state to <code>newState</code> is not allowed.
     */
    public void changeState (State newState)
  throws InvalidStateException, TransitionNotAllowedException {
  try {
      if (newState.isSameOrSubState(ClosedState.COMPLETED)) {
    complete ();
    return;
      }
  } catch (CannotCompleteException e) {
      throw new TransitionNotAllowedException
    (e.getClass().getName() + ": " + e.getMessage());
  }
        if (typedState().isSameOrSubState(SuspendedState.ABANDONING)
            && newState.isSameOrSubState(SuspendedState.CLEARING_EXCEPTION)) {
            updateImmediate(SuspendedState.SUSPENDED);
            return;
        }
  if (getPaDebug ()) {
      State oldState = typedState ();
      if (newState == DebugState.FORWARDING_EXCEPTION
    && STATE_TO_SUB_STATE.containsKey(oldState)) {
    setPaSubStateBackup
        (((Integer)STATE_TO_SUB_STATE.get(oldState)).intValue());
    setPaTypedState (newState);
    return;
      }
      if (newState == DebugState.AWAITING_EXCEPTION) {
    if (oldState == DebugState.ABANDONING) {
        // Proceed from abandoning
        setPaTypedState (newState);
        return;
    }
    throw new TransitionNotAllowedException
        ("Change to " + DebugState.AWAITING_EXCEPTION
         + " is only allowed from " + DebugState.ABANDONING);
      }
      if (oldState.isSameOrSubState (RunningState.DEBUG)) {
    if (!newState.isSameOrSubState (OpenState.RUNNING)
        && !(oldState == DebugState.INVOKING
       && newState.isSameOrSubState
                            (NotRunningState.SUSPENDED))) {
        throw new TransitionNotAllowedException
      ("Only valid change from debug states is to "
       + OpenState.RUNNING + " or sub-state");
    }

    // Resume closing with the proper state.
    State closeState
        = (State)DEBUG_STATE_TO_CLOSE_STATE.get(oldState);
    if (closeState != null) {
        // Reset to running state in order to generate proper
        // state change event later.
        setPaTypedState (RunningState.RUNNING);
        ((ExtProcessLocal)containerLocal())
            .closeActivity(toActivityLocal(), closeState);
        return;
    }
    // Maybe continue invocation
    if (oldState == DebugState.INVOKING) {
        // Reset to running state in order to generate proper
        // state change event later.
        setPaTypedState (RunningState.RUNNING);
        if (!newState.isSameOrSubState(NotRunningState.SUSPENDED)) {
      if (newState == DebugState.SKIPPING) {
          invokeNextImpl ();
      } else {
          invokeCurrentImpl ();
      }
      return;
        }
        // We cannot change the state to suspended
        // immediately. This would imply to change to a
        // state before the tool invocation. The decision
        // about the tool invocation has, however, already
        // been taken, we can only skip the actual
        // execution of the tool. If we were not running
        // in debug mode, the suspended state would also
        // be reached after the tool invocation.
        invokeCurrentImpl ();
    }
      }
  }
  super.changeState (newState);
    }

    /**
     * Return the activity's thread info.
     * @return the thread info.
     */
    public ThreadInfo threadInfo () {
  return getPaThreadInfo();
    }

    /**
     * Update the sub-state of an activity in state
     * <code>NotRunningState.NOT_STARTED</code> to
     * <code>NotStartedState.STARTABLE</code>. This update reflects
     * internal evaluation progression only and does neither update
     * last state change time nor fire an event.
     *
     * @param triggers the activities that caused the activity to be
     * startable (one or more activities depending on the join mode).
     * @param preliminarilyChosen if the activity is triggered due to a
     * deferred choice
     * @see #updateState
     */
    public void setStartable(Collection triggers, boolean preliminarilyChosen) {
  if (!getPaTypedState().isSameOrSubState(NotRunningState.NOT_STARTED)) {
      throw new IllegalStateException
    ("Cannot update not started substate, activity is "
     + getPaTypedState().toString());
  }
  setPaTypedState (NotStartedState.STARTABLE);
  if (getPaThreadInfo() == null) {
      setPaThreadInfo (new ThreadInfo (triggers));
  }
  setPaPreliminarilyChosen (preliminarilyChosen);
  if (logger.isDebugEnabled ()) {
      logger.debug (toString () + " triggered by " + getPaThreadInfo ());
  }
    }
   
    /**
     * Reset an activity to <code>NotStartedState.UNKNOWN</code>,
     * i.e. have it assume exactly the same state as it has when
     * newly created. An exception is the thread info which can
     * (optionally) be preserved in certain cases.
     * @param preserveThreadInfo preserve the thread info,
     * i.e. the information about the activity's predecessors.
     * @param publishChange publish the state change, i.e. create a
     * corresponding audit event
     */
    public void reset(boolean preserveThreadInfo, boolean publishChange) {
  if (publishChange) {
      updateInterim (NotStartedState.UNKNOWN);
  } else {
      setPaTypedState (NotStartedState.UNKNOWN);
  }
  setPaWaitOnProc (null);
  setPaWaitOnChan (null);
  setPaExecStat (new Integer(0));
  if (!preserveThreadInfo) {
      setPaThreadInfo (null);
  }
  if (getPaDeadlines().size () > 0) {
      for (Iterator dli = getPaDeadlines().iterator (); dli.hasNext ();) {
    Deadline dl = (Deadline)dli.next ();
    dl.setState(Deadline.STATE_INITIAL);
      }
      // notify persistence layer about change
      setPaDeadlines (getPaDeadlines());
  }
    }

    /**
     * Terminates and resets an activity that was started
     * preliminarily as part of a deferred choice.
     * @param reset if the activity's state is to be reset
     * @throws TransitionNotAllowedException if the activity has not
     * been preliminarily chosen
     */
    public void withdrawPreliminaryChoice (boolean reset)
  throws TransitionNotAllowedException {
  if (!getPaPreliminarilyChosen ()) {
      throw new TransitionNotAllowedException
    (this + " is not in state preliminary chosen");
  }
  setPaPreliminarilyChosen (false);
  if (!reset) {
      return;
  }
  try {
      terminateImpl ();
  } catch (ApplicationNotStoppedException e) {
      logger.warn
    ("Tool invoked in deferred choice could not be terminated: "
     + e.getMessage ());
  }
  reset (false, true);
  releaseResources ();
    }

    /**
     * Starts an activity. This is not an OMG method, but needed
     * in the communication between process and activity.
     * @throws AlreadyRunningException when the process has already been
     * started.
     */
    public void start ()
        throws AlreadyRunningException {
  if (!typedState().isSameOrSubState(NotRunningState.NOT_STARTED)) {
      throw new AlreadyRunningException
    (toString() + " state is: " + state());
  }
  setPaStartTime (new Date ());
  Deadline.armDeadlines
      (this, new Date
       (getPaStartTime().getTime() + getPaSuspendAccum ()),
       (ExtProcessLocal)containerLocal(), getPaDeadlines());
  if (getPaStartMode() != StartFinishMode.MANUAL) {
      updateState (RunningState.RUNNING);
      autoAssignResources ();
      invokeNextImpl ();
  } else {
      updateInterim (RunningState.RUNNING); // no handleStarted called
      updateState(SuspendedState.SUSPENDED);
  }
    }

    /* Comment copied from Interface. */
    public void suspend () throws CannotSuspendException,
        NotRunningException, AlreadySuspendedException {
  super.suspend ();
  Implementation impl = executor ();
  if (impl != null && (impl instanceof SubFlowImplementation)) {
      Collection subs = performersLocal();
      for (Iterator i = subs.iterator (); i.hasNext();) {
    WfProcessLocal p = (WfProcessLocal)i.next();
    try {
        p.suspend ();
    } catch (NotRunningException e) {
        throw new CannotSuspendException (e.getMessage ());
    } catch (AlreadySuspendedException e) {
        // may safely by ignored, make checkstyle happy
        continue;
    }
      }
  }
    }

    /* Comment copied from Interface. */
    public void resume ()
        throws CannotResumeException, NotRunningException,
        NotSuspendedException {
        // Check for special case "resuming exception processing"
        if (typedState().isSameOrSubState(SuspendedState.ABANDONING)) {
            // Tool was invoked with suspend flag, continue "normal"
            // processing. Complete abandoning and go to running first
            // to get consistent state changes.
            setPaExecStat(new Integer (Integer.MAX_VALUE));
            releaseResources ();
            updateInterim (RunningState.RUNNING);
            // now quit if debugging
            if (getPaDebug ()) {
                setPaTypedState (DebugState.ABANDONING);
                return;
            }
            updateInterim (ClosedCompletedState.ABANDONED);
               
            // propagate to process to cause triggering transitions
            String exception = getPaPendingException();
            setPaPendingException(null);
            ((ExtProcessLocal)containerLocal())
                .handleException(toActivityLocal(), exception);
            return;
        }
       
        // resume from normal manual suspend invocation
  Implementation impl = executor ();
  if (impl != null && (impl instanceof SubFlowImplementation)) {
      Collection subs = performersLocal();
      for (Iterator i = subs.iterator (); i.hasNext();) {
    WfProcessLocal p = (WfProcessLocal)i.next();
    try {
        p.resume ();
    } catch (NotRunningException e) {
        throw new CannotResumeException (e.getMessage ());
    } catch (NotSuspendedException e) {
        // may safely by ignored, make checkstyle happy
        continue;
    }
      }
  }
  super.resume ();
    }

    /**
     * Close this activity regularly.
     * @throws CannotCompleteException if the state can not be changed.
     * @ejb.interface-method view-type="remote"
     */
    public void complete() throws CannotCompleteException {
  if (!validTypedStates().contains (ClosedState.COMPLETED)) {
            if (typedState().isSameOrSubState(ClosedCompletedState.ABANDONED)) {
                logger.warn("Call to complete() ignored because " + toString()
                        + " has already been abandoned (probably called "
                        + "by tool that could not be terminated).");
                return;
            }
      throw new CannotCompleteException(toString() + " is " + state());
  }
  if (getPaPreliminarilyChosen ()) {
      try {
    choose ();
      } catch (TransitionNotAllowedException e) {
    // cannot happen because state has been checked already
    logger.error ("Unexpected exception: " + e.getMessage (), e);
      }
  }
        setPaWaitOnChan(null);
  // if this is the last implementation completing and we are
  // not debugging, and the activity is not suspended, then we
  // may complete immediately
  if (!haveMoreImpls() && !getPaDebug ()
      && !typedState().isSameOrSubState(NotRunningState.SUSPENDED)) {
      setPaExecStat(new Integer (Integer.MAX_VALUE));
      if (finishMode() == StartFinishMode.MANUAL) {
    updateState (SuspendedState.SUSPENDED);
    return;
      }
      // causes state changed event to be fired
      ((ExtProcessLocal)containerLocal()).closeActivity
    (toActivityLocal(), ClosedCompletedState.NORMAL);
      return;
  }
  fireAuditEvent
      (new ImplCompleteAuditEvent
       (auditEventBase(ImplCompleteAuditEvent.TOOL_COMPLETE),
        Math.abs(getPaExecStat().intValue()) - 1));
    }

    /* Comment copied from Interface. */
    public boolean initiateAbandoning
        (boolean blockException, String exceptionName) {
        boolean isSuspended = false;
        if (typedState().isSameOrSubState(OpenState.RUNNING)) {
            setPaTypedState(RunningState.ABANDONING);
            abandonImpl();
        } else if (typedState().isSameOrSubState(SuspendedState.SUSPENDED)) {
            if (blockException) {
                // Exceptions from block activity deadlines cannot be delayed.
                updateInterim(RunningState.RUNNING);
                abandonImpl();
            } else {
                if (getPaExecStat().intValue() == 0
                        || getPaExecStat().intValue() == Integer.MAX_VALUE) {
                    // suspend from manual start or finish mode, exception
                    // must be from deadline and is processed. Note that we
                    // do not have to abandon an implementation as no
                    // implementation is running.
                    updateInterim(RunningState.RUNNING);
                } else {
                    // if we get here, the activity has been
                    // suspended in response to an exception from a tool
                    // invocation (as exceptions from deadlines are delayed
                    // when an activity is suspended)
                    isSuspended = true;
                    setPaTypedState(SuspendedState.ABANDONING);
                    setPaPendingException(exceptionName);
                    setPaPendingExceptionIsFromBlock(blockException);
                }
            }
  } else {
      return false;
  }
  if (getPaPreliminarilyChosen ()) {
      try {
    choose ();
      } catch (TransitionNotAllowedException e) {
    // cannot happen because state has been checked already
    logger.error ("Unexpected exception: " + e.getMessage (), e);
      }
  }
  setPaWaitOnProc (null);
  setPaWaitOnChan (null);
        disarmDeadlines();
        if (isSuspended) {
            setPaSuspendStart (new Date ());
            setPaExecStat(new Integer(-getPaExecStat().intValue()));
            return false;
        }
        setPaExecStat(new Integer (Integer.MAX_VALUE));
        releaseResources ();
        if (getPaDebug ()) {
            setPaTypedState (DebugState.ABANDONING);
            return false;
        }
        updateInterim (ClosedCompletedState.ABANDONED);
        return true;
    }

    private void abandonImpl () {
        Implementation impl = executor();
        if (impl == null || typedState() == DebugState.INVOKING) {
            return;
        }
  if (impl instanceof ToolImplementation) {
      try {
    terminateTool ((ToolImplementation)impl);
      } catch (ApplicationNotStoppedException ans) {
    logger.debug
        ("Failed to terminate tool in abandon of "
         + toString() + " (ignored): " + ans.getMessage());
      }
  }
  if (impl instanceof SubFlowImplementation) {
      Collection subs = performersLocal();
      for (Iterator i = subs.iterator (); i.hasNext();) {
    ExtProcessLocal p = (ExtProcessLocal)i.next();
    try {
        if (p.typedState().isSameOrSubState
      (NotRunningState.SUSPENDED)) {
      p.abort ();
        } else {
      try {
          p.terminate ();
      } catch (CannotStopException e) {
          try {
        p.suspend ();
        p.abort ();
          } catch
        (InvalidControlOperationException ee) {
        logger.warn
            ("Cannot force termination of "
             + p + " (ignored): " + ee.getMessage ());
          }
      }
        }
    } catch (NotRunningException e) {
        logger.debug ("Trying to stop not running subflow "
          + "(propably race-condition, ignored): "
          + e.getMessage ());
    } catch (InvalidControlOperationException e) {
        logger.warn ("Cannot force termination of " + p
         + " (ignored): " + e.getMessage ());
    }
      }
  }
    }

    /* Comment copied from Interface. */
    public void abort () throws CannotStopException,
        NotRunningException {
  mayCloseCheck (ClosedState.ABORTED);
  try {
      terminateImpl ();
  } catch (ApplicationNotStoppedException e) {
      Implementation impl = executor ();
      if (impl == null || !(impl instanceof SubFlowImplementation)) {
    logger.warn
        (toString() + " has been aborted, but the currently "
         + "running tool could not be stopped: " + e.getMessage());
      } else {
    Collection subs = performersLocal();
    for (Iterator i = subs.iterator (); i.hasNext();) {
        WfProcessLocal p = (WfProcessLocal)i.next();
        try {
      try {
          p.suspend ();
      } catch (AlreadySuspendedException as) {
          // the better, make checkstyle happy
          int dummy = 0;
      }
      p.abort ();
        } catch (CannotSuspendException ee) {
      throw new CannotStopException
          ("To be aborted sub-process cannot be suspended: "
           + e.getMessage ());
        } catch (NotRunningException ee) {
      // may safely by ignored, make checkstyle happy
      continue;
        }
    }
      }
  }
  doAbort();
    }

    /* Comment copied from Interface. */
    public void abortRequester () {
        try {
            if (!typedState().isSameOrSubState(NotRunningState.SUSPENDED)) {
                super.suspend();
            }
            mayCloseCheck (ClosedState.ABORTED);
        } catch (InvalidControlOperationException e) {
            logger.error
                ("Sub-process requests abortion of " + toString()
                 + ", but cannot abort: " + e.getMessage(), e);
        }
        doAbort();
    }

    private void doAbort() {
        setPaWaitOnProc (null);
        setPaWaitOnChan (null);
        setPaExecStat(new Integer (Integer.MAX_VALUE));
        closeActivity (ClosedState.ABORTED);
    }
   
    /* Comment copied from Interface. */
    public void terminate () throws CannotStopException, NotRunningException {
  mayCloseCheck (ClosedState.TERMINATED);
  try {
      terminateImpl ();
  } catch (ApplicationNotStoppedException e) {
      throw new CannotStopException
    ("Cannot terminate, application not stopped: "
     + e.getMessage());
  }
  setPaWaitOnProc (null);
  setPaWaitOnChan (null);
  setPaExecStat(new Integer (Integer.MAX_VALUE));
  closeActivity (ClosedState.TERMINATED);
    }

    private void mayCloseCheck (ClosedState s)
  throws CannotStopException, NotRunningException {
  if (((ProcessLocal)containerLocal()).workflowState() == State.CLOSED) {
            if (typedState().isSameOrSubState(ClosedCompletedState.ABANDONED)) {
                logger.warn("Attempt to close ignored, " + toString()
                        + " has already been abandoned (probably attempted "
                        + "by tool that could not be terminated).");
                return;
            }
      throw new NotRunningException
    ("Cannot terminate "+toString()+", process already closed.");
  }
  if (!validTypedStates().contains (s)) {
      if (typedState().isSameOrSubState(OpenState.RUNNING)) {
    throw new CannotStopException(toString() + " is " + state());
      } else {
    throw new NotRunningException (toString() + " is " + state());
      }
  }
    }

    /* Comment copied from Interface. */
    public void abandon(String exception)
        throws TransitionNotAllowedException {
        abandon(new ExceptionResult(exception));
    }
   
    /* Comment copied from Interface. */
    public void abandon(ExceptionResult result)
  throws TransitionNotAllowedException {
        String exception = result.exceptionName();
  // handle forwarding of exception for debugging
  State oldState = typedState ();
  if (oldState == DebugState.AWAITING_EXCEPTION
      || oldState == DebugState.FORWARDING_EXCEPTION) {
      if (oldState == DebugState.AWAITING_EXCEPTION) {
    updateInterim (ClosedCompletedState.ABANDONED);
      } else {
    setPaTypedState ((State)SUB_STATE_TO_STATE
         .get(new Integer(getPaSubStateBackup())));
      }
      // propagate to process to cause triggering transitions
      ((ExtProcessLocal)containerLocal())
          .handleException(toActivityLocal(), exception);
      return;
  }

  // normal procedures. Note that having a running tool implies
        // that the activity is running or suspended
   if ((getPaExecStat().intValue() <= 0
       || getPaExecStat().intValue() == Integer.MAX_VALUE)
      && !typedState().isSameOrSubState (DebugState.COMPLETING)) {
       throw new TransitionNotAllowedException ("No tool executing");
   }
       
        if (result.suspendActivity()) {
            // Everything that is usually done when suspend() is called is
            // either not applicable or indirectly done here. Suspending
            // subflow (done in suspend()) is not applicable, as we have
            // a tool implementation running. And everything done in
            // handleSuspendedEvent() is also done in initiateAbandoning()
            // and when resuming from ...suspended.abandoning.
            updateImmediate(SuspendedState.SUSPENDED);
        }
  initiateAbandoning (false, exception);

  // now quit if debugging
  if (getPaDebug ()) {
      return;
  }

  // propagate to process to cause triggering transitions if activity
        // has really been completed (may still be suspended.abandoning)
        if (typedState().isSameOrSubState(State.CLOSED)) {
            ((ExtProcessLocal)containerLocal())
                .handleException(toActivityLocal(), exception);
        }
    }

    /**
     * Makes this activity the chosen one in a set of activities
     * started by an AND split with the "deferred choice" option
     * set. All other activities in the set are reset to their initial
     * state.
     *
     * <P>If the activity does not participate in a deferred choice,
     * this method does nothing and returns <code>true</code>.
     *
     * @return <code>true</code> if the activity could be made the
     * effectively chosen one
     * @throws TransitionNotAllowedException if the activity is
     * neither running nor suspended
     */
    public boolean choose ()
  throws TransitionNotAllowedException {
  return ((ExtProcessLocal)containerLocal()).choose (toActivityLocal());
    }

    /* Comment copied from Interface. */
    public void waitOnChannel (String procKey, String channel) {
  setPaWaitOnProc (new Long(procKey));
  setPaWaitOnChan (channel);
    }

    //
    // Timers
    //

    private void disarmDeadlines () {
  if (getPaDeadlines().size () == 0) {
      return;
  }
  if (logger.isDebugEnabled ()) {
      logger.debug ("Stopping timers for " + toString());
  }
  stopTimers ();
    }

    /**
     * Handle the timeout of a timer.
     * @param info the context.
     */
    public void handleTimeout (Serializable info) {
  int dlIndex = ((Integer)info).intValue ();
  Deadline dl = (Deadline)getPaDeadlines().get (dlIndex);
  handleDeadline (dl);
    }

    private void handleDeadline (Deadline dl) {
  if (logger.isDebugEnabled ()) {
      logger.debug (toString () + " reached " + dl);
  }
  if (!(typedState().isSameOrSubState(OpenState.RUNNING)
        || (typedState().isSameOrSubState(NotRunningState.SUSPENDED)
      && (getPaExecStat().intValue() == 0
          || getPaExecStat().intValue() == Integer.MAX_VALUE)))) {
      // if this activity is currently actively suspended, the
      // deadline will be retriggered when the activity is
      // resumed
      return;
  }

  // now accept deadline
  dl.setState(Deadline.STATE_REACHED);
  // notify persistence layer about change
  setPaDeadlines (getPaDeadlines());

  // now quit if debugging
  if (getPaDebug ()) {
      return;
  }

  // complete this if deadline is synchronous
  if (dl.getExecution() == Deadline.SYNCHR) {
      initiateAbandoning (false, null);
  }
        // Due to the previous check, we only get here if the activity was
        // initially running (not suspended). So it will be closed now
        // (i.e. cannot be suspended.abandoning).
  ((ExtProcessLocal)containerLocal())
      .handleException(toActivityLocal(), dl.getExceptionName());
    }

    /* Coment copied from interface. */
    public String[] handledExceptions() {
  Set res = new HashSet ();
  // exception name from deadlines
  for (Iterator i = getPaDeadlines().iterator (); i.hasNext ();) {
      Deadline dl = (Deadline)i.next ();
      res.add (dl.getExceptionName());
  }
  // exception names from transitions
  List fromTrans
      = ((ProcessLocal)containerLocal()).transitionsLocalFrom(key());
  for (Iterator tri = fromTrans.iterator(); tri.hasNext();) {
      TransitionLocal trans = (TransitionLocal)tri.next();
      if (trans.conditionType() ==
                de.danet.an.workflow.api.Transition.COND_TYPE_EXCEPTION) {
    res.add (trans.condition());
      }
  }
  return (String[])res.toArray(new String[res.size ()]);
    }

    //
    // State change handling
    //

    /**
     * Check if this class handles a specific event.
     * @param event the event to check
     * @return <code>true</code> if event is handled
     */
    public static boolean isHandled (WfAuditEvent event) {
  if (event instanceof ToolInvocationFailedAuditEvent) {
    return true;
  }
  if (event instanceof ImplCompleteAuditEvent) {
    return true;
  }
  if (event.eventType().equals
            (WfAuditEvent.ACTIVITY_STATE_CHANGED)) {
      try {
    State oldState = State.fromString
        (((WfStateAuditEvent)event).oldState());
    State newState = State.fromString
        (((WfStateAuditEvent)event).newState());
    if (oldState.isSameOrSubState(NotRunningState.NOT_STARTED)) {
        if (newState == ClosedState.TERMINATED) {
      return true;
        }
    } else if (oldState.isSameOrSubState
         (NotRunningState.SUSPENDED)) {
        if (newState == RunningState.RUNNING) {
      return true;
        }
        if (newState == ClosedState.ABORTED) {
      return true;
        }
    } else if (oldState.isSameOrSubState(OpenState.RUNNING)) {
        if (newState == SuspendedState.SUSPENDED) {
      return true;
        }   
        if (newState == ClosedCompletedState.NORMAL) {
      return true;
        }
        if (newState == ClosedState.TERMINATED) {
      return true;
        }
    }
      } catch (InvalidStateException e) {
    throw (IllegalArgumentException)
        (new IllegalArgumentException (e.getMessage ()))
        .initCause (e);
      }
  }
  return false;
    }

    /**
     * Handles a suspended audit event.
     * @param event the event.
     */
    protected void handleSuspendedEvent (WfStateAuditEvent event) {
  if (getPaExecStat().intValue() == 0) {
      // this is the event received from start with start mode manual
      Deadline.armDeadlines
    (this, new Date(getPaStartTime().getTime()
        + getPaSuspendAccum ()),
     (ExtProcessLocal)containerLocal(), getPaDeadlines());
      return;
  }
  if (getPaExecStat().intValue() == Integer.MAX_VALUE) {
      // this is the event received from complete with finish mode manual
      return;
  }
  setPaSuspendStart (new Date ());
  disarmDeadlines ();
    }

    /**
     * Handles a resumed audit event.
     * @param event the event.
     */
    protected void handleResumedEvent (WfStateAuditEvent event) {
  if (getPaSuspendStart () != null) {
      setPaSuspendAccum (getPaSuspendAccum ()
             + ((new Date()).getTime ()
          - getPaSuspendStart ().getTime ()));
      setPaSuspendStart (null);
  }
  // now handle expired deadlines first
  int exc = getPaExecStat().intValue();
  if (exc == 0) {
      // delayed start (start mode manual), timers are running
      autoAssignResources ();
      invokeNextImpl ();
      return;
  }
  // handle all expired timers first
  Date now = null;
  Date base = null;
  SortedMap expDls = new TreeMap ();
  for (Iterator i = getPaDeadlines().iterator (); i.hasNext ();) {
      Deadline dl = (Deadline)i.next ();
      if (dl.getState() != Deadline.STATE_INITIAL) {
    continue;
      }
      if (now == null) {
    now = new Date ();
    base = new Date
        (getPaStartTime().getTime() + getPaSuspendAccum ());
      }
      try {
    Date exp = dl.expirationDate(base, (ExtProcessLocal)containerLocal());
    if (!exp.after(now)) {
        expDls.put (exp, dl);
    }
      } catch (ParseException e) {
    logger.error (e.getMessage ());
      }
  }
  for (Iterator i = expDls.values().iterator(); i.hasNext();) {
      Deadline dl = (Deadline)i.next();
      handleDeadline (dl);
      if (typedState().isSameOrSubState (State.CLOSED)) {
    return;
      }
  }
  if (exc == Integer.MAX_VALUE) {
      // resume from manual finish
      closeActivity (ClosedCompletedState.NORMAL);
      return;
  }
  Deadline.armDeadlines
      (this, base, (ExtProcessLocal)containerLocal(), getPaDeadlines());
  if (exc < 0) {
      // tool was completed during suspend
      invokeNextImpl ();
  }
    }

    /**
     * Handles a tool invocation audit event.
     * @param event the event.
     */
    protected void handleToolInvocationFailedAuditEvent
  (ToolInvocationFailedAuditEvent event) {
  if (workflowState() == State.OPEN) {
      setPaWaitOnProc (null);
      setPaWaitOnChan (null);
      setPaExecStat(new Integer (Integer.MAX_VALUE));
      closeActivity (ClosedState.TERMINATED);
  }
    }

    /**
     * Handles a tool completed audit event.
     * @param event the event.
     */
    protected void handleImplCompletedEvent (ImplCompleteAuditEvent event) {
  if (typedState().isSameOrSubState(NotRunningState.SUSPENDED)) {
      setPaWaitOnProc (null);
      int exc = getPaExecStat().intValue();
      if (exc > 0 && exc != Integer.MAX_VALUE) {
    setPaExecStat(new Integer (-exc));
      }
  } else {
      invokeNextImpl ();
  }
    }

    /**
     * Handles a completed audit event. This is thrown after the last
     * implementation has terminated.
     * @param event the event.
     */
    protected void handleCompletedEvent (WfStateAuditEvent event) {
  disarmDeadlines ();
  releaseResources ();
    }

    /**
     * Handles a terminated audit event.
     * @param event the event.
     */
    protected void handleTerminatedEvent (WfStateAuditEvent event) {
  disarmDeadlines ();
  releaseResources ();
    }

    /**
     * Handles a aborting audit event.
     * @param event the event.
     */
    protected void handleAbortedEvent (WfStateAuditEvent event) {
  disarmDeadlines ();
  releaseResources ();
    }

    /**
     * Invokes the next tool.
     */
    private void invokeNextImpl() {
  setPaWaitOnProc (null);
  if (!typedState().workflowState().equals(State.OPEN)
      || typedState().isSameOrSubState(NotRunningState.SUSPENDED)) {
      return;
  }
  int exc = getPaExecStat().intValue();
  if (exc == Integer.MAX_VALUE) {
      logger.warn ("Call to invokeNextImpl after all implementations "
       + "have been invoked indicates some kind of problem.");
      return;
  }
  if (!haveMoreImpls ()) {
      setPaExecStat(new Integer (Integer.MAX_VALUE));
      if (finishMode() == StartFinishMode.MANUAL) {
    updateState (SuspendedState.SUSPENDED);
      } else {
    closeActivity (ClosedCompletedState.NORMAL);
      }
      return;
  }
  // Perform the next tool used for this activity
  setPaExecStat(new Integer (Math.abs(exc) + 1));
  if (!getPaDebug ()) {
      invokeCurrentImpl ();
  } else {
      setPaTypedState (DebugState.INVOKING);
  }
    }

    /**
     * Check if there are more implementations.
     */
    private boolean haveMoreImpls () {
  int exc = getPaExecStat().intValue();
  if (exc == Integer.MAX_VALUE) {
      return false;
  }
  int nextExc = Math.abs(exc) + 1;
  Implementation[] toolsList = getPaActImpl();
  return !(toolsList == null || nextExc > toolsList.length);
    }

    /**
     * Invoke the current tool.
     */
    private void invokeCurrentImpl () {
  ExtImplementationLocal impl = (ExtImplementationLocal)
      getPaActImpl()[Math.abs(getPaExecStat().intValue()) - 1];
  impl.invoke (toActivityLocal());
  if (impl instanceof ProcBasedImpl) {
      String key = ((ProcBasedImpl)impl).processKey ();
      setPaSubflow (key);
  }
    }

    /**
     * Returns an {@link de.danet.an.workflow.spis.ras.ActivityFinder
     * <code>ActivityFinder</code>} that identifies this activity
     * against a resource assignment service as defined by
     * {@link de.danet.an.workflow.spis.ras the ras package}.
     *
     * @return the activity finder.
     */
    public abstract ActivityFinder activityFinder ();

    /**
     * Called when the activity enters the running state. Calls the
     * resource assignment service to trigger the assignment.
     */
    protected void autoAssignResources () {
  try {
      RASInvocationHandler ras = rasInvocationHandler();
      if (ras == null) {
    return;
      }
      Participant participant = null;
      if (performer() != null) {
    try {
        participant = ((ProcessLocal)containerLocal())
                        .processDefinition().participantById(performer());
    } catch (InvalidIdException e) {
        String pid = (String)processContext().get (performer());
        if (pid == null) {
      throw new InvalidIdException
          ("Referenced data field \"" + performer() + "\""
           + " does not contain valid participant id.");
        }
        participant = ((ProcessLocal)containerLocal())
                        .processDefinition().participantById(pid);
    }
      }
            Collection resources = null;
            resources = ras.autoAssignResources
                (activityFinder(), getPaKey(), toActivity(),
                 ((ExtProcessLocal)containerLocal()).processCreator(), participant);
      Iterator i = resources.iterator();
      // Save info for optimization in releaseResources.
      setPaNoAssignments (!i.hasNext ());
      if (getPaAuditEventSelection()
    == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS) {
    while (i.hasNext ()) {
        WfResource resource = (WfResource)i.next();
        fireAuditEvent
                        (new DefaultAssignmentAuditEvent
                         (auditEventBase
        (WfAuditEvent.ACTIVITY_ASSIGNMENT_CHANGED),
        null, ras.retrieveKey(resource),
        null, ras.retrieveName(resource)));
    }
      }
  } catch (InvalidIdException iie) {
      logger.error ("Invalid participant: " + iie.getMessage());
  }
    }

    /**
     * Return the list of assignments.
     * @return list of assignments
     * @ejb.interface-method view-type="remote"
     */
    public Collection assignments() {
        RASInvocationHandler ras = rasInvocationHandler();
  if (ras == null) {
      return new ArrayList();
  }
  return ras.assignments (activityFinder(), getPaKey(), toActivity());
    }

    /**
     * Get the resource associated with an Assignment. The method calls
     * the corresponding method of the resource assignment service.
     *
     * This method is intended to be used by resource assignment
     * systems for implementing {@link
     * de.danet.an.workflow.localcoreapi.WfAssignment#assignee
     * <code>WfAssignment.assignee</code>}.<P>
     *
     * Clients should not use this method but rather call {@link
     * de.danet.an.workflow.localcoreapi.WfAssignment#setAssignee
     * <code>WfAssignment.setAssignee</code>}.
     *
     * @param asnmnt the assignment
     * @return the resource
     */
    public WfResource getResource (WfAssignment asnmnt) {
        RASInvocationHandler ras = rasInvocationHandler();
        if (ras == null) {
            throw new UnsupportedOperationException
                ("Resource assignment service not configured.");
        }
        return ras.getResource (asnmnt);
    }

    /**
     * Change an assignment for enacting the activity. This method calls
     * the corresponding method of the resource assignment service and
     * creates the appropriate audit event.<P>
     *
     * This method is intended to be used by resource assignment
     * systems for implementing {@link
     * de.danet.an.workflow.localcoreapi.WfAssignment#setAssignee
     * <code>WfAssignment.setAssignee</code>}. Resource assignment
     * systems are responsible for implementing
     * <code>WfAssignment</code> and could therefore perform the
     * reassignment directly; this would, however, leave the
     * generation of notifications unexecuted. <P>
     *
     * Clients should not use this method but rather call {@link
     * de.danet.an.workflow.localcoreapi.WfAssignment#setAssignee
     * <code>WfAssignment.setAssignee</code>}.
     *
     * @param oldResource the resource that has its assignment removed
     * @param newResource the resource to be assigned
     * @throws InvalidResourceException if the resource is invalid.
     * As the environment is a concurrent multi user environment,
     * <code>WfResource</code> objects may become invalid.
     * @throws AlreadyAssignedException if the assignment already
     * exists
     * @throws NotAssignedException if there is no assignment to the
     * old resource
     */
    public void changeAssignment
  (WfResource oldResource, WfResource newResource)
  throws InvalidResourceException, AlreadyAssignedException,
        NotAssignedException {
        RASInvocationHandler ras = rasInvocationHandler();
  if (ras == null) {
      throw new UnsupportedOperationException
    ("Resource assignment service not configured.");
  }
  ras.changeAssignment
      (activityFinder(), getPaKey(), toActivity(),
             oldResource, newResource);
  // The activity may or may not have assignments now. We don't
  // care, we just turn off the optimization of releaseResources.
  setPaNoAssignments (false);
        if (getPaAuditEventSelection()
            == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS) {
            fireAuditEvent
                (new DefaultAssignmentAuditEvent
     (auditEventBase(WfAuditEvent.ACTIVITY_ASSIGNMENT_CHANGED),
      ras.retrieveKey(oldResource), ras.retrieveKey(newResource),
      ras.retrieveName(oldResource),
                  ras.retrieveName(newResource)));
        }
    }

    /**
     * Removes an assignment for enacting the activity. This method calls
     * the corresponding method of the resource assignment service and
     * creates the appropriate audit event.<P>
     *
     * This method is redundant, as the OMG specification already
     * enables a client to remove an assignment by calling {@link
     * WfResource#release
     * <code>WfResource.release</code>}. <code>WfResource</code>
     * objects are, however, provided by the resource management
     * facility. This facility cannot create audit events, thus we
     * have to provide a method that can be used by the resource
     * management facility to implement <code>release</code>.
     *
     * @param resource the resource whose assignment is to be canceled
     * @throws InvalidResourceException if the resource is invalid.
     * As the environment is a concurrent multi user environment,
     * <code>WfResource</code> objects may become invalid.
     * @throws NotAssignedException if there is no such assignment
     */
    public void removeAssignment (WfResource resource)
  throws InvalidResourceException,
         NotAssignedException {
        RASInvocationHandler ras = rasInvocationHandler();
  if (ras == null) {
      throw new UnsupportedOperationException
    ("Resource assignment service not configured.");
  }
  ras.removeAssignment
      (activityFinder(), getPaKey(), toActivity(), resource);
  if (getPaAuditEventSelection()
      == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS) {
      fireAuditEvent
          (new DefaultAssignmentAuditEvent
           (auditEventBase(WfAuditEvent.ACTIVITY_ASSIGNMENT_CHANGED),
                  ras.retrieveKey(resource), null,
      ras.retrieveName(resource), null));
  }
    }
   
    /**
     * Close the activity with the given state after releasing all resources.
     */
    private void closeActivity (State closedState) {
  if (getPaPreliminarilyChosen ()
      && (closedState.isSameOrSubState (ClosedCompletedState.NORMAL)
    || (closedState.isSameOrSubState (ClosedState.TERMINATED)
        && (!((ExtProcessLocal)containerLocal()).typedState()
      .isSameOrSubState(RunningState.TERMINATING))))) {
      try {
    choose ();
      } catch (TransitionNotAllowedException e) {
    // cannot happen because state has been checked already
    logger.error ("Unexpected exception: " + e.getMessage (), e);
      }
  }
  // Debug only if process is still running normally, i.e. not
  // terminating or aborting.
  if (getPaDebug ()
      && ((ProcessLocal)containerLocal()).typedState()
                == RunningState.RUNNING) {
      State debState = (State)CLOSE_STATE_TO_DEBUG_STATE.get(closedState);
      if (debState != null) {
    setPaTypedState (debState);
    return;
      }
  }
  ((ExtProcessLocal)containerLocal())
      .closeActivity (toActivityLocal(), closedState);
    }

    /**
     * Called when the activity is closed.
     */
    protected void releaseResources () {
  // Optimization.
  if (getPaNoAssignments ()) {
      return;
  }
        RASInvocationHandler ras = rasInvocationHandler();
  if (ras == null) {
      return;
  }
  for (Iterator i = assignments().iterator(); i.hasNext ();) {
      WfAssignment assignment = (WfAssignment)i.next ();
            try {
                WfResource resource = ras.retrieveAssignee(assignment);
                ras.removeAssignment
                    (activityFinder(), getPaKey(), toActivity(), resource);
                if (getPaAuditEventSelection()
                    == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS) {
                    fireAuditEvent
                        (new DefaultAssignmentAuditEvent
                         (auditEventBase
                          (WfAuditEvent.ACTIVITY_ASSIGNMENT_CHANGED),
         ras.retrieveKey(resource),  null,
         ras.retrieveName(resource), null));
                }
      } catch (InvalidResourceException e) {
    logger.debug ("Trying to remove unknown resource: "
            + e.getMessage (), e);
      } catch (NotAssignedException e) {
    logger.debug ("Trying to remove unassigned resource: "
            + e.getMessage (), e);
      }
  }
  setPaNoAssignments (true);
    }

    /**
     * Terminate any implementation currently running for this
     * activity.
     * @throws ApplicationNotStoppedException if implementation
     * termination was not possible
     */
    protected void terminateImpl()
  throws ApplicationNotStoppedException {
  try {
      Implementation impl = executor ();
      if (impl == null) {
    return;
      }
      if (impl instanceof ToolImplementation) {
    terminateTool ((ToolImplementation)impl);
    return;
      }
      if (impl instanceof SubFlowImplementation) {
    Collection subs = performersLocal();
    for (Iterator i = subs.iterator (); i.hasNext();) {
        WfProcessLocal p = (WfProcessLocal)i.next();
        if (p.workflowState() == State.CLOSED) {
      continue;
        }
        p.terminate ();
    }
      }     
  } catch (CannotStopException e) {
      throw new ApplicationNotStoppedException (e.getMessage());
  } catch (NotRunningException e) {
      throw new ApplicationNotStoppedException (e.getMessage());
  }
    }

    /**
     * Terminate a tool currently running for this activity.
     * @throws ApplicationNotStoppedException if tool termination was
     * not possible.
     */
    protected void terminateTool()
  throws ApplicationNotStoppedException {
  Implementation impl = executor ();
  if (impl == null || !(impl instanceof ToolImplementation)) {
      return;
  }
  terminateTool ((ToolImplementation)impl);
    }

    /**
     * Terminate the given tool.
     * @throws ApplicationNotStoppedException if tool termination was
     * not possible.
     */
    private void terminateTool(ToolImplementation impl)
  throws ApplicationNotStoppedException {
  try {
      ApplicationDefinition app = (ApplicationDefinition)
    ((ProcessLocal)containerLocal()).processDefinition()
    .applicationById(impl.id());
      toolInvocationHandler().terminate (app, toActivity ());
  } catch (InvalidIdException ide) {
      logger.warn ("Invalid reference to tool: " + ide.getMessage());
  }
    }

    /**
     * Return a <code>ToolInvocationHandler</code>. Simple implementation
     * that will have to be refined by the persistence/remoting layer.
     * @return a handler.
     */
    protected ToolInvocationHandler toolInvocationHandler () {
  return (new ToolInvocationHandler () {
    public void invoke
        (ExtApplication appl, ActivityUniqueKey auk,
                     ExtActivity act, Map params)
                    throws ToolInvocationException {
                    try {
                        appl.invoke(null, act, params);
                    } catch (RemoteException e) {
                        logger.error (e.getMessage(), e);
                        throw new IllegalStateException ("Cannot invoke tool");
                    }
    }
    public void terminate
        (ExtApplication appl, Activity act)
        throws ApplicationNotStoppedException {
                    try {
                        appl.terminate(act);
                    } catch (RemoteException e) {
                        logger.error (e.getMessage(), e);
                        throw new IllegalStateException
                            ("Cannot terminate tool");
                    }
    }
      });
    }

    /**
     * Return a <code>RASInvocationHandler</code>. The default implementation
     * returns <code>null</code>, i.e. there is no resource assignment service
     * available.
     * @return a handler.
     */
    protected RASInvocationHandler rasInvocationHandler () {
        return null;
    }

    /**
     * Invoke a tool for this activity.
     *
     * @param appl the application description of the tool.
     * @param params the invocation parameters.
     */
    public void invokeTool (ExtApplication appl, Map params) {
        try {
            toolInvocationHandler().invoke
                (appl, uniqueKey(), (ExtActivity)toActivity(), params);
        } catch (ToolInvocationException e) {
            try {
                terminate();
            } catch(CannotStopException ee) {
                throw (IllegalStateException)
                    (new IllegalStateException (ee.getMessage()))
                         .initCause(ee);
            } catch(NotRunningException ee) {
                throw (IllegalStateException)
                    (new IllegalStateException (ee.getMessage()))
                        .initCause(ee);
            }
        }
    }

    /**
     * Returns a <code>WfAuditEvent</code> containing information about the
     * activity and its container, only.
     * @param eventType event type
     * @return the event containing the required information.
     */
    protected WfAuditEvent auditEventBase (String eventType) {
  return new DefaultAuditEvent
      (toActivity (), eventType, getPaKey(), getPaName(),
       containerAuditEventBase());
    }

    /**
     * Return string representation for debugging purposes.
     * @return a string representation.
     */
    public String toString() {
  return "Activity[key=" + getPaKey () + "]";
    }

}
TOP

Related Classes of de.danet.an.workflow.domain.AbstractActivity

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.