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: AbstractProcess.java,v 1.23.2.1 2007/11/02 16:00:33 drmlipp Exp $
0021: *
0022: * $Log: AbstractProcess.java,v $
0023: * Revision 1.23.2.1 2007/11/02 16:00:33 drmlipp
0024: * Merged bug fixes from HEAD.
0025: *
0026: * Revision 1.24 2007/09/21 06:19:35 mlipp
0027: * Fixed problem with NamingException during process deletion.
0028: *
0029: * Revision 1.23 2007/09/14 12:34:41 drmlipp
0030: * Improved map initialization.
0031: *
0032: * Revision 1.22 2007/05/03 21:58:16 mlipp
0033: * Internal refactoring for making better use of local EJBs.
0034: *
0035: * Revision 1.21 2007/03/27 21:59:43 mlipp
0036: * Fixed lots of checkstyle warnings.
0037: *
0038: * Revision 1.20 2007/02/27 14:34:13 drmlipp
0039: * Some refactoring to reduce cyclic dependencies.
0040: *
0041: * Revision 1.19 2007/01/25 23:00:31 mlipp
0042: * Fixed result().
0043: *
0044: * Revision 1.18 2006/12/03 22:44:20 mlipp
0045: * Fixed return object of requester() for subflows.
0046: *
0047: * Revision 1.17 2006/11/19 21:53:47 mlipp
0048: * Finished support for native Java types.
0049: *
0050: * Revision 1.16 2006/10/19 11:16:30 drmlipp
0051: * Block activity deadlines cannot be delayed.
0052: *
0053: * Revision 1.15 2006/10/17 22:58:39 mlipp
0054: * Continuing implementation of suspended exception handling.
0055: *
0056: * Revision 1.14 2006/10/17 15:55:12 drmlipp
0057: * Introducing new state ...suspended.abandoning.
0058: *
0059: * Revision 1.13 2006/10/07 20:41:34 mlipp
0060: * Merged J2EE 1.4 adaptions from test branch.
0061: *
0062: * Revision 1.12 2006/09/29 12:32:08 drmlipp
0063: * Consistently using WfMOpen as projct name now.
0064: *
0065: * Revision 1.11 2006/07/18 13:18:14 drmlipp
0066: * Merged changes from wfmopen-1.3.x branch up to 1.3.4.
0067: *
0068: * Revision 1.6.2.20 2006/03/15 10:46:07 drmlipp
0069: * Fixed problems with E4X and empty input/output documents.
0070: *
0071: * Revision 1.6.2.19 2006/03/14 16:44:27 drmlipp
0072: * Fixed loop.
0073: *
0074: * Revision 1.6.2.18 2006/03/07 13:51:32 drmlipp
0075: * Finished transition to E4X usage for actual parameter evaluation.
0076: *
0077: * Revision 1.10 2006/03/08 14:46:43 drmlipp
0078: * Synchronized with 1.3.3p5.
0079: *
0080: * Revision 1.9 2005/10/10 20:02:12 mlipp
0081: * Synchronized with 1.3.3.
0082: *
0083: * Revision 1.8 2005/04/22 15:10:53 drmlipp
0084: * Merged changes from 1.3 branch up to 1.3p15.
0085: *
0086: * Revision 1.6.2.11 2005/04/22 14:09:27 drmlipp
0087: * Support specification of all extended attributes in Value attribute.
0088: *
0089: * Revision 1.6.2.10 2005/04/18 11:11:20 drmlipp
0090: * More event handling optimization.
0091: *
0092: * Revision 1.6.2.9 2005/04/16 21:18:30 drmlipp
0093: * Made audit event filtering more flexible and added possibility to turn
0094: * off audit log.
0095: *
0096: * Revision 1.6.2.8 2005/04/15 21:07:39 drmlipp
0097: * Added "storeAuditEvents" flag.
0098: *
0099: * Revision 1.6.2.7 2005/04/15 08:26:34 drmlipp
0100: * Fixed isHandled.
0101: *
0102: * Revision 1.6.2.6 2005/04/14 21:40:52 drmlipp
0103: * Optimized event feedback.
0104: *
0105: * Revision 1.6.2.5 2005/04/14 15:08:51 drmlipp
0106: * Added "filterable" event auditing.
0107: *
0108: * Revision 1.7 2005/02/04 14:25:26 drmlipp
0109: * Synchronized with 1.3rc2.
0110: *
0111: * Revision 1.6.2.4 2005/02/01 21:15:51 drmlipp
0112: * Fixed audit event generation for deferred choice.
0113: *
0114: * Revision 1.6.2.3 2005/02/01 16:08:41 drmlipp
0115: * Implemented deferred choice.
0116: *
0117: * Revision 1.6.2.2 2005/01/31 21:12:58 drmlipp
0118: * Continued implementation of deferred choice.
0119: *
0120: * Revision 1.6.2.1 2005/01/31 15:41:12 drmlipp
0121: * Started implementation of deferred choice.
0122: *
0123: * Revision 1.6 2005/01/21 09:34:56 drmlipp
0124: * Moved initialization of debug attribute to proper class.
0125: *
0126: * Revision 1.5 2005/01/12 22:10:35 mlipp
0127: * Added trim to string comparison.
0128: *
0129: * Revision 1.4 2005/01/10 22:19:35 mlipp
0130: * Added extension attribute for debug mode.
0131: *
0132: * Revision 1.3 2005/01/02 20:49:13 mlipp
0133: * First version of debug mode.
0134: *
0135: * Revision 1.2 2004/08/19 13:24:49 drmlipp
0136: * Fixed AVK errors and (many) warnings.
0137: *
0138: * Revision 1.1.1.6 2004/08/18 15:17:38 drmlipp
0139: * Update to 1.2
0140: *
0141: * Revision 1.201 2004/07/14 12:08:39 lipp
0142: * Fixed problem with default for condition type.
0143: *
0144: * Revision 1.200 2004/07/14 11:06:03 lipp
0145: * Supplied default for condition type.
0146: *
0147: * Revision 1.199 2004/07/04 17:34:19 lipp
0148: * Fixed problem with event source.
0149: *
0150: * Revision 1.198 2004/06/30 12:05:39 lipp
0151: * Added jelly variable scoping.
0152: *
0153: * Revision 1.197 2004/06/29 14:52:46 lipp
0154: * Unpacking values from JavaScript argument evaluation.
0155: *
0156: * Revision 1.196 2004/06/29 13:16:29 lipp
0157: * Fixed jelly script argument access.
0158: *
0159: * Revision 1.195 2004/06/28 20:32:00 lipp
0160: * Added support for accessing process data in jelly scripts.
0161: *
0162: * Revision 1.194 2004/06/28 14:59:49 lipp
0163: * Started jelly interpretation of actual XML arguments.
0164: *
0165: * Revision 1.193 2004/05/09 18:42:59 lipp
0166: * Finished process instantiation restructuring.
0167: *
0168: * Revision 1.192 2004/05/07 15:02:27 lipp
0169: * Removed legacy initialization code.
0170: *
0171: * Revision 1.191 2004/05/06 19:39:17 lipp
0172: * Restructured block activity handling.
0173: *
0174: * Revision 1.190 2004/05/05 09:44:07 lipp
0175: * Finished SAX based process creation (no cleanup of old code, yet).
0176: *
0177: * Revision 1.189 2004/05/04 19:48:07 lipp
0178: * Getting on with SAX based process creation.
0179: *
0180: * Revision 1.188 2004/05/03 15:38:11 lipp
0181: * Getting on with SAX based process creation.
0182: *
0183: * Revision 1.187 2004/04/30 13:46:19 lipp
0184: * Getting on with SAX based initialization.
0185: *
0186: * Revision 1.186 2004/04/30 12:44:53 lipp
0187: * Fixed SAX initialization bug.
0188: *
0189: * Revision 1.185 2004/04/29 15:39:31 lipp
0190: * Getting on with SAX based initialization.
0191: *
0192: * Revision 1.184 2004/04/28 14:19:20 lipp
0193: * Getting started with SAX based process creation.
0194: *
0195: * Revision 1.183 2004/04/12 19:33:52 lipp
0196: * Clarified application invocation interface.
0197: *
0198: * Revision 1.182 2004/04/06 21:18:39 lipp
0199: * Reflect value conversions in audit event.
0200: *
0201: * Revision 1.181 2004/04/06 15:34:19 lipp
0202: * Supporting values of type Document.
0203: *
0204: * Revision 1.180 2004/04/01 20:53:04 lipp
0205: * Fixed problem with missing Type attribute.
0206: *
0207: * Revision 1.179 2004/03/25 14:41:46 lipp
0208: * Added possibility to specify actual parameters as XML.
0209: *
0210: * Revision 1.178 2004/03/21 12:37:12 lipp
0211: * Removed left over event logger.
0212: *
0213: * Revision 1.177 2004/03/20 21:08:43 lipp
0214: * Added access to requesting processes' channels.
0215: *
0216: * Revision 1.176 2004/03/20 19:28:02 lipp
0217: * Keeping relationship to super-flow both for sync and asynch call.
0218: *
0219: * Revision 1.175 2004/03/18 09:14:45 lipp
0220: * Workaround for transformer bug.
0221: *
0222: * Revision 1.174 2004/02/21 14:42:43 lipp
0223: * Moved TransitionManager to domain package. Having it in its own
0224: * package caused too many circular dependencies (and there are good
0225: * points for having it in the domain package anyway).
0226: *
0227: * Revision 1.173 2004/02/16 15:38:51 lipp
0228: * Fixed handling of non-well formed result values.
0229: *
0230: * Revision 1.172 2004/02/06 13:37:35 lipp
0231: * Added channel close notification.
0232: *
0233: * Revision 1.171 2004/01/21 09:54:45 lipp
0234: * Adapted to contextSignature modification.
0235: *
0236: * Revision 1.170 2003/12/16 21:56:02 lipp
0237: * Track all activity closures, transition manager might be reused.
0238: *
0239: * Revision 1.169 2003/12/16 16:46:25 lipp
0240: * Let process close activities to avoid inconsistent transition manager
0241: * states.
0242: *
0243: * Revision 1.168 2003/11/26 16:40:12 huaiyang
0244: * workflow for jboss calling ejbs toString.
0245: *
0246: * Revision 1.167 2003/11/17 16:16:36 lipp
0247: * Fixed refresh.
0248: *
0249: * Revision 1.166 2003/10/28 13:24:16 lipp
0250: * Fixed handling of RemoveException.
0251: *
0252: * Revision 1.165 2003/09/25 12:00:05 lipp
0253: * Avoid client dependency on rhino.
0254: *
0255: * Revision 1.164 2003/09/25 11:01:20 lipp
0256: * Fixed usage of jsScope (may not be used remotely).
0257: *
0258: * Revision 1.163 2003/09/24 13:48:49 lipp
0259: * Fixed JavaScript access to process relevant data of type Date. Fixed
0260: * thread handling for block activities.
0261: *
0262: * Revision 1.162 2003/09/23 17:05:04 lipp
0263: * Fixed various problems with block activities.
0264: *
0265: * Revision 1.161 2003/09/22 20:50:12 lipp
0266: * Most of deadline handling for block activities.
0267: *
0268: * Revision 1.160 2003/09/22 15:53:44 lipp
0269: * Receiving deadline events.
0270: *
0271: * Revision 1.159 2003/09/22 12:51:44 lipp
0272: * Fixed key creation for block activity (must be unique).
0273: *
0274: * Revision 1.158 2003/09/22 12:32:57 lipp
0275: * Implemented deadline creation for block activities.
0276: *
0277: * Revision 1.157 2003/09/21 21:28:14 lipp
0278: * Introducing "virtual" block activity.
0279: *
0280: * Revision 1.156 2003/09/19 13:11:13 lipp
0281: * New way of handling timeouts for subflows.
0282: *
0283: * Revision 1.155 2003/09/18 14:08:16 lipp
0284: * Added abandon method to handle deadlines.
0285: *
0286: * Revision 1.154 2003/09/15 15:43:40 lipp
0287: * Initial version of handling exceptions in transition manager.
0288: *
0289: * Revision 1.153 2003/09/04 08:46:44 lipp
0290: * Fixed client dependency on rhino.jar.
0291: *
0292: * Revision 1.152 2003/07/09 13:25:14 lipp
0293: * Accept strings as values for fields of type performer.
0294: *
0295: * Revision 1.151 2003/07/04 15:54:14 lipp
0296: * Fixed handling of W3C DOM result.
0297: *
0298: * Revision 1.150 2003/06/27 08:51:45 lipp
0299: * Fixed copyright/license information.
0300: *
0301: * Revision 1.149 2003/06/26 22:08:04 lipp
0302: * Added handling of JDOM results.
0303: *
0304: * Revision 1.148 2003/06/05 21:21:20 lipp
0305: * Real fix of state table problem.
0306: *
0307: * Revision 1.147 2003/06/05 17:24:44 lipp
0308: * Fixed bug in transition table.
0309: *
0310: * Revision 1.146 2003/05/31 20:05:25 lipp
0311: * Added support for different condition types.
0312: *
0313: * Revision 1.145 2003/05/25 20:47:00 lipp
0314: * Fixed initialization.
0315: *
0316: * Revision 1.144 2003/05/16 08:08:49 lipp
0317: * Handling OTHERWISE condition type.
0318: *
0319: * Revision 1.143 2003/05/07 14:45:49 lipp
0320: * Implemented synchronous subflow.
0321: *
0322: * Revision 1.142 2003/05/05 14:39:50 lipp
0323: * Moved code for removing process automatically to event handling.
0324: *
0325: * Revision 1.141 2003/05/05 07:04:51 lipp
0326: * Handling parameters for sub-flow now.
0327: *
0328: * Revision 1.140 2003/05/02 14:55:59 lipp
0329: * Resolved some more package dependencies.
0330: *
0331: * Revision 1.139 2003/04/26 18:56:24 lipp
0332: * Moved extended interfaces to own package.
0333: *
0334: * Revision 1.138 2003/04/26 16:11:15 lipp
0335: * Moved some classes to reduce package dependencies.
0336: *
0337: * Revision 1.137 2003/04/25 14:50:59 lipp
0338: * Fixed javadoc errors and warnings.
0339: *
0340: * Revision 1.136 2003/04/24 20:50:13 lipp
0341: * Fixed some warnings.
0342: *
0343: * Revision 1.135 2003/04/23 14:27:35 lipp
0344: * Improved modelling of header data.
0345: *
0346: * Revision 1.134 2003/04/19 18:33:29 lipp
0347: * Improved handling of info.
0348: *
0349: * Revision 1.133 2003/04/16 09:52:48 lipp
0350: * Added initialization of formal parameters as data fields.
0351: *
0352: * Revision 1.132 2003/04/09 07:51:26 lipp
0353: * Storing description now.
0354: *
0355: * Revision 1.131 2003/04/08 13:08:45 lipp
0356: * Fixed null pointer exception.
0357: *
0358: * Revision 1.130 2003/04/03 11:44:06 lipp
0359: * Support for W3C DOM arguments.
0360: *
0361: * Revision 1.129 2003/04/02 11:20:06 lipp
0362: * Moved type adaption to framework.
0363: *
0364: * Revision 1.128 2003/04/02 09:30:05 lipp
0365: * Supporting more data types.
0366: *
0367: * Revision 1.127 2003/03/31 16:50:28 huaiyang
0368: * Logging using common-logging.
0369: *
0370: * Revision 1.126 2003/03/28 14:42:35 lipp
0371: * Moved some code to XPDLUtil.
0372: *
0373: * Revision 1.125 2003/03/28 12:44:08 lipp
0374: * Moved XPDL related constants to XPDLUtil.
0375: *
0376: * Revision 1.124 2003/03/28 11:41:59 lipp
0377: * More changes for data type support.
0378: *
0379: * Revision 1.123 2003/03/27 16:32:20 lipp
0380: * Started support for addditional data types.
0381: *
0382: * Revision 1.122 2003/03/13 14:07:13 lipp
0383: * Improved implementation of condition evaluation.
0384: *
0385: */
0386: package de.danet.an.workflow.domain;
0387:
0388: import java.io.IOException;
0389: import java.io.Serializable;
0390: import java.io.StringReader;
0391:
0392: import java.util.ArrayList;
0393: import java.util.Collection;
0394: import java.util.Collections;
0395: import java.util.Date;
0396: import java.util.HashMap;
0397: import java.util.Iterator;
0398: import java.util.List;
0399: import java.util.Map;
0400:
0401: import java.lang.reflect.Method;
0402: import java.text.ParseException;
0403:
0404: import javax.xml.parsers.ParserConfigurationException;
0405: import javax.xml.parsers.SAXParserFactory;
0406: import javax.xml.stream.XMLStreamException;
0407: import javax.xml.stream.XMLStreamReader;
0408: import javax.xml.transform.Transformer;
0409: import javax.xml.transform.TransformerConfigurationException;
0410: import javax.xml.transform.TransformerException;
0411: import javax.xml.transform.TransformerFactory;
0412: import javax.xml.transform.dom.DOMSource;
0413: import javax.xml.transform.sax.SAXResult;
0414:
0415: import org.apache.commons.jelly.JellyContext;
0416: import org.apache.commons.jelly.JellyException;
0417: import org.apache.commons.jelly.Script;
0418: import org.apache.commons.jelly.XMLOutput;
0419: import org.apache.commons.jelly.parser.XMLParser;
0420: import org.apache.xmlbeans.XmlCursor;
0421: import org.apache.xmlbeans.XmlException;
0422: import org.apache.xmlbeans.XmlObject;
0423: import org.apache.xmlbeans.XmlOptions;
0424: import org.apache.xmlbeans.XmlSaxHandler;
0425:
0426: import org.dom4j.io.SAXContentHandler;
0427: import org.jdom.Document;
0428: import org.jdom.Element;
0429: import org.jdom.JDOMException;
0430: import org.jdom.input.DOMBuilder;
0431: import org.jdom.output.SAXOutputter;
0432: import org.mozilla.javascript.Context;
0433: import org.mozilla.javascript.Function;
0434: import org.mozilla.javascript.JavaScriptException;
0435: import org.mozilla.javascript.Scriptable;
0436: import org.mozilla.javascript.ScriptableObject;
0437: import org.mozilla.javascript.Wrapper;
0438: import org.mozilla.javascript.xml.XMLObject;
0439: import org.xml.sax.Attributes;
0440: import org.xml.sax.ContentHandler;
0441: import org.xml.sax.InputSource;
0442: import org.xml.sax.SAXException;
0443: import org.xml.sax.XMLReader;
0444: import org.xml.sax.helpers.DefaultHandler;
0445: import org.xml.sax.helpers.XMLFilterImpl;
0446:
0447: import de.danet.an.util.XMLUtil;
0448: import de.danet.an.util.sax.BodyFilter;
0449: import de.danet.an.util.sax.HandlerStack;
0450: import de.danet.an.util.sax.NamespaceAttributesFilter;
0451: import de.danet.an.util.sax.StackedHandler;
0452: import de.danet.an.util.sax.XmlnsUrisPatcher;
0453:
0454: import de.danet.an.workflow.util.SAXEventBufferImpl;
0455: import de.danet.an.workflow.util.XPDLUtil;
0456:
0457: import de.danet.an.workflow.internalapi.ExtActivityLocal;
0458: import de.danet.an.workflow.internalapi.ExtProcessLocal;
0459: import de.danet.an.workflow.internalapi.ScriptException;
0460: import de.danet.an.workflow.localapi.ActivityLocal;
0461: import de.danet.an.workflow.localapi.ProcessLocal;
0462: import de.danet.an.workflow.localapi.TransitionLocal;
0463: import de.danet.an.workflow.localcoreapi.WfActivityLocal;
0464: import de.danet.an.workflow.localcoreapi.WfProcessLocal;
0465: import de.danet.an.workflow.omgcore.AlreadyRunningException;
0466: import de.danet.an.workflow.omgcore.CannotChangeRequesterException;
0467: import de.danet.an.workflow.omgcore.CannotStartException;
0468: import de.danet.an.workflow.omgcore.CannotStopException;
0469: import de.danet.an.workflow.omgcore.InvalidControlOperationException;
0470: import de.danet.an.workflow.omgcore.InvalidDataException;
0471: import de.danet.an.workflow.omgcore.InvalidPriorityException;
0472: import de.danet.an.workflow.omgcore.InvalidStateException;
0473: import de.danet.an.workflow.omgcore.NotRunningException;
0474: import de.danet.an.workflow.omgcore.ProcessData;
0475: import de.danet.an.workflow.omgcore.ProcessDataInfo;
0476: import de.danet.an.workflow.omgcore.ResultNotAvailableException;
0477: import de.danet.an.workflow.omgcore.TransitionNotAllowedException;
0478: import de.danet.an.workflow.omgcore.UpdateNotAllowedException;
0479: import de.danet.an.workflow.omgcore.WfAuditEvent;
0480: import de.danet.an.workflow.omgcore.WfRequester;
0481: import de.danet.an.workflow.omgcore.WfStateAuditEvent;
0482: import de.danet.an.workflow.omgcore.WfExecutionObject.ClosedState;
0483: import de.danet.an.workflow.omgcore.WfExecutionObject.NotRunningState;
0484: import de.danet.an.workflow.omgcore.WfExecutionObject.OpenState;
0485: import de.danet.an.workflow.omgcore.WfExecutionObject.State;
0486:
0487: import de.danet.an.workflow.api.CannotRemoveException;
0488: import de.danet.an.workflow.api.DefaultProcessData;
0489: import de.danet.an.workflow.api.ExternalReference;
0490: import de.danet.an.workflow.api.FormalParameter;
0491: import de.danet.an.workflow.api.InvalidKeyException;
0492: import de.danet.an.workflow.api.Participant;
0493: import de.danet.an.workflow.api.ProcessDefinition;
0494: import de.danet.an.workflow.api.SAXEventBuffer;
0495: import de.danet.an.workflow.api.Transition;
0496: import de.danet.an.workflow.api.Activity.ClosedCompletedState;
0497: import de.danet.an.workflow.api.Activity.Implementation;
0498: import de.danet.an.workflow.api.Activity.JoinAndSplitMode;
0499: import de.danet.an.workflow.api.Activity.StartFinishMode;
0500: import de.danet.an.workflow.api.Activity.SubFlowImplementation;
0501: import de.danet.an.workflow.api.Process;
0502:
0503: /**
0504: * <code>AbstractProcess</code> represents the base implementation
0505: * of the interface {@link ProcessLocal <code>ProcessLocal</code>}.<P>
0506: *
0507: * With logger level <code>DEBUG</code>, event handling information
0508: * and information about process context changes will be logged.
0509: */
0510: public abstract class AbstractProcess extends AbstractExecutionObject
0511: implements TimedObject, Serializable {
0512:
0513: private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
0514: .getLog(AbstractProcess.class);
0515:
0516: /** The immutable root for JavaScript evaluation. */
0517: private static final ScriptableObject GLOBAL_JS_SCOPE = (new Context())
0518: .initStandardObjects(null, false);
0519: static {
0520: Context cx = Context.enter();
0521: try {
0522: cx.evaluateString(GLOBAL_JS_SCOPE, "java", "<init>", 1,
0523: null);
0524: } catch (JavaScriptException e) {
0525: logger.warn("Cannot initialize \"java\" in rhino context: "
0526: + e.getMessage());
0527: }
0528: try {
0529: cx.evaluateString(GLOBAL_JS_SCOPE, "new XML()", "<init>",
0530: 1, null);
0531: } catch (JavaScriptException e) {
0532: logger.warn("Cannot initialize \"XML\" in rhino context: "
0533: + e.getMessage());
0534: }
0535: GLOBAL_JS_SCOPE.sealObject();
0536: Context.exit();
0537: }
0538:
0539: private static class TimeoutInfo implements Serializable {
0540: public Long activity;
0541: public int dlIndex;
0542:
0543: public TimeoutInfo(Long a, int d) {
0544: activity = a;
0545: dlIndex = d;
0546: }
0547:
0548: public String toString() {
0549: return "TimeoutInfo[activity=" + activity
0550: + ",deadlineIndex=" + dlIndex + "]";
0551: }
0552: }
0553:
0554: private Collection participants = null;
0555:
0556: /** The TransitionManager for this process. */
0557: private TransitionManager transitionMgrCache = null;
0558:
0559: /** The JavaScript scope for this process. */
0560: private ScriptableObject jsScopeCache = null;
0561:
0562: /** The jelly context for this process. */
0563: private JellyContext jellyContextCache = null;
0564:
0565: /** All block activity representations by key. */
0566: private Map baRepresentations = new HashMap();
0567:
0568: /** Set in init, cleared in subsequent refresh */
0569: private boolean initedBeforeRefresh = false;
0570:
0571: /**
0572: * Creates a new <code>AbstractProcess</code>.
0573: */
0574: public AbstractProcess() {
0575: }
0576:
0577: //
0578: // Persistent attribute accessors and associated methods
0579: //
0580:
0581: /**
0582: * The getter method for the persistent attribute <code>requester</code>.
0583: *
0584: * @return the value of requester.
0585: */
0586: protected abstract WfRequester getPaRequester();
0587:
0588: /**
0589: * The getter method for the persistent attribute <code>id</code>.
0590: *
0591: * @return the value of process id.
0592: * @see #setPaId
0593: */
0594: protected abstract String getPaId();
0595:
0596: /**
0597: * The setter method for the persistent attribute <code>Id</code>.
0598: *
0599: * @param newId the new value of process id.
0600: * @see #getPaId
0601: */
0602: protected abstract void setPaId(String newId);
0603:
0604: /**
0605: * The getter method for the persistent attribute <code>createTime</code>.
0606: *
0607: * @return the value of createTime.
0608: */
0609: protected abstract Date getPaCreateTime();
0610:
0611: /**
0612: * The getter method for the persistent attribute
0613: * <code>processMgrName</code>.
0614: *
0615: * @return the value of processMgrName.
0616: * @see #setPaProcessMgrName
0617: */
0618: protected abstract String getPaProcessMgrName();
0619:
0620: /**
0621: * The setter method for the persistent attribute
0622: * <code>processMgrName</code>.
0623: *
0624: * @param newProcessMgrName the new value of processMgrName.
0625: * @see #getPaProcessMgrName
0626: */
0627: protected abstract void setPaProcessMgrName(String newProcessMgrName);
0628:
0629: /**
0630: * The getter method for the persistent attribute
0631: * <code>processMgrVersion</code>.
0632: *
0633: * @return the value of processMgrVersion.
0634: * @see #setPaProcessMgrVersion
0635: */
0636: protected abstract String getPaProcessMgrVersion();
0637:
0638: /**
0639: * The setter method for the persistent attribute
0640: * <code>processMgrVersion</code>.
0641: *
0642: * @param newProcessMgrVersion the new value of processMgrVersion.
0643: * @see #getPaProcessMgrVersion
0644: */
0645: protected abstract void setPaProcessMgrVersion(
0646: String newProcessMgrVersion);
0647:
0648: /**
0649: * The getter method for the persistent attribute <code>processDef</code>.
0650: *
0651: * @return the value of processDef.
0652: * @see #setPaProcessDef
0653: */
0654: protected abstract ProcessDefinition getPaProcessDef();
0655:
0656: /**
0657: * The setter method for the persistent attribute <code>processDef</code>.
0658: *
0659: * @param newProcessDef the new value of processDef.
0660: * @see #getPaProcessDef
0661: */
0662: protected abstract void setPaProcessDef(
0663: ProcessDefinition newProcessDef);
0664:
0665: /**
0666: * The getter method for the persistent attribute <code>processData</code>.
0667: *
0668: * @return the value of processData.
0669: */
0670: protected abstract ProcessData getPaProcessData();
0671:
0672: /**
0673: * The getter method for the persistent attribute <code>priority</code>.
0674: *
0675: * @return the value of priority.
0676: * @see #setPriority
0677: */
0678: protected abstract Priority getPaPriority();
0679:
0680: /**
0681: * The setter method for the persistent attribute <code>priority</code>.
0682: *
0683: * @param newPriority the new value of priority.
0684: * @see #getPaPriority
0685: */
0686: protected abstract void setPaPriority(Priority newPriority);
0687:
0688: /**
0689: * The getter method for the persistent attribute
0690: * <code>blockDeadlines</code>.
0691: *
0692: * @return the value of blockDeadlines.
0693: */
0694: protected abstract Map getPaBlockDeadlines();
0695:
0696: /**
0697: * Returns the transition manager.
0698: * @return the transition manager
0699: */
0700: protected TransitionManager transitionManager() {
0701: if (transitionMgrCache == null) {
0702: // create new transition manager
0703: transitionMgrCache = new TransitionManager(this );
0704: }
0705: return transitionMgrCache;
0706: }
0707:
0708: /**
0709: * Initializes the class, i.e. resets all attributes to default
0710: * values. Note that
0711: * {@link #refresh <code>refresh</code>} will be called subsequently.
0712: *
0713: * @param procDef the process definition.
0714: * @see #dispose
0715: */
0716: protected void init(ProcessDefinition procDef) {
0717: super .init();
0718: jsScopeCache = null;
0719: jellyContextCache = null;
0720: setPaProcessDef(procDef);
0721: setPaTypedState(NotRunningState.NOT_STARTED);
0722: setPaProcessMgrName(procDef.mgrName());
0723: setPaProcessMgrVersion(procDef.version());
0724: setPaDescription(procDef.processHeader().description());
0725: setPaAuditEventSelection(procDef.auditEventSelection());
0726: setPaStoreAuditEvents(procDef.storeAuditEvents());
0727: baRepresentations.clear();
0728:
0729: setPaPriority(Priority.NORMAL);
0730: HandlerStack hs = new HandlerStack(new SAXInitializer());
0731: hs.setContextData("packageId", procDef.packageId());
0732: try {
0733: procDef.toSAX().emit(hs.contentHandler());
0734: } catch (SAXException e) {
0735: logger.error(e.getMessage(), e);
0736: throw new IllegalArgumentException(e.getMessage());
0737: }
0738:
0739: // fire an appropriate audit event
0740: if (getPaAuditEventSelection() == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS) {
0741: fireAuditEvent(new DefaultCreateProcessAuditEvent(
0742: auditEventBase(WfAuditEvent.PROCESS_CREATED),
0743: activityRequesterInfo(getPaRequester())));
0744: fireAuditEvent(new DefaultDataAuditEvent(
0745: auditEventBase(WfAuditEvent.PROCESS_CONTEXT_CHANGED),
0746: null, getPaProcessData()));
0747: }
0748: }
0749:
0750: /**
0751: * Called after change of persistent attributes. May be used to
0752: * synchronise state derived from persistent attributes with
0753: * the new values.
0754: *
0755: * @see #init
0756: */
0757: protected void refresh() {
0758: super .refresh();
0759: transitionMgrCache = null;
0760: jsScopeCache = null;
0761: jellyContextCache = null;
0762: participants = null;
0763: if (initedBeforeRefresh) {
0764: initedBeforeRefresh = false;
0765: } else {
0766: baRepresentations.clear();
0767: }
0768: }
0769:
0770: /**
0771: * Releases all allocated resources. The object will be in an
0772: * unusable state until resources are reallocated by calling
0773: * {@link #init <code>init</code>} and
0774: * {@link #refresh <code>refresh</code>}.
0775: */
0776: protected void dispose() {
0777: super .dispose();
0778: participants = null;
0779: // deallocate TransitionManager
0780: transitionMgrCache = null;
0781: baRepresentations.clear();
0782: }
0783:
0784: //
0785: // Domain methods
0786: //
0787:
0788: /**
0789: * Indicates if some other object is equal to this one. <P>
0790: *
0791: * Note that <code>obj</code> may be a stub representing the
0792: * object. Stubs do not in general implement
0793: * <code>equals</code> correctly, so while
0794: * <code>this.equals(obj)</code> does indicate the equality as
0795: * defined for this class, <code>obj.equals(this)</code> generally
0796: * does not.
0797: *
0798: * @param obj the object to compare with.
0799: * @return <code>true</code> if the other object is equal.
0800: */
0801: public boolean equals(Object obj) {
0802: return (obj instanceof AbstractProcess)
0803: && getPaKey().equals(((AbstractProcess) obj).key())
0804: || (obj instanceof WfProcessLocal)
0805: && getPaKey().equals(((WfProcessLocal) obj).key());
0806: }
0807:
0808: /**
0809: * Provide a new unique activity key.
0810: *
0811: * @return the key.
0812: */
0813: protected abstract Long createActivityKey();
0814:
0815: /**
0816: * Returns the requester for this process. Method of the omg interface.
0817: * @return requester for this process.
0818: */
0819: public WfRequester requester() {
0820: WfRequester req = getPaRequester();
0821: if (req instanceof SubProcRequester) {
0822: try {
0823: return lookupActivityLocal(
0824: ((SubProcRequester) req).requestingActivity())
0825: .toActivity();
0826: } catch (InvalidKeyException e) {
0827: return null;
0828: }
0829: }
0830: return req;
0831: }
0832:
0833: /**
0834: * Returns the requester for this process without resolving the
0835: * internally used SubProcRequester to the associated WfActivity.
0836: * @return requester for this process.
0837: */
0838: public WfRequester unresolvedRequester() {
0839: return getPaRequester();
0840: }
0841:
0842: /**
0843: * Return the process this process is a subflow of.
0844: * @return the process or <code>null</code> if this process is not
0845: * a subflow
0846: */
0847: public Process requestingProcess() {
0848: if (!(getPaRequester() instanceof SubProcRequester)) {
0849: return null;
0850: }
0851: String key = ((SubProcRequester) getPaRequester())
0852: .requestingActivity();
0853: try {
0854: ActivityLocal act = lookupActivityLocal(key);
0855: return ((ExtProcessLocal) act.containerLocal()).toProcess();
0856: } catch (InvalidKeyException e) {
0857: logger.warn(toString()
0858: + " cannot find requesting activity " + key + " : "
0859: + e.getMessage());
0860: return null;
0861: }
0862: }
0863:
0864: /**
0865: * Returns the creation time of the process.
0866: * @return the creation time.
0867: */
0868: public Date createTime() {
0869: return getPaCreateTime();
0870: }
0871:
0872: /**
0873: * For the first iteration throws an
0874: * CannotChangeRequesterException. Method of the omg interface.
0875: * @param requester the requester.
0876: * @throws CannotChangeRequesterException if modification is not allowed.
0877: * @ejb.interface-method view-type="remote"
0878: */
0879: public void setRequester(WfRequester requester)
0880: throws CannotChangeRequesterException {
0881: throw new CannotChangeRequesterException();
0882: }
0883:
0884: /**
0885: * Return the context of this <code>WfExecutionObject</code>.
0886: * The process data object returned is a copy of the name value
0887: * association, the values are not copied, however.
0888: *
0889: * @return the process relevant data that define the context of the
0890: * process.
0891: */
0892: public ProcessData processContext() {
0893: return new DefaultProcessData(getPaProcessData());
0894: }
0895:
0896: /**
0897: * Filter out duplicate endDocument events.
0898: */
0899: private class DupEndFilter extends XMLFilterImpl {
0900: private boolean gotEnd = false;
0901:
0902: public DupEndFilter(ContentHandler parent) {
0903: setContentHandler(parent);
0904: }
0905:
0906: public void endDocument() throws SAXException {
0907: if (!gotEnd) {
0908: super .endDocument();
0909: gotEnd = true;
0910: }
0911: }
0912: }
0913:
0914: /**
0915: * Updates process context of the execution object.
0916: * @param newValues the name-value pairs to be set.
0917: * @throws InvalidDataException If a name or value type does not match
0918: * the signature of this process.
0919: * @throws UpdateNotAllowedException If the update is not allowed.
0920: */
0921: public void setProcessContext(ProcessData newValues)
0922: throws InvalidDataException, UpdateNotAllowedException {
0923: if (!workflowState().equals(State.OPEN)) {
0924: throw new UpdateNotAllowedException(
0925: "Process is not in state open.");
0926: }
0927: // verify first to avoid partial change.
0928: ProcessDataInfo sig = getPaProcessDef().contextSignature();
0929: for (Iterator i = newValues.keySet().iterator(); i.hasNext();) {
0930: String name = (String) i.next();
0931: Object type = sig.get(name);
0932: if (type == null) {
0933: throw new InvalidDataException("No such data field: "
0934: + name);
0935: }
0936: Object v = newValues.get(name);
0937: if (v == null) {
0938: continue;
0939: }
0940: if ((type instanceof ExternalReference)
0941: && XPDLUtil.isJavaType((ExternalReference) type)) {
0942: Class vc = null;
0943: try {
0944: vc = XPDLUtil.getJavaType((ExternalReference) type);
0945: } catch (ClassNotFoundException e) {
0946: throw (InvalidDataException) (new InvalidDataException(
0947: "Required Java class no longer available: "
0948: + e.getMessage())).initCause(e);
0949: }
0950: if (vc.isAssignableFrom(v.getClass())) {
0951: continue;
0952: } else {
0953: throw new InvalidDataException("Context entry "
0954: + name + " is " + v.getClass().getName()
0955: + " must be instance of " + vc.getName());
0956: }
0957: }
0958: if ((type instanceof SAXEventBuffer)
0959: || (type instanceof ExternalReference)
0960: || type.equals(org.w3c.dom.Element.class)) {
0961: boolean elemList = false;
0962: if (v instanceof List) {
0963: if (((List) v).size() == 0) {
0964: elemList = true;
0965: } else {
0966: Iterator ti = ((List) v).iterator();
0967: if (ti.next() instanceof Element) {
0968: elemList = true;
0969: }
0970: }
0971: }
0972: if (!((v instanceof Element) || (v instanceof Document)
0973: || elemList
0974: || (v instanceof org.w3c.dom.Element)
0975: || (v instanceof org.w3c.dom.DocumentFragment)
0976: || (v instanceof org.w3c.dom.Document) || (v instanceof SAXEventBuffer))) {
0977: throw new InvalidDataException(
0978: "Not a usable XML representation: " + name);
0979: }
0980: continue;
0981: }
0982: if (type instanceof Class) {
0983: Class vc = v.getClass();
0984: if (v instanceof Float) {
0985: vc = Double.class;
0986: } else if (v instanceof Integer) {
0987: vc = Long.class;
0988: }
0989: if (type == Participant.class) {
0990: type = String.class;
0991: }
0992: if (!((Class) type).isAssignableFrom(vc)) {
0993: throw new InvalidDataException(
0994: "Values for data field \"" + name
0995: + "\" must be of type "
0996: + ((Class) type).getName()
0997: + " (is " + v.getClass().getName()
0998: + ")");
0999: }
1000: continue;
1001: }
1002: throw new InvalidDataException(
1003: "Invalid type for data field \"" + name + "\": "
1004: + ((Class) type).getName());
1005: }
1006: // now do changes
1007: DOMBuilder builder = null;
1008: ProcessData oldValues = new DefaultProcessData();
1009: for (Iterator i = (new ArrayList(newValues.keySet()))
1010: .iterator(); i.hasNext();) {
1011: String name = (String) i.next();
1012: oldValues.put(name, getPaProcessData().get(name));
1013: Object v = newValues.get(name);
1014: if (logger.isDebugEnabled()) {
1015: logger.debug("Setting context data item " + name
1016: + " = " + v);
1017: }
1018: Object type = sig.get(name);
1019: if ((type instanceof ExternalReference)
1020: && XPDLUtil.isJavaType((ExternalReference) type)) {
1021: // accept literally
1022: } else if (v instanceof Float) {
1023: v = new Double(((Float) v).doubleValue());
1024: newValues.put(name, v);
1025: } else if (v instanceof Integer) {
1026: v = new Long(((Integer) v).longValue());
1027: newValues.put(name, v);
1028: } else if ((v instanceof Element)
1029: || (v instanceof Document) || (v instanceof List)) {
1030: try {
1031: if (logger.isDebugEnabled()) {
1032: logger.debug("Convering item " + name
1033: + " from JDOM to SAX");
1034: }
1035: SAXOutputter outputter = new SAXOutputter();
1036: SAXEventBufferImpl b = new SAXEventBufferImpl();
1037: outputter.setContentHandler(b);
1038: outputter.setLexicalHandler(b);
1039: if (v instanceof Document) {
1040: outputter.output((Document) v);
1041: } else {
1042: List l;
1043: if (v instanceof List) {
1044: l = (List) v;
1045: } else {
1046: l = new ArrayList();
1047: l.add(v);
1048: }
1049: outputter.output(l);
1050: }
1051: b.pack();
1052: v = b;
1053: newValues.put(name, v);
1054: } catch (JDOMException e) {
1055: logger.error(e.getMessage(), e);
1056: throw new InvalidDataException(e.getMessage());
1057: }
1058: } else if ((v instanceof org.w3c.dom.Element)
1059: || (v instanceof org.w3c.dom.DocumentFragment)
1060: || (v instanceof org.w3c.dom.Document)) {
1061: try {
1062: if (logger.isDebugEnabled()) {
1063: logger.debug("Convering item " + name
1064: + " from W3C DOM to SAX");
1065: }
1066: TransformerFactory tf = TransformerFactory
1067: .newInstance();
1068: Transformer t = tf.newTransformer();
1069: SAXEventBufferImpl b = new SAXEventBufferImpl();
1070: // There seems to be a bug in Xalan that causes it to
1071: // fire two endDocument events when transforming a
1072: // DocumentFragment, filter it out.
1073: t.transform(new DOMSource((org.w3c.dom.Node) v),
1074: new SAXResult(new DupEndFilter(b)));
1075: b.pack();
1076: v = b;
1077: newValues.put(name, v);
1078: } catch (TransformerConfigurationException e) {
1079: String s = "Error converting DOM to SAX: "
1080: + e.getMessage();
1081: logger.error(s, e);
1082: throw new IllegalStateException(s);
1083: } catch (TransformerException e) {
1084: String s = "Error converting DOM to SAX: "
1085: + e.getMessage();
1086: logger.error(s, e);
1087: throw new InvalidDataException(s);
1088: }
1089: }
1090: getPaProcessData().put(name, v);
1091: }
1092:
1093: // fire appropriate audit event
1094: if (getPaAuditEventSelection() == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS) {
1095: fireAuditEvent(new DefaultDataAuditEvent(
1096: auditEventBase(WfAuditEvent.PROCESS_CONTEXT_CHANGED),
1097: oldValues, newValues));
1098: }
1099: }
1100:
1101: /**
1102: * The <code>result</code> method returns the process context.
1103: *
1104: * @return the result.
1105: * @exception ResultNotAvailableException not thrown, intermediate results
1106: * are available.
1107: * @ejb.interface-method view-type="remote"
1108: */
1109: public ProcessData result() throws ResultNotAvailableException {
1110: ProcessDataInfo resSig = processDefinition().resultSignature();
1111: ProcessData procCtx = getPaProcessData();
1112: ProcessData resData = new DefaultProcessData();
1113: for (Iterator i = resSig.keySet().iterator(); i.hasNext();) {
1114: String key = (String) i.next();
1115: resData.put(key, procCtx.get(key));
1116: }
1117: return resData;
1118: }
1119:
1120: /**
1121: * Returns a collection of activities of the process. This can usually
1122: * be handled more efficiently by the derived class that provides the
1123: * persistence. The default implementation simply wraps each local
1124: * objects.
1125: *
1126: * @return collection of activities of the process
1127: */
1128: public Collection steps() {
1129: Collection res = new ArrayList();
1130: for (Iterator i = stepsLocal().iterator(); i.hasNext();) {
1131: ExtActivityLocal act = (ExtActivityLocal) i.next();
1132: res.add(act.toActivity());
1133: }
1134: return res;
1135: }
1136:
1137: /**
1138: * Returns all WfActivityLocals associated with this <code>WfProcess</code>.
1139: * @return the collection of all the WfActivities.
1140: */
1141: public abstract Collection stepsLocal();
1142:
1143: /**
1144: * Returns the {@link ActivityLocal <code>Activity</code>} with the
1145: * given key.
1146: * @param key the
1147: * {@link de.danet.an.workflow.localcoreapi.WfActivityLocal#key key}
1148: * of the activity
1149: * @return the activity associated with the key
1150: * @throws InvalidKeyException if no activity with the given key
1151: * exists
1152: */
1153: public abstract ActivityLocal activityByKeyLocal(String key)
1154: throws InvalidKeyException;
1155:
1156: /**
1157: * Returns all transitions associated with this <code>WfProcess</code>.
1158: * @return the collection of all the WfActivities.
1159: */
1160: public abstract List transitionsLocal();
1161:
1162: /**
1163: * Return all <code>WfActivity</code> objects that are in a certain state.
1164: * @param state the given state.
1165: * @return the collection of all WfActivities.
1166: * @throws InvalidStateException if an invalid state has been specified.
1167: */
1168: public Collection activitiesInState(String state)
1169: throws InvalidStateException {
1170: State s = State.fromString(state);
1171: Collection returnList = new ArrayList();
1172:
1173: Iterator it = stepsLocal().iterator();
1174: while (it.hasNext()) {
1175: ExtActivityLocal act = (ExtActivityLocal) it.next();
1176: if (act.typedState().isSameOrSubState(s)) {
1177: returnList.add(act.toActivity());
1178: }
1179: }
1180: return returnList;
1181: }
1182:
1183: /**
1184: * Return all <code>WfActivity</code> objects that belong to the
1185: * given block activity.
1186: * @param blockActivity the given block activity
1187: * @return the collection of all WfActivities
1188: */
1189: public Collection activitiesOfBlockActivity(String blockActivity) {
1190: Collection returnList = new ArrayList();
1191:
1192: Iterator it = stepsLocal().iterator();
1193: while (it.hasNext()) {
1194: ActivityLocal act = (ActivityLocal) it.next();
1195: String ba = act.blockActivity();
1196: if (ba != null && ba.equals(blockActivity)) {
1197: returnList.add(act);
1198: }
1199: }
1200: return returnList;
1201: }
1202:
1203: /**
1204: * Returns the associated process defintion.
1205: * @return the process definition
1206: */
1207: public ProcessDefinition processDefinition() {
1208: return getPaProcessDef();
1209: }
1210:
1211: /**
1212: * Returns the cleanup mode for the process as defined in its process
1213: * definition.
1214: * @return the cleanup mode.
1215: */
1216: protected int cleanupMode() {
1217: return processDefinition().cleanupMode();
1218: }
1219:
1220: /**
1221: * Return the remote version of this object. This may be, but need
1222: * not be <code>this</code>, depending on the way remote objects
1223: * are implemented.
1224: *
1225: * @return the client side object.
1226: */
1227: public abstract Process toProcess();
1228:
1229: /**
1230: * Return the version of this object to be passed to as local reference.
1231: *
1232: * @return the client side object.
1233: */
1234: public abstract ExtProcessLocal toProcessLocal();
1235:
1236: /**
1237: * Helper for implementing access to process data in JavaScript.
1238: */
1239: public class DataAccessor implements Serializable {
1240: private String itemName;
1241: private Scriptable scope;
1242:
1243: /**
1244: * Create a new <code>DataAccessor</code> for the given item.
1245: * @param item the process data item to access.
1246: */
1247: public DataAccessor(Scriptable scope, String item) {
1248: this .scope = scope;
1249: itemName = item;
1250: }
1251:
1252: /**
1253: * Return the the value of the item this object was constructed for.
1254: * @param so needed to match rhino's interface requirements, ignored.
1255: * @return the value.
1256: */
1257: public Object getValue(ScriptableObject so) {
1258: Object res = getPaProcessData().get(itemName);
1259: if (res instanceof Date) {
1260: try {
1261: res = Context.getCurrentContext().newObject(
1262: so,
1263: "Date",
1264: new Object[] { new Long(((Date) res)
1265: .getTime()) });
1266: } catch (JavaScriptException e) {
1267: logger.error(e.getMessage(), e);
1268: }
1269: return res;
1270: }
1271: if (res instanceof SAXEventBuffer) {
1272: try {
1273: XmlSaxHandler sh = XmlObject.Factory
1274: .newXmlSaxHandler();
1275: ((SAXEventBuffer) res).emit(sh.getContentHandler());
1276: XmlObject xo = sh.getObject();
1277: XmlCursor cur = xo.newCursor();
1278: while (!cur.isStart()) {
1279: if (cur.isEnddoc()) {
1280: try {
1281: Context cx = Context.enter();
1282: return cx.evaluateString(scope,
1283: "<></>", "<script>", 1, null);
1284: } finally {
1285: Context.exit();
1286: }
1287: }
1288: cur.toNextToken();
1289: }
1290: xo = cur.getObject();
1291: Object wxo = Context.javaToJS(xo, scope);
1292: res = Context.getCurrentContext().newObject(scope,
1293: "XML", new Object[] { wxo });
1294: } catch (SAXException e) {
1295: logger.error(e.getMessage(), e);
1296: } catch (XmlException e) {
1297: logger.error(e.getMessage(), e);
1298: }
1299: return res;
1300: }
1301: return res;
1302: }
1303: }
1304:
1305: /**
1306: * Return a scope for evaluating JavaScript. The scope includes
1307: * the process relevant data as top-level variables. This scope
1308: * may be used as prototype for a thread specific evaluation
1309: * context only. This implies that it can not be used to modify
1310: * process data. This behaviour is intended.
1311: * @return the process specific sope.
1312: */
1313: public Scriptable jsScope() {
1314: if (jsScopeCache == null) {
1315: try {
1316: jsScopeCache = (ScriptableObject) (new Context())
1317: .newObject(GLOBAL_JS_SCOPE);
1318: jsScopeCache.setPrototype(GLOBAL_JS_SCOPE);
1319: jsScopeCache.setParentScope(null);
1320: Method getter = DataAccessor.class.getMethod(
1321: "getValue",
1322: new Class[] { ScriptableObject.class });
1323: Map procData = getPaProcessData();
1324: for (Iterator i = procData.keySet().iterator(); i
1325: .hasNext();) {
1326: String key = (String) i.next();
1327: DataAccessor da = new DataAccessor(jsScopeCache,
1328: key);
1329: try {
1330: jsScopeCache.defineProperty(key, da, getter,
1331: null, 0);
1332: } catch (JavaScriptException e) {
1333: logger.error(e.getMessage(), e);
1334: }
1335: }
1336: jsScopeCache.sealObject();
1337: } catch (NoSuchMethodException e) {
1338: logger.error(e.getMessage(), e);
1339: } catch (JavaScriptException e) {
1340: logger.error(e.getMessage(), e);
1341: }
1342: }
1343: return jsScopeCache;
1344: }
1345:
1346: /**
1347: * Evaluate the given script using the process specific scope as
1348: * returned by {@link #jsScope <code>jsScope</code>}. <P>
1349: *
1350: * results of type JavaScript Date are converted to Java Date.
1351: *
1352: * @param script the script
1353: * @return the result
1354: * @throws ScriptException if evaluation fails
1355: */
1356: public Serializable evalScript(String script)
1357: throws ScriptException {
1358: Context cx = Context.enter();
1359: try {
1360: Scriptable proto = jsScope();
1361: Scriptable scope = cx.newObject(proto);
1362: scope.setPrototype(proto);
1363: scope.setParentScope(null);
1364:
1365: Object res = cx.evaluateString(scope, script, "<script>",
1366: 1, null);
1367: if (res instanceof Wrapper) {
1368: res = ((Wrapper) res).unwrap();
1369: } else if (res instanceof XMLObject) {
1370: SAXEventBufferImpl seb = new SAXEventBufferImpl();
1371: if (((XMLObject) res).getClassName().equals("XMLList")) {
1372: seb.startDocument();
1373: for (int i = 0; true; i++) {
1374: Object item = ((XMLObject) res).get(i,
1375: (XMLObject) res);
1376: if (item.equals(Scriptable.NOT_FOUND)) {
1377: break;
1378: }
1379: xmlObjectToSax(seb, (XMLObject) item, true);
1380: }
1381: seb.endDocument();
1382: } else {
1383: xmlObjectToSax(seb, (XMLObject) res, false);
1384: }
1385: seb.pack();
1386: return seb;
1387: } else if ((res instanceof Scriptable)
1388: && ((Scriptable) res).getClassName().equals("Date")) {
1389: Scriptable s = (Scriptable) res;
1390: Object gt = Scriptable.NOT_FOUND;
1391: for (Scriptable c = s; c != null
1392: && gt.equals(Scriptable.NOT_FOUND); c = c
1393: .getPrototype()) {
1394: gt = c.get("getTime", s);
1395: }
1396: Number millis = (Number) ((Function) gt).call(cx,
1397: scope, s, new Object[] {});
1398: return new Date(millis.longValue());
1399: }
1400: return (Serializable) res;
1401: } catch (JavaScriptException e) {
1402: logger.error(e.getMessage(), e);
1403: throw new ScriptException(e.getMessage(),
1404: (e.getValue() instanceof Throwable) ? (Throwable) e
1405: .getValue() : e);
1406: } catch (SAXException e) {
1407: logger.error(e.getMessage(), e);
1408: throw new ScriptException(e.getMessage(), e);
1409: } finally {
1410: Context.exit();
1411: }
1412: }
1413:
1414: /**
1415: * Serialize a JavaScript XML object into the SAX event buffer.
1416: *
1417: * @param seb the SAX event buffer
1418: * @param xmlObj the XML object
1419: * @param fragment if <code>startDocument</code> and
1420: * <code>endDocument</code> events are to be suppressed
1421: * @throws SAXException
1422: */
1423: private void xmlObjectToSax(SAXEventBufferImpl seb,
1424: XMLObject xmlObj, boolean fragment) throws SAXException {
1425: Wrapper wrap = (Wrapper) ScriptableObject.callMethod(
1426: (XMLObject) xmlObj, "getXmlObject", new Object[0]);
1427: XmlObject result = (XmlObject) wrap.unwrap();
1428: XmlCursor cursor = result.newCursor();
1429: result = cursor.getObject();
1430: ContentHandler ch = seb;
1431: if (fragment) {
1432: ch = new BodyFilter(ch);
1433: }
1434: result.save(ch, seb, (new XmlOptions()).setSaveOuter());
1435: }
1436:
1437: /**
1438: * Evaluate the given expressions using the process specific data
1439: * (the scope as returned by {@link #jsScope
1440: * <code>jsScope</code>} for JavaScript).
1441: * @param expressionData an array of object arrays containing the result
1442: * type and the expression
1443: * @return an array that contains the result of each evaluation.
1444: * Note that the result may be an exception.
1445: */
1446: public Serializable[] evalExpressions(Object[][] expressionData) {
1447: Serializable[] res = new Serializable[expressionData.length];
1448: for (int i = 0; i < expressionData.length; i++) {
1449: try {
1450: Object[] item = expressionData[i];
1451: Object resType = item[0];
1452: String expr = (String) item[1];
1453: if (XPDLUtil.isXMLType(resType) && expr.length() > 0
1454: && expr.startsWith("<j:jelly")) {
1455: res[i] = evalXML(expr);
1456: } else {
1457: res[i] = evalScript(expr);
1458: }
1459: } catch (Exception e) {
1460: res[i] = e;
1461: }
1462: }
1463: return res;
1464: }
1465:
1466: private Serializable evalXML(String xml) throws SAXException,
1467: IOException {
1468: try {
1469: SAXParserFactory spf = SAXParserFactory.newInstance();
1470: spf.setValidating(false);
1471: spf.setNamespaceAware(true);
1472: spf.setFeature(
1473: "http://xml.org/sax/features/namespace-prefixes",
1474: true);
1475: XMLReader xr = null;
1476: try {
1477: spf.setFeature(
1478: "http://xml.org/sax/features/xmlns-uris", true);
1479: xr = spf.newSAXParser().getXMLReader();
1480: } catch (SAXException e) {
1481: xr = new XmlnsUrisPatcher(spf.newSAXParser()
1482: .getXMLReader());
1483: }
1484: SAXEventBufferImpl seb = new SAXEventBufferImpl();
1485: XMLFilterImpl filter = new XMLFilterImpl() {
1486: private int level = 0;
1487:
1488: public void startElement(String uri, String localName,
1489: String qName, Attributes atts)
1490: throws SAXException {
1491: if (level > 0) {
1492: super .startElement(uri, localName, qName, atts);
1493: }
1494: level += 1;
1495: }
1496:
1497: public void endElement(String uri, String localName,
1498: String qName) throws SAXException {
1499: level -= 1;
1500: if (level > 0) {
1501: super .endElement(uri, localName, qName);
1502: }
1503: }
1504: };
1505: filter.setContentHandler(seb);
1506: xr.setContentHandler(filter);
1507: xr.parse(new InputSource(new StringReader(
1508: "<temporary-root>" + xml + "</temporary-root>")));
1509: seb.pack();
1510: XMLStreamReader rdr = seb.createXMLStreamReader();
1511: while (rdr.next() != XMLStreamReader.START_ELEMENT) {
1512: }
1513: if (rdr.getNamespaceURI().equals("jelly:core")
1514: && rdr.getLocalName().equals("jelly")) {
1515: return evalJelly(seb);
1516: }
1517: return seb;
1518: } catch (ParserConfigurationException e) {
1519: throw new IllegalArgumentException(
1520: "Error initiliazing schema type: " + e.getMessage());
1521: } catch (SAXException e) {
1522: throw new IllegalArgumentException(
1523: "Error initiliazing schema type: " + e.getMessage());
1524: } catch (XMLStreamException e) {
1525: throw new IllegalArgumentException(
1526: "Error initiliazing schema type: " + e.getMessage());
1527: } catch (IOException e) {
1528: throw new IllegalArgumentException(
1529: "Error initiliazing schema type: " + e.getMessage());
1530: }
1531: }
1532:
1533: private class ExtXMLParser extends XMLParser {
1534: public void configure() {
1535: super .configure();
1536: }
1537: }
1538:
1539: private JellyContext jellyContext() {
1540: if (jellyContextCache == null) {
1541: jellyContextCache = new JellyContext() {
1542: private Map xmlVals = new HashMap();
1543:
1544: public Object getVariable(String name) {
1545: if (getPaProcessData().containsKey(name)) {
1546: Object v = getPaProcessData().get(name);
1547: if (v instanceof SAXEventBuffer) {
1548: if (!xmlVals.containsKey(name)) {
1549: SAXContentHandler hdlr = new SAXContentHandler();
1550: try {
1551: ((SAXEventBuffer) v).emit(hdlr);
1552: } catch (SAXException e) {
1553: throw (IllegalArgumentException) (new IllegalArgumentException(
1554: "Error converting SAX events: "
1555: + e.getMessage()))
1556: .initCause(e);
1557: }
1558: xmlVals.put(name, hdlr.getDocument());
1559: }
1560: return xmlVals.get(name);
1561: }
1562: return v;
1563: } else {
1564: return super .getVariable(name);
1565: }
1566: }
1567: };
1568: }
1569: return jellyContextCache;
1570: }
1571:
1572: private SAXEventBufferImpl evalJelly(SAXEventBufferImpl seb)
1573: throws SAXException {
1574: if (logger.isDebugEnabled()) {
1575: logger.debug("Evaluating jelly script");
1576: }
1577: try {
1578: JellyContext context = new JellyContext(jellyContext());
1579: ExtXMLParser jellyParser = new ExtXMLParser();
1580: jellyParser.setContext(context);
1581: jellyParser.configure();
1582: seb.emit(new NamespaceAttributesFilter(jellyParser));
1583: Script script = jellyParser.getScript();
1584: script.compile();
1585: SAXEventBufferImpl jres = new SAXEventBufferImpl();
1586: jres.startDocument();
1587: script.run(context, new XMLOutput(jres));
1588: jres.endDocument();
1589: jres.pack();
1590: return jres;
1591: } catch (JellyException e) {
1592: throw (IllegalArgumentException) (new IllegalArgumentException(
1593: "Error evaluating jelly script: " + e.getMessage()))
1594: .initCause(e);
1595: }
1596: }
1597:
1598: /* Comment copied from Interface. */
1599: public void closeActivity(ExtActivityLocal activity,
1600: State closedState) {
1601: // initialize transition manager before changing activity state!
1602: TransitionManager tm = transitionManager();
1603: activity.doCloseActivity(closedState);
1604: tm.update(activity);
1605: // everything else is done when the activity's close event is
1606: // received, because we want to do it in another transaction
1607: }
1608:
1609: /* Comment copied from Interface. */
1610: public boolean choose(ExtActivityLocal activity)
1611: throws TransitionNotAllowedException {
1612: State state = activity.typedState();
1613: if (state.isSameOrSubState(NotRunningState.NOT_STARTED)) {
1614: // this activity has been reset by some other activity
1615: return false;
1616: }
1617: if (!state.isSameOrSubState(OpenState.RUNNING)
1618: && !state.isSameOrSubState(NotRunningState.SUSPENDED)) {
1619: throw new TransitionNotAllowedException(
1620: "Cannot choose activity in state " + state
1621: + " must be " + OpenState.RUNNING + " or "
1622: + NotRunningState.SUSPENDED);
1623: }
1624: if (!activity.preliminarilyChosen()) {
1625: return true;
1626: }
1627: transitionManager().resetCompeting(activity);
1628: return true;
1629: }
1630:
1631: //
1632: // State change API implementation
1633: //
1634:
1635: private static Map vstates = null;
1636:
1637: /**
1638: * Returns the {@link java.util.Map <code>Map</code>} that maps
1639: * the process states to a {@link java.util.Map <code>List</code>}
1640: * of reachable process states. <P>
1641: *
1642: * The map returns the state transitions allowed as parameters of
1643: * {@link
1644: * de.danet.an.workflow.localcoreapi.WfExecutionObjectLocal#changeState
1645: * <code>changeState</code>} only. I.e. the map does not reflect
1646: * all possible transitions, there may be more, but those are only
1647: * accessible to the workflow engine itself.
1648: * @return the resulting map.
1649: */
1650: protected Map getStateTransitionMap() {
1651: // Map must be initialized lazily. Static initialization holds
1652: // danger of "race conditions" with constant initialization
1653: // (resulting in null-entries). And new map must be fully initialized
1654: // before assigning to member variable to avoid concurrent
1655: // initialization.
1656: if (vstates == null) {
1657: Map transMap = new HashMap();
1658: // Transitions from open.not_running.not_started
1659: List l = new ArrayList();
1660: transMap.put(NotRunningState.NOT_STARTED, l);
1661: l.add(ClosedState.TERMINATED);
1662: l.add(OpenState.RUNNING);
1663: // Transitions from open.running
1664: l = new ArrayList();
1665: transMap.put(RunningState.RUNNING, l);
1666: l.add(NotRunningState.SUSPENDED);
1667: l.add(ClosedState.TERMINATED);
1668: // Transitions from open.not_running.suspended
1669: l = new ArrayList();
1670: transMap.put(SuspendedState.SUSPENDED, l);
1671: l.add(OpenState.RUNNING);
1672: l.add(ClosedState.ABORTED);
1673: vstates = Collections.unmodifiableMap(transMap);
1674: }
1675: return vstates;
1676: }
1677:
1678: /**
1679: * Adds the possibility to change the state to
1680: * <code>OpenState.RUNNING</code> (i.e. "<code>start()</code>") to
1681: * the <code>changeState</code> method of the superclass.
1682: *
1683: * @param newState the new state.
1684: * @throws InvalidStateException If <code>newState</code> is an invalid
1685: * state for the execution object.
1686: * @throws TransitionNotAllowedException If the transition from the current
1687: * state to <code>newState</code> is not allowed.
1688: */
1689: public void changeState(State newState)
1690: throws InvalidStateException, TransitionNotAllowedException {
1691: try {
1692: if (typedState() == NotRunningState.NOT_STARTED
1693: && newState == OpenState.RUNNING) {
1694: start();
1695: return;
1696: }
1697: } catch (CannotStartException e) {
1698: throw new TransitionNotAllowedException(e.getClass()
1699: .getName()
1700: + ": " + e.getMessage());
1701: } catch (AlreadyRunningException e) {
1702: throw new TransitionNotAllowedException(e.getClass()
1703: .getName()
1704: + ": " + e.getMessage());
1705: }
1706: super .changeState(newState);
1707: }
1708:
1709: /**
1710: * Starts a process.
1711: * @throws CannotStartException when the process cannot be started (e.g.,
1712: * because it is not properly initialized).
1713: * @throws AlreadyRunningException when the process has already been
1714: * started.
1715: * @ejb.interface-method view-type="remote"
1716: */
1717: public void start() throws CannotStartException,
1718: AlreadyRunningException {
1719: if (typedState() != NotRunningState.NOT_STARTED) {
1720: throw new AlreadyRunningException(toString()
1721: + " state is: " + state());
1722: }
1723: updateState(RunningState.RUNNING);
1724: }
1725:
1726: /**
1727: * Enable or disable debugging of the activity.
1728: * @param debug if the activity is to be debugged
1729: * @throws InvalidStateException if changing debug mode is not
1730: * allowed
1731: */
1732: public void setDebugEnabled(boolean debug)
1733: throws InvalidStateException {
1734: setPaDebug(debug);
1735: Iterator it = stepsLocal().iterator();
1736: while (it.hasNext()) {
1737: ExtActivityLocal act = (ExtActivityLocal) it.next();
1738: act.setDebugEnabled(true);
1739: }
1740: return;
1741: }
1742:
1743: /* Comment copied from Interface. */
1744: public void terminate() throws CannotStopException,
1745: NotRunningException {
1746: mayCloseCheck(ClosedState.TERMINATED);
1747: if (stepsLocal() != null) {
1748: for (Iterator it = stepsLocal().iterator(); it.hasNext();) {
1749: ActivityLocal act = (ActivityLocal) it.next();
1750: State s = act.typedState();
1751: if (s.isSameOrSubState(NotRunningState.SUSPENDED)
1752: || s == ClosedState.ABORTED) {
1753: throw new CannotStopException(act.toString()
1754: + " is suspended.");
1755: }
1756: }
1757: String unstoppable = null;
1758: try {
1759: setPaTypedState(RunningState.TERMINATING);
1760: for (Iterator it = stepsLocal().iterator(); it
1761: .hasNext();) {
1762: ActivityLocal act = (ActivityLocal) it.next();
1763: if (act.workflowState() == State.OPEN
1764: && (!act.typedState().isSameOrSubState(
1765: NotRunningState.NOT_STARTED))) {
1766: try {
1767: act.terminate();
1768: } catch (CannotStopException e) {
1769: unstoppable = act.toString()
1770: + " cannot be terminated: "
1771: + e.getMessage();
1772: } catch (NotRunningException e) {
1773: // cannot happen
1774: logger.error(e.getMessage(), e);
1775: }
1776: }
1777: }
1778: } finally {
1779: setPaTypedState(RunningState.RUNNING);
1780: }
1781: if (unstoppable != null) {
1782: throw new CannotStopException(unstoppable);
1783: }
1784: }
1785: updateState(ClosedState.TERMINATED);
1786: }
1787:
1788: /* Comment copied from Interface. */
1789: public void abort() throws CannotStopException, NotRunningException {
1790: mayCloseCheck(ClosedState.ABORTED);
1791: doAbort();
1792: }
1793:
1794: private void doAbort() throws CannotStopException {
1795: if (stepsLocal() != null) {
1796: String unstoppable = null;
1797: try {
1798: setPaTypedState(SuspendedState.ABORTING);
1799: for (Iterator it = stepsLocal().iterator(); it
1800: .hasNext();) {
1801: ActivityLocal act = (ActivityLocal) it.next();
1802: try {
1803: if (act.typedState().isSameOrSubState(
1804: NotRunningState.SUSPENDED)) {
1805: act.abort();
1806: } else if (act.typedState().isSameOrSubState(
1807: OpenState.RUNNING)) {
1808: try {
1809: act.terminate();
1810: } catch (CannotStopException e) {
1811: act.suspend();
1812: act.abort();
1813: }
1814: }
1815: } catch (CannotStopException e) {
1816: unstoppable = act.toString()
1817: + " cannot be closed: "
1818: + e.getMessage();
1819: } catch (InvalidControlOperationException e) {
1820: // cannot happen, we check states before changing
1821: logger.error(e.getMessage(), e);
1822: }
1823: }
1824: } finally {
1825: setPaTypedState(SuspendedState.SUSPENDED);
1826: }
1827: if (unstoppable != null) {
1828: throw new CannotStopException(unstoppable);
1829: }
1830: }
1831: updateState(ClosedState.ABORTED);
1832: }
1833:
1834: private void mayCloseCheck(ClosedState s)
1835: throws CannotStopException, NotRunningException {
1836: if (!validTypedStates().contains(s)) {
1837: if (typedState().isSameOrSubState(OpenState.RUNNING)) {
1838: throw new CannotStopException(toString() + " is "
1839: + state());
1840: } else {
1841: throw new NotRunningException(toString() + " is "
1842: + state());
1843: }
1844: }
1845: }
1846:
1847: //
1848: // Timers
1849: //
1850:
1851: /**
1852: * Handle the timeout of a timer.
1853: * @param info the context.
1854: */
1855: public void handleTimeout(Serializable info) {
1856: TimeoutInfo toi = (TimeoutInfo) info;
1857: List dls = (List) getPaBlockDeadlines().get(toi.activity);
1858: Deadline dl = (Deadline) dls.get(toi.dlIndex);
1859: if (logger.isDebugEnabled()) {
1860: logger.debug(toString() + " received timeout for " + dl);
1861: }
1862: // now accept deadline
1863: dl.setState(Deadline.STATE_REACHED);
1864: // notify persistence layer about change
1865: getPaBlockDeadlines().put(toi.activity,
1866: getPaBlockDeadlines().get(toi.activity));
1867:
1868: // now find not completed/not started activities and/or abandon
1869: Collection openActs = new ArrayList();
1870: for (Iterator i = activitiesOfBlockActivity(
1871: toi.activity.toString()).iterator(); i.hasNext();) {
1872: ExtActivityLocal a = (ExtActivityLocal) i.next();
1873: if (a.typedState().isSameOrSubState(OpenState.RUNNING)
1874: || a.typedState().isSameOrSubState(
1875: NotRunningState.SUSPENDED)) {
1876: openActs.add(a);
1877: // complete this if deadline is synchronous
1878: if (dl.getExecution() == Deadline.SYNCHR) {
1879: a.initiateAbandoning(true, dl.getExceptionName());
1880: transitionManager().update(a);
1881: }
1882: }
1883: }
1884: if (openActs.size() > 0) {
1885: BAForExceptionHandling ba = (BAForExceptionHandling) blockActivityRepresentation(toi.activity
1886: .toString());
1887: ba
1888: .update(
1889: dls,
1890: dl.getExecution() == Deadline.SYNCHR ? (State) ClosedCompletedState.ABANDONED
1891: : (State) RunningState.RUNNING,
1892: openActs);
1893: handleException(ba, dl.getExceptionName());
1894: }
1895: }
1896:
1897: //
1898: // State change handling
1899: //
1900:
1901: /**
1902: * Check if this class handles a specific event.
1903: * @param event the event to check
1904: * @return <code>true</code> if event is handled
1905: */
1906: public static boolean isHandled(WfAuditEvent event) {
1907: try {
1908: if (!(event instanceof WfStateAuditEvent)) {
1909: return false;
1910: }
1911: if (event.eventType().equals(
1912: WfAuditEvent.ACTIVITY_STATE_CHANGED)) {
1913: State newState = State
1914: .fromString(((WfStateAuditEvent) event)
1915: .newState());
1916: if (newState.isSameOrSubState(State.CLOSED)) {
1917: return true;
1918: }
1919: } else if (event.eventType().equals(
1920: WfAuditEvent.PROCESS_STATE_CHANGED)) {
1921: State oldState = State
1922: .fromString(((WfStateAuditEvent) event)
1923: .oldState());
1924: State newState = State
1925: .fromString(((WfStateAuditEvent) event)
1926: .newState());
1927: if (oldState
1928: .isSameOrSubState(NotRunningState.NOT_STARTED)) {
1929: if (newState == RunningState.RUNNING) {
1930: return true;
1931: }
1932: } else if (oldState
1933: .isSameOrSubState(NotRunningState.NOT_STARTED)) {
1934: if (newState == ClosedState.TERMINATED) {
1935: return true;
1936: }
1937: } else if (oldState
1938: .isSameOrSubState(NotRunningState.SUSPENDED)) {
1939: if (newState == RunningState.RUNNING) {
1940: return true;
1941: }
1942: if (newState == ClosedState.ABORTED) {
1943: return true;
1944: }
1945: } else if (oldState.isSameOrSubState(OpenState.RUNNING)) {
1946: if (newState == ClosedCompletedState.NORMAL) {
1947: return true;
1948: }
1949: if (newState == ClosedState.TERMINATED) {
1950: return true;
1951: }
1952: }
1953: }
1954: } catch (InvalidStateException e) {
1955: throw (IllegalArgumentException) (new IllegalArgumentException(
1956: e.getMessage())).initCause(e);
1957: }
1958: return false;
1959: }
1960:
1961: /**
1962: * Handles the given audit event.
1963: * @param event the event.
1964: * @ejb.interface-method view-type="remote"
1965: */
1966: public void handleAuditEvent(WfAuditEvent event) {
1967: if (event.activityKey() == null) {
1968: super .handleAuditEvent(event);
1969: return;
1970: }
1971: // One of our activities has closed (events are pre-filtered)
1972: State ps = typedState();
1973: if (ps.isSameOrSubState(RunningState.TERMINATING)
1974: || ps.isSameOrSubState(SuspendedState.ABORTING)) {
1975: return;
1976: }
1977: State closedState = null;
1978: try {
1979: closedState = State.fromString(((WfStateAuditEvent) event)
1980: .newState());
1981: } catch (InvalidStateException e) {
1982: // actually, this is impossible
1983: logger.error(e.getMessage());
1984: return;
1985: }
1986: if (closedState.isSameOrSubState(ClosedState.ABORTED)) {
1987: if (ps.isSameOrSubState(OpenState.RUNNING)) {
1988: updateInterim(SuspendedState.SUSPENDED);
1989: } else if ((!ps.isSameOrSubState(NotRunningState.SUSPENDED))) {
1990: return;
1991: }
1992: try {
1993: doAbort();
1994: } catch (CannotStopException e) {
1995: logger.warn("Cannot abort process: " + e.getMessage());
1996: }
1997: return;
1998: }
1999: if (ps == RunningState.RUNNING) {
2000: continueProcessing();
2001: }
2002: }
2003:
2004: /**
2005: * Handles a terminated audit event.
2006: * @param event the event.
2007: */
2008: protected void handleTerminatedEvent(WfStateAuditEvent event) {
2009: WfRequester req = unresolvedRequester();
2010: if ((req instanceof SubProcRequester)
2011: && (((SubProcRequester) req).execution() == SubFlowImplementation.SYNCHR)) {
2012: String key = ((SubProcRequester) req).requestingActivity();
2013: try {
2014: ActivityLocal act = lookupActivityLocal(key);
2015: if (act.typedState() != RunningState.ABANDONING
2016: && act.typedState() != ClosedCompletedState.ABANDONED) {
2017: act.terminate();
2018: }
2019: } catch (InvalidKeyException e) {
2020: logger.warn(toString()
2021: + " cannot notify requesting activity " + key
2022: + " : " + e.getMessage());
2023: } catch (InvalidControlOperationException e) {
2024: logger.warn(toString()
2025: + " cannot terminate requesting activity "
2026: + key + " : " + e.getMessage());
2027: }
2028: }
2029: handleClosedEvent(event);
2030: }
2031:
2032: /**
2033: * Handles a aborting audit event.
2034: * @param event the event.
2035: */
2036: protected void handleAbortedEvent(WfStateAuditEvent event) {
2037: WfRequester req = unresolvedRequester();
2038: if ((req instanceof SubProcRequester)
2039: && (((SubProcRequester) req).execution() == SubFlowImplementation.SYNCHR)) {
2040: String key = ((SubProcRequester) req).requestingActivity();
2041: try {
2042: ExtActivityLocal act = lookupActivityLocal(key);
2043: act.abortRequester();
2044: } catch (InvalidKeyException e) {
2045: logger.warn(toString()
2046: + " cannot notify requesting activity " + key
2047: + " : " + e.getMessage());
2048: }
2049: }
2050: handleClosedEvent(event);
2051: }
2052:
2053: /**
2054: * Handles a completed audit event.
2055: * @param event the event.
2056: */
2057: protected void handleCompletedEvent(WfStateAuditEvent event) {
2058: WfRequester req = unresolvedRequester();
2059: if ((req instanceof SubProcRequester)
2060: && (((SubProcRequester) req).execution() == SubFlowImplementation.SYNCHR)) {
2061: String key = ((SubProcRequester) req).requestingActivity();
2062: try {
2063: ActivityLocal act = lookupActivityLocal(key);
2064: if (act.typedState() != RunningState.ABANDONING
2065: && act.typedState() != ClosedCompletedState.ABANDONED) {
2066: ProcessData res = processContext();
2067: FormalParameter[] fps = processDefinition()
2068: .formalParameters();
2069: ProcessData pd = new ProcessDataWithParams(fps);
2070: for (int i = 0; i < fps.length; i++) {
2071: FormalParameter fp = fps[i];
2072: if (fp.mode() == FormalParameter.Mode.IN) {
2073: continue;
2074: }
2075: String fpn = fps[i].id();
2076: pd.put(fpn, res.get(fpn));
2077: }
2078: act.setResult(pd);
2079: act.complete();
2080: }
2081: } catch (InvalidKeyException e) {
2082: logger.warn(toString()
2083: + " cannot notify requesting activity " + key
2084: + " : " + e.getMessage());
2085: } catch (InvalidDataException e) {
2086: logger.warn(toString()
2087: + " cannot set data on requesting activity "
2088: + key + " : " + e.getMessage());
2089: } catch (InvalidControlOperationException e) {
2090: logger.warn(toString()
2091: + " cannot notify requesting activity " + key
2092: + " : " + e.getMessage());
2093: }
2094: }
2095: handleClosedEvent(event);
2096: }
2097:
2098: /**
2099: * Called when the process is closed. Derived classes should
2100: * should override this method and notify clients waiting on
2101: * channels that the channel is closed and clean up any data
2102: * structures associated with channels.
2103: */
2104: protected void closeChannels() {
2105: }
2106:
2107: private void handleClosedEvent(WfStateAuditEvent event) {
2108: stopTimers();
2109: closeChannels();
2110: int cleanupMode = cleanupMode();
2111: if (cleanupMode == ProcessDefinition.REMOVE_AUTOMATIC
2112: || (cleanupMode == ProcessDefinition.REMOVE_COMPLETED && event
2113: .newState().startsWith("closed.completed"))) {
2114: try {
2115: removeThis();
2116: } catch (CannotRemoveException e) {
2117: // cannot happen, we got this event because process
2118: // was closed, so it must be removable
2119: logger.error("Closed process not removable (?): "
2120: + e.getMessage(), e);
2121: }
2122: }
2123: }
2124:
2125: /* Comment copied from interface. */
2126: public void handleException(ExtActivityLocal activity,
2127: String exceptionName) {
2128: transitionManager().update(activity, exceptionName);
2129: if (typedState() == RunningState.RUNNING) {
2130: continueProcessing();
2131: }
2132: }
2133:
2134: /**
2135: * Remove this process. Since this involves the persistence layer,
2136: * this method must be provided by the derived class.
2137: * @throws CannotRemoveException if the process is still in progress
2138: */
2139: protected abstract void removeThis() throws CannotRemoveException;
2140:
2141: /**
2142: * Handles a started audit event.
2143: * @param event the event.
2144: */
2145: protected void handleStartedEvent(WfStateAuditEvent event) {
2146: // start all activities
2147: continueProcessing();
2148: }
2149:
2150: /**
2151: * Handles a resumed audit event.
2152: * @param event the event.
2153: */
2154: protected void handleResumedEvent(WfStateAuditEvent event) {
2155: continueProcessing();
2156: }
2157:
2158: private void continueProcessing() {
2159: // checkAllActivities
2160: TransitionManager tm = transitionManager();
2161: if (tm.isAtEnd()) {
2162: State resState = ClosedCompletedState.NORMAL;
2163: for (Iterator i = stepsLocal().iterator(); i.hasNext();) {
2164: State s = ((ActivityLocal) i.next()).typedState();
2165: if (s.isSameOrSubState(ClosedState.TERMINATED)) {
2166: resState = ClosedState.TERMINATED;
2167: break; // can't get worse, aborted activity
2168: // is handled separately.
2169: }
2170: }
2171: updateState(resState);
2172: return;
2173: }
2174: // start all runnableActivities
2175: Collection activitiesToStart = tm.startableActivities();
2176: for (Iterator it = activitiesToStart.iterator(); it.hasNext();) {
2177: ExtActivityLocal a = (ExtActivityLocal) it.next();
2178: try {
2179: String ba = a.blockActivity();
2180: if (ba != null && tm.isTransitionSource(ba)) {
2181: // We are about to start an activity that is part
2182: // of a block activity with exception
2183: // condition. Find out if it is the first activity
2184: // to be started in the set.
2185: boolean isFirst = true;
2186: for (Iterator i = activitiesOfBlockActivity(ba)
2187: .iterator(); i.hasNext();) {
2188: ActivityLocal bm = (ActivityLocal) i.next();
2189: if (!bm.typedState().isSameOrSubState(
2190: NotRunningState.NOT_STARTED)) {
2191: isFirst = false;
2192: break;
2193: }
2194: }
2195: if (isFirst) {
2196: armDeadlines(ba);
2197: }
2198: }
2199: a.start();
2200: } catch (AlreadyRunningException e) {
2201: // can't do much about this, shouldn't happen
2202: logger.error(e.getMessage(), e);
2203: }
2204: }
2205: }
2206:
2207: private void armDeadlines(String ba) {
2208: if (logger.isDebugEnabled()) {
2209: logger.debug("Starting first activity of "
2210: + "block activity " + ba);
2211: }
2212: try {
2213: int dlIndex = 0;
2214: Date now = new Date();
2215: Long bak = new Long(ba);
2216: for (Iterator dli = ((List) getPaBlockDeadlines().get(bak))
2217: .iterator(); dli.hasNext(); dlIndex += 1) {
2218: Deadline dl = (Deadline) dli.next();
2219: if (dl.getState() != Deadline.STATE_INITIAL) {
2220: continue;
2221: }
2222: dl.arm(this , now, toProcessLocal(), new TimeoutInfo(
2223: bak, dlIndex));
2224: }
2225: } catch (NumberFormatException e) {
2226: // can't do much about this, shouldn't happen
2227: logger.error(e.getMessage(), e);
2228: }
2229: }
2230:
2231: /**
2232: * Returns an audit event object with process relevant information
2233: * initialized.
2234: * @param eventType event type
2235: * @return the process related information.
2236: */
2237: protected WfAuditEvent auditEventBase(String eventType) {
2238: return new DefaultAuditEvent(toProcess(), eventType,
2239: getPaKey(), getPaName(), getPaProcessMgrName(),
2240: getPaProcessMgrVersion(), getPaAuditEventSelection(),
2241: getPaStoreAuditEvents());
2242: }
2243:
2244: //
2245: // Helpers
2246: //
2247:
2248: /**
2249: * Return an arbitrary activity with the given key. The activity
2250: * need not belong to this process.
2251: * @param key activity key
2252: * @return the activity
2253: * @throws InvalidKeyException if the activity cannot be found
2254: */
2255: protected abstract ExtActivityLocal lookupActivityLocal(String key)
2256: throws InvalidKeyException;
2257:
2258: /**
2259: * Retrieve the base event information about a requesting activity.
2260: * @param req the requester.
2261: * @return the base info using an audit event as DTO.
2262: */
2263: protected abstract WfAuditEvent activityRequesterInfo(
2264: WfRequester req);
2265:
2266: /**
2267: * Return a runtime representation of a block activity. Block
2268: * activities are not persisted. However, the persistence layer
2269: * needs a representation to assign a "from"-activity to exception
2270: * transitions starting at block activities. This representation
2271: * is subsequently used by the domain layer only and therefore
2272: * provided by the domain layer.
2273: * @param key the block activity key
2274: * @return the block activity representation
2275: */
2276: protected ActivityLocal blockActivityRepresentation(String key) {
2277: ActivityLocal act = (ActivityLocal) baRepresentations.get(key);
2278: if (act == null) {
2279: act = new BAForExceptionHandling(key);
2280: baRepresentations.put(key, act);
2281: }
2282: return act;
2283: }
2284:
2285: /**
2286: * Return string representation for debugging purposes.
2287: * @return a string representation.
2288: */
2289: public String toString() {
2290: return "Process[key=" + getPaKey() + "]";
2291: }
2292:
2293: //
2294: // Process instantiation
2295: //
2296:
2297: /**
2298: * Helper class for retrieving the initialization information from
2299: * the process definition.
2300: */
2301: public class SAXInitializer extends StackedHandler {
2302:
2303: private Map actIdMap = null;
2304: private Map trefsMap = null;
2305: private Map actSetDefs = null;
2306:
2307: private String actSetId = null;
2308: private SAXEventBufferImpl actSetDef = null;
2309:
2310: private ProcessData procDataMap = getPaProcessData();
2311:
2312: private String extAttrName = null;
2313: private String extAttrValue = null;
2314:
2315: /**
2316: * Create a new SAX initializer with the given parameters.
2317: */
2318: public SAXInitializer() {
2319: actIdMap = new HashMap();
2320: trefsMap = new HashMap();
2321: actSetDefs = new HashMap();
2322: }
2323:
2324: /**
2325: * Receive notification of the beginning of an element.
2326: *
2327: * @param uri the Namespace URI, or the empty string if the
2328: * element has no Namespace URI or if Namespace processing is not
2329: * being performed.
2330: * @param loc the local name (without prefix), or the empty string
2331: * if Namespace processing is not being performed.
2332: * @param raw the raw XML 1.0 name (with prefix), or the empty
2333: * string if raw names are not available.
2334: * @param a the attributes attached to the element. If there are
2335: * no attributes, it shall be an empty Attributes object.
2336: * @throws SAXException not thrown.
2337: */
2338: public void startElement(String uri, String loc, String raw,
2339: Attributes a) throws SAXException {
2340: if (loc.equals("WorkflowProcess")) {
2341: setPaName(a.getValue("Name"));
2342: setPaId(a.getValue("Id"));
2343: } else if (loc.equals("Applications")) {
2344: getStack().push(new DefaultHandler());
2345: } else if (loc.equals("DataField")
2346: || loc.equals("FormalParameter")) {
2347: setContextData("ProcDataId", a.getValue("Id"));
2348: setContextData("InitialValue", null);
2349: } else if (loc.equals("DataType")) {
2350: getStack().push(new XPDLUtil.SAXDataTypeHandler());
2351: } else if (loc.equals("Activity")) {
2352: getStack().push(
2353: new ActivityInitializer(actIdMap, trefsMap,
2354: actSetDefs, null));
2355: } else if (loc.equals("Transition")) {
2356: getStack().push(
2357: new TransitionInitializer(actIdMap, trefsMap,
2358: null));
2359: } else if (loc.equals("ActivitySet")) {
2360: actSetId = a.getValue("Id");
2361: actSetDef = new SAXEventBufferImpl();
2362: getStack().push(actSetDef);
2363: } else if (loc.equals("ExtendedAttribute")
2364: && getStack().getRelativeDepth() == 4) {
2365: extAttrName = a.getValue("Name");
2366: extAttrValue = a.getValue("Value");
2367: }
2368: }
2369:
2370: /**
2371: * Receive notification of the end of an element.
2372: *
2373: * @param uri the Namespace URI, or the empty string if the
2374: * element has no Namespace URI or if Namespace processing is not
2375: * being performed.
2376: * @param loc the local name (without prefix), or the empty string
2377: * if Namespace processing is not being performed.
2378: * @param raw the raw XML 1.0 name (with prefix), or the empty
2379: * string if raw names are not available.
2380: * @throws SAXException not thrown.
2381: */
2382: public void endElement(String uri, String loc, String raw)
2383: throws SAXException {
2384: if (loc.equals("Priority")) {
2385: try {
2386: setPaPriority(Priority.fromInt(Integer
2387: .parseInt(text().trim())));
2388: } catch (InvalidPriorityException e) {
2389: logger.error(e.getMessage(), e);
2390: throw new SAXException(e);
2391: }
2392: } else if (loc.equals("InitialValue")) {
2393: Object dt = getContextData("DataType");
2394: try {
2395: if (dt.equals(String.class)) {
2396: setContextData("InitialValue", text());
2397: } else if (dt.equals(Double.class)) {
2398: setContextData("InitialValue", new Double(
2399: text()));
2400: } else if (dt.equals(Long.class)) {
2401: setContextData("InitialValue", new Long(text()));
2402: } else if (dt.equals(Date.class)) {
2403: setContextData("InitialValue", XMLUtil
2404: .parseXsdDateTime(text()));
2405: } else if (dt.equals(Boolean.class)) {
2406: setContextData("InitialValue", new Boolean(
2407: text()));
2408: } else if (dt.equals(Participant.class)) {
2409: setContextData("InitialValue", text());
2410: } else if ((dt instanceof ExternalReference)
2411: || (dt instanceof SAXEventBuffer)
2412: || dt.equals(org.w3c.dom.Element.class)) {
2413: setContextData("InitialValue",
2414: parseInitialXMLValue(text()));
2415: }
2416: } catch (NumberFormatException e) {
2417: throw new SAXException("Cannot convert to type: "
2418: + e.getMessage());
2419: } catch (ParseException e) {
2420: throw new SAXException("Cannot convert to type: "
2421: + e.getMessage());
2422: }
2423: } else if (loc.equals("DataField")
2424: || loc.equals("FormalParameter")) {
2425: procDataMap.put(getContextData("ProcDataId"),
2426: getContextData("InitialValue"));
2427: } else if (loc.equals("ActivitySet")) {
2428: actSetDefs.put(actSetId, actSetDef);
2429: } else if (loc.equals("ExtendedAttribute")
2430: && extAttrName != null) {
2431: if (extAttrValue == null) {
2432: extAttrValue = text().trim();
2433: }
2434: if (extAttrName.equals("Debug")
2435: && extAttrValue.equalsIgnoreCase("true")) {
2436: try {
2437: setDebugEnabled(true);
2438: } catch (InvalidStateException e) {
2439: logger.error("Cannot set debug enabled: "
2440: + e.getMessage(), e);
2441: }
2442: }
2443: extAttrName = null;
2444: extAttrValue = null;
2445: }
2446: }
2447: }
2448:
2449: private SAXEventBuffer parseInitialXMLValue(String text) {
2450: try {
2451: SAXParserFactory spf = SAXParserFactory.newInstance();
2452: spf.setValidating(false);
2453: spf.setNamespaceAware(true);
2454: spf.setFeature(
2455: "http://xml.org/sax/features/namespace-prefixes",
2456: true);
2457: XMLReader xr = null;
2458: try {
2459: spf.setFeature(
2460: "http://xml.org/sax/features/xmlns-uris", true);
2461: xr = spf.newSAXParser().getXMLReader();
2462: } catch (SAXException e) {
2463: xr = new XmlnsUrisPatcher(spf.newSAXParser()
2464: .getXMLReader());
2465: }
2466: SAXEventBufferImpl seb = new SAXEventBufferImpl();
2467: XMLFilterImpl filter = new XMLFilterImpl() {
2468: private int level = 0;
2469:
2470: public void startElement(String uri, String localName,
2471: String qName, Attributes atts)
2472: throws SAXException {
2473: if (level > 0) {
2474: super .startElement(uri, localName, qName, atts);
2475: }
2476: level += 1;
2477: }
2478:
2479: public void endElement(String uri, String localName,
2480: String qName) throws SAXException {
2481: level -= 1;
2482: if (level > 0) {
2483: super .endElement(uri, localName, qName);
2484: }
2485: }
2486: };
2487: filter.setContentHandler(seb);
2488: xr.setContentHandler(filter);
2489: xr.parse(new InputSource(new StringReader(
2490: "<temporary-root>" + text + "</temporary-root>")));
2491: seb.pack();
2492: return seb;
2493: } catch (ParserConfigurationException e) {
2494: throw new IllegalArgumentException(
2495: "Error initiliazing schema type: " + e.getMessage());
2496: } catch (SAXException e) {
2497: throw new IllegalArgumentException(
2498: "Error initiliazing schema type: " + e.getMessage());
2499: } catch (IOException e) {
2500: throw new IllegalArgumentException(
2501: "Error initiliazing schema type: " + e.getMessage());
2502: }
2503: }
2504:
2505: /**
2506: * Helper class for retrieving the Activity information from
2507: * the process definition.
2508: */
2509: public class ActivityInitializer extends StackedHandler {
2510:
2511: private Map actIdMap = null;
2512: private Map trefsMap = null;
2513: private Map actSetDefs = null;
2514: private String blkActKey = null;
2515:
2516: private String actId = null;
2517: private Priority priority = Priority.NORMAL;
2518: private String name = null;
2519: private String description = null;
2520: private StartFinishMode startMode = StartFinishMode.AUTOMATIC;
2521: private StartFinishMode finishMode = StartFinishMode.AUTOMATIC;
2522: private JoinAndSplitMode joinMode = JoinAndSplitMode.AND;
2523: private JoinAndSplitMode splitMode = JoinAndSplitMode.AND;
2524: private String performer = null;
2525: private List deadlines = new ArrayList();
2526:
2527: private boolean waitingForStartMode = false;
2528: private boolean waitingForFinishMode = false;
2529: private List trefs = null;
2530: private List impls = null;
2531: private String actSetRef = null;
2532: private int dlMode;
2533: private String dlCond = null;
2534: private String dlException = null;
2535: private boolean deferredChoice = false;
2536:
2537: private String extAttrName = null;
2538: private String extAttrValue = null;
2539:
2540: /**
2541: * Create a new initializer with the given parameters.
2542: * @param actIds activity id map
2543: * @param trefs transition reference map
2544: * @param actSets activity set definition map
2545: * @param blkActKey the key of the block activity this
2546: * activity belongs to or <code>null</code>
2547: */
2548: public ActivityInitializer(Map actIds, Map trefs, Map actSets,
2549: String blkActKey) {
2550: actIdMap = actIds;
2551: trefsMap = trefs;
2552: actSetDefs = actSets;
2553: this .blkActKey = blkActKey;
2554: }
2555:
2556: /**
2557: * Receive notification of the beginning of an element.
2558: *
2559: * @param uri the Namespace URI, or the empty string if the
2560: * element has no Namespace URI or if Namespace processing is not
2561: * being performed.
2562: * @param loc the local name (without prefix), or the empty string
2563: * if Namespace processing is not being performed.
2564: * @param raw the raw XML 1.0 name (with prefix), or the empty
2565: * string if raw names are not available.
2566: * @param a the attributes attached to the element. If there are
2567: * no attributes, it shall be an empty Attributes object.
2568: * @throws SAXException not thrown.
2569: */
2570: public void startElement(String uri, String loc, String raw,
2571: Attributes a) throws SAXException {
2572: if (loc.equals("Activity")) {
2573: actId = a.getValue("Id");
2574: name = a.getValue("Name");
2575: trefs = new ArrayList();
2576: impls = new ArrayList();
2577: } else if (loc.equals("StartMode")) {
2578: waitingForStartMode = true;
2579: } else if (loc.equals("FinishMode")) {
2580: waitingForFinishMode = true;
2581: } else if (loc.equals("Automatic")) {
2582: if (waitingForStartMode) {
2583: startMode = StartFinishMode.AUTOMATIC;
2584: } else if (waitingForFinishMode) {
2585: finishMode = StartFinishMode.AUTOMATIC;
2586: }
2587: } else if (loc.equals("Manual")) {
2588: if (waitingForStartMode) {
2589: startMode = StartFinishMode.MANUAL;
2590: } else if (waitingForFinishMode) {
2591: finishMode = StartFinishMode.MANUAL;
2592: }
2593: } else if (loc.equals("Join")) {
2594: joinMode = JoinAndSplitMode.fromString(a
2595: .getValue("Type"));
2596: } else if (loc.equals("Split")) {
2597: splitMode = JoinAndSplitMode.fromString(a
2598: .getValue("Type"));
2599: } else if (loc.equals("TransitionRef")) {
2600: trefs.add(a.getValue("Id"));
2601: } else if (loc.equals("Tool")) {
2602: getStack().push(ToolBasedImpl.saxConstructor());
2603: } else if (loc.equals("SubFlow")) {
2604: getStack().push(ProcBasedImpl.saxConstructor());
2605: } else if (loc.equals("Deadline")) {
2606: dlMode = Deadline.SYNCHR;
2607: if (a.getValue("Execution") != null
2608: && a.getValue("Execution").equals("ASYNCHR")) {
2609: dlMode = Deadline.ASYNCHR;
2610: }
2611: } else if (loc.equals("BlockActivity")) {
2612: actSetRef = a.getValue("BlockId");
2613: } else if (loc.equals("ExtendedAttribute")
2614: && getStack().getRelativeDepth() == 2) {
2615: extAttrName = a.getValue("Name");
2616: extAttrValue = a.getValue("Value");
2617: }
2618: }
2619:
2620: /**
2621: * Receive notification of the end of an element.
2622: *
2623: * @param uri the Namespace URI, or the empty string if the
2624: * element has no Namespace URI or if Namespace processing is not
2625: * being performed.
2626: * @param loc the local name (without prefix), or the empty string
2627: * if Namespace processing is not being performed.
2628: * @param raw the raw XML 1.0 name (with prefix), or the empty
2629: * string if raw names are not available.
2630: * @throws SAXException not thrown.
2631: */
2632: public void endElement(String uri, String loc, String raw)
2633: throws SAXException {
2634: if (loc.equals("Activity")) {
2635: if (actSetRef != null) {
2636: // create block activity, i.e. the activities defined
2637: // in the activity set
2638: Long baKey = new Long(-createActivityKey()
2639: .longValue());
2640: BlockActivity ba = new BAForProcessInstantiation(
2641: AbstractProcess.this , baKey.toString(),
2642: (SAXEventBuffer) actSetDefs.get(actSetRef),
2643: (String) getContextData("packageId"),
2644: actSetDefs, actId, joinMode, splitMode);
2645: actIdMap.put(actId, ba);
2646: getPaBlockDeadlines().put(baKey, deadlines);
2647: } else {
2648: // create a simple activity
2649: WfActivityLocal act = createActivity(
2650: blkActKey,
2651: priority,
2652: name,
2653: description,
2654: startMode,
2655: finishMode,
2656: joinMode,
2657: splitMode,
2658: impls.size() == 0 ? null
2659: : ((Implementation[]) impls
2660: .toArray(new Implementation[impls
2661: .size()])),
2662: performer, deadlines, deferredChoice,
2663: getPaAuditEventSelection(),
2664: getPaStoreAuditEvents());
2665: actIdMap.put(actId, act);
2666: }
2667: trefsMap.put(actId, trefs);
2668: } else if (loc.equals("Priority")) {
2669: try {
2670: priority = Priority.fromInt(Integer.parseInt(text()
2671: .trim()));
2672: } catch (InvalidPriorityException e) {
2673: throw new SAXException(e);
2674: }
2675: } else if (loc.equals("Description")) {
2676: description = text().trim();
2677: } else if (loc.equals("StartMode")) {
2678: waitingForStartMode = false;
2679: } else if (loc.equals("FinishMode")) {
2680: waitingForFinishMode = false;
2681: } else if (loc.equals("Performer")) {
2682: performer = text().trim();
2683: } else if (loc.equals("Tool") || loc.equals("SubFlow")) {
2684: impls.add(getContextData("implementation"));
2685: } else if (loc.equals("DeadlineCondition")) {
2686: dlCond = text();
2687: } else if (loc.equals("ExceptionName")) {
2688: dlException = text();
2689: } else if (loc.equals("Deadline")) {
2690: deadlines
2691: .add(new Deadline(dlMode, dlCond, dlException));
2692: } else if (loc.equals("ExtendedAttribute")
2693: && extAttrName != null) {
2694: if (extAttrValue == null) {
2695: extAttrValue = text().trim();
2696: }
2697: if (extAttrName.equals("DeferredChoice")
2698: && extAttrValue.equalsIgnoreCase("true")) {
2699: deferredChoice = true;
2700: }
2701: extAttrName = null;
2702: extAttrValue = null;
2703: }
2704: }
2705: }
2706:
2707: /**
2708: * Return a new initializer with the given parameters.
2709: * @param actIds activity id map
2710: * @param trefs transition reference map
2711: * @param actSets activity set definition map
2712: * @param blkActKey the id of the containing block activity or
2713: * <code>null</code>
2714: * @return the initializer
2715: */
2716: ActivityInitializer activityInitializer(Map actIds, Map trefs,
2717: Map actSets, String blkActKey) {
2718: return new ActivityInitializer(actIds, trefs, actSets,
2719: blkActKey);
2720: }
2721:
2722: /**
2723: * Factory method that create new persistent objects of type
2724: * <code>WfActivity</code>. Must be implement by the persistence
2725: * layer.
2726: *
2727: * @param blockActId if the activity is part of a block activity,
2728: * else <code>null</code>
2729: * @param priority a <code>Priority</code> value
2730: * @param name the activity's name
2731: * @param description activity description
2732: * @param startMode the start mode
2733: * @param finishMode the finish mode
2734: * @param joinMode the join mode
2735: * @param splitMode the split mode
2736: * @param implementation the implementation description
2737: * @param performer the performer
2738: * @param deadlines the deadlines
2739: * @param deferredChoice if true, the split is to be made as
2740: * deferred choice
2741: * @param auditEventSelection the audit event selection
2742: * @param storeAuditEvents if true, audit events are stored in the
2743: * database
2744: * @return the created activity.
2745: */
2746: protected abstract WfActivityLocal createActivity(
2747: String blockActId, Priority priority, String name,
2748: String description, StartFinishMode startMode,
2749: StartFinishMode finishMode, JoinAndSplitMode joinMode,
2750: JoinAndSplitMode splitMode,
2751: Implementation[] implementation, String performer,
2752: List deadlines, boolean deferredChoice,
2753: int auditEventSelection, boolean storeAuditEvents);
2754:
2755: /**
2756: * Helper class for retrieving the transition information from
2757: * the process definition.
2758: */
2759: public class TransitionInitializer extends StackedHandler {
2760:
2761: private Map actIdMap = null;
2762: private Map trefsMap = null;
2763: private String blkActId = null;
2764:
2765: private String transId = null;
2766: private String fromId = null;
2767: private String toId = null;
2768: private String condType = null;
2769: private String condExpr = null;
2770:
2771: /**
2772: * Create a new initializer with the given parameters.
2773: * @param actIds activity id map
2774: * @param trefs transition reference map
2775: * @param blkActId the Id of the block activity being
2776: * created, i.e. to which the transitions belong.
2777: */
2778: public TransitionInitializer(Map actIds, Map trefs,
2779: String blkActId) {
2780: actIdMap = actIds;
2781: trefsMap = trefs;
2782: this .blkActId = blkActId;
2783: }
2784:
2785: /**
2786: * Receive notification of the beginning of an element.
2787: *
2788: * @param uri the Namespace URI, or the empty string if the
2789: * element has no Namespace URI or if Namespace processing is not
2790: * being performed.
2791: * @param loc the local name (without prefix), or the empty string
2792: * if Namespace processing is not being performed.
2793: * @param raw the raw XML 1.0 name (with prefix), or the empty
2794: * string if raw names are not available.
2795: * @param a the attributes attached to the element. If there are
2796: * no attributes, it shall be an empty Attributes object.
2797: * @throws SAXException not thrown.
2798: */
2799: public void startElement(String uri, String loc, String raw,
2800: Attributes a) throws SAXException {
2801: if (loc.equals("Transition")) {
2802: transId = a.getValue("Id");
2803: fromId = a.getValue("From");
2804: toId = a.getValue("To");
2805: } else if (loc.equals("Condition")) {
2806: condType = a.getValue("Type");
2807: if (condType == null) {
2808: condType = "CONDITION";
2809: }
2810: }
2811: }
2812:
2813: /**
2814: * Receive notification of the end of an element.
2815: *
2816: * @param uri the Namespace URI, or the empty string if the
2817: * element has no Namespace URI or if Namespace processing is not
2818: * being performed.
2819: * @param loc the local name (without prefix), or the empty string
2820: * if Namespace processing is not being performed.
2821: * @param raw the raw XML 1.0 name (with prefix), or the empty
2822: * string if raw names are not available.
2823: * @throws SAXException not thrown.
2824: */
2825: public void endElement(String uri, String loc, String raw)
2826: throws SAXException {
2827: if (loc.equals("Condition")) {
2828: condExpr = text();
2829: } else if (loc.equals("Transition")) {
2830: createTransition(actIdMap, trefsMap, transId, fromId,
2831: toId, condType, condExpr, blkActId);
2832: }
2833: }
2834: }
2835:
2836: /**
2837: * Return a new initializer with the given parameters.
2838: * @param actIds activity id map
2839: * @param trefs transition reference map
2840: * @param blkActId the Id of the block activity being
2841: * created, i.e. to which the transitions belong.
2842: * @return the initializer
2843: */
2844: TransitionInitializer transitionInitializer(Map actIds, Map trefs,
2845: String blkActId) {
2846: return new TransitionInitializer(actIds, trefs, blkActId);
2847: }
2848:
2849: private void createTransition(Map actIdMap, Map trefsMap,
2850: String transId, String fromId, String toId,
2851: String condType, String condExpr, String blkActId) {
2852: // find associated "from"-activities
2853: int order = evalTransitionOrder(transId, fromId, trefsMap);
2854:
2855: ActivityLocal fromAct = (ActivityLocal) actIdMap.get(fromId);
2856: BAForProcessInstantiation fromBA = null;
2857: List fromActivities = new ArrayList();
2858: if (fromAct instanceof BlockActivity) {
2859: // we do not generate exception transitions from block
2860: // internals to block externals
2861: if (condExpr != null
2862: && (condType.equals("EXCEPTION") || condType
2863: .equals("DEFAULTEXCEPTION"))) {
2864: fromActivities.add(blockActivityRepresentation(fromAct
2865: .key()));
2866: } else {
2867: fromBA = (BAForProcessInstantiation) fromAct;
2868: fromActivities.addAll(fromBA.exitActivities());
2869: }
2870: } else {
2871: fromActivities.add(fromAct);
2872: }
2873:
2874: ActivityLocal toAct = (ActivityLocal) actIdMap.get(toId);
2875: BAForProcessInstantiation toBA = null;
2876: List toActivities = new ArrayList();
2877: if (toAct instanceof BlockActivity) {
2878: toBA = (BAForProcessInstantiation) toAct;
2879: toActivities.addAll(toBA.entryActivities());
2880: } else {
2881: toActivities.add(toAct);
2882: }
2883:
2884: // create all the possible transitions
2885: for (Iterator fromIt = fromActivities.iterator(); fromIt
2886: .hasNext();) {
2887: ActivityLocal fa = (ActivityLocal) fromIt.next();
2888: for (Iterator toIt = toActivities.iterator(); toIt
2889: .hasNext();) {
2890: ActivityLocal ta = (ActivityLocal) toIt.next();
2891:
2892: // enhance transition id if block activities are
2893: // involved
2894: String id = transId;
2895: if (blkActId != null) {
2896: id += "/" + blkActId;
2897: }
2898: if (fromBA != null) {
2899: id += "/" + fromBA.setId() + "/"
2900: + fromBA.getMemberId(fa.key());
2901: }
2902: if (toBA != null) {
2903: id += "/" + toBA.setId() + "/"
2904: + toBA.getMemberId(ta.key());
2905: }
2906:
2907: // create and register transition(s)
2908: int ct = Transition.COND_TYPE_CONDITION;
2909: if (condExpr != null) {
2910: if (condType.equals("OTHERWISE")) {
2911: ct = Transition.COND_TYPE_OTHERWISE;
2912: } else if (condType.equals("EXCEPTION")) {
2913: ct = Transition.COND_TYPE_EXCEPTION;
2914: } else if (condType.equals("DEFAULTEXCEPTION")) {
2915: ct = Transition.COND_TYPE_DEFAULTEXCEPTION;
2916: }
2917: }
2918: doCreateTransition(id, transId, order, fa, ta, ct,
2919: condExpr);
2920: }
2921: }
2922: }
2923:
2924: /**
2925: * Returns the transition order of the given transition id as defined with
2926: * the set of transitions assigned to a given activity id - using the given
2927: * map of transition refrences.
2928: * @param transId the transition id to define the order for
2929: * @param actId the activity id to determine the order for
2930: * @param trefsMap the map of transition references
2931: * @return the order value
2932: */
2933: private int evalTransitionOrder(String transId, String actId,
2934: Map trefsMap) {
2935: int order = 0;
2936: List trefs = (List) trefsMap.get(actId);
2937: if (trefs == null) {
2938: return Integer.MAX_VALUE;
2939: }
2940: Iterator refIt = trefs.iterator();
2941: while (refIt.hasNext()) {
2942: String refId = (String) refIt.next();
2943: if (transId.equals(refId)) {
2944: break;
2945: }
2946: order += 1;
2947: }
2948: return order;
2949: }
2950:
2951: /**
2952: * Persist a new transition with given id, from-activity,
2953: * to-activity. The created transitions must be made persistent by
2954: * the persistence layer.
2955: * @param id the transition id
2956: * @param group the transition group id
2957: * @param order the transition priority
2958: * @param fromAct the from activity
2959: * @param toAct the to activity
2960: * @param condType the type of the condition
2961: * @param condition condition of this transition
2962: * @return the created transition.
2963: */
2964: protected abstract TransitionLocal doCreateTransition(String id,
2965: String group, int order, ActivityLocal fromAct,
2966: ActivityLocal toAct, int condType, String condition);
2967:
2968: }
|