/*
* 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 () + "]";
}
}