0001: /*
0002: * This file is part of the WfMOpen project.
0003: * Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
0004: * All rights reserved.
0005: *
0006: * This program is free software; you can redistribute it and/or modify
0007: * it under the terms of the GNU General Public License as published by
0008: * the Free Software Foundation; either version 2 of the License, or
0009: * (at your option) any later version.
0010: *
0011: * This program is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0014: * GNU General Public License for more details.
0015: *
0016: * You should have received a copy of the GNU General Public License
0017: * along with this program; if not, write to the Free Software
0018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0019: *
0020: * $Id: AbstractActivity.java,v 1.37.2.1 2007/11/02 16:00:33 drmlipp Exp $
0021: *
0022: * $Log: AbstractActivity.java,v $
0023: * Revision 1.37.2.1 2007/11/02 16:00:33 drmlipp
0024: * Merged bug fixes from HEAD.
0025: *
0026: * Revision 1.38 2007/09/21 06:19:35 mlipp
0027: * Fixed problem with NamingException during process deletion.
0028: *
0029: * Revision 1.37 2007/09/14 12:34:42 drmlipp
0030: * Improved map initialization.
0031: *
0032: * Revision 1.36 2007/05/03 21:58:19 mlipp
0033: * Internal refactoring for making better use of local EJBs.
0034: *
0035: */
0036: package de.danet.an.workflow.domain;
0037:
0038: import java.io.Serializable;
0039:
0040: import java.util.ArrayList;
0041: import java.util.Collection;
0042: import java.util.Collections;
0043: import java.util.Date;
0044: import java.util.HashMap;
0045: import java.util.HashSet;
0046: import java.util.Iterator;
0047: import java.util.List;
0048: import java.util.Map;
0049: import java.util.Set;
0050: import java.util.SortedMap;
0051: import java.util.TreeMap;
0052:
0053: import java.rmi.RemoteException;
0054: import java.text.ParseException;
0055:
0056: import de.danet.an.workflow.internalapi.ExtActivityLocal;
0057: import de.danet.an.workflow.internalapi.ExtApplication;
0058: import de.danet.an.workflow.internalapi.ExtImplementationLocal;
0059: import de.danet.an.workflow.internalapi.ExtProcessLocal;
0060: import de.danet.an.workflow.internalapi.ThreadInfo;
0061: import de.danet.an.workflow.internalapi.ToolInvocationException;
0062: import de.danet.an.workflow.internalapi.ExtActivityLocal.NotStartedState;
0063: import de.danet.an.workflow.localapi.ActivityLocal;
0064: import de.danet.an.workflow.localapi.ProcessLocal;
0065: import de.danet.an.workflow.localapi.TransitionLocal;
0066: import de.danet.an.workflow.localcoreapi.WfActivityLocal;
0067: import de.danet.an.workflow.localcoreapi.WfProcessLocal;
0068: import de.danet.an.workflow.omgcore.AlreadyRunningException;
0069: import de.danet.an.workflow.omgcore.AlreadySuspendedException;
0070: import de.danet.an.workflow.omgcore.CannotCompleteException;
0071: import de.danet.an.workflow.omgcore.CannotResumeException;
0072: import de.danet.an.workflow.omgcore.CannotStopException;
0073: import de.danet.an.workflow.omgcore.CannotSuspendException;
0074: import de.danet.an.workflow.omgcore.InvalidControlOperationException;
0075: import de.danet.an.workflow.omgcore.InvalidDataException;
0076: import de.danet.an.workflow.omgcore.InvalidPerformerException;
0077: import de.danet.an.workflow.omgcore.InvalidResourceException;
0078: import de.danet.an.workflow.omgcore.InvalidStateException;
0079: import de.danet.an.workflow.omgcore.NotAssignedException;
0080: import de.danet.an.workflow.omgcore.NotRunningException;
0081: import de.danet.an.workflow.omgcore.NotSuspendedException;
0082: import de.danet.an.workflow.omgcore.ProcessData;
0083: import de.danet.an.workflow.omgcore.ResultNotAvailableException;
0084: import de.danet.an.workflow.omgcore.TransitionNotAllowedException;
0085: import de.danet.an.workflow.omgcore.UpdateNotAllowedException;
0086: import de.danet.an.workflow.omgcore.WfAssignment;
0087: import de.danet.an.workflow.omgcore.WfAuditEvent;
0088: import de.danet.an.workflow.omgcore.WfResource;
0089: import de.danet.an.workflow.omgcore.WfStateAuditEvent;
0090: import de.danet.an.workflow.omgcore.WfExecutionObject.ClosedState;
0091: import de.danet.an.workflow.omgcore.WfExecutionObject.NotRunningState;
0092: import de.danet.an.workflow.omgcore.WfExecutionObject.OpenState;
0093: import de.danet.an.workflow.omgcore.WfExecutionObject.State;
0094:
0095: import de.danet.an.workflow.api.Activity;
0096: import de.danet.an.workflow.api.ActivityUniqueKey;
0097: import de.danet.an.workflow.api.AlreadyAssignedException;
0098: import de.danet.an.workflow.api.InvalidIdException;
0099: import de.danet.an.workflow.api.InvalidKeyException;
0100: import de.danet.an.workflow.api.Participant;
0101: import de.danet.an.workflow.api.ProcessDefinition;
0102: import de.danet.an.workflow.api.Activity.ClosedCompletedState;
0103: import de.danet.an.workflow.api.Activity.DeadlineInfo;
0104: import de.danet.an.workflow.api.Activity.Implementation;
0105: import de.danet.an.workflow.api.Activity.Info;
0106: import de.danet.an.workflow.api.Activity.JoinAndSplitMode;
0107: import de.danet.an.workflow.api.Activity.StartFinishMode;
0108: import de.danet.an.workflow.api.Activity.SubFlowImplementation;
0109: import de.danet.an.workflow.api.Activity.ToolImplementation;
0110: import de.danet.an.workflow.apix.ExtActivity;
0111:
0112: import de.danet.an.workflow.spis.aii.ApplicationNotStoppedException;
0113: import de.danet.an.workflow.spis.aii.ResultProvider.ExceptionResult;
0114: import de.danet.an.workflow.spis.ras.ActivityFinder;
0115:
0116: /**
0117: * <code>AbstractActivity</code> represents a simple implementation
0118: * of the interface {@link ActivityLocal <code>ActivityLocal</code>}.
0119: * It has the following features:
0120: * <ul>
0121: * <li>It has no transition restrictions (INLINE, SPLIT, JOIN)
0122: * </li>
0123: * <li>The activity is an implementation activity of the type NO.
0124: * No SubFlows (SubProcess) or Loops can be expressed with this activity.
0125: * </li>
0126: * <li>The activity supports the StartMode and the FinishMode properties.
0127: * </li>
0128: * <li>The activity cannot act as a requester, i.e. can not start a
0129: * sub process.
0130: * </li>
0131: * </ul>
0132: */
0133: public abstract class AbstractActivity extends AbstractExecutionObject
0134: implements TimedObject, Serializable {
0135:
0136: private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
0137: .getLog(AbstractActivity.class);
0138:
0139: // Make sure that loading this class loads the new states
0140: static {
0141: State s = NotStartedState.UNKNOWN;
0142: }
0143:
0144: // A mapping of states to sub-state to save the state during
0145: // DebugState.FORWARDING_EXCEPTION
0146: private static final int SUB_STATE_RUNNING = 1;
0147: private static final int SUB_STATE_ABANDONING = 2;
0148: private static final int SUB_STATE_ABORTING = 3;
0149: private static final int SUB_STATE_COMPLETING = 4;
0150: private static final int SUB_STATE_INVOKING = 5;
0151: private static final int SUB_STATE_TERMINATING = 6;
0152:
0153: private static final Map STATE_TO_SUB_STATE = new HashMap();
0154: private static final Map SUB_STATE_TO_STATE = new HashMap();
0155:
0156: static {
0157: STATE_TO_SUB_STATE.put(RunningState.RUNNING, new Integer(
0158: SUB_STATE_RUNNING));
0159: STATE_TO_SUB_STATE.put(DebugState.ABANDONING, new Integer(
0160: SUB_STATE_ABANDONING));
0161: STATE_TO_SUB_STATE.put(DebugState.ABORTING, new Integer(
0162: SUB_STATE_ABORTING));
0163: STATE_TO_SUB_STATE.put(DebugState.COMPLETING, new Integer(
0164: SUB_STATE_COMPLETING));
0165: STATE_TO_SUB_STATE.put(DebugState.INVOKING, new Integer(
0166: SUB_STATE_INVOKING));
0167: STATE_TO_SUB_STATE.put(DebugState.TERMINATING, new Integer(
0168: SUB_STATE_TERMINATING));
0169: for (Iterator i = STATE_TO_SUB_STATE.entrySet().iterator(); i
0170: .hasNext();) {
0171: Map.Entry e = (Map.Entry) i.next();
0172: SUB_STATE_TO_STATE.put(e.getValue(), e.getKey());
0173: }
0174: }
0175:
0176: private static final Map CLOSE_STATE_TO_DEBUG_STATE = new HashMap();
0177: private static final Map DEBUG_STATE_TO_CLOSE_STATE = new HashMap();
0178:
0179: static {
0180: CLOSE_STATE_TO_DEBUG_STATE.put(ClosedState.ABORTED,
0181: DebugState.ABORTING);
0182: CLOSE_STATE_TO_DEBUG_STATE.put(ClosedState.TERMINATED,
0183: DebugState.TERMINATING);
0184: CLOSE_STATE_TO_DEBUG_STATE.put(ClosedCompletedState.NORMAL,
0185: DebugState.COMPLETING);
0186: for (Iterator i = CLOSE_STATE_TO_DEBUG_STATE.entrySet()
0187: .iterator(); i.hasNext();) {
0188: Map.Entry e = (Map.Entry) i.next();
0189: DEBUG_STATE_TO_CLOSE_STATE.put(e.getValue(), e.getKey());
0190: }
0191: }
0192:
0193: // The sorted list of transition id's for the split
0194: private List splitList = null;
0195:
0196: /**
0197: * Creates a new <code>AbstractActivity</code>.
0198: */
0199: public AbstractActivity() {
0200: }
0201:
0202: //
0203: // Persistent attribute accessors and associated methods
0204: //
0205:
0206: /**
0207: * The getter method for the persistent attribute <code>actImpl</code>.
0208: *
0209: * @return the value of actImpl.
0210: * @see #setPaActImpl
0211: */
0212: protected abstract Implementation[] getPaActImpl();
0213:
0214: /**
0215: * The setter method for the persistent attribute <code>actImpl</code>.
0216: *
0217: * @param newActImpl the new value of actImpl.
0218: * @see #getPaActImpl
0219: */
0220: protected abstract void setPaActImpl(Implementation[] newActImpl);
0221:
0222: /**
0223: * The getter method for the persistent attribute <code>performer</code>.
0224: *
0225: * @return the value of performer.
0226: * @see #setPaPerformer
0227: */
0228: protected abstract String getPaPerformer();
0229:
0230: /**
0231: * The setter method for the persistent attribute <code>performer</code>.
0232: *
0233: * @param newPerformer the new value of performer.
0234: * @see #getPaPerformer
0235: */
0236: protected abstract void setPaPerformer(String newPerformer);
0237:
0238: /**
0239: * The getter method for the persistent attribute <code>executor</code>.
0240: *
0241: * @return the value of executor.
0242: * @see #setPaExecStat
0243: */
0244: protected abstract Integer getPaExecStat();
0245:
0246: /**
0247: * The setter method for the persistent attribute <code>executor</code>.
0248: *
0249: * @param newExecutor the new value of executor.
0250: * @see #getPaExecStat
0251: */
0252: protected abstract void setPaExecStat(Integer newExecutor);
0253:
0254: /**
0255: * The getter method for the persistent attribute <code>startMode</code>.
0256: *
0257: * @return the value of startMode.
0258: * @see #setPaStartMode
0259: */
0260: protected abstract StartFinishMode getPaStartMode();
0261:
0262: /**
0263: * The setter method for the persistent attribute <code>startMode</code>.
0264: *
0265: * @param newStartMode the new value of startMode.
0266: * @see #getPaStartMode
0267: */
0268: protected abstract void setPaStartMode(StartFinishMode newStartMode);
0269:
0270: /**
0271: * The getter method for the persistent attribute <code>finishMode</code>.
0272: *
0273: * @return the value of finishMode.
0274: * @see #setPaFinishMode
0275: */
0276: protected abstract StartFinishMode getPaFinishMode();
0277:
0278: /**
0279: * The setter method for the persistent attribute <code>finishMode</code>.
0280: *
0281: * @param newFinishMode the new value of finishMode.
0282: * @see #getPaFinishMode
0283: */
0284: protected abstract void setPaFinishMode(
0285: StartFinishMode newFinishMode);
0286:
0287: /**
0288: * The getter method for the persistent attribute <code>joinMode</code>.
0289: *
0290: * @return the value of joinMode.
0291: * @see #setPaJoinMode
0292: */
0293: protected abstract JoinAndSplitMode getPaJoinMode();
0294:
0295: /**
0296: * The setter method for the persistent attribute <code>joinMode</code>.
0297: *
0298: * @param newJoinMode the new value of joinMode.
0299: * @see #getPaJoinMode
0300: */
0301: protected abstract void setPaJoinMode(JoinAndSplitMode newJoinMode);
0302:
0303: /**
0304: * The getter method for the persistent attribute <code>splitMode</code>.
0305: *
0306: * @return the value of splitMode.
0307: * @see #setPaSplitMode
0308: */
0309: protected abstract JoinAndSplitMode getPaSplitMode();
0310:
0311: /**
0312: * The setter method for the persistent attribute <code>splitMode</code>.
0313: *
0314: * @param newSplitMode the new value of splitMode.
0315: * @see #getPaSplitMode
0316: */
0317: protected abstract void setPaSplitMode(JoinAndSplitMode newSplitMode);
0318:
0319: /**
0320: * The getter method for the persistent attribute
0321: * <code>pendingException</code>.
0322: *
0323: * @return the value of pendingException.
0324: * @see #setPaPendingException
0325: */
0326: protected abstract String getPaPendingException();
0327:
0328: /**
0329: * The setter method for the persistent attribute
0330: * <code>pendingException</code>.
0331: *
0332: * @param newPendingException the new value of pendingException.
0333: * @see #getPaPendingException
0334: */
0335: protected abstract void setPaPendingException(
0336: String newPendingException);
0337:
0338: /**
0339: * The getter method for the persistent flag
0340: * <code>pendingExceptionIsFromBlock</code>.
0341: *
0342: * @return the value of the flag.
0343: * @see #setPaPendingExceptionIsFromBlock
0344: */
0345: protected abstract boolean getPaPendingExceptionIsFromBlock();
0346:
0347: /**
0348: * The setter method for the persistent flags
0349: * <code>pendingExceptionIsFromBlock</code>.
0350: *
0351: * @param newValue the new value of the flag.
0352: * @see #getPaPendingExceptionIsFromBlock
0353: */
0354: protected abstract void setPaPendingExceptionIsFromBlock(
0355: boolean newValue);
0356:
0357: /**
0358: * The getter method implementation for the persistent
0359: * read-only attribute <code>processKey</code>.
0360: * May be overridden by the derived class to speed up access
0361: * in distsributed systems.
0362: *
0363: * @return the value of processKey.
0364: */
0365: protected String getPaProcessKey() {
0366: return containerLocal().key();
0367: }
0368:
0369: /**
0370: * The getter method implementation for the persistent
0371: * read-only attribute <code>processName</code>.
0372: * May be overridden by the derived class to speed up access
0373: * in distsributed systems.
0374: *
0375: * @return the value of processName.
0376: */
0377: protected String getPaProcessName() {
0378: return containerLocal().name();
0379: }
0380:
0381: /**
0382: * The getter method implementation for the persistent
0383: * read-only attribute <code>processMgrName</code>.
0384: * May be overridden by the derived class to speed up access
0385: * in distsributed systems.
0386: *
0387: * @return the value of processMgrName.
0388: */
0389: protected String getPaProcessMgrName() {
0390: return ((ProcessLocal) containerLocal()).processDefinition()
0391: .mgrName();
0392: }
0393:
0394: /**
0395: * The getter method implementation for the persistent
0396: * read-only attribute <code>processMgrVersion</code>.
0397: * May be overridden by the derived class to speed up access
0398: * in distsributed systems.
0399: *
0400: * @return the value of processMgrVersion.
0401: */
0402: protected String getPaProcessMgrVersion() {
0403: return ((ProcessLocal) containerLocal()).processDefinition()
0404: .version();
0405: }
0406:
0407: /**
0408: * The getter method for the persistent attribute <code>threadInfo</code>.
0409: *
0410: * @return the value of threadInfo.
0411: * @see #setPaThreadInfo
0412: */
0413: protected abstract ThreadInfo getPaThreadInfo();
0414:
0415: /**
0416: * The setter method for the persistent attribute <code>threadInfo</code>.
0417: *
0418: * @param newThreadInfo the new value of threadInfo.
0419: * @see #getPaThreadInfo
0420: */
0421: protected abstract void setPaThreadInfo(ThreadInfo newThreadInfo);
0422:
0423: /**
0424: * The getter method for the persistent attribute <code>Subflow</code>.
0425: *
0426: * @return the value of Subflow.
0427: * @see #setPaSubflow
0428: */
0429: protected abstract String getPaSubflow();
0430:
0431: /**
0432: * The setter method for the persistent attribute <code>Subflow</code>.
0433: *
0434: * @param newSubflow the new value of Subflow.
0435: * @see #getPaSubflow
0436: */
0437: protected abstract void setPaSubflow(String newSubflow);
0438:
0439: /**
0440: * The getter method for the persistent attribute <code>deadlines</code>.
0441: *
0442: * @return the value of deadlines.
0443: * @see #setPaDeadlines
0444: */
0445: protected abstract List getPaDeadlines();
0446:
0447: /**
0448: * The setter method for the persistent attribute <code>deadlines</code>.
0449: *
0450: * @param newDeadlines the new value of deadlines.
0451: * @see #getPaDeadlines
0452: */
0453: protected abstract void setPaDeadlines(List newDeadlines);
0454:
0455: /**
0456: * The getter method for the persistent attribute <code>startTime</code>.
0457: *
0458: * @return the value of startTime.
0459: * @see #setPaStartTime
0460: */
0461: protected abstract Date getPaStartTime();
0462:
0463: /**
0464: * The setter method for the persistent attribute <code>startTime</code>.
0465: *
0466: * @param newStartTime the new value of startTime.
0467: * @see #getPaStartTime
0468: */
0469: protected abstract void setPaStartTime(Date newStartTime);
0470:
0471: /**
0472: * The getter method for the persistent attribute <code>suspendStart</code>.
0473: *
0474: * @return the value of suspendStart.
0475: * @see #setPaSuspendStart
0476: */
0477: protected abstract Date getPaSuspendStart();
0478:
0479: /**
0480: * The setter method for the persistent attribute <code>suspendStart</code>.
0481: *
0482: * @param newSuspendStart the new value of suspendStart.
0483: * @see #getPaSuspendStart
0484: */
0485: protected abstract void setPaSuspendStart(Date newSuspendStart);
0486:
0487: /**
0488: * The getter method for the persistent attribute <code>suspendAccum</code>.
0489: *
0490: * @return the value of suspendAccum.
0491: * @see #setPaSuspendAccum
0492: */
0493: protected abstract long getPaSuspendAccum();
0494:
0495: /**
0496: * The setter method for the persistent attribute <code>suspendAccum</code>.
0497: *
0498: * @param newSuspendAccum the new value of suspendAccum.
0499: * @see #getPaSuspendAccum
0500: */
0501: protected abstract void setPaSuspendAccum(long newSuspendAccum);
0502:
0503: /**
0504: * The getter method for the persistent attribute
0505: * <code>blockActivity</code>.
0506: *
0507: * @return the value of blockActivity.
0508: * @see #setPaBlockActivity
0509: */
0510: protected abstract Long getPaBlockActivity();
0511:
0512: /**
0513: * The setter method for the persistent attribute
0514: * <code>blockActivity</code>.
0515: *
0516: * @param newBlockActivity the new value of blockActivity.
0517: * @see #getPaBlockActivity
0518: */
0519: protected abstract void setPaBlockActivity(Long newBlockActivity);
0520:
0521: /**
0522: * The getter method for the persistent attribute
0523: * <code>waitOnProc</code>.
0524: *
0525: * @return the value of waitOnProc.
0526: * @see #setPaWaitOnProc
0527: */
0528: protected abstract Long getPaWaitOnProc();
0529:
0530: /**
0531: * The setter method for the persistent attribute
0532: * <code>waitOnProc</code>.
0533: *
0534: * @param newWaitOnProc the new value of waitOnProc.
0535: * @see #getPaWaitOnProc
0536: */
0537: protected abstract void setPaWaitOnProc(Long newWaitOnProc);
0538:
0539: /**
0540: * The getter method for the persistent attribute
0541: * <code>waitOnChan</code>.
0542: *
0543: * @return the value of waitOnChan.
0544: * @see #setPaWaitOnChan
0545: */
0546: protected abstract String getPaWaitOnChan();
0547:
0548: /**
0549: * The setter method for the persistent attribute
0550: * <code>waitOnChan</code>.
0551: *
0552: * @param newWaitOnChan the new value of waitOnChan.
0553: * @see #getPaWaitOnChan
0554: */
0555: protected abstract void setPaWaitOnChan(String newWaitOnChan);
0556:
0557: /**
0558: * The getter method for the persistent attribute
0559: * <code>subStateBackup</code>.
0560: *
0561: * @return the value of subStateBackup.
0562: * @see #setPaSubStateBackup
0563: */
0564: protected abstract int getPaSubStateBackup();
0565:
0566: /**
0567: * The setter method for the persistent attribute
0568: * <code>subStateBackup</code>.
0569: *
0570: * @param newSubStateBackup the new value of subStateBackup.
0571: * @see #getPaSubStateBackup
0572: */
0573: protected abstract void setPaSubStateBackup(int newSubStateBackup);
0574:
0575: /**
0576: * The getter method for the persistent attribute
0577: * <code>deferChoiceOnSplit</code>.
0578: *
0579: * @return the value of deferChoiceOnSplit.
0580: * @see #setPaDeferChoiceOnSplit
0581: */
0582: protected abstract boolean getPaDeferChoiceOnSplit();
0583:
0584: /**
0585: * The setter method for the persistent attribute
0586: * <code>deferChoiceOnSplit</code>.
0587: *
0588: * @param newDeferChoiceOnSplit the new value of deferChoiceOnSplit.
0589: * @see #getPaDeferChoiceOnSplit
0590: */
0591: protected abstract void setPaDeferChoiceOnSplit(
0592: boolean newDeferChoiceOnSplit);
0593:
0594: /**
0595: * The getter method for the persistent attribute
0596: * <code>electedInChoice</code>.
0597: *
0598: * @return the value of electedInChoice.
0599: * @see #setPaPreliminarilyChosen
0600: */
0601: protected abstract boolean getPaPreliminarilyChosen();
0602:
0603: /**
0604: * The setter method for the persistent attribute
0605: * <code>electedInChoice</code>.
0606: *
0607: * @param newElectedInChoice the new value of electedInChoice.
0608: * @see #getPaPreliminarilyChosen
0609: */
0610: protected abstract void setPaPreliminarilyChosen(
0611: boolean newElectedInChoice);
0612:
0613: /**
0614: * The getter method for the persistent attribute
0615: * <code>noAssignments</code>.
0616: *
0617: * @return the value of noAssignments.
0618: * @see #setPaNoAssignments
0619: */
0620: protected abstract boolean getPaNoAssignments();
0621:
0622: /**
0623: * The setter method for the persistent attribute
0624: * <code>noAssignments</code>.
0625: *
0626: * @param newNoAssignments the new value of noAssignments.
0627: * @see #getPaNoAssignments
0628: */
0629: protected abstract void setPaNoAssignments(boolean newNoAssignments);
0630:
0631: /**
0632: * Initializes the class, i.e. sets all attributes to the given
0633: * values. Note that {@link #refresh <code>refresh</code>} will be
0634: * called subsequently.
0635: *
0636: * @param blockActId if the activity is part of a block activity,
0637: * else <code>null</code>
0638: * @param priority a <code>Priority</code> value
0639: * @param name the activity's name
0640: * @param description activity description
0641: * @param startMode the start mode
0642: * @param finishMode the finish mode
0643: * @param joinMode the join mode
0644: * @param splitMode the split mode
0645: * @param implementation the implementation description
0646: * @param performer the performer
0647: * @param deadlines the deadlines
0648: * @param deferChoiceOnSplit if the split is to be made as
0649: * deferred choice
0650: * @param auditEventSelection the audit event selection
0651: * @param storeAuditEvents if true, audit events are stored in the
0652: * database
0653: * @see #dispose
0654: */
0655: protected void init(Long blockActId, Priority priority,
0656: String name, String description, StartFinishMode startMode,
0657: StartFinishMode finishMode, JoinAndSplitMode joinMode,
0658: JoinAndSplitMode splitMode,
0659: Implementation[] implementation, String performer,
0660: List deadlines, boolean deferChoiceOnSplit,
0661: int auditEventSelection, boolean storeAuditEvents) {
0662: super .init();
0663: reset(false, false);
0664: // (re-)init attributes
0665: setPaSubflow(null);
0666: setPaStartTime(null);
0667: setPaSuspendStart(null);
0668: setPaSuspendAccum(0);
0669: setPaWaitOnProc(null);
0670: setPaWaitOnChan(null);
0671: setPaNoAssignments(true);
0672: // set given properties
0673: setPaBlockActivity(blockActId);
0674: setPaPriority(priority);
0675: setPaName(name);
0676: setPaDescription(description);
0677: setPaStartMode(startMode);
0678: setPaFinishMode(finishMode);
0679: setPaJoinMode(joinMode);
0680: setPaSplitMode(splitMode);
0681: setPaActImpl(implementation);
0682: setPaPerformer(performer);
0683: setPaDeadlines(deadlines);
0684: setPaDeferChoiceOnSplit(deferChoiceOnSplit);
0685: setPaAuditEventSelection(auditEventSelection);
0686: setPaStoreAuditEvents(storeAuditEvents);
0687: }
0688:
0689: /**
0690: * Called after change of persistent attributes. May be used to
0691: * synchronise state derived from persistent attributes with
0692: * the new values.
0693: *
0694: * @see #init
0695: */
0696: protected void refresh() {
0697: super .refresh();
0698: }
0699:
0700: /**
0701: * Releases all allocated resources. The object will be in an
0702: * unusable state until resources are reallocated by calling
0703: * {@link #init <code>init</code>} and
0704: * {@link #refresh <code>refresh</code>}.
0705: */
0706: protected void dispose() {
0707: super .dispose();
0708: }
0709:
0710: //
0711: // Domain methods
0712: //
0713:
0714: /**
0715: * Indicates if some other object is equal to this one. <P>
0716: *
0717: * Note that <code>obj</code> may be a stub representing the
0718: * object. Stubs do not in general implement
0719: * <code>equals</code> correctly, so while
0720: * <code>this.equals(obj)</code> does indicate the equality as
0721: * defined for this class, <code>obj.equals(this)</code> generally
0722: * does not.
0723: *
0724: * @param obj the object to compare with.
0725: * @return <code>true</code> if the other object is equal.
0726: */
0727: public boolean equals(Object obj) {
0728: return (obj instanceof AbstractActivity)
0729: && getPaKey().equals(((AbstractActivity) obj).key())
0730: || (obj instanceof WfActivityLocal)
0731: && getPaKey().equals(((WfActivityLocal) obj).key());
0732: }
0733:
0734: /**
0735: * Return the context of this <code>WfExecutionObject</code>.
0736: * The process data object returned is a copy of the name value
0737: * association, the values are not copied, however.
0738: *
0739: * @return the process relevant data that define the context of the
0740: * process.
0741: */
0742: public ProcessData processContext() {
0743: return containerLocal().processContext();
0744: }
0745:
0746: /**
0747: * Set new process context.
0748: * @param newValue new value for proces context.
0749: * @throws InvalidDataException if data is invalid
0750: * @throws UpdateNotAllowedException if update is not allowed
0751: */
0752: public void setProcessContext(ProcessData newValue)
0753: throws InvalidDataException, UpdateNotAllowedException {
0754: throw new UpdateNotAllowedException("Not supported");
0755: }
0756:
0757: /**
0758: * The default implementation of <code>result</code> throws a
0759: * <code>ResultNotAvailableException</code> as this implementation does
0760: * not provide access to the results of an activity.
0761: *
0762: * @return a <code>ProcessData</code> value
0763: * @exception ResultNotAvailableException if no result is available.
0764: * @ejb.interface-method view-type="remote"
0765: */
0766: public ProcessData result() throws ResultNotAvailableException {
0767: throw new ResultNotAvailableException();
0768: }
0769:
0770: /**
0771: * The default implementation of <code>setResult</code> maps the
0772: * formal parameter names to actual process data item names and
0773: * merges the result data in the process context.
0774: *
0775: * @param result the result data.
0776: * @exception InvalidDataException if the data does not match the
0777: * parameter list.
0778: * @ejb.interface-method view-type="remote"
0779: */
0780: public void setResult(ProcessData result)
0781: throws InvalidDataException {
0782: int curExec = getPaExecStat().intValue();
0783: if (!workflowState().equals(State.OPEN)) {
0784: // maybe this activity is already closed by deadline event
0785: if (typedState().isSameOrSubState(
0786: ClosedCompletedState.ABANDONED)) {
0787: logger
0788: .warn("Call to setResult() ignored because "
0789: + toString()
0790: + " has already been abandoned (probably called "
0791: + "by tool that could not be terminated).");
0792: return;
0793: }
0794: throw new InvalidDataException("Cannot set result for "
0795: + this + ": state is " + state() + " (not open).");
0796: }
0797: if (curExec == 0 || curExec == Integer.MAX_VALUE) {
0798: throw new InvalidDataException("Cannot set result for "
0799: + this + ": no tool running.");
0800: }
0801: ((ExtImplementationLocal) getPaActImpl()[Math.abs(curExec) - 1])
0802: .mergeResult(toActivityLocal(), result);
0803: if (getPaPreliminarilyChosen()) {
0804: try {
0805: choose();
0806: } catch (TransitionNotAllowedException e) {
0807: // cannot happen because state has been checked already
0808: logger.error("Unexpected exception: " + e.getMessage(),
0809: e);
0810: }
0811: }
0812: if (getPaAuditEventSelection() == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS) {
0813: fireAuditEvent(new DefaultDataAuditEvent(
0814: auditEventBase(WfAuditEvent.ACTIVITY_RESULT_CHANGED),
0815: null, result));
0816: }
0817: }
0818:
0819: /**
0820: * Check if the given assignment is among the assignments of this
0821: * activity.
0822: * @param member the assignment in question.
0823: * @return true if the assignment is among the assignments of this
0824: * activity.
0825: */
0826: public boolean isMemberOfAssignments(WfAssignment member) {
0827: throw new UnsupportedOperationException();
0828: }
0829:
0830: /**
0831: * Return all performers associated with this requester.
0832: *
0833: * @return A {@link java.util.Collection collection} of
0834: * associated performers.
0835: */
0836: public Collection performers() {
0837: Collection res = new ArrayList();
0838: for (Iterator i = performersLocal().iterator(); i.hasNext();) {
0839: res.add(((ExtProcessLocal) i.next()).toProcess());
0840: }
0841: return res;
0842: }
0843:
0844: /**
0845: * Return all performers associated with this requester.
0846: *
0847: * @return A {@link java.util.Collection collection} of
0848: * associated performers.
0849: */
0850: public Collection performersLocal() {
0851: Collection res = new ArrayList();
0852: if (getPaSubflow() != null) {
0853: try {
0854: res.add(lookupProcessLocal(getPaSubflow()));
0855: } catch (InvalidKeyException e) {
0856: // process has been deleted, avoid repeated lookup
0857: setPaSubflow(null);
0858: }
0859: }
0860: return res;
0861: }
0862:
0863: /**
0864: * Lookup a process by its key. Can only be implemented by the
0865: * persistence layer.
0866: * @param key the primary key
0867: * @return the process
0868: * @throws InvalidKeyException if no process with the given key
0869: * exists
0870: */
0871: protected abstract ExtProcessLocal lookupProcessLocal(String key)
0872: throws InvalidKeyException;
0873:
0874: /**
0875: * Called by the workflow engine if the activity is a requester.
0876: *
0877: * @param e the event.
0878: * @throws InvalidPerformerException thrown by the derived
0879: * {@link de.danet.an.workflow.localcoreapi.WfRequesterLocal
0880: * <code>WfRequesterLocal</code>} if it receives an event from a
0881: * process that is not among its performers.
0882: */
0883: public void receiveEvent(WfAuditEvent e)
0884: throws InvalidPerformerException {
0885: }
0886:
0887: /**
0888: * Return the remote version of this object.
0889: *
0890: * @return the client side object.
0891: */
0892: public abstract Activity toActivity();
0893:
0894: /**
0895: * Return the version of this object to be passed to as local reference.
0896: *
0897: * @return the client side object.
0898: */
0899: protected abstract ExtActivityLocal toActivityLocal();
0900:
0901: /**
0902: * Returns the <code>WfProcessLocal</code> that this activity is a part of.
0903: * @return the process.
0904: */
0905: public abstract WfProcessLocal containerLocal();
0906:
0907: /**
0908: * Return a unique key for the activity. (Note that the OMG
0909: * interface defines the key returned by the {@link
0910: * de.danet.an.workflow.localcoreapi.WfExecutionObjectLocal#key
0911: * <code>key()</code>} method as unique within the scope of the
0912: * containing process only.)
0913: * @return value of uniqueKey.
0914: */
0915: public ActivityUniqueKey uniqueKey() {
0916: return new ActivityUniqueKey(getPaProcessMgrName(),
0917: getPaProcessKey(), getPaKey());
0918: }
0919:
0920: /**
0921: * This method returns all available information about the
0922: * activity in a single operation.
0923: *
0924: * @return the resulting <code>Activity.Info</code> value
0925: */
0926: public Info activityInfo() {
0927: WfProcessLocal proc = containerLocal();
0928: return new Info(uniqueKey(), name(), description(), priority(),
0929: lastStateTime(), getPaProcessName(), proc.description());
0930: }
0931:
0932: /**
0933: * Returns the key of the "parent" block activity. All activities
0934: * implicitly created by a block activity share the same block
0935: * activity key.<P>
0936: *
0937: * Note that there need not be an activity with the returned key,
0938: * as an activity set is actually a template describing how to
0939: * implement block activities. The information obtained can mainly
0940: * be used to group all activities that have been instantiated as
0941: * part of an activity set.<P>
0942: *
0943: * @return an identification of the block activity that caused
0944: * this activity to be instantiated or <code>null</code> if this
0945: * activity was not instantiated as part of an activity set
0946: */
0947: public String blockActivity() {
0948: Long ba = getPaBlockActivity();
0949: if (ba == null) {
0950: return null;
0951: }
0952: return ba.toString();
0953: }
0954:
0955: /**
0956: * Returns an <code>WfAuditEvent</code> containing container-related
0957: * information.
0958: * @return the audit event
0959: */
0960: protected abstract WfAuditEvent containerAuditEventBase();
0961:
0962: /**
0963: * List of possible modes for the activity.
0964: * Handles the mapping between the mode name and the mode object.
0965: */
0966: private static Map activityModes = null;
0967:
0968: /**
0969: * Returns the implementation of the activity as {@link
0970: * de.danet.an.workflow.localapi.ActivityLocal.Implementation
0971: * <code>Implementation</code>}s.
0972: *
0973: * @return an array of <code>Implementation</code>}s or
0974: * <code>null</code> if no implementation is defined.
0975: */
0976: public Implementation[] implementation() {
0977: return getPaActImpl();
0978: }
0979:
0980: /**
0981: * Returns the deadlines defined for this activity.
0982: *
0983: * @return the deadlines
0984: */
0985: public DeadlineInfo[] deadlines() {
0986: DeadlineInfo[] dli = new DeadlineInfo[getPaDeadlines().size()];
0987: boolean closed = typedState().isSameOrSubState(State.CLOSED);
0988: boolean running = typedState().isSameOrSubState(
0989: OpenState.RUNNING);
0990: int i = 0;
0991: for (Iterator it = getPaDeadlines().iterator(); it.hasNext(); i++) {
0992: Deadline dl = (Deadline) it.next();
0993: int state = dl.getState();
0994: if (closed && state == Deadline.STATE_INITIAL) {
0995: state = DeadlineInfo.STATE_CANCELED;
0996: } else if (running && state == Deadline.STATE_INITIAL) {
0997: state = DeadlineInfo.STATE_ACTIVE;
0998: }
0999: dli[i] = new DeadlineInfo(dl.getExecution(), dl
1000: .getExceptionName(), dl.getCondition(), state);
1001: }
1002: return dli;
1003: }
1004:
1005: /**
1006: * Return the start time of the activity.
1007: * @return result
1008: * @throws NotRunningException if the activity has not been started yet
1009: */
1010: public Date startTime() throws NotRunningException {
1011: if (getPaStartTime() == null) {
1012: throw new NotRunningException(state());
1013: }
1014: return getPaStartTime();
1015: }
1016:
1017: /**
1018: * Returns the performer as string.
1019: * @return performer as string
1020: */
1021: public String performer() {
1022: return getPaPerformer();
1023: }
1024:
1025: /**
1026: * Returns the current executor.
1027: *
1028: * @return current executor or <code>null</code> if no executor
1029: * running.
1030: */
1031: public Implementation executor() {
1032: if (getPaExecStat().intValue() <= 0
1033: || getPaExecStat().intValue() == Integer.MAX_VALUE) {
1034: return null;
1035: }
1036: Implementation impl = getPaActImpl()[getPaExecStat().intValue() - 1];
1037: if (impl instanceof ProcBasedImpl) {
1038: ((ProcBasedImpl) impl).setProcessKey(getPaSubflow());
1039: }
1040: return impl;
1041: }
1042:
1043: /**
1044: * Returns the start mode.
1045: * @return start mode
1046: */
1047: protected StartFinishMode startMode() {
1048: return getPaStartMode();
1049: }
1050:
1051: /**
1052: * Returns the finish mode.
1053: * @return finish mode
1054: */
1055: protected StartFinishMode finishMode() {
1056: return getPaFinishMode();
1057: }
1058:
1059: /**
1060: * Set the join mode of the activity.
1061: * @param joinMode the new join mode
1062: */
1063: public void setJoinMode(JoinAndSplitMode joinMode) {
1064: setPaJoinMode(joinMode);
1065: }
1066:
1067: /**
1068: * Returns the join mode.
1069: * @return join mode
1070: */
1071: public JoinAndSplitMode joinMode() {
1072: return getPaJoinMode();
1073: }
1074:
1075: /**
1076: * Set the join mode of the activity.
1077: * @param splitMode the new split mode
1078: */
1079: public void setSplitMode(JoinAndSplitMode splitMode) {
1080: setPaSplitMode(splitMode);
1081: }
1082:
1083: /**
1084: * Returns the split mode.
1085: * @return split mode
1086: */
1087: public JoinAndSplitMode splitMode() {
1088: return getPaSplitMode();
1089: }
1090:
1091: /* Comment copied from Interface. */
1092: public void doCloseActivity(State closedState) {
1093: updateState(closedState);
1094: }
1095:
1096: /**
1097: * Returns if the activity's split is to be executed as deferred
1098: * choice.
1099: * @return <code>true</code> if the activity has been elected
1100: */
1101: public boolean deferChoiceOnSplit() {
1102: return getPaDeferChoiceOnSplit();
1103: }
1104:
1105: /**
1106: * Returns if the activity has been preliminarily chosen in a
1107: * deferred choice.
1108: * @return <code>true</code> if the activity has been elected
1109: */
1110: public boolean preliminarilyChosen() {
1111: return getPaPreliminarilyChosen();
1112: }
1113:
1114: //
1115: // State change API implementation
1116: //
1117:
1118: private static Map vstates = null;
1119:
1120: /**
1121: * Returns the {@link java.util.Map <code>Map</code>} that maps
1122: * the activity states to a {@link java.util.Map
1123: * <code>List</code>} of reachable process states. <P>
1124: *
1125: * The map returns the state transitions allowed as parameters of
1126: * {@link
1127: * de.danet.an.workflow.localcoreapi.WfExecutionObjectLocal#changeState
1128: * <code>changeState</code>} only. I.e. the map does not reflect
1129: * all possible transitions, there may be more, but those are only
1130: * accessible to the workflow engine itself.
1131: * @return the resulting map.
1132: */
1133: protected Map getStateTransitionMap() {
1134: // Map must be initialized lazily. Static initialization holds
1135: // danger of "race conditions" with constant initialization
1136: // (resulting in null-entries). And new map must be fully initialized
1137: // before assigning to member variable to avoid concurrent
1138: // initialization.
1139: if (vstates == null) {
1140: Map transMap = new HashMap();
1141: // Transitions from open.not_running.not_started
1142: List l = new ArrayList();
1143: transMap.put(NotStartedState.UNKNOWN, l);
1144: transMap.put(NotStartedState.STARTABLE, l);
1145: l.add(ClosedState.TERMINATED);
1146: // Transitions from open.running
1147: l = new ArrayList();
1148: transMap.put(RunningState.RUNNING, l);
1149: l.add(NotRunningState.SUSPENDED);
1150: l.add(ClosedState.TERMINATED);
1151: l.add(ClosedState.COMPLETED);
1152: // Transitions from open.not_running.suspended.suspended
1153: l = new ArrayList();
1154: transMap.put(SuspendedState.SUSPENDED, l);
1155: l.add(OpenState.RUNNING);
1156: l.add(ClosedState.ABORTED);
1157: // i.e. complete may be called, state changes later, of course.
1158: l.add(ClosedState.COMPLETED);
1159: // Transitions from open.not_running.suspended.abandoning
1160: l = new ArrayList();
1161: transMap.put(SuspendedState.ABANDONING, l);
1162: l.add(OpenState.RUNNING);
1163: l.add(SuspendedState.CLEARING_EXCEPTION);
1164: // Transitions from open.not_running.suspended.clearing_exception
1165: l = new ArrayList();
1166: transMap.put(SuspendedState.CLEARING_EXCEPTION, l);
1167: l.add(OpenState.RUNNING);
1168: // Transitions from debug states
1169: l = new ArrayList();
1170: transMap.put(DebugState.ABORTING, l);
1171: transMap.put(DebugState.COMPLETING, l);
1172: transMap.put(DebugState.TERMINATING, l);
1173: l.add(OpenState.RUNNING);
1174: l = new ArrayList();
1175: transMap.put(DebugState.INVOKING, l);
1176: l.add(OpenState.RUNNING);
1177: l.add(DebugState.SKIPPING);
1178: l.add(NotRunningState.SUSPENDED);
1179: vstates = Collections.unmodifiableMap(transMap);
1180: }
1181: return vstates;
1182: }
1183:
1184: /**
1185: * Adds the possibility to change the state to
1186: * <code>ClosedState.COMPLETED</code>
1187: * (i.e. "<code>complete()</code>") to the
1188: * <code>changeState</code> method of the superclass.<P>
1189: *
1190: * Adds handling of change from
1191: * <code>open.not_running.suspended.abandoning</code> to
1192: * <code>open.not_running.suspended.clearing_exception</code>.
1193: *
1194: * @param newState the new state.
1195: * @throws InvalidStateException If <code>newState</code> is an invalid
1196: * state for the execution object.
1197: * @throws TransitionNotAllowedException If the transition from the current
1198: * state to <code>newState</code> is not allowed.
1199: */
1200: public void changeState(State newState)
1201: throws InvalidStateException, TransitionNotAllowedException {
1202: try {
1203: if (newState.isSameOrSubState(ClosedState.COMPLETED)) {
1204: complete();
1205: return;
1206: }
1207: } catch (CannotCompleteException e) {
1208: throw new TransitionNotAllowedException(e.getClass()
1209: .getName()
1210: + ": " + e.getMessage());
1211: }
1212: if (typedState().isSameOrSubState(SuspendedState.ABANDONING)
1213: && newState
1214: .isSameOrSubState(SuspendedState.CLEARING_EXCEPTION)) {
1215: updateImmediate(SuspendedState.SUSPENDED);
1216: return;
1217: }
1218: if (getPaDebug()) {
1219: State oldState = typedState();
1220: if (newState == DebugState.FORWARDING_EXCEPTION
1221: && STATE_TO_SUB_STATE.containsKey(oldState)) {
1222: setPaSubStateBackup(((Integer) STATE_TO_SUB_STATE
1223: .get(oldState)).intValue());
1224: setPaTypedState(newState);
1225: return;
1226: }
1227: if (newState == DebugState.AWAITING_EXCEPTION) {
1228: if (oldState == DebugState.ABANDONING) {
1229: // Proceed from abandoning
1230: setPaTypedState(newState);
1231: return;
1232: }
1233: throw new TransitionNotAllowedException("Change to "
1234: + DebugState.AWAITING_EXCEPTION
1235: + " is only allowed from "
1236: + DebugState.ABANDONING);
1237: }
1238: if (oldState.isSameOrSubState(RunningState.DEBUG)) {
1239: if (!newState.isSameOrSubState(OpenState.RUNNING)
1240: && !(oldState == DebugState.INVOKING && newState
1241: .isSameOrSubState(NotRunningState.SUSPENDED))) {
1242: throw new TransitionNotAllowedException(
1243: "Only valid change from debug states is to "
1244: + OpenState.RUNNING
1245: + " or sub-state");
1246: }
1247:
1248: // Resume closing with the proper state.
1249: State closeState = (State) DEBUG_STATE_TO_CLOSE_STATE
1250: .get(oldState);
1251: if (closeState != null) {
1252: // Reset to running state in order to generate proper
1253: // state change event later.
1254: setPaTypedState(RunningState.RUNNING);
1255: ((ExtProcessLocal) containerLocal()).closeActivity(
1256: toActivityLocal(), closeState);
1257: return;
1258: }
1259: // Maybe continue invocation
1260: if (oldState == DebugState.INVOKING) {
1261: // Reset to running state in order to generate proper
1262: // state change event later.
1263: setPaTypedState(RunningState.RUNNING);
1264: if (!newState
1265: .isSameOrSubState(NotRunningState.SUSPENDED)) {
1266: if (newState == DebugState.SKIPPING) {
1267: invokeNextImpl();
1268: } else {
1269: invokeCurrentImpl();
1270: }
1271: return;
1272: }
1273: // We cannot change the state to suspended
1274: // immediately. This would imply to change to a
1275: // state before the tool invocation. The decision
1276: // about the tool invocation has, however, already
1277: // been taken, we can only skip the actual
1278: // execution of the tool. If we were not running
1279: // in debug mode, the suspended state would also
1280: // be reached after the tool invocation.
1281: invokeCurrentImpl();
1282: }
1283: }
1284: }
1285: super .changeState(newState);
1286: }
1287:
1288: /**
1289: * Return the activity's thread info.
1290: * @return the thread info.
1291: */
1292: public ThreadInfo threadInfo() {
1293: return getPaThreadInfo();
1294: }
1295:
1296: /**
1297: * Update the sub-state of an activity in state
1298: * <code>NotRunningState.NOT_STARTED</code> to
1299: * <code>NotStartedState.STARTABLE</code>. This update reflects
1300: * internal evaluation progression only and does neither update
1301: * last state change time nor fire an event.
1302: *
1303: * @param triggers the activities that caused the activity to be
1304: * startable (one or more activities depending on the join mode).
1305: * @param preliminarilyChosen if the activity is triggered due to a
1306: * deferred choice
1307: * @see #updateState
1308: */
1309: public void setStartable(Collection triggers,
1310: boolean preliminarilyChosen) {
1311: if (!getPaTypedState().isSameOrSubState(
1312: NotRunningState.NOT_STARTED)) {
1313: throw new IllegalStateException(
1314: "Cannot update not started substate, activity is "
1315: + getPaTypedState().toString());
1316: }
1317: setPaTypedState(NotStartedState.STARTABLE);
1318: if (getPaThreadInfo() == null) {
1319: setPaThreadInfo(new ThreadInfo(triggers));
1320: }
1321: setPaPreliminarilyChosen(preliminarilyChosen);
1322: if (logger.isDebugEnabled()) {
1323: logger.debug(toString() + " triggered by "
1324: + getPaThreadInfo());
1325: }
1326: }
1327:
1328: /**
1329: * Reset an activity to <code>NotStartedState.UNKNOWN</code>,
1330: * i.e. have it assume exactly the same state as it has when
1331: * newly created. An exception is the thread info which can
1332: * (optionally) be preserved in certain cases.
1333: * @param preserveThreadInfo preserve the thread info,
1334: * i.e. the information about the activity's predecessors.
1335: * @param publishChange publish the state change, i.e. create a
1336: * corresponding audit event
1337: */
1338: public void reset(boolean preserveThreadInfo, boolean publishChange) {
1339: if (publishChange) {
1340: updateInterim(NotStartedState.UNKNOWN);
1341: } else {
1342: setPaTypedState(NotStartedState.UNKNOWN);
1343: }
1344: setPaWaitOnProc(null);
1345: setPaWaitOnChan(null);
1346: setPaExecStat(new Integer(0));
1347: if (!preserveThreadInfo) {
1348: setPaThreadInfo(null);
1349: }
1350: if (getPaDeadlines().size() > 0) {
1351: for (Iterator dli = getPaDeadlines().iterator(); dli
1352: .hasNext();) {
1353: Deadline dl = (Deadline) dli.next();
1354: dl.setState(Deadline.STATE_INITIAL);
1355: }
1356: // notify persistence layer about change
1357: setPaDeadlines(getPaDeadlines());
1358: }
1359: }
1360:
1361: /**
1362: * Terminates and resets an activity that was started
1363: * preliminarily as part of a deferred choice.
1364: * @param reset if the activity's state is to be reset
1365: * @throws TransitionNotAllowedException if the activity has not
1366: * been preliminarily chosen
1367: */
1368: public void withdrawPreliminaryChoice(boolean reset)
1369: throws TransitionNotAllowedException {
1370: if (!getPaPreliminarilyChosen()) {
1371: throw new TransitionNotAllowedException(this
1372: + " is not in state preliminary chosen");
1373: }
1374: setPaPreliminarilyChosen(false);
1375: if (!reset) {
1376: return;
1377: }
1378: try {
1379: terminateImpl();
1380: } catch (ApplicationNotStoppedException e) {
1381: logger
1382: .warn("Tool invoked in deferred choice could not be terminated: "
1383: + e.getMessage());
1384: }
1385: reset(false, true);
1386: releaseResources();
1387: }
1388:
1389: /**
1390: * Starts an activity. This is not an OMG method, but needed
1391: * in the communication between process and activity.
1392: * @throws AlreadyRunningException when the process has already been
1393: * started.
1394: */
1395: public void start() throws AlreadyRunningException {
1396: if (!typedState().isSameOrSubState(NotRunningState.NOT_STARTED)) {
1397: throw new AlreadyRunningException(toString()
1398: + " state is: " + state());
1399: }
1400: setPaStartTime(new Date());
1401: Deadline.armDeadlines(this , new Date(getPaStartTime().getTime()
1402: + getPaSuspendAccum()),
1403: (ExtProcessLocal) containerLocal(), getPaDeadlines());
1404: if (getPaStartMode() != StartFinishMode.MANUAL) {
1405: updateState(RunningState.RUNNING);
1406: autoAssignResources();
1407: invokeNextImpl();
1408: } else {
1409: updateInterim(RunningState.RUNNING); // no handleStarted called
1410: updateState(SuspendedState.SUSPENDED);
1411: }
1412: }
1413:
1414: /* Comment copied from Interface. */
1415: public void suspend() throws CannotSuspendException,
1416: NotRunningException, AlreadySuspendedException {
1417: super .suspend();
1418: Implementation impl = executor();
1419: if (impl != null && (impl instanceof SubFlowImplementation)) {
1420: Collection subs = performersLocal();
1421: for (Iterator i = subs.iterator(); i.hasNext();) {
1422: WfProcessLocal p = (WfProcessLocal) i.next();
1423: try {
1424: p.suspend();
1425: } catch (NotRunningException e) {
1426: throw new CannotSuspendException(e.getMessage());
1427: } catch (AlreadySuspendedException e) {
1428: // may safely by ignored, make checkstyle happy
1429: continue;
1430: }
1431: }
1432: }
1433: }
1434:
1435: /* Comment copied from Interface. */
1436: public void resume() throws CannotResumeException,
1437: NotRunningException, NotSuspendedException {
1438: // Check for special case "resuming exception processing"
1439: if (typedState().isSameOrSubState(SuspendedState.ABANDONING)) {
1440: // Tool was invoked with suspend flag, continue "normal"
1441: // processing. Complete abandoning and go to running first
1442: // to get consistent state changes.
1443: setPaExecStat(new Integer(Integer.MAX_VALUE));
1444: releaseResources();
1445: updateInterim(RunningState.RUNNING);
1446: // now quit if debugging
1447: if (getPaDebug()) {
1448: setPaTypedState(DebugState.ABANDONING);
1449: return;
1450: }
1451: updateInterim(ClosedCompletedState.ABANDONED);
1452:
1453: // propagate to process to cause triggering transitions
1454: String exception = getPaPendingException();
1455: setPaPendingException(null);
1456: ((ExtProcessLocal) containerLocal()).handleException(
1457: toActivityLocal(), exception);
1458: return;
1459: }
1460:
1461: // resume from normal manual suspend invocation
1462: Implementation impl = executor();
1463: if (impl != null && (impl instanceof SubFlowImplementation)) {
1464: Collection subs = performersLocal();
1465: for (Iterator i = subs.iterator(); i.hasNext();) {
1466: WfProcessLocal p = (WfProcessLocal) i.next();
1467: try {
1468: p.resume();
1469: } catch (NotRunningException e) {
1470: throw new CannotResumeException(e.getMessage());
1471: } catch (NotSuspendedException e) {
1472: // may safely by ignored, make checkstyle happy
1473: continue;
1474: }
1475: }
1476: }
1477: super .resume();
1478: }
1479:
1480: /**
1481: * Close this activity regularly.
1482: * @throws CannotCompleteException if the state can not be changed.
1483: * @ejb.interface-method view-type="remote"
1484: */
1485: public void complete() throws CannotCompleteException {
1486: if (!validTypedStates().contains(ClosedState.COMPLETED)) {
1487: if (typedState().isSameOrSubState(
1488: ClosedCompletedState.ABANDONED)) {
1489: logger
1490: .warn("Call to complete() ignored because "
1491: + toString()
1492: + " has already been abandoned (probably called "
1493: + "by tool that could not be terminated).");
1494: return;
1495: }
1496: throw new CannotCompleteException(toString() + " is "
1497: + state());
1498: }
1499: if (getPaPreliminarilyChosen()) {
1500: try {
1501: choose();
1502: } catch (TransitionNotAllowedException e) {
1503: // cannot happen because state has been checked already
1504: logger.error("Unexpected exception: " + e.getMessage(),
1505: e);
1506: }
1507: }
1508: setPaWaitOnChan(null);
1509: // if this is the last implementation completing and we are
1510: // not debugging, and the activity is not suspended, then we
1511: // may complete immediately
1512: if (!haveMoreImpls()
1513: && !getPaDebug()
1514: && !typedState().isSameOrSubState(
1515: NotRunningState.SUSPENDED)) {
1516: setPaExecStat(new Integer(Integer.MAX_VALUE));
1517: if (finishMode() == StartFinishMode.MANUAL) {
1518: updateState(SuspendedState.SUSPENDED);
1519: return;
1520: }
1521: // causes state changed event to be fired
1522: ((ExtProcessLocal) containerLocal()).closeActivity(
1523: toActivityLocal(), ClosedCompletedState.NORMAL);
1524: return;
1525: }
1526: fireAuditEvent(new ImplCompleteAuditEvent(
1527: auditEventBase(ImplCompleteAuditEvent.TOOL_COMPLETE),
1528: Math.abs(getPaExecStat().intValue()) - 1));
1529: }
1530:
1531: /* Comment copied from Interface. */
1532: public boolean initiateAbandoning(boolean blockException,
1533: String exceptionName) {
1534: boolean isSuspended = false;
1535: if (typedState().isSameOrSubState(OpenState.RUNNING)) {
1536: setPaTypedState(RunningState.ABANDONING);
1537: abandonImpl();
1538: } else if (typedState().isSameOrSubState(
1539: SuspendedState.SUSPENDED)) {
1540: if (blockException) {
1541: // Exceptions from block activity deadlines cannot be delayed.
1542: updateInterim(RunningState.RUNNING);
1543: abandonImpl();
1544: } else {
1545: if (getPaExecStat().intValue() == 0
1546: || getPaExecStat().intValue() == Integer.MAX_VALUE) {
1547: // suspend from manual start or finish mode, exception
1548: // must be from deadline and is processed. Note that we
1549: // do not have to abandon an implementation as no
1550: // implementation is running.
1551: updateInterim(RunningState.RUNNING);
1552: } else {
1553: // if we get here, the activity has been
1554: // suspended in response to an exception from a tool
1555: // invocation (as exceptions from deadlines are delayed
1556: // when an activity is suspended)
1557: isSuspended = true;
1558: setPaTypedState(SuspendedState.ABANDONING);
1559: setPaPendingException(exceptionName);
1560: setPaPendingExceptionIsFromBlock(blockException);
1561: }
1562: }
1563: } else {
1564: return false;
1565: }
1566: if (getPaPreliminarilyChosen()) {
1567: try {
1568: choose();
1569: } catch (TransitionNotAllowedException e) {
1570: // cannot happen because state has been checked already
1571: logger.error("Unexpected exception: " + e.getMessage(),
1572: e);
1573: }
1574: }
1575: setPaWaitOnProc(null);
1576: setPaWaitOnChan(null);
1577: disarmDeadlines();
1578: if (isSuspended) {
1579: setPaSuspendStart(new Date());
1580: setPaExecStat(new Integer(-getPaExecStat().intValue()));
1581: return false;
1582: }
1583: setPaExecStat(new Integer(Integer.MAX_VALUE));
1584: releaseResources();
1585: if (getPaDebug()) {
1586: setPaTypedState(DebugState.ABANDONING);
1587: return false;
1588: }
1589: updateInterim(ClosedCompletedState.ABANDONED);
1590: return true;
1591: }
1592:
1593: private void abandonImpl() {
1594: Implementation impl = executor();
1595: if (impl == null || typedState() == DebugState.INVOKING) {
1596: return;
1597: }
1598: if (impl instanceof ToolImplementation) {
1599: try {
1600: terminateTool((ToolImplementation) impl);
1601: } catch (ApplicationNotStoppedException ans) {
1602: logger.debug("Failed to terminate tool in abandon of "
1603: + toString() + " (ignored): "
1604: + ans.getMessage());
1605: }
1606: }
1607: if (impl instanceof SubFlowImplementation) {
1608: Collection subs = performersLocal();
1609: for (Iterator i = subs.iterator(); i.hasNext();) {
1610: ExtProcessLocal p = (ExtProcessLocal) i.next();
1611: try {
1612: if (p.typedState().isSameOrSubState(
1613: NotRunningState.SUSPENDED)) {
1614: p.abort();
1615: } else {
1616: try {
1617: p.terminate();
1618: } catch (CannotStopException e) {
1619: try {
1620: p.suspend();
1621: p.abort();
1622: } catch (InvalidControlOperationException ee) {
1623: logger
1624: .warn("Cannot force termination of "
1625: + p
1626: + " (ignored): "
1627: + ee.getMessage());
1628: }
1629: }
1630: }
1631: } catch (NotRunningException e) {
1632: logger.debug("Trying to stop not running subflow "
1633: + "(propably race-condition, ignored): "
1634: + e.getMessage());
1635: } catch (InvalidControlOperationException e) {
1636: logger.warn("Cannot force termination of " + p
1637: + " (ignored): " + e.getMessage());
1638: }
1639: }
1640: }
1641: }
1642:
1643: /* Comment copied from Interface. */
1644: public void abort() throws CannotStopException, NotRunningException {
1645: mayCloseCheck(ClosedState.ABORTED);
1646: try {
1647: terminateImpl();
1648: } catch (ApplicationNotStoppedException e) {
1649: Implementation impl = executor();
1650: if (impl == null
1651: || !(impl instanceof SubFlowImplementation)) {
1652: logger.warn(toString()
1653: + " has been aborted, but the currently "
1654: + "running tool could not be stopped: "
1655: + e.getMessage());
1656: } else {
1657: Collection subs = performersLocal();
1658: for (Iterator i = subs.iterator(); i.hasNext();) {
1659: WfProcessLocal p = (WfProcessLocal) i.next();
1660: try {
1661: try {
1662: p.suspend();
1663: } catch (AlreadySuspendedException as) {
1664: // the better, make checkstyle happy
1665: int dummy = 0;
1666: }
1667: p.abort();
1668: } catch (CannotSuspendException ee) {
1669: throw new CannotStopException(
1670: "To be aborted sub-process cannot be suspended: "
1671: + e.getMessage());
1672: } catch (NotRunningException ee) {
1673: // may safely by ignored, make checkstyle happy
1674: continue;
1675: }
1676: }
1677: }
1678: }
1679: doAbort();
1680: }
1681:
1682: /* Comment copied from Interface. */
1683: public void abortRequester() {
1684: try {
1685: if (!typedState().isSameOrSubState(
1686: NotRunningState.SUSPENDED)) {
1687: super .suspend();
1688: }
1689: mayCloseCheck(ClosedState.ABORTED);
1690: } catch (InvalidControlOperationException e) {
1691: logger.error("Sub-process requests abortion of "
1692: + toString() + ", but cannot abort: "
1693: + e.getMessage(), e);
1694: }
1695: doAbort();
1696: }
1697:
1698: private void doAbort() {
1699: setPaWaitOnProc(null);
1700: setPaWaitOnChan(null);
1701: setPaExecStat(new Integer(Integer.MAX_VALUE));
1702: closeActivity(ClosedState.ABORTED);
1703: }
1704:
1705: /* Comment copied from Interface. */
1706: public void terminate() throws CannotStopException,
1707: NotRunningException {
1708: mayCloseCheck(ClosedState.TERMINATED);
1709: try {
1710: terminateImpl();
1711: } catch (ApplicationNotStoppedException e) {
1712: throw new CannotStopException(
1713: "Cannot terminate, application not stopped: "
1714: + e.getMessage());
1715: }
1716: setPaWaitOnProc(null);
1717: setPaWaitOnChan(null);
1718: setPaExecStat(new Integer(Integer.MAX_VALUE));
1719: closeActivity(ClosedState.TERMINATED);
1720: }
1721:
1722: private void mayCloseCheck(ClosedState s)
1723: throws CannotStopException, NotRunningException {
1724: if (((ProcessLocal) containerLocal()).workflowState() == State.CLOSED) {
1725: if (typedState().isSameOrSubState(
1726: ClosedCompletedState.ABANDONED)) {
1727: logger
1728: .warn("Attempt to close ignored, "
1729: + toString()
1730: + " has already been abandoned (probably attempted "
1731: + "by tool that could not be terminated).");
1732: return;
1733: }
1734: throw new NotRunningException("Cannot terminate "
1735: + toString() + ", process already closed.");
1736: }
1737: if (!validTypedStates().contains(s)) {
1738: if (typedState().isSameOrSubState(OpenState.RUNNING)) {
1739: throw new CannotStopException(toString() + " is "
1740: + state());
1741: } else {
1742: throw new NotRunningException(toString() + " is "
1743: + state());
1744: }
1745: }
1746: }
1747:
1748: /* Comment copied from Interface. */
1749: public void abandon(String exception)
1750: throws TransitionNotAllowedException {
1751: abandon(new ExceptionResult(exception));
1752: }
1753:
1754: /* Comment copied from Interface. */
1755: public void abandon(ExceptionResult result)
1756: throws TransitionNotAllowedException {
1757: String exception = result.exceptionName();
1758: // handle forwarding of exception for debugging
1759: State oldState = typedState();
1760: if (oldState == DebugState.AWAITING_EXCEPTION
1761: || oldState == DebugState.FORWARDING_EXCEPTION) {
1762: if (oldState == DebugState.AWAITING_EXCEPTION) {
1763: updateInterim(ClosedCompletedState.ABANDONED);
1764: } else {
1765: setPaTypedState((State) SUB_STATE_TO_STATE
1766: .get(new Integer(getPaSubStateBackup())));
1767: }
1768: // propagate to process to cause triggering transitions
1769: ((ExtProcessLocal) containerLocal()).handleException(
1770: toActivityLocal(), exception);
1771: return;
1772: }
1773:
1774: // normal procedures. Note that having a running tool implies
1775: // that the activity is running or suspended
1776: if ((getPaExecStat().intValue() <= 0 || getPaExecStat()
1777: .intValue() == Integer.MAX_VALUE)
1778: && !typedState()
1779: .isSameOrSubState(DebugState.COMPLETING)) {
1780: throw new TransitionNotAllowedException("No tool executing");
1781: }
1782:
1783: if (result.suspendActivity()) {
1784: // Everything that is usually done when suspend() is called is
1785: // either not applicable or indirectly done here. Suspending
1786: // subflow (done in suspend()) is not applicable, as we have
1787: // a tool implementation running. And everything done in
1788: // handleSuspendedEvent() is also done in initiateAbandoning()
1789: // and when resuming from ...suspended.abandoning.
1790: updateImmediate(SuspendedState.SUSPENDED);
1791: }
1792: initiateAbandoning(false, exception);
1793:
1794: // now quit if debugging
1795: if (getPaDebug()) {
1796: return;
1797: }
1798:
1799: // propagate to process to cause triggering transitions if activity
1800: // has really been completed (may still be suspended.abandoning)
1801: if (typedState().isSameOrSubState(State.CLOSED)) {
1802: ((ExtProcessLocal) containerLocal()).handleException(
1803: toActivityLocal(), exception);
1804: }
1805: }
1806:
1807: /**
1808: * Makes this activity the chosen one in a set of activities
1809: * started by an AND split with the "deferred choice" option
1810: * set. All other activities in the set are reset to their initial
1811: * state.
1812: *
1813: * <P>If the activity does not participate in a deferred choice,
1814: * this method does nothing and returns <code>true</code>.
1815: *
1816: * @return <code>true</code> if the activity could be made the
1817: * effectively chosen one
1818: * @throws TransitionNotAllowedException if the activity is
1819: * neither running nor suspended
1820: */
1821: public boolean choose() throws TransitionNotAllowedException {
1822: return ((ExtProcessLocal) containerLocal())
1823: .choose(toActivityLocal());
1824: }
1825:
1826: /* Comment copied from Interface. */
1827: public void waitOnChannel(String procKey, String channel) {
1828: setPaWaitOnProc(new Long(procKey));
1829: setPaWaitOnChan(channel);
1830: }
1831:
1832: //
1833: // Timers
1834: //
1835:
1836: private void disarmDeadlines() {
1837: if (getPaDeadlines().size() == 0) {
1838: return;
1839: }
1840: if (logger.isDebugEnabled()) {
1841: logger.debug("Stopping timers for " + toString());
1842: }
1843: stopTimers();
1844: }
1845:
1846: /**
1847: * Handle the timeout of a timer.
1848: * @param info the context.
1849: */
1850: public void handleTimeout(Serializable info) {
1851: int dlIndex = ((Integer) info).intValue();
1852: Deadline dl = (Deadline) getPaDeadlines().get(dlIndex);
1853: handleDeadline(dl);
1854: }
1855:
1856: private void handleDeadline(Deadline dl) {
1857: if (logger.isDebugEnabled()) {
1858: logger.debug(toString() + " reached " + dl);
1859: }
1860: if (!(typedState().isSameOrSubState(OpenState.RUNNING) || (typedState()
1861: .isSameOrSubState(NotRunningState.SUSPENDED) && (getPaExecStat()
1862: .intValue() == 0 || getPaExecStat().intValue() == Integer.MAX_VALUE)))) {
1863: // if this activity is currently actively suspended, the
1864: // deadline will be retriggered when the activity is
1865: // resumed
1866: return;
1867: }
1868:
1869: // now accept deadline
1870: dl.setState(Deadline.STATE_REACHED);
1871: // notify persistence layer about change
1872: setPaDeadlines(getPaDeadlines());
1873:
1874: // now quit if debugging
1875: if (getPaDebug()) {
1876: return;
1877: }
1878:
1879: // complete this if deadline is synchronous
1880: if (dl.getExecution() == Deadline.SYNCHR) {
1881: initiateAbandoning(false, null);
1882: }
1883: // Due to the previous check, we only get here if the activity was
1884: // initially running (not suspended). So it will be closed now
1885: // (i.e. cannot be suspended.abandoning).
1886: ((ExtProcessLocal) containerLocal()).handleException(
1887: toActivityLocal(), dl.getExceptionName());
1888: }
1889:
1890: /* Coment copied from interface. */
1891: public String[] handledExceptions() {
1892: Set res = new HashSet();
1893: // exception name from deadlines
1894: for (Iterator i = getPaDeadlines().iterator(); i.hasNext();) {
1895: Deadline dl = (Deadline) i.next();
1896: res.add(dl.getExceptionName());
1897: }
1898: // exception names from transitions
1899: List allTrans = ((ProcessLocal) containerLocal())
1900: .transitionsLocal();
1901: for (Iterator tri = allTrans.iterator(); tri.hasNext();) {
1902: TransitionLocal trans = (TransitionLocal) tri.next();
1903: if (key().equals(trans.from().key())
1904: && trans.conditionType() == de.danet.an.workflow.api.Transition.COND_TYPE_EXCEPTION) {
1905: res.add(trans.condition());
1906: }
1907: }
1908: return (String[]) res.toArray(new String[res.size()]);
1909: }
1910:
1911: //
1912: // State change handling
1913: //
1914:
1915: /**
1916: * Check if this class handles a specific event.
1917: * @param event the event to check
1918: * @return <code>true</code> if event is handled
1919: */
1920: public static boolean isHandled(WfAuditEvent event) {
1921: if (event instanceof ToolInvocationFailedAuditEvent) {
1922: return true;
1923: }
1924: if (event instanceof ImplCompleteAuditEvent) {
1925: return true;
1926: }
1927: if (event.eventType().equals(
1928: WfAuditEvent.ACTIVITY_STATE_CHANGED)) {
1929: try {
1930: State oldState = State
1931: .fromString(((WfStateAuditEvent) event)
1932: .oldState());
1933: State newState = State
1934: .fromString(((WfStateAuditEvent) event)
1935: .newState());
1936: if (oldState
1937: .isSameOrSubState(NotRunningState.NOT_STARTED)) {
1938: if (newState == ClosedState.TERMINATED) {
1939: return true;
1940: }
1941: } else if (oldState
1942: .isSameOrSubState(NotRunningState.SUSPENDED)) {
1943: if (newState == RunningState.RUNNING) {
1944: return true;
1945: }
1946: if (newState == ClosedState.ABORTED) {
1947: return true;
1948: }
1949: } else if (oldState.isSameOrSubState(OpenState.RUNNING)) {
1950: if (newState == SuspendedState.SUSPENDED) {
1951: return true;
1952: }
1953: if (newState == ClosedCompletedState.NORMAL) {
1954: return true;
1955: }
1956: if (newState == ClosedState.TERMINATED) {
1957: return true;
1958: }
1959: }
1960: } catch (InvalidStateException e) {
1961: throw (IllegalArgumentException) (new IllegalArgumentException(
1962: e.getMessage())).initCause(e);
1963: }
1964: }
1965: return false;
1966: }
1967:
1968: /**
1969: * Handles a suspended audit event.
1970: * @param event the event.
1971: */
1972: protected void handleSuspendedEvent(WfStateAuditEvent event) {
1973: if (getPaExecStat().intValue() == 0) {
1974: // this is the event received from start with start mode manual
1975: Deadline.armDeadlines(this , new Date(getPaStartTime()
1976: .getTime()
1977: + getPaSuspendAccum()),
1978: (ExtProcessLocal) containerLocal(),
1979: getPaDeadlines());
1980: return;
1981: }
1982: if (getPaExecStat().intValue() == Integer.MAX_VALUE) {
1983: // this is the event received from complete with finish mode manual
1984: return;
1985: }
1986: setPaSuspendStart(new Date());
1987: disarmDeadlines();
1988: }
1989:
1990: /**
1991: * Handles a resumed audit event.
1992: * @param event the event.
1993: */
1994: protected void handleResumedEvent(WfStateAuditEvent event) {
1995: if (getPaSuspendStart() != null) {
1996: setPaSuspendAccum(getPaSuspendAccum()
1997: + ((new Date()).getTime() - getPaSuspendStart()
1998: .getTime()));
1999: setPaSuspendStart(null);
2000: }
2001: // now handle expired deadlines first
2002: int exc = getPaExecStat().intValue();
2003: if (exc == 0) {
2004: // delayed start (start mode manual), timers are running
2005: autoAssignResources();
2006: invokeNextImpl();
2007: return;
2008: }
2009: // handle all expired timers first
2010: Date now = null;
2011: Date base = null;
2012: SortedMap expDls = new TreeMap();
2013: for (Iterator i = getPaDeadlines().iterator(); i.hasNext();) {
2014: Deadline dl = (Deadline) i.next();
2015: if (dl.getState() != Deadline.STATE_INITIAL) {
2016: continue;
2017: }
2018: if (now == null) {
2019: now = new Date();
2020: base = new Date(getPaStartTime().getTime()
2021: + getPaSuspendAccum());
2022: }
2023: try {
2024: Date exp = dl.expirationDate(base,
2025: (ExtProcessLocal) containerLocal());
2026: if (!exp.after(now)) {
2027: expDls.put(exp, dl);
2028: }
2029: } catch (ParseException e) {
2030: logger.error(e.getMessage());
2031: }
2032: }
2033: for (Iterator i = expDls.values().iterator(); i.hasNext();) {
2034: Deadline dl = (Deadline) i.next();
2035: handleDeadline(dl);
2036: if (typedState().isSameOrSubState(State.CLOSED)) {
2037: return;
2038: }
2039: }
2040: if (exc == Integer.MAX_VALUE) {
2041: // resume from manual finish
2042: closeActivity(ClosedCompletedState.NORMAL);
2043: return;
2044: }
2045: Deadline.armDeadlines(this , base,
2046: (ExtProcessLocal) containerLocal(), getPaDeadlines());
2047: if (exc < 0) {
2048: // tool was completed during suspend
2049: invokeNextImpl();
2050: }
2051: }
2052:
2053: /**
2054: * Handles a tool invocation audit event.
2055: * @param event the event.
2056: */
2057: protected void handleToolInvocationFailedAuditEvent(
2058: ToolInvocationFailedAuditEvent event) {
2059: if (workflowState() == State.OPEN) {
2060: setPaWaitOnProc(null);
2061: setPaWaitOnChan(null);
2062: setPaExecStat(new Integer(Integer.MAX_VALUE));
2063: closeActivity(ClosedState.TERMINATED);
2064: }
2065: }
2066:
2067: /**
2068: * Handles a tool completed audit event.
2069: * @param event the event.
2070: */
2071: protected void handleImplCompletedEvent(ImplCompleteAuditEvent event) {
2072: if (typedState().isSameOrSubState(NotRunningState.SUSPENDED)) {
2073: setPaWaitOnProc(null);
2074: int exc = getPaExecStat().intValue();
2075: if (exc > 0 && exc != Integer.MAX_VALUE) {
2076: setPaExecStat(new Integer(-exc));
2077: }
2078: } else {
2079: invokeNextImpl();
2080: }
2081: }
2082:
2083: /**
2084: * Handles a completed audit event. This is thrown after the last
2085: * implementation has terminated.
2086: * @param event the event.
2087: */
2088: protected void handleCompletedEvent(WfStateAuditEvent event) {
2089: disarmDeadlines();
2090: releaseResources();
2091: }
2092:
2093: /**
2094: * Handles a terminated audit event.
2095: * @param event the event.
2096: */
2097: protected void handleTerminatedEvent(WfStateAuditEvent event) {
2098: disarmDeadlines();
2099: releaseResources();
2100: }
2101:
2102: /**
2103: * Handles a aborting audit event.
2104: * @param event the event.
2105: */
2106: protected void handleAbortedEvent(WfStateAuditEvent event) {
2107: disarmDeadlines();
2108: releaseResources();
2109: }
2110:
2111: /**
2112: * Invokes the next tool.
2113: */
2114: private void invokeNextImpl() {
2115: setPaWaitOnProc(null);
2116: if (!typedState().workflowState().equals(State.OPEN)
2117: || typedState().isSameOrSubState(
2118: NotRunningState.SUSPENDED)) {
2119: return;
2120: }
2121: int exc = getPaExecStat().intValue();
2122: if (exc == Integer.MAX_VALUE) {
2123: logger
2124: .warn("Call to invokeNextImpl after all implementations "
2125: + "have been invoked indicates some kind of problem.");
2126: return;
2127: }
2128: if (!haveMoreImpls()) {
2129: setPaExecStat(new Integer(Integer.MAX_VALUE));
2130: if (finishMode() == StartFinishMode.MANUAL) {
2131: updateState(SuspendedState.SUSPENDED);
2132: } else {
2133: closeActivity(ClosedCompletedState.NORMAL);
2134: }
2135: return;
2136: }
2137: // Perform the next tool used for this activity
2138: setPaExecStat(new Integer(Math.abs(exc) + 1));
2139: if (!getPaDebug()) {
2140: invokeCurrentImpl();
2141: } else {
2142: setPaTypedState(DebugState.INVOKING);
2143: }
2144: }
2145:
2146: /**
2147: * Check if there are more implementations.
2148: */
2149: private boolean haveMoreImpls() {
2150: int exc = getPaExecStat().intValue();
2151: if (exc == Integer.MAX_VALUE) {
2152: return false;
2153: }
2154: int nextExc = Math.abs(exc) + 1;
2155: Implementation[] toolsList = getPaActImpl();
2156: return !(toolsList == null || nextExc > toolsList.length);
2157: }
2158:
2159: /**
2160: * Invoke the current tool.
2161: */
2162: private void invokeCurrentImpl() {
2163: ExtImplementationLocal impl = (ExtImplementationLocal) getPaActImpl()[Math
2164: .abs(getPaExecStat().intValue()) - 1];
2165: impl.invoke(toActivityLocal());
2166: if (impl instanceof ProcBasedImpl) {
2167: String key = ((ProcBasedImpl) impl).processKey();
2168: setPaSubflow(key);
2169: }
2170: }
2171:
2172: /**
2173: * Returns an {@link de.danet.an.workflow.spis.ras.ActivityFinder
2174: * <code>ActivityFinder</code>} that identifies this activity
2175: * against a resource assignment service as defined by
2176: * {@link de.danet.an.workflow.spis.ras the ras package}.
2177: *
2178: * @return the activity finder.
2179: */
2180: public abstract ActivityFinder activityFinder();
2181:
2182: /**
2183: * Called when the activity enters the running state. Calls the
2184: * resource assignment service to trigger the assignment.
2185: */
2186: protected void autoAssignResources() {
2187: try {
2188: RASInvocationHandler ras = rasInvocationHandler();
2189: if (ras == null) {
2190: return;
2191: }
2192: Participant participant = null;
2193: if (performer() != null) {
2194: try {
2195: participant = ((ProcessLocal) containerLocal())
2196: .processDefinition().participantById(
2197: performer());
2198: } catch (InvalidIdException e) {
2199: String pid = (String) processContext().get(
2200: performer());
2201: if (pid == null) {
2202: throw new InvalidIdException(
2203: "Referenced data field \""
2204: + performer()
2205: + "\""
2206: + " does not contain valid participant id.");
2207: }
2208: participant = ((ProcessLocal) containerLocal())
2209: .processDefinition().participantById(pid);
2210: }
2211: }
2212: Collection resources = null;
2213: resources = ras.autoAssignResources(activityFinder(),
2214: getPaKey(), toActivity(),
2215: ((ExtProcessLocal) containerLocal())
2216: .processCreator(), participant);
2217: Iterator i = resources.iterator();
2218: // Save info for optimization in releaseResources.
2219: setPaNoAssignments(!i.hasNext());
2220: if (getPaAuditEventSelection() == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS) {
2221: while (i.hasNext()) {
2222: WfResource resource = (WfResource) i.next();
2223: fireAuditEvent(new DefaultAssignmentAuditEvent(
2224: auditEventBase(WfAuditEvent.ACTIVITY_ASSIGNMENT_CHANGED),
2225: null, ras.retrieveKey(resource), null, ras
2226: .retrieveName(resource)));
2227: }
2228: }
2229: } catch (InvalidIdException iie) {
2230: logger.error("Invalid participant: " + iie.getMessage());
2231: }
2232: }
2233:
2234: /**
2235: * Return the list of assignments.
2236: * @return list of assignments
2237: * @ejb.interface-method view-type="remote"
2238: */
2239: public Collection assignments() {
2240: RASInvocationHandler ras = rasInvocationHandler();
2241: if (ras == null) {
2242: return new ArrayList();
2243: }
2244: return ras.assignments(activityFinder(), getPaKey(),
2245: toActivity());
2246: }
2247:
2248: /**
2249: * Get the resource associated with an Assignment. The method calls
2250: * the corresponding method of the resource assignment service.
2251: *
2252: * This method is intended to be used by resource assignment
2253: * systems for implementing {@link
2254: * de.danet.an.workflow.localcoreapi.WfAssignment#assignee
2255: * <code>WfAssignment.assignee</code>}.<P>
2256: *
2257: * Clients should not use this method but rather call {@link
2258: * de.danet.an.workflow.localcoreapi.WfAssignment#setAssignee
2259: * <code>WfAssignment.setAssignee</code>}.
2260: *
2261: * @param asnmnt the assignment
2262: * @return the resource
2263: */
2264: public WfResource getResource(WfAssignment asnmnt) {
2265: RASInvocationHandler ras = rasInvocationHandler();
2266: if (ras == null) {
2267: throw new UnsupportedOperationException(
2268: "Resource assignment service not configured.");
2269: }
2270: return ras.getResource(asnmnt);
2271: }
2272:
2273: /**
2274: * Change an assignment for enacting the activity. This method calls
2275: * the corresponding method of the resource assignment service and
2276: * creates the appropriate audit event.<P>
2277: *
2278: * This method is intended to be used by resource assignment
2279: * systems for implementing {@link
2280: * de.danet.an.workflow.localcoreapi.WfAssignment#setAssignee
2281: * <code>WfAssignment.setAssignee</code>}. Resource assignment
2282: * systems are responsible for implementing
2283: * <code>WfAssignment</code> and could therefore perform the
2284: * reassignment directly; this would, however, leave the
2285: * generation of notifications unexecuted. <P>
2286: *
2287: * Clients should not use this method but rather call {@link
2288: * de.danet.an.workflow.localcoreapi.WfAssignment#setAssignee
2289: * <code>WfAssignment.setAssignee</code>}.
2290: *
2291: * @param oldResource the resource that has its assignment removed
2292: * @param newResource the resource to be assigned
2293: * @throws InvalidResourceException if the resource is invalid.
2294: * As the environment is a concurrent multi user environment,
2295: * <code>WfResource</code> objects may become invalid.
2296: * @throws AlreadyAssignedException if the assignment already
2297: * exists
2298: * @throws NotAssignedException if there is no assignment to the
2299: * old resource
2300: */
2301: public void changeAssignment(WfResource oldResource,
2302: WfResource newResource) throws InvalidResourceException,
2303: AlreadyAssignedException, NotAssignedException {
2304: RASInvocationHandler ras = rasInvocationHandler();
2305: if (ras == null) {
2306: throw new UnsupportedOperationException(
2307: "Resource assignment service not configured.");
2308: }
2309: ras.changeAssignment(activityFinder(), getPaKey(),
2310: toActivity(), oldResource, newResource);
2311: // The activity may or may not have assignments now. We don't
2312: // care, we just turn off the optimization of releaseResources.
2313: setPaNoAssignments(false);
2314: if (getPaAuditEventSelection() == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS) {
2315: fireAuditEvent(new DefaultAssignmentAuditEvent(
2316: auditEventBase(WfAuditEvent.ACTIVITY_ASSIGNMENT_CHANGED),
2317: ras.retrieveKey(oldResource), ras
2318: .retrieveKey(newResource), ras
2319: .retrieveName(oldResource), ras
2320: .retrieveName(newResource)));
2321: }
2322: }
2323:
2324: /**
2325: * Removes an assignment for enacting the activity. This method calls
2326: * the corresponding method of the resource assignment service and
2327: * creates the appropriate audit event.<P>
2328: *
2329: * This method is redundant, as the OMG specification already
2330: * enables a client to remove an assignment by calling {@link
2331: * WfResource#release
2332: * <code>WfResource.release</code>}. <code>WfResource</code>
2333: * objects are, however, provided by the resource management
2334: * facility. This facility cannot create audit events, thus we
2335: * have to provide a method that can be used by the resource
2336: * management facility to implement <code>release</code>.
2337: *
2338: * @param resource the resource whose assignment is to be canceled
2339: * @throws InvalidResourceException if the resource is invalid.
2340: * As the environment is a concurrent multi user environment,
2341: * <code>WfResource</code> objects may become invalid.
2342: * @throws NotAssignedException if there is no such assignment
2343: */
2344: public void removeAssignment(WfResource resource)
2345: throws InvalidResourceException, NotAssignedException {
2346: RASInvocationHandler ras = rasInvocationHandler();
2347: if (ras == null) {
2348: throw new UnsupportedOperationException(
2349: "Resource assignment service not configured.");
2350: }
2351: ras.removeAssignment(activityFinder(), getPaKey(),
2352: toActivity(), resource);
2353: if (getPaAuditEventSelection() == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS) {
2354: fireAuditEvent(new DefaultAssignmentAuditEvent(
2355: auditEventBase(WfAuditEvent.ACTIVITY_ASSIGNMENT_CHANGED),
2356: ras.retrieveKey(resource), null, ras
2357: .retrieveName(resource), null));
2358: }
2359: }
2360:
2361: /**
2362: * Close the activity with the given state after releasing all resources.
2363: */
2364: private void closeActivity(State closedState) {
2365: if (getPaPreliminarilyChosen()
2366: && (closedState
2367: .isSameOrSubState(ClosedCompletedState.NORMAL) || (closedState
2368: .isSameOrSubState(ClosedState.TERMINATED) && (!((ExtProcessLocal) containerLocal())
2369: .typedState().isSameOrSubState(
2370: RunningState.TERMINATING))))) {
2371: try {
2372: choose();
2373: } catch (TransitionNotAllowedException e) {
2374: // cannot happen because state has been checked already
2375: logger.error("Unexpected exception: " + e.getMessage(),
2376: e);
2377: }
2378: }
2379: // Debug only if process is still running normally, i.e. not
2380: // terminating or aborting.
2381: if (getPaDebug()
2382: && ((ProcessLocal) containerLocal()).typedState() == RunningState.RUNNING) {
2383: State debState = (State) CLOSE_STATE_TO_DEBUG_STATE
2384: .get(closedState);
2385: if (debState != null) {
2386: setPaTypedState(debState);
2387: return;
2388: }
2389: }
2390: ((ExtProcessLocal) containerLocal()).closeActivity(
2391: toActivityLocal(), closedState);
2392: }
2393:
2394: /**
2395: * Called when the activity is closed.
2396: */
2397: protected void releaseResources() {
2398: // Optimization.
2399: if (getPaNoAssignments()) {
2400: return;
2401: }
2402: RASInvocationHandler ras = rasInvocationHandler();
2403: if (ras == null) {
2404: return;
2405: }
2406: for (Iterator i = assignments().iterator(); i.hasNext();) {
2407: WfAssignment assignment = (WfAssignment) i.next();
2408: try {
2409: WfResource resource = ras.retrieveAssignee(assignment);
2410: ras.removeAssignment(activityFinder(), getPaKey(),
2411: toActivity(), resource);
2412: if (getPaAuditEventSelection() == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS) {
2413: fireAuditEvent(new DefaultAssignmentAuditEvent(
2414: auditEventBase(WfAuditEvent.ACTIVITY_ASSIGNMENT_CHANGED),
2415: ras.retrieveKey(resource), null, ras
2416: .retrieveName(resource), null));
2417: }
2418: } catch (InvalidResourceException e) {
2419: logger.debug("Trying to remove unknown resource: "
2420: + e.getMessage(), e);
2421: } catch (NotAssignedException e) {
2422: logger.debug("Trying to remove unassigned resource: "
2423: + e.getMessage(), e);
2424: }
2425: }
2426: setPaNoAssignments(true);
2427: }
2428:
2429: /**
2430: * Terminate any implementation currently running for this
2431: * activity.
2432: * @throws ApplicationNotStoppedException if implementation
2433: * termination was not possible
2434: */
2435: protected void terminateImpl()
2436: throws ApplicationNotStoppedException {
2437: try {
2438: Implementation impl = executor();
2439: if (impl == null) {
2440: return;
2441: }
2442: if (impl instanceof ToolImplementation) {
2443: terminateTool((ToolImplementation) impl);
2444: return;
2445: }
2446: if (impl instanceof SubFlowImplementation) {
2447: Collection subs = performersLocal();
2448: for (Iterator i = subs.iterator(); i.hasNext();) {
2449: WfProcessLocal p = (WfProcessLocal) i.next();
2450: if (p.workflowState() == State.CLOSED) {
2451: continue;
2452: }
2453: p.terminate();
2454: }
2455: }
2456: } catch (CannotStopException e) {
2457: throw new ApplicationNotStoppedException(e.getMessage());
2458: } catch (NotRunningException e) {
2459: throw new ApplicationNotStoppedException(e.getMessage());
2460: }
2461: }
2462:
2463: /**
2464: * Terminate a tool currently running for this activity.
2465: * @throws ApplicationNotStoppedException if tool termination was
2466: * not possible.
2467: */
2468: protected void terminateTool()
2469: throws ApplicationNotStoppedException {
2470: Implementation impl = executor();
2471: if (impl == null || !(impl instanceof ToolImplementation)) {
2472: return;
2473: }
2474: terminateTool((ToolImplementation) impl);
2475: }
2476:
2477: /**
2478: * Terminate the given tool.
2479: * @throws ApplicationNotStoppedException if tool termination was
2480: * not possible.
2481: */
2482: private void terminateTool(ToolImplementation impl)
2483: throws ApplicationNotStoppedException {
2484: try {
2485: ApplicationDefinition app = (ApplicationDefinition) ((ProcessLocal) containerLocal())
2486: .processDefinition().applicationById(impl.id());
2487: toolInvocationHandler().terminate(app, toActivity());
2488: } catch (InvalidIdException ide) {
2489: logger.warn("Invalid reference to tool: "
2490: + ide.getMessage());
2491: }
2492: }
2493:
2494: /**
2495: * Return a <code>ToolInvocationHandler</code>. Simple implementation
2496: * that will have to be refined by the persistence/remoting layer.
2497: * @return a handler.
2498: */
2499: protected ToolInvocationHandler toolInvocationHandler() {
2500: return (new ToolInvocationHandler() {
2501: public void invoke(ExtApplication appl,
2502: ActivityUniqueKey auk, ExtActivity act, Map params)
2503: throws ToolInvocationException {
2504: try {
2505: appl.invoke(null, act, params);
2506: } catch (RemoteException e) {
2507: logger.error(e.getMessage(), e);
2508: throw new IllegalStateException(
2509: "Cannot invoke tool");
2510: }
2511: }
2512:
2513: public void terminate(ExtApplication appl, Activity act)
2514: throws ApplicationNotStoppedException {
2515: try {
2516: appl.terminate(act);
2517: } catch (RemoteException e) {
2518: logger.error(e.getMessage(), e);
2519: throw new IllegalStateException(
2520: "Cannot terminate tool");
2521: }
2522: }
2523: });
2524: }
2525:
2526: /**
2527: * Return a <code>RASInvocationHandler</code>. The default implementation
2528: * returns <code>null</code>, i.e. there is no resource assignment service
2529: * available.
2530: * @return a handler.
2531: */
2532: protected RASInvocationHandler rasInvocationHandler() {
2533: return null;
2534: }
2535:
2536: /**
2537: * Invoke a tool for this activity.
2538: *
2539: * @param appl the application description of the tool.
2540: * @param params the invocation parameters.
2541: */
2542: public void invokeTool(ExtApplication appl, Map params) {
2543: try {
2544: toolInvocationHandler().invoke(appl, uniqueKey(),
2545: (ExtActivity) toActivity(), params);
2546: } catch (ToolInvocationException e) {
2547: try {
2548: terminate();
2549: } catch (CannotStopException ee) {
2550: throw (IllegalStateException) (new IllegalStateException(
2551: ee.getMessage())).initCause(ee);
2552: } catch (NotRunningException ee) {
2553: throw (IllegalStateException) (new IllegalStateException(
2554: ee.getMessage())).initCause(ee);
2555: }
2556: }
2557: }
2558:
2559: /**
2560: * Returns a <code>WfAuditEvent</code> containing information about the
2561: * activity and its container, only.
2562: * @param eventType event type
2563: * @return the event containing the required information.
2564: */
2565: protected WfAuditEvent auditEventBase(String eventType) {
2566: return new DefaultAuditEvent(toActivity(), eventType,
2567: getPaKey(), getPaName(), containerAuditEventBase());
2568: }
2569:
2570: /**
2571: * Return string representation for debugging purposes.
2572: * @return a string representation.
2573: */
2574: public String toString() {
2575: return "Activity[key=" + getPaKey() + "]";
2576: }
2577:
2578: }
|