0001: /*--
0002:
0003: Copyright (C) 2002-2005 Adrian Price.
0004: All rights reserved.
0005:
0006: Redistribution and use in source and binary forms, with or without
0007: modification, are permitted provided that the following conditions
0008: are met:
0009:
0010: 1. Redistributions of source code must retain the above copyright
0011: notice, this list of conditions, and the following disclaimer.
0012:
0013: 2. Redistributions in binary form must reproduce the above copyright
0014: notice, this list of conditions, and the disclaimer that follows
0015: these conditions in the documentation and/or other materials
0016: provided with the distribution.
0017:
0018: 3. The names "OBE" and "Open Business Engine" must not be used to
0019: endorse or promote products derived from this software without prior
0020: written permission. For written permission, please contact
0021: adrianprice@sourceforge.net.
0022:
0023: 4. Products derived from this software may not be called "OBE" or
0024: "Open Business Engine", nor may "OBE" or "Open Business Engine"
0025: appear in their name, without prior written permission from
0026: Adrian Price (adrianprice@users.sourceforge.net).
0027:
0028: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0029: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0030: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0031: DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
0032: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
0033: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
0034: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0035: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
0036: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
0037: IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0038: POSSIBILITY OF SUCH DAMAGE.
0039:
0040: For more information on OBE, please see
0041: <http://obe.sourceforge.net/>.
0042:
0043: */
0044:
0045: package org.obe.engine;
0046:
0047: import org.apache.commons.logging.Log;
0048: import org.apache.commons.logging.LogFactory;
0049: import org.obe.OBERuntimeException;
0050: import org.obe.client.api.WMClient;
0051: import org.obe.client.api.WMClientFactory;
0052: import org.obe.client.api.base.WorkflowEngineIntf;
0053: import org.obe.client.api.repository.EventTypeMetaData;
0054: import org.obe.client.api.repository.ObjectNotFoundException;
0055: import org.obe.client.api.repository.RepositoryException;
0056: import org.obe.client.api.tool.Parameter;
0057: import org.obe.client.api.tool.ToolAgent;
0058: import org.obe.client.api.tool.ToolInvocation;
0059: import org.obe.engine.persistence.WAPIHelper;
0060: import org.obe.runtime.participant.BasicPrincipal;
0061: import org.obe.spi.WMLocalClient;
0062: import org.obe.spi.WorkflowService;
0063: import org.obe.spi.evaluator.EvaluatorException;
0064: import org.obe.spi.event.ApplicationEvent;
0065: import org.obe.spi.event.ApplicationEventListener;
0066: import org.obe.spi.model.*;
0067: import org.obe.spi.runtime.AssignmentStrategy;
0068: import org.obe.spi.runtime.CompletionStrategy;
0069: import org.obe.spi.service.*;
0070: import org.obe.util.CommonConfig;
0071: import org.obe.util.WorkflowUtilities;
0072: import org.obe.client.api.xpdl.InvalidPackageException;
0073: import org.obe.xpdl.model.activity.*;
0074: import org.obe.xpdl.model.data.ActualParameter;
0075: import org.obe.xpdl.model.data.DataField;
0076: import org.obe.xpdl.model.data.FormalParameter;
0077: import org.obe.xpdl.model.data.Type;
0078: import org.obe.xpdl.model.ext.*;
0079: import org.obe.xpdl.model.ext.Timer;
0080: import org.obe.xpdl.model.misc.ExecutionType;
0081: import org.obe.xpdl.model.participant.Participant;
0082: import org.obe.xpdl.model.participant.ParticipantType;
0083: import org.obe.xpdl.model.pkg.XPDLPackage;
0084: import org.obe.xpdl.model.transition.Transition;
0085: import org.obe.xpdl.model.workflow.ProcessHeader;
0086: import org.obe.xpdl.model.workflow.WorkflowProcess;
0087: import org.obe.xpdl.parser.XPDLParser;
0088: import org.obe.xpdl.parser.XPDLParserException;
0089: import org.obe.xpdl.serializer.XPDLSerializer;
0090: import org.obe.xpdl.serializer.XPDLSerializerException;
0091: import org.wfmc.audit.WMAAuditEntry;
0092: import org.wfmc.wapi.*;
0093:
0094: import java.io.IOException;
0095: import java.io.StringReader;
0096: import java.io.StringWriter;
0097: import java.security.Principal;
0098: import java.util.*;
0099:
0100: /**
0101: * The workflow engine provides the means to create and execute workflow
0102: * processes, as well as means for querying open activities and work items.
0103: * <p/>
0104: * <em>N.B. Applications which embed OBE should never call WorkflowEngine
0105: * directly; instead, they should call via one of the {@link WMClient}
0106: * implementations, which provides authentication, authorization and transaction
0107: * control.</em>
0108: *
0109: * @author Adrian Price
0110: * @author Anthony Eden
0111: */
0112: public class WorkflowEngine implements WorkflowEngineIntf,
0113: WorkflowService {
0114: private static final String SERVICE_NAME = "WorkflowEngine";
0115: private static final Log _logger = LogFactory
0116: .getLog(WorkflowEngine.class);
0117: private static final Principal[] EMPTY_PRINCIPALS = {};
0118: private static final String[] EMPTY_STRINGS = {};
0119: private static final ToolInvocation[] EMPTY_TOOL_INVOCATIONS = {};
0120: private static final ThreadLocal _engines = new ThreadLocal();
0121: private static WorkflowEngine _theEngine;
0122: private final ServiceManager _svcMgr;
0123: private EngineApplicationEventListener _applicationEventListener;
0124:
0125: private class EngineApplicationEventListener implements
0126: ApplicationEventListener {
0127:
0128: private WMLocalClient _client;
0129:
0130: private EngineApplicationEventListener() {
0131: // If requested, instantiate a client and connect.
0132: if (ServerConfig.useEventAPI()) {
0133: try {
0134: // If using the event API, use the J2EE local interface.
0135: _client = (WMLocalClient) WMClientFactory
0136: .createClient(WMClientFactory.J2EE_LOCAL);
0137: String userId = CommonConfig.getPrincipal();
0138: String password = CommonConfig.getCredentials();
0139: WMConnectInfo connectInfo = userId == null ? null
0140: : new WMConnectInfo(userId, password, null,
0141: null);
0142: _client.connect(connectInfo);
0143: } catch (WMWorkflowException e) {
0144: throw new OBERuntimeException(e);
0145: }
0146: }
0147: }
0148:
0149: public void onEvent(ApplicationEvent event,
0150: String[] correlationKeys) {
0151:
0152: try {
0153: // Inject event via client or directly, as configured.
0154: if (_client != null)
0155: _client.raiseEvent(event, correlationKeys);
0156: else
0157: raiseEvent(event, correlationKeys);
0158: } catch (WMWorkflowException e) {
0159: _logger.error(
0160: "Exception occurred during event handling", e);
0161: }
0162: }
0163:
0164: public String toString() {
0165: return "EngineApplicationEventListener@"
0166: + System.identityHashCode(this );
0167: }
0168: }
0169:
0170: /**
0171: * Returns a reference to the current thread's workflow engine. Generally
0172: * speaking, the engine (which is threadsafe) will be shared amongst all
0173: * threads. This method is present to support the case where multiple,
0174: * differently configured engines are running concurrently in the same JVM.
0175: * <p/>
0176: * If an engine is not currently associated with the current thread (by
0177: * virtue of there being an engine call on the JVM call stack for the
0178: * current thread), this method returns the default
0179: * <code>WorkflowEngine</code>, creating this if it does not already exist.
0180: * The default engine is set to the first <code>WorkflowEngine</code>
0181: * instance to be created. If this method creates the default engine, its
0182: * {@link ServiceManager} and repositories will have the default
0183: * configuration.
0184: * <p/>
0185: * N.B. It is preferable for applications which embed an engine to create it
0186: * explicitly and retain a global reference to it rather than rely on this
0187: * method to locate the global instance.
0188: *
0189: * @return The current engine, or the default engine if no engine call is
0190: * currently in progress on the current thread.
0191: */
0192: public static WorkflowEngine getEngine() {
0193: WorkflowEngine engine = (WorkflowEngine) _engines.get();
0194: if (engine == null)
0195: engine = _theEngine != null ? _theEngine
0196: : new WorkflowEngine();
0197: return engine;
0198: }
0199:
0200: public WorkflowEngine() {
0201: this (new ServiceManager());
0202: }
0203:
0204: public WorkflowEngine(ServiceManager svcMgr) {
0205: if (svcMgr == null)
0206: throw new IllegalArgumentException();
0207: _svcMgr = svcMgr;
0208: synchronized (WorkflowEngine.class) {
0209: if (_theEngine == null) {
0210: _logger.info("Default engine created");
0211: _theEngine = this ;
0212: }
0213: }
0214: }
0215:
0216: public String toString() {
0217: return "WorkflowEngine@" + System.identityHashCode(this );
0218: }
0219:
0220: private void setEngine() {
0221: _engines.set(this );
0222: }
0223:
0224: public ServiceManager getServiceManager() {
0225: return _svcMgr;
0226: }
0227:
0228: public String getServiceName() {
0229: return SERVICE_NAME;
0230: }
0231:
0232: public void init() {
0233: _applicationEventListener = new EngineApplicationEventListener();
0234: _svcMgr.getApplicationEventBroker()
0235: .addApplicationEventListener(_applicationEventListener);
0236: }
0237:
0238: public void exit() {
0239: _svcMgr.getApplicationEventBroker()
0240: .removeApplicationEventListener(
0241: _applicationEventListener);
0242: _applicationEventListener = null;
0243: }
0244:
0245: // ProcessDefinition API
0246:
0247: public String createPackage(XPDLPackage pkg)
0248: throws WMWorkflowException {
0249: setEngine();
0250: if (_logger.isDebugEnabled()) {
0251: _logger.debug("createPackage(" + pkg + ')');
0252: }
0253:
0254: try {
0255: WorkflowEngineUtilities.validatePackage(pkg);
0256: _svcMgr.getProcessRepository().createPackage(pkg);
0257:
0258: // Notify package listeners.
0259: _svcMgr.getWorkflowEventBroker().firePackageCreated(pkg);
0260:
0261: return pkg.getId();
0262: } catch (InvalidPackageException e) {
0263: throw new WMWorkflowException(e);
0264: } catch (RepositoryException e) {
0265: throw new WMWorkflowException(e);
0266: }
0267: }
0268:
0269: public void updatePackage(XPDLPackage pkg)
0270: throws WMWorkflowException {
0271: setEngine();
0272: if (_logger.isDebugEnabled()) {
0273: _logger.debug("updatePackage(" + pkg + ')');
0274: }
0275:
0276: try {
0277: WorkflowEngineUtilities.validatePackage(pkg);
0278: _svcMgr.getProcessRepository().updatePackage(pkg);
0279:
0280: // Notify package listeners.
0281: _svcMgr.getWorkflowEventBroker().firePackageUpdated(pkg);
0282: } catch (InvalidPackageException e) {
0283: throw new WMWorkflowException(e);
0284: } catch (RepositoryException e) {
0285: throw new WMWorkflowException(e);
0286: }
0287: }
0288:
0289: public XPDLPackage getPackage(String packageId)
0290: throws WMWorkflowException {
0291: setEngine();
0292: if (_logger.isDebugEnabled()) {
0293: _logger.debug("getPackage(" + packageId + ')');
0294: }
0295:
0296: return findPackage(packageId);
0297: }
0298:
0299: /**
0300: * Creates a package using the supplied content.
0301: *
0302: * @param content Package definition in XPDL format.
0303: * @param contentType The MIME content type of the process definition.
0304: * @return The ID of the new package.
0305: * @throws WMWorkflowException
0306: * @throws IllegalArgumentException if the content type is not supported.
0307: */
0308: public String createPackage(String content, String contentType)
0309: throws WMWorkflowException {
0310:
0311: setEngine();
0312: if (_logger.isDebugEnabled()) {
0313: _logger.debug("createPackage(" + content + ", "
0314: + contentType + ')');
0315: }
0316:
0317: try {
0318: if (!WMClient.XPDL.equals(contentType))
0319: throw new IllegalArgumentException(contentType);
0320:
0321: XPDLParser parser = _svcMgr.getParserFactory()
0322: .createParser(contentType);
0323: XPDLPackage pkg = parser.parse(new StringReader(content));
0324:
0325: return createPackage(pkg);
0326: } catch (IOException e) {
0327: throw new WMIOException(e);
0328: } catch (ObjectNotFoundException e) {
0329: throw new WMWorkflowException(e);
0330: } catch (XPDLParserException e) {
0331: throw new WMWorkflowException(e);
0332: }
0333: }
0334:
0335: /**
0336: * Retrieves the content of the specified package.
0337: *
0338: * @param packageId The package ID.
0339: * @return Package definition in XPDL format.
0340: * @throws WMWorkflowException
0341: */
0342: public String getPackageContent(String packageId, String contentType)
0343: throws WMWorkflowException {
0344:
0345: setEngine();
0346: if (_logger.isDebugEnabled()) {
0347: _logger.debug("getPackageContent(" + packageId + ", "
0348: + contentType + ')');
0349: }
0350:
0351: try {
0352: XPDLPackage pkg = findPackage(packageId);
0353: XPDLSerializer serializer = _svcMgr.getParserFactory()
0354: .createSerializer(contentType);
0355: StringWriter writer = new StringWriter();
0356: serializer.serialize(pkg, writer);
0357: return writer.toString();
0358: } catch (IOException e) {
0359: throw new WMIOException(e);
0360: } catch (XPDLSerializerException e) {
0361: throw new WMWorkflowException(e);
0362: } catch (RepositoryException e) {
0363: throw new WMWorkflowException(e);
0364: }
0365: }
0366:
0367: /**
0368: * Sets the content of the specified package.
0369: *
0370: * @param packageId The package ID.
0371: * @param content Package definition in XPDL format.
0372: * @throws WMWorkflowException
0373: * @throws IllegalArgumentException if the content type is not supported.
0374: */
0375: public void setPackageContent(String packageId, String content,
0376: String contentType) throws WMWorkflowException {
0377:
0378: setEngine();
0379: if (_logger.isDebugEnabled()) {
0380: _logger.debug("setPackageContent(" + packageId + ", "
0381: + content + ", " + contentType + ')');
0382: }
0383:
0384: try {
0385: // Update the package - package ID must match.
0386: XPDLParser parser = _svcMgr.getParserFactory()
0387: .createParser(contentType);
0388: XPDLPackage pkg = parser.parse(new StringReader(content));
0389: if (packageId != null && !packageId.equals(pkg.getId()))
0390: throw new WMInvalidPackageException(packageId);
0391: WorkflowEngineUtilities.validatePackage(pkg);
0392:
0393: updatePackage(pkg);
0394: } catch (IOException e) {
0395: throw new WMIOException(e);
0396: } catch (InvalidPackageException e) {
0397: throw new WMWorkflowException(e);
0398: } catch (ObjectNotFoundException e) {
0399: throw new WMWorkflowException(e);
0400: } catch (XPDLParserException e) {
0401: throw new WMWorkflowException(e);
0402: }
0403: }
0404:
0405: /**
0406: * Retrieve a list of process definitions.
0407: *
0408: * @param filter A Filter specification.
0409: * @param countFlag The count flag
0410: * @return An array of matching process definitions.
0411: */
0412: public WMProcessDefinition[] listProcessDefinitions(
0413: WMFilter filter, boolean countFlag)
0414: throws WMWorkflowException {
0415:
0416: setEngine();
0417: if (_logger.isDebugEnabled()) {
0418: _logger.debug("listProcessDefinitions(" + filter + ", "
0419: + countFlag + ')');
0420: }
0421:
0422: try {
0423: ProcessRepository processRepository = _svcMgr
0424: .getProcessRepository();
0425: WorkflowProcess[] processes = processRepository
0426: .findWorkflowProcesses(filter, countFlag);
0427: return WAPIHelper.fromWorkflowProcesses(processes);
0428: } catch (RepositoryException e) {
0429: throw new WMWorkflowException(e);
0430: }
0431: }
0432:
0433: /**
0434: * Change the process definition state.
0435: *
0436: * @param processDefinitionId The process definition id
0437: * @param newState The new process definition state
0438: * @throws WMWorkflowException Workflow client exception
0439: */
0440: public void changeProcessDefinitionState(
0441: String processDefinitionId,
0442: WMProcessDefinitionState newState)
0443: throws WMWorkflowException {
0444:
0445: setEngine();
0446: if (_logger.isDebugEnabled()) {
0447: _logger.debug("changeProcessDefinitionState("
0448: + processDefinitionId + ", " + newState + ')');
0449: }
0450:
0451: try {
0452: ProcessRepository processRepository = _svcMgr
0453: .getProcessRepository();
0454: int state = processRepository
0455: .findProcessDefinitionState(processDefinitionId);
0456: if (newState.value() != state) {
0457: // Check that this state transition is valid.
0458: WMProcessDefinitionState oldState = WMProcessDefinitionState
0459: .valueOf(state);
0460: oldState.checkTransition(newState, true);
0461:
0462: // Perform the update.
0463: processRepository.updateProcessDefinitionState(
0464: processDefinitionId, newState.value());
0465:
0466: WorkflowProcess workflow = findWorkflow(processDefinitionId);
0467: if (newState == WMProcessDefinitionState.ENABLED) {
0468: // Enable event triggers.
0469: createTriggers(workflow);
0470:
0471: // Notify process definition listeners.
0472: _svcMgr.getWorkflowEventBroker()
0473: .fireProcessDefinitionEnabled(workflow);
0474: } else {
0475: // Disable event triggers.
0476: deleteTriggers(workflow, true);
0477:
0478: // Notify process definition listeners.
0479: _svcMgr.getWorkflowEventBroker()
0480: .fireProcessDefinitionDisabled(workflow);
0481: }
0482: }
0483: } catch (RepositoryException e) {
0484: throw new WMWorkflowException(e);
0485: }
0486: }
0487:
0488: public void createTriggers(XPDLPackage pkg)
0489: throws WMWorkflowException {
0490: WorkflowProcess[] workflows = pkg.getWorkflowProcess();
0491: for (int i = 0; i < workflows.length; i++)
0492: createTriggers(workflows[i]);
0493: }
0494:
0495: public void updateTriggers(XPDLPackage pkg)
0496: throws WMWorkflowException {
0497: deleteTriggers(pkg, true);
0498: createTriggers(pkg);
0499: }
0500:
0501: public void deleteTriggers(XPDLPackage pkg,
0502: boolean workflowTriggersOnly) throws WMWorkflowException {
0503:
0504: WorkflowProcess[] workflows = pkg.getWorkflowProcess();
0505: for (int i = 0; i < workflows.length; i++)
0506: deleteTriggers(workflows[i], workflowTriggersOnly);
0507: }
0508:
0509: private void createTriggers(WorkflowProcess workflow)
0510: throws WMWorkflowException {
0511:
0512: Trigger trigger = workflow.getTrigger();
0513: if (trigger != null) {
0514: ProcessHeader header = workflow.getProcessHeader();
0515: String eventType = trigger.getId();
0516: int count = trigger.getCount();
0517: Date effective = header.getValidFrom();
0518: Date expiry = header.getValidTo();
0519: String[] correlationKeys = new String[] { workflow.getId() };
0520: ApplicationEventBroker broker = _svcMgr
0521: .getApplicationEventBroker();
0522: EngineContext ctx = EngineContext.pushContext(this ,
0523: workflow, null);
0524: try {
0525: if (trigger instanceof Event) {
0526: Event event = (Event) trigger;
0527: EventTypeMetaData metaData = broker
0528: .findEventTypeMetaData(eventType);
0529: ActualParameter[] actualParms = event
0530: .getActualParameter();
0531: Object[] eventKeys = WorkflowEngineUtilities
0532: .createEventKeys(metaData, actualParms, ctx);
0533: broker.subscribe(eventType, eventKeys, event
0534: .getPredicate(), effective, expiry, count,
0535: correlationKeys, ctx);
0536: } else if (trigger instanceof Timer) {
0537: Timer timer = (Timer) trigger;
0538: broker.subscribe(eventType, effective, expiry,
0539: count, timer.getInterval(), timer
0540: .getCalendar(), timer
0541: .isRecoverable(), correlationKeys);
0542: }
0543: } catch (RepositoryException e) {
0544: throw new WMWorkflowException(e);
0545: } finally {
0546: EngineContext.popContext();
0547: }
0548: }
0549: }
0550:
0551: private void deleteTriggers(WorkflowProcess workflow,
0552: boolean workflowTriggersOnly) throws WMWorkflowException {
0553:
0554: Trigger trigger = workflow.getTrigger();
0555: if (trigger != null) {
0556: try {
0557: _svcMgr.getApplicationEventBroker().unsubscribe(
0558: new String[] { workflow.getId() },
0559: workflowTriggersOnly);
0560: } catch (RepositoryException e) {
0561: throw new WMWorkflowException(e);
0562: }
0563: }
0564: }
0565:
0566: /**
0567: * Retrieve a list of process definition states.
0568: *
0569: * @param filter A Filter specification.
0570: * @return An array of matching process definition states.
0571: * @throws WMWorkflowException Workflow client exception
0572: */
0573: public WMProcessDefinitionState[] listProcessDefinitionStates(
0574: WMFilter filter, boolean countFlag)
0575: throws WMWorkflowException {
0576:
0577: setEngine();
0578: if (_logger.isDebugEnabled()) {
0579: _logger.debug("listProcessDefinitionStates(" + filter
0580: + ", " + countFlag + ')');
0581: }
0582:
0583: if (filter != null) {
0584: throw new WMUnsupportedOperationException(
0585: "Filters are not supported");
0586: }
0587:
0588: WMProcessDefinitionState[] states = WMProcessDefinitionState
0589: .states();
0590: if (countFlag)
0591: Arrays.fill(states, null);
0592: return states;
0593: }
0594:
0595: /**
0596: * Permanently deletes the specified package.
0597: *
0598: * @param packageId The package ID.
0599: * @throws WMWorkflowException
0600: */
0601: public void deletePackage(String packageId)
0602: throws WMWorkflowException {
0603: setEngine();
0604: if (_logger.isDebugEnabled()) {
0605: _logger.debug("deletePackage(" + packageId + ')');
0606: }
0607:
0608: try {
0609: // Defer enforcement of RI constraints to the database.
0610: ProcessRepository processRepository = _svcMgr
0611: .getProcessRepository();
0612: XPDLPackage pkg = findPackage(packageId);
0613: processRepository.deletePackage(packageId);
0614:
0615: // Notify package listeners.
0616: _svcMgr.getWorkflowEventBroker().firePackageDeleted(pkg);
0617: } catch (RepositoryException e) {
0618: throw new WMWorkflowException(e);
0619: }
0620: }
0621:
0622: // ProcessInstance API
0623:
0624: /**
0625: * Creates a new process instance for the given process definition.
0626: *
0627: * @param processDefinitionId The process definition id
0628: * @param processInstanceName The name of the process instance
0629: * @return The process instance id
0630: * @throws WMInvalidProcessDefinitionException
0631: * if the process definition
0632: * does not exist, or is disabled.
0633: * @throws WMWorkflowException Workflow client exception
0634: */
0635: public String createProcessInstance(String processDefinitionId,
0636: String processInstanceName) throws WMWorkflowException {
0637:
0638: setEngine();
0639: return _createProcessInstance(processDefinitionId,
0640: processInstanceName, null, false)
0641: .getProcessInstanceId();
0642: }
0643:
0644: /**
0645: * Creates a new instance of the named workflow process. The system
0646: * instantiates the 'most valid' version of the named process, based on the
0647: * versioning metadata in the ProcessHeader (ValidFrom, ValidTo).
0648: *
0649: * @param name The process definition name.
0650: * @param processInstanceName The name of the process instance.
0651: * @return The process instance id.
0652: * @throws WMInvalidProcessDefinitionException
0653: * if the process definition
0654: * does not exist, is disabled, is under revision, or has no valid versions
0655: * as determined for the current system time.
0656: * @throws WMWorkflowException Workflow client exception
0657: */
0658: public String createProcessInstanceVersioned(String name,
0659: String processInstanceName) throws WMWorkflowException {
0660:
0661: setEngine();
0662: try {
0663: WorkflowProcess[] workflows = _svcMgr
0664: .getProcessRepository().findWorkflowProcesses(name,
0665: true);
0666: if (workflows.length == 0) {
0667: throw new WMInvalidProcessDefinitionException(
0668: "No valid versions of the workflow process '"
0669: + name + "' exist.");
0670: }
0671: WorkflowEngineUtilities.sortForValidity(workflows);
0672: return _createProcessInstance(workflows[0],
0673: processInstanceName, null, false)
0674: .getProcessInstanceId();
0675: } catch (RepositoryException e) {
0676: throw new WMWorkflowException(e);
0677: }
0678: }
0679:
0680: /*package*/ProcessInstance _createProcessInstance(
0681: String processDefinitionId, String processInstanceName,
0682: String parentActivityInstanceId, boolean internal)
0683: throws WMWorkflowException {
0684:
0685: // Find the workflow.
0686: WorkflowProcess workflow = findWorkflow(processDefinitionId);
0687:
0688: // Instantiate it.
0689: return _createProcessInstance(workflow, processInstanceName,
0690: parentActivityInstanceId, internal);
0691: }
0692:
0693: private ProcessInstance _createProcessInstance(
0694: WorkflowProcess workflow, String processInstanceName,
0695: String parentActivityInstanceId, boolean internal)
0696: throws WMWorkflowException {
0697:
0698: String processDefinitionId = workflow.getId();
0699:
0700: if (_logger.isDebugEnabled()) {
0701: _logger.debug("createProcessInstance("
0702: + processDefinitionId + ", " + processInstanceName
0703: + ", " + parentActivityInstanceId + ')');
0704: }
0705:
0706: try {
0707: // Validate that the workflow is enabled, not under revision (and
0708: // also PUBLIC if there is no parent process instance), and that
0709: // it is past the ValidFrom and before the ValidTo dates.
0710: WorkflowEngineUtilities.checkValid(workflow, internal);
0711:
0712: // Get the instance repository.
0713: InstanceRepository instanceRepository = _svcMgr
0714: .getInstanceRepository();
0715:
0716: // Create the process instance.
0717: int priority = WorkflowUtilities
0718: .findWorkflowPriority(workflow);
0719: ProcessInstance processInstance = instanceRepository
0720: .createProcessInstance(
0721: processDefinitionId,
0722: parentActivityInstanceId,
0723: processInstanceName,
0724: priority,
0725: WMProcessInstanceState.OPEN_NOTRUNNING_NOTSTARTED_INT,
0726: new Date(), null, EMPTY_STRINGS);
0727:
0728: // Create workflow relevant data from formal parameters.
0729: FormalParameter[] formalParms = workflow
0730: .getFormalParameter();
0731: if (formalParms != null) {
0732: for (int i = 0, n = formalParms.length; i < n; i++) {
0733: FormalParameter fp = formalParms[i];
0734: Type type = fp.getDataType().getType();
0735: instanceRepository.createProcessInstanceAttribute(
0736: processInstance.getProcessInstanceId(), fp
0737: .getId(), type.getImpliedType()
0738: .value(), null);
0739: }
0740: }
0741:
0742: // Create workflow relevant data from data fields.
0743: XPDLPackage pkg = workflow.getPackage();
0744: createProcessAttributes(processInstance, pkg.getDataField());
0745: createProcessAttributes(processInstance, workflow
0746: .getDataField());
0747:
0748: // Notify process instance listeners.
0749: _svcMgr.getWorkflowEventBroker()
0750: .fireProcessInstanceCreated(processInstance,
0751: workflow);
0752:
0753: return processInstance;
0754: } catch (RepositoryException e) {
0755: throw new WMWorkflowException(e);
0756: }
0757: }
0758:
0759: /**
0760: * Start a specific process. The process instance id is retrieved
0761: * from a previous call to <code>createProcessInstance()</code>
0762: *
0763: * @param processInstanceId The process instance id retrieved from
0764: * a previous call to <code>createProcessInstance()</code>
0765: * @return The new process instance id (which may be the same as
0766: * the old.
0767: * @throws WMWorkflowException Workflow client exception
0768: */
0769: public String startProcess(String processInstanceId)
0770: throws WMWorkflowException {
0771:
0772: setEngine();
0773: if (_logger.isDebugEnabled()) {
0774: _logger.debug("startProcess(" + processInstanceId + ')');
0775: }
0776:
0777: // Find the process instance and start it.
0778: startProcess(findProcessInstance(processInstanceId));
0779:
0780: return processInstanceId;
0781: }
0782:
0783: /*package*/boolean startProcess(ProcessInstance processInstance)
0784: throws WMWorkflowException {
0785:
0786: WorkflowProcess workflow = findWorkflow(processInstance);
0787: try {
0788: // Create a workflow context.
0789: EngineContext ctx = EngineContext.pushContext(this ,
0790: workflow, processInstance);
0791:
0792: // Start the process.
0793: WorkflowRunner runner = new WorkflowRunner(_svcMgr, ctx);
0794: return runner.startProcess();
0795: } finally {
0796: EngineContext.popContext();
0797: }
0798: }
0799:
0800: /**
0801: * Get the specified process instance.
0802: *
0803: * @param processInstanceId The process instance id
0804: * @return The process instance
0805: * @throws WMWorkflowException Workflow client exception
0806: */
0807: public WMProcessInstance getProcessInstance(String processInstanceId)
0808: throws WMWorkflowException {
0809:
0810: setEngine();
0811: if (_logger.isDebugEnabled()) {
0812: _logger.debug("getProcessInstance(" + processInstanceId
0813: + ')');
0814: }
0815:
0816: return WAPIHelper
0817: .fromProcessInstance(findProcessInstance(processInstanceId));
0818: }
0819:
0820: /**
0821: * Retrieve a list of process instances.
0822: *
0823: * @param filter A Filter specification.
0824: * @return An array of matching process instances.
0825: */
0826: public WMProcessInstance[] listProcessInstances(WMFilter filter,
0827: boolean countFlag) throws WMWorkflowException {
0828:
0829: setEngine();
0830: if (_logger.isDebugEnabled()) {
0831: _logger.debug("listProcessInstances(" + filter + ", "
0832: + countFlag + ')');
0833: }
0834:
0835: try {
0836: InstanceRepository instanceRepository = _svcMgr
0837: .getInstanceRepository();
0838: ProcessInstance[] processInstances = instanceRepository
0839: .findProcessInstances(null, filter, countFlag);
0840: return WAPIHelper.fromProcessInstances(processInstances);
0841: } catch (RepositoryException e) {
0842: throw new WMWorkflowException(e);
0843: }
0844: }
0845:
0846: /**
0847: * Abort the specified process instance.
0848: *
0849: * @param processInstanceId The process instance ID
0850: * @throws WMWorkflowException Any exception
0851: */
0852: public void abortProcessInstance(String processInstanceId)
0853: throws WMWorkflowException {
0854:
0855: setEngine();
0856: if (_logger.isDebugEnabled()) {
0857: _logger.debug("abortProcessInstance(" + processInstanceId
0858: + ')');
0859: }
0860:
0861: // Abort the instance (also notifies process instance listeners).
0862: cascadeProcessInstanceState(null,
0863: findProcessInstance(processInstanceId),
0864: WMProcessInstanceState.CLOSED_ABORTED_INT, true,
0865: WMActivityInstanceState.CLOSED_ABORTED_INT, false,
0866: WMWorkItemState.CLOSED_ABORTED_INT, false, false);
0867: }
0868:
0869: /**
0870: * Abort the process instances which were spawned from the given
0871: * process definition and which match the specified filter.
0872: *
0873: * @param processDefinitionId The process definition ID
0874: * @param filter The filter
0875: * @throws WMWorkflowException Any exception
0876: */
0877: public void abortProcessInstances(String processDefinitionId,
0878: WMFilter filter) throws WMWorkflowException {
0879:
0880: setEngine();
0881: if (_logger.isDebugEnabled()) {
0882: _logger.debug("abortProcessInstances("
0883: + processDefinitionId + ", " + filter + ')');
0884: }
0885:
0886: try {
0887: // Find the process instances.
0888: InstanceRepository instanceRepository = _svcMgr
0889: .getInstanceRepository();
0890: ProcessInstance[] processes = instanceRepository
0891: .findProcessInstances(processDefinitionId, filter,
0892: false);
0893:
0894: // Abort the process instances.
0895: for (int i = 0; i < processes.length; i++) {
0896: // Abort the instance (also notifies process instance listeners)
0897: cascadeProcessInstanceState(null, processes[i],
0898: WMProcessInstanceState.CLOSED_ABORTED_INT,
0899: false,
0900: WMActivityInstanceState.CLOSED_ABORTED_INT,
0901: false, WMWorkItemState.CLOSED_ABORTED_INT,
0902: false, false);
0903: }
0904: } catch (RepositoryException e) {
0905: throw new WMWorkflowException(e);
0906: }
0907: }
0908:
0909: /**
0910: * Terminates a process instance.
0911: *
0912: * @param processInstanceId The process instance id
0913: * @throws WMWorkflowException Workflow client exception
0914: */
0915: public void terminateProcessInstance(String processInstanceId)
0916: throws WMWorkflowException {
0917:
0918: setEngine();
0919: if (_logger.isDebugEnabled()) {
0920: _logger.debug("terminateProcessInstance("
0921: + processInstanceId + ')');
0922: }
0923:
0924: // Terminate the instance (also notifies process instance listeners)
0925: cascadeProcessInstanceState(null,
0926: findProcessInstance(processInstanceId),
0927: WMProcessInstanceState.CLOSED_TERMINATED_INT, true,
0928: WMActivityInstanceState.CLOSED_TERMINATED_INT, false,
0929: WMWorkItemState.CLOSED_TERMINATED_INT, false, false);
0930: }
0931:
0932: /**
0933: * Terminates the process instances which were spawned from the specified
0934: * process definition and which match the specified filter.
0935: *
0936: * @param processDefinitionId The process definition ID.
0937: * @param filter The filter.
0938: * @throws WMWorkflowException
0939: */
0940: public void terminateProcessInstances(String processDefinitionId,
0941: WMFilter filter) throws WMWorkflowException {
0942:
0943: setEngine();
0944: if (_logger.isDebugEnabled()) {
0945: _logger.debug("terminateProcessInstances("
0946: + processDefinitionId + ", " + filter + ')');
0947: }
0948:
0949: try {
0950: // Find the process instances.
0951: InstanceRepository instanceRepository = _svcMgr
0952: .getInstanceRepository();
0953: ProcessInstance[] processes = instanceRepository
0954: .findProcessInstances(processDefinitionId, filter,
0955: false);
0956:
0957: // Terminate the process instances.
0958: for (int i = 0; i < processes.length; i++) {
0959: // Terminate the instance (also notifies process instance
0960: // listeners).
0961: cascadeProcessInstanceState(null, processes[i],
0962: WMProcessInstanceState.CLOSED_TERMINATED_INT,
0963: false,
0964: WMActivityInstanceState.CLOSED_TERMINATED_INT,
0965: false, WMWorkItemState.CLOSED_TERMINATED_INT,
0966: false, false);
0967: }
0968: } catch (RepositoryException e) {
0969: throw new WMWorkflowException(e);
0970: }
0971: }
0972:
0973: /**
0974: * Retrieve a list of process instance states.
0975: *
0976: * @param processInstanceId The process instance id
0977: * @param filter A Filter specification.
0978: * @param countFlag True to return count value
0979: * @return An array of matching process instance states.
0980: */
0981: public WMProcessInstanceState[] listProcessInstanceStates(
0982: String processInstanceId, WMFilter filter, boolean countFlag)
0983: throws WMWorkflowException {
0984:
0985: setEngine();
0986: if (_logger.isDebugEnabled()) {
0987: _logger.debug("listProcessInstanceStates("
0988: + processInstanceId + ", " + filter + ", "
0989: + countFlag + ')');
0990: }
0991:
0992: if (filter != null) {
0993: throw new WMUnsupportedOperationException(
0994: "Filters are not supported");
0995: }
0996:
0997: ProcessInstance processInstance = findProcessInstance(processInstanceId);
0998: WMProcessInstanceState state = WMProcessInstanceState
0999: .valueOf(processInstance.getState());
1000: WMProcessInstanceState[] states = (WMProcessInstanceState[]) state
1001: .getStates();
1002: if (countFlag)
1003: Arrays.fill(states, null);
1004: return states;
1005: }
1006:
1007: /**
1008: * Change the process instance state.
1009: *
1010: * @param processInstanceId The process instance id
1011: * @param newState The new process instance state
1012: * @throws WMWorkflowException Workflow client exception
1013: */
1014: public void changeProcessInstanceState(String processInstanceId,
1015: WMProcessInstanceState newState) throws WMWorkflowException {
1016:
1017: setEngine();
1018: if (_logger.isDebugEnabled()) {
1019: _logger.debug("changeProcessInstanceState("
1020: + processInstanceId + ", " + newState + ')');
1021: }
1022:
1023: changeProcessInstanceState(
1024: findProcessInstance(processInstanceId), newState, true,
1025: false, false);
1026: }
1027:
1028: /**
1029: * Change the process instance state for all of the process instances
1030: * spawned from the given process definition.
1031: *
1032: * @param processDefinitionId The process definition ID
1033: * @param filter The filter
1034: * @param newState The new state
1035: * @throws WMWorkflowException
1036: */
1037: public void changeProcessInstancesState(String processDefinitionId,
1038: WMFilter filter, WMProcessInstanceState newState)
1039: throws WMWorkflowException {
1040:
1041: setEngine();
1042: if (_logger.isDebugEnabled()) {
1043: _logger.debug("changeProcessInstancesState("
1044: + processDefinitionId + ", " + filter + ", "
1045: + newState + ')');
1046: }
1047:
1048: try {
1049: // Find the process instance.
1050: InstanceRepository instanceRepository = _svcMgr
1051: .getInstanceRepository();
1052: ProcessInstance[] processes = instanceRepository
1053: .findProcessInstances(processDefinitionId, filter,
1054: false);
1055:
1056: // Update the process instances.
1057: for (int i = 0; i < processes.length; i++) {
1058: changeProcessInstanceState(processes[i], newState,
1059: false, false, false);
1060: }
1061: } catch (RepositoryException e) {
1062: throw new WMWorkflowException(e);
1063: }
1064: }
1065:
1066: private void changeProcessInstanceState(
1067: ProcessInstance processInstance,
1068: WMProcessInstanceState newState,
1069: boolean throwProcessException,
1070: boolean throwActivityException,
1071: boolean throwWorkItemException) throws WMWorkflowException {
1072:
1073: // If there's no state change, return immediately.
1074: if (newState.value() == processInstance.getState())
1075: return;
1076:
1077: // If the process instance has finished, remove all event watches.
1078: if (newState.isClosed()) {
1079: try {
1080: _svcMgr
1081: .getApplicationEventBroker()
1082: .unsubscribe(
1083: new String[] {
1084: processInstance
1085: .getProcessDefinitionId(),
1086: processInstance
1087: .getProcessInstanceId() },
1088: false);
1089: } catch (RepositoryException e) {
1090: throw new WMWorkflowException(e);
1091: }
1092: }
1093:
1094: // Find the required definition.
1095: WorkflowProcess workflow = findWorkflow(processInstance);
1096:
1097: // If starting or completing the process, there's more to do.
1098: if (newState == WMProcessInstanceState.OPEN_RUNNING
1099: && processInstance.getStartedDate() == null
1100: || newState == WMProcessInstanceState.CLOSED_COMPLETED) {
1101:
1102: try {
1103: // Continue or complete workflow enactment.
1104: EngineContext ctx = EngineContext.pushContext(this ,
1105: workflow, processInstance);
1106: WorkflowRunner runner = new WorkflowRunner(_svcMgr, ctx);
1107: switch (newState.value()) {
1108: case WMProcessInstanceState.OPEN_RUNNING_INT:
1109: runner.startProcess();
1110: break;
1111: case WMProcessInstanceState.CLOSED_COMPLETED_INT:
1112: runner.completeProcess();
1113: break;
1114: }
1115: } finally {
1116: EngineContext.popContext();
1117: }
1118: } else {
1119: // Obtain the resulting states for activities and work items.
1120: int processState = newState.value();
1121: int activityState = WorkflowEngineUtilities
1122: .processStateToActivityState(processState);
1123: int workItemState = WorkflowEngineUtilities
1124: .activityStateToWorkItemState(activityState);
1125:
1126: // Update the process instance (also notifies process instance
1127: // listeners).
1128: cascadeProcessInstanceState(workflow, processInstance,
1129: processState, throwProcessException, activityState,
1130: throwActivityException, workItemState,
1131: throwWorkItemException, false);
1132: }
1133: }
1134:
1135: /**
1136: * Permanently deletes the specified process instance.
1137: *
1138: * @param processInstanceId The ID of the process instance to delete.
1139: * @throws WMWorkflowException
1140: */
1141: public void deleteProcessInstance(String processInstanceId)
1142: throws WMWorkflowException {
1143:
1144: setEngine();
1145: if (_logger.isDebugEnabled()) {
1146: _logger.debug("deleteProcessInstance(" + processInstanceId
1147: + ')');
1148: }
1149:
1150: try {
1151: // Notify process instance listeners. N.B. Must do this before
1152: // deleting it, in case a listener calls a process instance method.
1153: // If we'd already deleted the instance, they'd get a
1154: // NoSuchEntityException from the EJBInstanceRepository.
1155: ProcessInstance processInstance = findProcessInstance(processInstanceId);
1156: _svcMgr.getWorkflowEventBroker()
1157: .fireProcessInstanceDeleted(processInstance, null,
1158: processInstance.getState());
1159:
1160: // Remove all event watches.
1161: _svcMgr.getApplicationEventBroker().unsubscribe(
1162: new String[] {
1163: processInstance.getProcessDefinitionId(),
1164: processInstanceId }, false);
1165:
1166: // Delete the instance. The repository handles the cascade delete.
1167: _svcMgr.getInstanceRepository().deleteProcessInstance(
1168: processInstanceId);
1169: } catch (ObjectNotFoundException e) {
1170: throw new WMInvalidProcessInstanceException(
1171: processInstanceId);
1172: } catch (RepositoryException e) {
1173: throw new WMWorkflowException(e);
1174: }
1175: }
1176:
1177: /**
1178: * Permanently deletes the specified process instance.
1179: *
1180: * @param processDefinitionId The ID of the process definition for which
1181: * instances are to be deleted.
1182: * @param filter Process instance filter.
1183: * @throws WMWorkflowException
1184: */
1185: public void deleteProcessInstances(String processDefinitionId,
1186: WMFilter filter) throws WMWorkflowException {
1187:
1188: setEngine();
1189: if (_logger.isDebugEnabled()) {
1190: _logger.debug("deleteProcessInstances("
1191: + processDefinitionId + ", " + filter + ')');
1192: }
1193:
1194: try {
1195: // Find the process instance.
1196: InstanceRepository instanceRepository = _svcMgr
1197: .getInstanceRepository();
1198: ProcessInstance[] processes = instanceRepository
1199: .findProcessInstances(processDefinitionId, filter,
1200: false);
1201:
1202: // Delete the process instances.
1203: WorkflowEventBroker broker = _svcMgr
1204: .getWorkflowEventBroker();
1205: for (int i = 0; i < processes.length; i++) {
1206: ProcessInstance process = processes[i];
1207: String processInstanceId = processes[i]
1208: .getProcessInstanceId();
1209:
1210: // Notify process instance listeners. N.B. Must do this before
1211: // deleting it, in case a listener calls a process instance
1212: // method. If we'd already deleted the instance, they'd get a
1213: // NoSuchEntityException from the EJBInstanceRepository.
1214: broker.fireProcessInstanceDeleted(process, null,
1215: process.getState());
1216:
1217: // Remove all event watches.
1218: _svcMgr.getApplicationEventBroker().unsubscribe(
1219: new String[] { processDefinitionId,
1220: processInstanceId }, false);
1221:
1222: // Delete the instance - repository handles the cascade.
1223: instanceRepository
1224: .deleteProcessInstance(processInstanceId);
1225: }
1226: } catch (RepositoryException e) {
1227: throw new WMWorkflowException(e);
1228: }
1229: }
1230:
1231: /**
1232: * Set the specified process instance attribute value.
1233: *
1234: * @param processInstanceId The process instance id
1235: * @param attributeName The attribute name
1236: * @param attributeValue The attribute value
1237: * @throws WMWorkflowException Workflow client exception
1238: */
1239: public void assignProcessInstanceAttribute(
1240: String processInstanceId, String attributeName,
1241: Object attributeValue) throws WMWorkflowException {
1242:
1243: setEngine();
1244: if (_logger.isDebugEnabled()) {
1245: _logger.debug("assignProcessInstanceAttribute("
1246: + processInstanceId + ", " + attributeName + ", "
1247: + attributeValue + ')');
1248: }
1249:
1250: try {
1251: // Find the attribute.
1252: InstanceRepository instanceRepository = _svcMgr
1253: .getInstanceRepository();
1254: AttributeInstance attributeInstance = instanceRepository
1255: .findProcessInstanceAttribute(processInstanceId,
1256: attributeName);
1257:
1258: // Update the attribute.
1259: Object previousValue = attributeInstance.getValue();
1260: attributeInstance.setValue(Type.DEFAULT_TYPE,
1261: attributeValue);
1262:
1263: // Notify attribute instance listeners.
1264: _svcMgr.getWorkflowEventBroker()
1265: .fireAttributeInstanceUpdated(attributeInstance,
1266: null, previousValue);
1267: } catch (ObjectNotFoundException e) {
1268: String id = e.getMessage();
1269: if (id != null && id.equals(processInstanceId))
1270: throw new WMInvalidProcessInstanceException(
1271: processInstanceId);
1272: else
1273: throw new WMInvalidAttributeException(attributeName);
1274: } catch (AttributeReadOnlyException e) {
1275: throw new WMWorkflowException(e);
1276: } catch (RepositoryException e) {
1277: throw new WMWorkflowException(e);
1278: }
1279: }
1280:
1281: /**
1282: * Set the process instance attribute value for the process instances
1283: * spawned from the specified process definition which match the given
1284: * filter.
1285: *
1286: * @param processDefinitionId The process definition id
1287: * @param filter The filter
1288: * @param attributeName The attribute name
1289: * @param attributeValue The attribute value
1290: * @throws WMWorkflowException Workflow client exception
1291: */
1292: public void assignProcessInstancesAttribute(
1293: String processDefinitionId, WMFilter filter,
1294: String attributeName, Object attributeValue)
1295: throws WMWorkflowException {
1296:
1297: setEngine();
1298: if (_logger.isDebugEnabled()) {
1299: _logger.debug("assignProcessInstancesAttribute("
1300: + processDefinitionId + ", " + filter + ", "
1301: + attributeName + ", " + attributeValue + ')');
1302: }
1303:
1304: try {
1305: // Find the attribute instances.
1306: InstanceRepository instanceRepository = _svcMgr
1307: .getInstanceRepository();
1308: ProcessInstance[] processInstances = instanceRepository
1309: .findProcessInstances(processDefinitionId, filter,
1310: false);
1311:
1312: // Update the attribute instances.
1313: WorkflowEventBroker broker = _svcMgr
1314: .getWorkflowEventBroker();
1315: for (int i = 0; i < processInstances.length; i++) {
1316: ProcessInstance processInstance = processInstances[i];
1317: AttributeInstance attr = processInstance
1318: .getAttributeInstance(attributeName);
1319:
1320: // Update the attribute.
1321: Object previousValue = attr.getValue();
1322: attr.setValue(Type.DEFAULT_TYPE, attributeValue);
1323:
1324: // Notify attribute instance listeners.
1325: broker.fireAttributeInstanceUpdated(attr, null,
1326: previousValue);
1327: }
1328: } catch (AttributeReadOnlyException e) {
1329: throw new WMWorkflowException(e);
1330: } catch (RepositoryException e) {
1331: throw new WMWorkflowException(e);
1332: }
1333: }
1334:
1335: /**
1336: * Get the specified process attribute value.
1337: *
1338: * @param processInstanceId The process instance id
1339: * @param attributeName The attribute name
1340: * @return The attribute
1341: * @throws WMWorkflowException Workflow client exception
1342: */
1343: public WMAttribute getProcessInstanceAttributeValue(
1344: String processInstanceId, String attributeName)
1345: throws WMWorkflowException {
1346:
1347: setEngine();
1348: if (_logger.isDebugEnabled()) {
1349: _logger.debug("getProcessInstanceAttributeValue("
1350: + processInstanceId + ", " + attributeName + ')');
1351: }
1352:
1353: try {
1354: InstanceRepository instanceRepository = _svcMgr
1355: .getInstanceRepository();
1356: AttributeInstance attributeInstance = instanceRepository
1357: .findProcessInstanceAttribute(processInstanceId,
1358: attributeName);
1359: return WAPIHelper.fromAttributeInstance(attributeInstance);
1360: } catch (ObjectNotFoundException e) {
1361: String id = e.getMessage();
1362: if (id != null && id.equals(processInstanceId))
1363: throw new WMInvalidProcessInstanceException(
1364: processInstanceId);
1365: else
1366: throw new WMInvalidAttributeException(attributeName);
1367: } catch (RepositoryException e) {
1368: throw new WMWorkflowException(e);
1369: }
1370: }
1371:
1372: /**
1373: * Retrieve a list of process instance attributes.
1374: *
1375: * @param processInstanceId The process instance id
1376: * @param filter The filter or null
1377: * @param countFlag True to return count value
1378: * @return The query handle
1379: * @throws WMWorkflowException Workflow client exception
1380: */
1381: public WMAttribute[] listProcessInstanceAttributes(
1382: String processInstanceId, WMFilter filter, boolean countFlag)
1383: throws WMWorkflowException {
1384:
1385: setEngine();
1386: if (_logger.isDebugEnabled()) {
1387: _logger.debug("listProcessInstanceAttributes("
1388: + processInstanceId + ", " + filter + ", "
1389: + countFlag + ')');
1390: }
1391:
1392: try {
1393: InstanceRepository instanceRepository = _svcMgr
1394: .getInstanceRepository();
1395: AttributeInstance[] attributeInstances = instanceRepository
1396: .findProcessInstanceAttributes(null,
1397: processInstanceId, filter, null, countFlag);
1398: return WAPIHelper
1399: .fromAttributeInstances(attributeInstances);
1400: } catch (ObjectNotFoundException e) {
1401: throw new WMInvalidProcessInstanceException(
1402: processInstanceId);
1403: } catch (RepositoryException e) {
1404: throw new WMWorkflowException(e);
1405: }
1406: }
1407:
1408: // ActivityInstance API
1409:
1410: /**
1411: * Get the specified activity instance.
1412: *
1413: * @param processInstanceId The process instance id
1414: * @param activityInstanceId The activity instance id
1415: * @return The activity instance
1416: * @throws WMWorkflowException Workflow client exception
1417: */
1418: public WMActivityInstance getActivityInstance(
1419: String processInstanceId, String activityInstanceId)
1420: throws WMWorkflowException {
1421:
1422: setEngine();
1423: if (_logger.isDebugEnabled()) {
1424: _logger.debug("getActivityInstance(" + processInstanceId
1425: + ", " + activityInstanceId + ')');
1426: }
1427:
1428: return WAPIHelper
1429: .fromActivityInstance(findActivityInstance(activityInstanceId));
1430: }
1431:
1432: /**
1433: * Retrieve a list of activity instance states.
1434: *
1435: * @param filter A Filter specification.
1436: * @return An array of matching activity instance attributes.
1437: */
1438: public WMActivityInstance[] listActivityInstances(WMFilter filter,
1439: boolean countFlag) throws WMWorkflowException {
1440:
1441: setEngine();
1442: if (_logger.isDebugEnabled()) {
1443: _logger.debug("listActivityInstances(" + filter + ", "
1444: + countFlag + ')');
1445: }
1446:
1447: try {
1448: InstanceRepository instanceRepository = _svcMgr
1449: .getInstanceRepository();
1450: ActivityInstance[] activityInstances = instanceRepository
1451: .findActivityInstances(null, null, filter,
1452: countFlag);
1453: return WAPIHelper.fromActivityInstances(activityInstances);
1454: } catch (RepositoryException e) {
1455: throw new WMWorkflowException(e);
1456: }
1457: }
1458:
1459: public void changeActivityInstanceState(String processInstanceId,
1460: String activityInstanceId, WMActivityInstanceState newState)
1461: throws WMWorkflowException {
1462:
1463: changeActivityInstanceState(processInstanceId,
1464: activityInstanceId, newState, false);
1465: }
1466:
1467: /**
1468: * Change the activity instance state.
1469: *
1470: * @param processInstanceId The process instance id
1471: * @param activityInstanceId The activity instance id
1472: * @param newState The new activity instance state
1473: * @throws WMWorkflowException Workflow client exception
1474: */
1475: public void changeActivityInstanceState(String processInstanceId,
1476: String activityInstanceId,
1477: WMActivityInstanceState newState, boolean allowAutoStart)
1478: throws WMWorkflowException {
1479:
1480: setEngine();
1481: if (_logger.isDebugEnabled()) {
1482: _logger.debug("changeActivityInstanceState("
1483: + processInstanceId + ", " + activityInstanceId
1484: + ", " + newState + ')');
1485: }
1486:
1487: changeActivityInstanceState(
1488: findActivityInstance(activityInstanceId), newState,
1489: allowAutoStart, true, false);
1490: }
1491:
1492: /**
1493: * Change the activity instance states for the activities spawned
1494: * from the given process definition which match the filter.
1495: *
1496: * @param processDefinitionId The process definition ID
1497: * @param activityDefinitionId The activity definition ID
1498: * @param filter The filter
1499: * @param newState The new state
1500: * @throws WMWorkflowException
1501: */
1502: public void changeActivityInstancesState(
1503: String processDefinitionId, String activityDefinitionId,
1504: WMFilter filter, WMActivityInstanceState newState)
1505: throws WMWorkflowException {
1506:
1507: setEngine();
1508: if (_logger.isDebugEnabled()) {
1509: _logger.debug("changeActivityInstancesState("
1510: + processDefinitionId + ", " + activityDefinitionId
1511: + ", " + filter + ", " + newState + ')');
1512: }
1513:
1514: try {
1515: // Find the activity instances.
1516: InstanceRepository instanceRepository = _svcMgr
1517: .getInstanceRepository();
1518: ActivityInstance[] activities = instanceRepository
1519: .findActivityInstances(processDefinitionId,
1520: activityDefinitionId, filter, false);
1521:
1522: // Update the activity instances.
1523: for (int i = 0; i < activities.length; i++) {
1524: changeActivityInstanceState(activities[i], newState,
1525: false, false, false);
1526: }
1527: } catch (RepositoryException e) {
1528: throw new WMWorkflowException(e);
1529: }
1530: }
1531:
1532: private void changeActivityInstanceState(
1533: ActivityInstance activityInstance,
1534: WMActivityInstanceState newState, boolean allowAutoStart,
1535: boolean throwActivityException,
1536: boolean throwWorkItemException) throws WMWorkflowException {
1537:
1538: // If there's no state change, return immediately.
1539: if (newState.value() == activityInstance.getState())
1540: return;
1541:
1542: // Cannot change the state of an activity whose process is suspended.
1543: if (activityInstance.getProcessInstance().getState() == WMProcessInstanceState.OPEN_NOTRUNNING_SUSPENDED_INT) {
1544:
1545: if (throwActivityException) {
1546: throw new WMTransitionNotAllowedException(
1547: WMActivityInstanceState.valueOf(
1548: activityInstance.getState()).toString(),
1549: newState.toString(),
1550: "Cannot change the state of activity '"
1551: + activityInstance
1552: .getActivityInstanceId()
1553: + "', because its process instance is suspended");
1554: }
1555: return;
1556: }
1557:
1558: EngineContext ctx = null;
1559: try {
1560: // Find the required definitions, and the process instance.
1561: ProcessRepository processRepository = _svcMgr
1562: .getProcessRepository();
1563: WorkflowProcess workflow = processRepository
1564: .findWorkflowProcess(activityInstance
1565: .getProcessDefinitionId());
1566: Activity activity = WorkflowUtilities.findActivity(
1567: workflow, activityInstance
1568: .getActivityDefinitionId());
1569:
1570: // If the activity was started or completed, there's more to do.
1571: // We must distinguish between starting an activity for the first
1572: // time versus resuming a suspended activity. In the latter case
1573: // the started date is non-null.
1574: if (newState.isOpen()
1575: && activityInstance.getStartedDate() == null
1576: || newState.isClosed()) {
1577:
1578: // Can't manually transition to open.running or closed.completed
1579: // if the join hasn't fired yet.
1580: JoinInstance join = activityInstance.getJoin();
1581: if (join != null && !join.hasFired()) {
1582: if (throwActivityException) {
1583: throw new WMTransitionNotAllowedException(
1584: WMActivityInstanceState.valueOf(
1585: activityInstance.getState())
1586: .stringValue(),
1587: newState.stringValue(),
1588: "Cannot change the state of activity '"
1589: + activityInstance
1590: .getActivityInstanceId()
1591: + "', because its join has not yet fired");
1592: }
1593: return;
1594: }
1595:
1596: ProcessInstance processInstance = activityInstance
1597: .getProcessInstance();
1598:
1599: // Auto-start activities cannot be manually started, and
1600: // auto-finish activities cannot be manually completed.
1601: if (newState == WMActivityInstanceState.OPEN_RUNNING
1602: && activity.getStartMode() != AutomationMode.MANUAL
1603: && activityInstance.getState() == WMActivityInstanceState.OPEN_NOTRUNNING_INT
1604: && !allowAutoStart
1605: || newState == WMActivityInstanceState.CLOSED_COMPLETED
1606: && activity.getFinishMode() != AutomationMode.MANUAL) {
1607:
1608: if (throwActivityException) {
1609: throw new WMTransitionNotAllowedException(
1610: WMActivityInstanceState.valueOf(
1611: activityInstance.getState())
1612: .stringValue(),
1613: newState.stringValue(),
1614: "Cannot manually change the state of activity '"
1615: + activityInstance
1616: .getActivityInstanceId()
1617: + "', because it is defined as automatic");
1618: } else {
1619: return;
1620: }
1621: }
1622:
1623: // Continue enacting the workflow.
1624: ctx = EngineContext.pushContext(this , workflow,
1625: processInstance, activity, activityInstance,
1626: null, null);
1627: WorkflowRunner runner = new WorkflowRunner(_svcMgr, ctx);
1628: switch (newState.value()) {
1629: case WMActivityInstanceState.OPEN_RUNNING_INT:
1630: // Execute the activity if it is now open.running.
1631: runner.startActivity(activity, activityInstance,
1632: true);
1633: break;
1634: case WMActivityInstanceState.CLOSED_COMPLETED_INT:
1635: // Fire the activity's efferent transitions if it is
1636: // now closed.complete.
1637: runner.completeActivity(activity, activityInstance,
1638: true);
1639: break;
1640: }
1641: } else {
1642: // Otherwise, just cascade the activity instance state (also
1643: // notifies activity instance listeners).
1644: int activityState = newState.value();
1645: int workItemState = WorkflowEngineUtilities
1646: .activityStateToWorkItemState(activityState);
1647: cascadeActivityInstanceState(workflow, activity,
1648: activityInstance, activityState,
1649: throwActivityException, workItemState,
1650: throwWorkItemException, false);
1651: }
1652: } catch (ObjectNotFoundException e) {
1653: throw new WMInvalidProcessDefinitionException(
1654: activityInstance.getProcessDefinitionId());
1655: } catch (RepositoryException e) {
1656: throw new WMWorkflowException(e);
1657: } finally {
1658: if (ctx != null)
1659: EngineContext.popContext();
1660: }
1661: }
1662:
1663: /**
1664: * Retrieve a list of activity instance states.
1665: *
1666: * @param processInstanceId The ID of the process instance.
1667: * @param activityInstanceId The ID of the activity instance.
1668: * @param filter A Filter specification.
1669: * @return An array of matching activity instance attributes.
1670: */
1671: public WMActivityInstanceState[] listActivityInstanceStates(
1672: String processInstanceId, String activityInstanceId,
1673: WMFilter filter, boolean countFlag)
1674: throws WMWorkflowException {
1675:
1676: setEngine();
1677: if (_logger.isDebugEnabled()) {
1678: _logger.debug("listActivityInstanceStates("
1679: + processInstanceId + ", " + activityInstanceId
1680: + ", " + filter + ", " + countFlag + ')');
1681: }
1682:
1683: if (filter != null) {
1684: throw new WMUnsupportedOperationException(
1685: "Filters are not supported");
1686: }
1687:
1688: ActivityInstance activityInstance = findActivityInstance(activityInstanceId);
1689: WMActivityInstanceState state = WMActivityInstanceState
1690: .valueOf(activityInstance.getState());
1691: WMActivityInstanceState[] states = (WMActivityInstanceState[]) state
1692: .getStates();
1693: if (countFlag)
1694: Arrays.fill(states, null);
1695: return states;
1696: }
1697:
1698: /**
1699: * Set the specified activity attribute value.
1700: *
1701: * @param processInstanceId The process instance id
1702: * @param activityInstanceId The activity instance id
1703: * @param attributeName The attribute name
1704: * @param attributeValue The attribute value
1705: * @throws WMWorkflowException Workflow client exception
1706: */
1707: public void assignActivityInstanceAttribute(
1708: String processInstanceId, String activityInstanceId,
1709: String attributeName, Object attributeValue)
1710: throws WMWorkflowException {
1711:
1712: setEngine();
1713: if (_logger.isDebugEnabled()) {
1714: _logger.debug("assignActivityInstanceAttribute("
1715: + processInstanceId + ", " + activityInstanceId
1716: + ", " + attributeName + ", " + attributeValue
1717: + ", " + ')');
1718: }
1719:
1720: try {
1721: // Find the attribute instance.
1722: InstanceRepository instanceRepository = _svcMgr
1723: .getInstanceRepository();
1724: AttributeInstance attributeInstance = instanceRepository
1725: .findActivityInstanceAttribute(processInstanceId,
1726: activityInstanceId, attributeName);
1727:
1728: // Update the attribute instance.
1729: Object previousValue = attributeInstance.getValue();
1730: attributeInstance.setValue(Type.DEFAULT_TYPE,
1731: attributeValue);
1732:
1733: // Notify attribute instance listeners.
1734: _svcMgr.getWorkflowEventBroker()
1735: .fireAttributeInstanceUpdated(attributeInstance,
1736: null, previousValue);
1737: } catch (ObjectNotFoundException e) {
1738: throw new WMInvalidActivityInstanceException(
1739: activityInstanceId);
1740: } catch (AttributeReadOnlyException e) {
1741: throw new WMWorkflowException(e);
1742: } catch (RepositoryException e) {
1743: throw new WMWorkflowException(e);
1744: }
1745: }
1746:
1747: /**
1748: * Set the specified activity attribute value for the activity instances
1749: * which were spawned from the specified process and activity definition
1750: * and which match the filter.
1751: *
1752: * @param processDefinitionId The process definition id
1753: * @param activityDefinitionId The activity definition id
1754: * @param filter The filter
1755: * @param attributeName The attribute name
1756: * @param attributeValue The attribute value
1757: * @throws WMWorkflowException Workflow client exception
1758: */
1759: public void assignActivityInstancesAttribute(
1760: String processDefinitionId, String activityDefinitionId,
1761: WMFilter filter, String attributeName, Object attributeValue)
1762: throws WMWorkflowException {
1763:
1764: setEngine();
1765: if (_logger.isDebugEnabled()) {
1766: _logger.debug("assignActivityInstancesAttribute("
1767: + processDefinitionId + ", " + activityDefinitionId
1768: + ", " + filter + ", " + attributeName + ", "
1769: + attributeValue + ')');
1770: }
1771:
1772: try {
1773: InstanceRepository instanceRepository = _svcMgr
1774: .getInstanceRepository();
1775: WorkflowEventBroker broker = _svcMgr
1776: .getWorkflowEventBroker();
1777:
1778: // If there's no filter and we're updating instances of one
1779: // particular activity, we can go direct to the activity instances.
1780: if (processDefinitionId != null
1781: && activityDefinitionId != null && filter == null) {
1782:
1783: // Find the activity instances.
1784: ActivityInstance[] activities = instanceRepository
1785: .findActivityInstances(processDefinitionId,
1786: activityDefinitionId, null, false);
1787:
1788: // Update the attribute instances.
1789: for (int i = 0; i < activities.length; i++) {
1790: // Find and update the attribute instance.
1791: ActivityInstance activity = activities[i];
1792: AttributeInstance attr = activity
1793: .getAttributeInstance(attributeName);
1794:
1795: // Update the attribute.
1796: Object previousValue = attr.getValue();
1797: attr.setValue(Type.DEFAULT_TYPE, attributeValue);
1798:
1799: // Notify attribute instance listeners.
1800: broker.fireAttributeInstanceUpdated(attr, null,
1801: previousValue);
1802: }
1803: } else {
1804: // Find the activity instances.
1805: AttributeInstance[] attrs = instanceRepository
1806: .findActivityInstanceAttributes(
1807: processDefinitionId, null,
1808: activityDefinitionId, null, filter,
1809: attributeName, false);
1810:
1811: // Update the attribute instances.
1812: for (int i = 0; i < attrs.length; i++) {
1813: // Update the attribute.
1814: AttributeInstance attr = attrs[i];
1815: Object previousValue = attr.getValue();
1816: attr.setValue(Type.DEFAULT_TYPE, attributeValue);
1817:
1818: // Notify attribute instance listeners.
1819: broker.fireAttributeInstanceUpdated(attr, null,
1820: previousValue);
1821: }
1822: }
1823: } catch (ObjectNotFoundException e) {
1824: throw new WMInvalidAttributeException(attributeName);
1825: } catch (AttributeReadOnlyException e) {
1826: throw new WMWorkflowException(e);
1827: } catch (RepositoryException e) {
1828: throw new WMWorkflowException(e);
1829: }
1830: }
1831:
1832: /**
1833: * Get the specified activity instance attribute value.
1834: *
1835: * @param processInstanceId The process instance id.
1836: * @param activityInstanceId The activity instance id.
1837: * @param attributeName The attribute name.
1838: * @return The attribute.
1839: * @throws WMWorkflowException Workflow client exception
1840: */
1841: public WMAttribute getActivityInstanceAttributeValue(
1842: String processInstanceId, String activityInstanceId,
1843: String attributeName) throws WMWorkflowException {
1844:
1845: setEngine();
1846: if (_logger.isDebugEnabled()) {
1847: _logger.debug("getActivityInstanceAttributeValue("
1848: + processInstanceId + ", " + activityInstanceId
1849: + ", " + attributeName + ')');
1850: }
1851:
1852: try {
1853: ActivityInstance activityInstance = findActivityInstance(activityInstanceId);
1854: AttributeInstance attributeInstance = activityInstance
1855: .getAttributeInstance(attributeName);
1856: return WAPIHelper.fromAttributeInstance(attributeInstance);
1857: } catch (ObjectNotFoundException e) {
1858: throw new WMInvalidAttributeException(attributeName);
1859: } catch (RepositoryException e) {
1860: throw new WMWorkflowException(e);
1861: }
1862: }
1863:
1864: /**
1865: * Retrieve a list of activity instance attributes.
1866: *
1867: * @param processInstanceId The ID of the process instance.
1868: * @param activityInstanceId The ID of the activity instance.
1869: * @param filter A filter specification.
1870: * @return An array of matching activity instance attributes.
1871: */
1872: public WMAttribute[] listActivityInstanceAttributes(
1873: String processInstanceId, String activityInstanceId,
1874: WMFilter filter, boolean countFlag)
1875: throws WMWorkflowException {
1876:
1877: setEngine();
1878: if (_logger.isDebugEnabled()) {
1879: _logger.debug("listActivityInstanceAttributes("
1880: + processInstanceId + ", " + activityInstanceId
1881: + ", " + filter + ", " + countFlag + ')');
1882: }
1883:
1884: try {
1885: InstanceRepository instanceRepository = _svcMgr
1886: .getInstanceRepository();
1887: AttributeInstance[] attributeInstances = instanceRepository
1888: .findActivityInstanceAttributes(null,
1889: processInstanceId, null,
1890: activityInstanceId, filter, null, countFlag);
1891: return WAPIHelper
1892: .fromAttributeInstances(attributeInstances);
1893: } catch (ObjectNotFoundException e) {
1894: throw new WMInvalidActivityInstanceException(
1895: activityInstanceId);
1896: } catch (RepositoryException e) {
1897: throw new WMWorkflowException(e);
1898: }
1899: }
1900:
1901: // WorkItem API
1902:
1903: public void refreshWorkItems(String processInstanceId)
1904: throws WMWorkflowException {
1905:
1906: setEngine();
1907: if (_logger.isDebugEnabled()) {
1908: _logger
1909: .debug("refreshWorkItems(" + processInstanceId
1910: + ')');
1911: }
1912:
1913: ProcessInstance processInstance = findProcessInstance(processInstanceId);
1914: WorkflowProcess workflow = findWorkflow(processInstance);
1915: for (Iterator iter = processInstance.getActivityInstances()
1916: .iterator(); iter.hasNext();) {
1917:
1918: ActivityInstance activityInstance = (ActivityInstance) iter
1919: .next();
1920: WMActivityInstanceState state = WMActivityInstanceState
1921: .valueOf(activityInstance.getState());
1922: if (state.isOpen()) {
1923: Activity activity = WorkflowUtilities.findActivity(
1924: workflow, activityInstance
1925: .getActivityDefinitionId());
1926: findCreateWorkItems(workflow, activity,
1927: processInstance, activityInstance, true);
1928:
1929: // Ensure that all open work items are in a state
1930: // corresponding to that of their activity. A manual start
1931: // activity requires its participants to start their
1932: // work items manually. A suspended activity's work items
1933: // should also be in the suspended state.
1934: if (activity.getStartMode() != AutomationMode.MANUAL
1935: || state == WMActivityInstanceState.OPEN_SUSPENDED) {
1936:
1937: int workItemState = WorkflowEngineUtilities
1938: .activityStateToWorkItemState(state.value());
1939: for (Iterator iter2 = activityInstance
1940: .getWorkItems().iterator(); iter2.hasNext();) {
1941:
1942: cascadeWorkItemState(activity, (WorkItem) iter2
1943: .next(), workItemState, false, false);
1944: }
1945: }
1946: }
1947: }
1948: }
1949:
1950: void findCreateWorkItems(WorkflowProcess workflow,
1951: Activity activity, ProcessInstance processInstance,
1952: ActivityInstance activityInstance, boolean refresh)
1953: throws WMWorkflowException {
1954:
1955: // If the activity has no performer or there is one performer of type
1956: // SYSTEM, no work items are required.
1957: String performerStr = activity.getPerformer();
1958: if (performerStr == null)
1959: return;
1960: Participant[] performerDefs = WorkflowUtilities
1961: .findParticipants(workflow, performerStr);
1962: if (performerDefs.length == 1
1963: && performerDefs[0].getParticipantType() == ParticipantType.SYSTEM) {
1964:
1965: return;
1966: }
1967:
1968: if (_logger.isDebugEnabled()) {
1969: _logger.debug("Creating/updating work items for activity '"
1970: + activity.getId() + "' instance "
1971: + activityInstance.getActivityInstanceId() + '\'');
1972: }
1973:
1974: // TODO: Understand multiple activity instantiation.
1975: // Determine whether this activity supports multiple instantiation.
1976: /*
1977: SimulationInformation si = activity.getSimulationInformation();
1978: boolean multiInstance = si != null &&
1979: si.getInstantiationType() == InstantiationType.MULTIPLE;
1980: */
1981:
1982: Implementation impl = activity.getImplementation();
1983: boolean isToolSet = impl instanceof ToolSet;
1984: boolean sequential = activity.getToolMode() != ToolMode.PARALLEL;
1985:
1986: // If no tools are associated with this tool-based activity, no
1987: // work items are required.
1988: if (isToolSet && ((ToolSet) impl).getTool().length == 0)
1989: return;
1990:
1991: // Find the work item assignment strategy.
1992: AssignmentStrategyDef strategyDef = WorkflowEngineUtilities
1993: .findAssignmentStrategy(activity);
1994: AssignmentStrategy strategy;
1995: try {
1996: strategy = _svcMgr.getAssignmentStrategyFactory()
1997: .findStrategy(strategyDef.getId());
1998: } catch (RepositoryException e) {
1999: throw new WMWorkflowException(e);
2000: }
2001:
2002: // Get the current set of work items.
2003: Collection workItems = activityInstance.getWorkItems();
2004:
2005: Set participantSet = new TreeSet(); // (For lexical userID ordering)
2006: Principal[] participants;
2007:
2008: // Resolve the performers into actual participants, keeping track of
2009: // the resolved participant sets so that we can terminate unwanted work
2010: // items afterwards.
2011: Map resolvedPerformers = new HashMap();
2012: SecurityRealm realm = _svcMgr.getSecurityRealm();
2013: for (int i = 0; i < performerDefs.length; i++) {
2014: Participant performerDef = performerDefs[i];
2015: String performerId = performerDef.getId();
2016: String mappedPerformerId = WorkflowEngineUtilities
2017: .mapPerformerId(performerDef);
2018:
2019: switch (performerDef.getParticipantType().value()) {
2020: // These are concrete participant types, and work items
2021: // are assigned to them directly.
2022: case ParticipantType.HUMAN_INT:
2023: // Check that mappedPerformerId is a valid user name.
2024: try {
2025: participants = new Principal[] { realm
2026: .findPrincipal(mappedPerformerId) };
2027: } catch (ObjectNotFoundException e) {
2028: throw new WMInvalidTargetUserException(performerId);
2029: } catch (RepositoryException e) {
2030: throw new WMWorkflowException(e);
2031: }
2032: break;
2033: case ParticipantType.ORGANIZATIONAL_UNIT_INT:
2034: // Assume that any mapping has already been done in XPDL.
2035: participants = new Principal[] { new BasicPrincipal(
2036: mappedPerformerId) };
2037: break;
2038: case ParticipantType.RESOURCE_INT:
2039: case ParticipantType.RESOURCE_SET_INT:
2040: case ParticipantType.ROLE_INT:
2041: // These participant types are abstract actors that must
2042: // be resolved by the SecurityRealm at runtime to concrete
2043: // humans / programs / machines etc.
2044: try {
2045: participants = realm.resolveParticipants(
2046: mappedPerformerId, processInstance);
2047: } catch (RepositoryException e) {
2048: Throwable cause = e.getCause();
2049: if (cause instanceof WMInvalidTargetUserException)
2050: throw (WMInvalidTargetUserException) cause;
2051: throw new WMWorkflowException(e);
2052: }
2053: break;
2054: default:
2055: participants = EMPTY_PRINCIPALS;
2056: break;
2057: }
2058:
2059: // Apply the work item assignment strategy.
2060: participants = strategy.apply(participants, strategyDef
2061: .getExpandGroups(), EngineContext.peekContext());
2062: resolvedPerformers.put(performerId, participants);
2063:
2064: // Make sure there's a work item for each participant.
2065: for (int j = 0, n = participants.length; j < n; j++) {
2066: Principal principal = participants[j];
2067: String participantId = principal.getName();
2068:
2069: // Aggregate all participants into a single set, with which we
2070: // will update the activity instance.
2071: participantSet.add(participantId);
2072:
2073: WorkItem workItem = null;
2074:
2075: // If re-using or refreshing work items, check whether one
2076: // already exists for this performer/participant combo.
2077: if (ServerConfig.reuseWorkItems() || refresh) {
2078: for (Iterator iter = workItems.iterator(); iter
2079: .hasNext();) {
2080: WorkItem wi = (WorkItem) iter.next();
2081:
2082: // If an activity has more than one role-type
2083: // performer, a user could belong to multiple roles
2084: // within the activity. In this case they will get
2085: // one work item per role.
2086: if (wi.getPerformer().equals(performerId)
2087: && wi.getParticipant().equals(
2088: participantId)) {
2089:
2090: workItem = wi;
2091: break;
2092: }
2093: }
2094: }
2095:
2096: // If necessary, create the work item now.
2097: if (workItem == null) {
2098: try {
2099: InstanceRepository instanceRepository = _svcMgr
2100: .getInstanceRepository();
2101: workItem = instanceRepository
2102: .createWorkItem(
2103: activityInstance
2104: .getProcessDefinitionId(),
2105: activityInstance
2106: .getProcessInstanceId(),
2107: activityInstance
2108: .getActivityInstanceId(),
2109: isToolSet && sequential ? 0
2110: : -1,
2111: WMWorkItemState.OPEN_NOTRUNNING_INT,
2112: performerId, participantId);
2113: } catch (RepositoryException e) {
2114: throw new WMWorkflowException(e);
2115: }
2116: workItem.setName(activity.getName());
2117: workItem
2118: .setPriority(activityInstance.getPriority());
2119:
2120: // Notify work item listeners.
2121: _svcMgr.getWorkflowEventBroker()
2122: .fireWorkItemCreated(workItem, activity);
2123: } else if (!refresh) {
2124: // Set work item state, notify listeners. (Refresh does not
2125: // force the state transition).
2126: workItem.setToolIndex(isToolSet ? 0 : -1);
2127: cascadeWorkItemState(activity, workItem,
2128: WMWorkItemState.OPEN_NOTRUNNING_INT, false,
2129: true);
2130: }
2131: }
2132: }
2133: String[] participantIds = (String[]) participantSet
2134: .toArray(new String[participantSet.size()]);
2135: activityInstance.setParticipants(participantIds);
2136:
2137: // Terminate unwanted work items.
2138: if (refresh) {
2139: // Refresh only: we now have the complete, updated set of
2140: // participants associated with each performer. Any open work items
2141: // that do not correspond to the new participant set must be terminated.
2142: int openWorkItemCount = 0;
2143: for (Iterator iter = workItems.iterator(); iter.hasNext();) {
2144: WorkItem workItem = (WorkItem) iter.next();
2145: WMWorkItemState wiState = WMWorkItemState
2146: .valueOf(workItem.getState());
2147: if (wiState.isOpen()) {
2148: boolean keep = false;
2149: String participant = workItem.getParticipant();
2150: participants = (Principal[]) resolvedPerformers
2151: .get(workItem.getPerformer());
2152: if (participants != null) {
2153: for (int j = 0, n = participants.length; j < n; j++) {
2154: if (participant.equals(participants[j]
2155: .getName())) {
2156: keep = true;
2157: break;
2158: }
2159: }
2160: }
2161: if (keep) {
2162: openWorkItemCount++;
2163: } else {
2164: cascadeWorkItemState(activity, workItem,
2165: WMWorkItemState.CLOSED_TERMINATED_INT,
2166: true, false);
2167: }
2168: }
2169: }
2170:
2171: // If there are no open work items left, an auto-finish activity
2172: // must be marked complete.
2173: if (openWorkItemCount == 0) {
2174: handleWorkItemClosed(workflow, activity,
2175: processInstance, activityInstance, null);
2176: }
2177: }
2178: return;
2179: }
2180:
2181: /**
2182: * Reassign the specified work item to another user.
2183: *
2184: * @param sourceUser The current user
2185: * @param targetUser The new user
2186: * @param processInstanceId The process instance id
2187: * @param workItemId The work item id
2188: * @throws WMWorkflowException Workflow client exception
2189: */
2190: public void reassignWorkItem(String sourceUser, String targetUser,
2191: String processInstanceId, String workItemId)
2192: throws WMWorkflowException {
2193:
2194: setEngine();
2195: if (_logger.isDebugEnabled()) {
2196: _logger.debug("reassignWorkItem(" + sourceUser + ", "
2197: + targetUser + ", " + processInstanceId + ", "
2198: + workItemId + ", " + ')');
2199: }
2200:
2201: try {
2202: // Find the work item.
2203: SecurityRealm realm = _svcMgr.getSecurityRealm();
2204: WorkItem workItem = findWorkItem(processInstanceId,
2205: workItemId);
2206:
2207: String currentUser = workItem.getParticipant();
2208:
2209: // Validate the source user.
2210: if (!currentUser.equals(sourceUser) || sourceUser == null)
2211: throw new WMInvalidSourceUserException(sourceUser);
2212:
2213: // Reassign the work item (if necessary).
2214: if (!currentUser.equals(targetUser)) {
2215: // Target user must be specified.
2216: if (targetUser == null)
2217: throw new WMInvalidSourceUserException(targetUser);
2218:
2219: // Validate the target user.
2220: try {
2221: realm.findPrincipal(targetUser);
2222: } catch (ObjectNotFoundException e) {
2223: throw new WMInvalidTargetUserException(targetUser);
2224: }
2225:
2226: workItem.setParticipant(targetUser);
2227:
2228: // Notify work item listeners.
2229: _svcMgr.getWorkflowEventBroker().fireWorkItemAssigned(
2230: workItem, null);
2231: }
2232: } catch (RepositoryException e) {
2233: throw new WMWorkflowException(e);
2234: }
2235: }
2236:
2237: /**
2238: * Complete the specified work item.
2239: *
2240: * @param processInstanceId The process instance id
2241: * @param workItemId The work item id
2242: * @throws WMWorkflowException Workflow client exception
2243: */
2244: public void completeWorkItem(String processInstanceId,
2245: String workItemId) throws WMWorkflowException {
2246:
2247: setEngine();
2248: if (_logger.isDebugEnabled()) {
2249: _logger.debug("completeWorkItem(" + processInstanceId
2250: + ", " + workItemId + ')');
2251: }
2252:
2253: changeWorkItemState(processInstanceId, workItemId,
2254: WMWorkItemState.CLOSED_COMPLETED);
2255: }
2256:
2257: // Called when a work item is closed, or when no open work items remain
2258: // following a refresh.
2259: private void handleWorkItemClosed(WorkflowProcess workflow,
2260: Activity activity, ProcessInstance processInstance,
2261: ActivityInstance activityInstance, WorkItem workItem)
2262: throws WMWorkflowException {
2263:
2264: try {
2265: EngineContext ctx = EngineContext.pushContext(this ,
2266: workflow, processInstance, activity,
2267: activityInstance, workItem, null);
2268:
2269: // If an auto-finish activity, apply the completion strategy.
2270: WMActivityInstanceState activityState;
2271: if (activity.getFinishMode() == AutomationMode.MANUAL) {
2272: activityState = WMActivityInstanceState
2273: .valueOf(activityInstance.getState());
2274: } else {
2275: // Find and apply the activity completion strategy.
2276: CompletionStrategy strategy = WorkflowEngineUtilities
2277: .findCompletionStrategy(activity, _svcMgr);
2278: activityState = strategy.apply(ctx);
2279: }
2280:
2281: // Complete the activity if all the work has been done.
2282: if (activityState.isClosed()) {
2283: WorkflowRunner runner = new WorkflowRunner(_svcMgr, ctx);
2284: runner.completeActivity(activity, activityInstance,
2285: true);
2286: }
2287: } finally {
2288: EngineContext.popContext();
2289: }
2290: }
2291:
2292: /**
2293: * Get the specified work item.
2294: *
2295: * @param processInstanceId The process instance id
2296: * @param workItemId The work item id
2297: * @return The work item
2298: * @throws WMWorkflowException Workflow client exception
2299: */
2300: public WMWorkItem getWorkItem(String processInstanceId,
2301: String workItemId) throws WMWorkflowException {
2302:
2303: setEngine();
2304: if (_logger.isDebugEnabled()) {
2305: _logger.debug("getWorkItem(" + processInstanceId + ", "
2306: + workItemId + ')');
2307: }
2308:
2309: return WAPIHelper.fromWorkItem(findWorkItem(processInstanceId,
2310: workItemId));
2311: }
2312:
2313: /**
2314: * Set the specified work item attribute value.
2315: *
2316: * @param processInstanceId The process instance id
2317: * @param workItemId The work item id
2318: * @param attributeName The attribute name
2319: * @param attributeValue The attribute value
2320: * @throws WMWorkflowException Workflow client exception
2321: */
2322: public void assignWorkItemAttribute(String processInstanceId,
2323: String workItemId, String attributeName,
2324: Object attributeValue) throws WMWorkflowException {
2325:
2326: setEngine();
2327: if (_logger.isDebugEnabled()) {
2328: _logger.debug("assignWorkItemAttribute("
2329: + processInstanceId + ", " + workItemId + ", "
2330: + attributeName + ", " + attributeValue + ')');
2331: }
2332:
2333: try {
2334: // Find the work item attribute.
2335: WorkItem workItem = findWorkItem(processInstanceId,
2336: workItemId);
2337: AttributeInstance attr = workItem
2338: .getAttributeInstance(attributeName);
2339:
2340: // Update the attribute instance value.
2341: Object previousValue = attr.getValue();
2342: attr.setValue(Type.DEFAULT_TYPE, attributeValue);
2343:
2344: // Notify attribute instance listeners.
2345: _svcMgr.getWorkflowEventBroker()
2346: .fireAttributeInstanceUpdated(attr, null,
2347: previousValue);
2348: } catch (AttributeReadOnlyException e) {
2349: throw new WMWorkflowException(e);
2350: } catch (RepositoryException e) {
2351: throw new WMWorkflowException(e);
2352: }
2353: }
2354:
2355: /**
2356: * Get the specified work item attribute value.
2357: *
2358: * @param processInstanceId The process instance id
2359: * @param workItemId The work item id
2360: * @param attributeName The attribute name
2361: * @return The attribute
2362: * @throws WMWorkflowException Workflow client exception
2363: */
2364: public WMAttribute getWorkItemAttributeValue(
2365: String processInstanceId, String workItemId,
2366: String attributeName) throws WMWorkflowException {
2367:
2368: setEngine();
2369: if (_logger.isDebugEnabled()) {
2370: _logger.debug("getWorkItemAttributeValue("
2371: + processInstanceId + ", " + workItemId + ", "
2372: + attributeName + ')');
2373: }
2374:
2375: try {
2376: WorkItem workItem = findWorkItem(processInstanceId,
2377: workItemId);
2378: AttributeInstance attr = workItem
2379: .getAttributeInstance(attributeName);
2380: return WAPIHelper.fromAttributeInstance(attr);
2381: } catch (ObjectNotFoundException e) {
2382: throw new WMInvalidAttributeException(attributeName);
2383: } catch (RepositoryException e) {
2384: throw new WMWorkflowException(e);
2385: }
2386: }
2387:
2388: /**
2389: * Retrieve a list of work item attributes.
2390: *
2391: * @param filter A Filter specification.
2392: * @return An array of matching work item attributes.
2393: */
2394: public WMAttribute[] listWorkItemAttributes(
2395: String processInstanceId, String workItemId,
2396: WMFilter filter, boolean countFlag)
2397: throws WMWorkflowException {
2398:
2399: setEngine();
2400: if (_logger.isDebugEnabled()) {
2401: _logger.debug("listWorkItemAttributes(" + processInstanceId
2402: + ", " + workItemId + ", " + filter + ", "
2403: + countFlag + ')');
2404: }
2405:
2406: // TODO: Implement listWorkItemAttributes() filtering.
2407: // InstanceRepository provides a means to filter, but the results
2408: // do not include the system attributes.
2409: if (filter != null) {
2410: throw new WMUnsupportedOperationException(
2411: "Filters are not supported");
2412: }
2413:
2414: try {
2415: WorkItem workItem = findWorkItem(processInstanceId,
2416: workItemId);
2417: Collection coll = workItem.getAttributeInstances().values();
2418: AttributeInstance[] attrs = (AttributeInstance[]) coll
2419: .toArray(new AttributeInstance[coll.size()]);
2420: return WAPIHelper.fromAttributeInstances(attrs);
2421: } catch (RepositoryException e) {
2422: throw new WMWorkflowException(e);
2423: }
2424: }
2425:
2426: /**
2427: * Retrieve a list of work items.
2428: *
2429: * @param filter A Filter specification.
2430: * @return An array of matching work items.
2431: */
2432: public WMWorkItem[] listWorkItems(WMFilter filter, boolean countFlag)
2433: throws WMWorkflowException {
2434:
2435: setEngine();
2436: if (_logger.isDebugEnabled()) {
2437: _logger.debug("listWorkItems(" + filter + ", " + countFlag
2438: + ')');
2439: }
2440:
2441: try {
2442: InstanceRepository instanceRepository = _svcMgr
2443: .getInstanceRepository();
2444: WorkItem[] workList = instanceRepository.findWorkItems(
2445: filter, countFlag);
2446: return WAPIHelper.fromWorkItems(workList);
2447: } catch (RepositoryException e) {
2448: throw new WMWorkflowException(e);
2449: }
2450: }
2451:
2452: public void changeWorkItemState(String processInstanceId,
2453: String workItemId, WMWorkItemState newState)
2454: throws WMWorkflowException {
2455:
2456: setEngine();
2457: // Find the process instance first, to avoid deadlocks.
2458: ProcessInstance processInstance = findProcessInstance(processInstanceId);
2459: // Find the work item.
2460: WorkItem workItem = findWorkItem(processInstanceId, workItemId);
2461:
2462: // If there's no state change, there's nothing to do.
2463: WMWorkItemState oldState = WMWorkItemState.valueOf(workItem
2464: .getState());
2465: if (newState == oldState)
2466: return;
2467:
2468: // Cannot change the state of a work item whose activity is suspended.
2469: if (workItem.getActivityInstance().getState() == WMActivityInstanceState.OPEN_SUSPENDED_INT) {
2470:
2471: throw new WMTransitionNotAllowedException(
2472: oldState.toString(),
2473: newState.toString(),
2474: "Cannot change the state of work item '"
2475: + workItemId
2476: + "', because its activity instance is suspended");
2477: }
2478:
2479: // Find the workflow and activity definitions.
2480: WorkflowProcess workflow = findWorkflow(processInstance);
2481: String activityDefinitionId = workItem
2482: .getActivityDefinitionId();
2483: Activity activity = WorkflowUtilities.findActivity(workflow,
2484: activityDefinitionId);
2485:
2486: changeWorkItemState(workflow, activity, processInstance,
2487: workItem, newState);
2488: }
2489:
2490: private void changeWorkItemState(WorkflowProcess workflow,
2491: Activity activity, ProcessInstance processInstance,
2492: WorkItem workItem, WMWorkItemState newState)
2493: throws WMWorkflowException {
2494:
2495: // If there's no state change, there's nothing to do.
2496: if (newState.value() == workItem.getState())
2497: return;
2498:
2499: if (_logger.isDebugEnabled()) {
2500: _logger.debug("changeWorkItemState("
2501: + processInstance.getProcessInstanceId() + ", "
2502: + workItem.getWorkItemId() + ", " + newState + ')');
2503: }
2504:
2505: // If this is a tool-based activity and the tool invocation mode is
2506: // SEQUENTIAL, we must not allow the work item to be marked completed
2507: // until all its tools have been invoked.
2508: Implementation impl = activity.getImplementation();
2509: if (newState == WMWorkItemState.CLOSED_COMPLETED
2510: && impl instanceof ToolSet
2511: && activity.getToolMode() != ToolMode.PARALLEL) {
2512:
2513: int toolIndex = workItem.getToolIndex();
2514: if (toolIndex < ((ToolSet) impl).getTool().length - 1) {
2515: workItem.setToolIndex(++toolIndex);
2516: newState = WMWorkItemState.OPEN_SUSPENDED;
2517: }
2518: }
2519:
2520: // Update work item state (also notifies work item listeners).
2521: cascadeWorkItemState(activity, workItem, newState.value(),
2522: true, false);
2523:
2524: // If we're starting or closing a work item, there's more to do.
2525: ActivityInstance activityInstance = workItem
2526: .getActivityInstance();
2527: switch (newState.value()) {
2528: case WMWorkItemState.OPEN_RUNNING_INT:
2529: // Check the start mode for the work item's activity.
2530: // If the activity is manual-start, starting the first work
2531: // item also has the effect of starting the activity.
2532: if (activity.getStartMode() == AutomationMode.MANUAL) {
2533: if (activityInstance.getState() == WMActivityInstanceState.OPEN_NOTRUNNING_INT) {
2534:
2535: try {
2536: EngineContext ctx = EngineContext.pushContext(
2537: this , workflow, processInstance,
2538: activity, activityInstance, workItem,
2539: null);
2540: WorkflowRunner runner = new WorkflowRunner(
2541: _svcMgr, ctx);
2542: runner.startActivity(activity,
2543: activityInstance, true);
2544: } finally {
2545: EngineContext.popContext();
2546: }
2547: return;
2548: }
2549: }
2550: break;
2551: case WMWorkItemState.CLOSED_ABORTED_INT:
2552: case WMWorkItemState.CLOSED_COMPLETED_INT:
2553: case WMWorkItemState.CLOSED_TERMINATED_INT:
2554: // Check activity finish mode; closing the last open work
2555: // item for an auto-finish activity completes the activity.
2556: handleWorkItemClosed(workflow, activity, processInstance,
2557: activityInstance, workItem);
2558: break;
2559: }
2560: }
2561:
2562: public WMWorkItemState[] listWorkItemStates(
2563: String processInstanceId, String workItemId,
2564: WMFilter filter, boolean countFlag)
2565: throws WMWorkflowException {
2566:
2567: setEngine();
2568: if (_logger.isDebugEnabled()) {
2569: _logger.debug("listWorkItemStates(" + processInstanceId
2570: + ", " + workItemId + ", " + filter + ", "
2571: + countFlag + ')');
2572: }
2573:
2574: if (filter != null) {
2575: throw new WMUnsupportedOperationException(
2576: "Filters are not supported");
2577: }
2578:
2579: WorkItem workItem = findWorkItem(processInstanceId, workItemId);
2580: WMWorkItemState state = WMWorkItemState.valueOf(workItem
2581: .getState());
2582: WMWorkItemState[] states = (WMWorkItemState[]) state
2583: .getStates();
2584: if (countFlag)
2585: Arrays.fill(states, null);
2586: return states;
2587: }
2588:
2589: public WMAAuditEntry[] listAuditEntries(WMFilter filter)
2590: throws WMWorkflowException {
2591:
2592: setEngine();
2593: if (_logger.isDebugEnabled())
2594: _logger.debug("listAuditEntries(" + filter + ')');
2595:
2596: try {
2597: return _svcMgr.getInstanceRepository().findAuditEntries(
2598: filter);
2599: } catch (RepositoryException e) {
2600: throw new WMWorkflowException(e);
2601: }
2602: }
2603:
2604: public int deleteAuditEntries(WMFilter filter)
2605: throws WMWorkflowException {
2606: setEngine();
2607: if (_logger.isDebugEnabled())
2608: _logger.debug("deleteAuditEntries(" + filter + ')');
2609:
2610: try {
2611: return _svcMgr.getInstanceRepository().deleteAuditEntries(
2612: filter);
2613: } catch (RepositoryException e) {
2614: throw new WMWorkflowException(e);
2615: }
2616: }
2617:
2618: public ToolInvocation[] executeWorkItem(String processInstanceId,
2619: String workItemId) throws WMWorkflowException {
2620:
2621: setEngine();
2622: if (_logger.isDebugEnabled()) {
2623: _logger.debug("executeWorkItem(" + processInstanceId + ", "
2624: + workItemId + ')');
2625: }
2626:
2627: try {
2628: WorkItem workItem = findWorkItem(processInstanceId,
2629: workItemId);
2630:
2631: // Can't invoke a tool from a work item that is closed.
2632: if (WMWorkItemState.valueOf(workItem.getState()).isClosed())
2633: throw new WMInvalidWorkItemException(workItemId);
2634:
2635: WorkflowProcess workflow = findWorkflow(workItem
2636: .getProcessDefinitionId());
2637: Activity activity = WorkflowUtilities.findActivity(
2638: workflow, workItem.getActivityDefinitionId());
2639: ActivityInstance activityInstance = workItem
2640: .getActivityInstance();
2641: ProcessInstance processInstance = activityInstance
2642: .getProcessInstance();
2643: EngineContext ctx = EngineContext.pushContext(this ,
2644: workflow, processInstance, activity,
2645: activityInstance, workItem, null);
2646:
2647: // Prepare the tool invocations.
2648: ToolInvocation[] invocations = WorkflowEngineUtilities
2649: .prepareToolInvocations(workItem, -1, ctx);
2650:
2651: // Invoke any procedures immediately.
2652: int procCount = 0;
2653: for (int i = 0; i < invocations.length; i++) {
2654: ToolInvocation ti = invocations[i];
2655: if (ti.toolType == ToolType.PROCEDURE) {
2656: invocations[i] = null;
2657: procCount++;
2658:
2659: try {
2660: // Update work item to reflect 'tool running' state.
2661: toolStarted(workflow, activity,
2662: processInstance, workItem);
2663:
2664: ti.invokeTool(null, true);
2665: } finally {
2666: // Update work item to reflect 'tool finished' state.
2667: toolFinished(workflow, activity,
2668: processInstance, workItem, ti
2669: .requestAppStatus(),
2670: ti.parameters);
2671: }
2672: }
2673: }
2674:
2675: // If any procedure invocations were present we must remove them
2676: // so that the client only receives application invocations.
2677: if (procCount > 0) {
2678: int newLength = invocations.length - procCount;
2679: if (newLength == 0) {
2680: invocations = EMPTY_TOOL_INVOCATIONS;
2681: } else {
2682: ToolInvocation[] oldInvocations = invocations;
2683: invocations = new ToolInvocation[newLength];
2684: int i = 0, j = 0, n = oldInvocations.length;
2685: while (i < n) {
2686: ToolInvocation invocation = oldInvocations[i];
2687: if (invocation != null)
2688: invocations[j++] = invocation;
2689: i++;
2690: }
2691: }
2692: }
2693:
2694: return invocations;
2695: } catch (RepositoryException e) {
2696: throw new WMWorkflowException(e);
2697: } finally {
2698: EngineContext.popContext();
2699: }
2700: }
2701:
2702: private void toolStarted(WorkflowProcess workflow,
2703: Activity activity, ProcessInstance processInstance,
2704: WorkItem workItem) throws WMWorkflowException {
2705:
2706: if (_logger.isDebugEnabled()) {
2707: _logger.debug("toolStarted("
2708: + processInstance.getProcessInstanceId() + ", "
2709: + workItem.getWorkItemId() + ')');
2710: }
2711:
2712: // Set the work item into the open.running state.
2713: changeWorkItemState(workflow, activity, processInstance,
2714: workItem, WMWorkItemState.OPEN_RUNNING);
2715: }
2716:
2717: private void toolFinished(WorkflowProcess workflow,
2718: Activity activity, ProcessInstance processInstance,
2719: WorkItem workItem, int appStatus, Parameter[] parms)
2720: throws WMWorkflowException {
2721:
2722: if (_logger.isDebugEnabled()) {
2723: _logger.debug("toolFinished("
2724: + processInstance.getProcessInstanceId() + ", "
2725: + workItem.getWorkItemId() + ", " + appStatus
2726: + ", "
2727: + (parms == null ? null : Arrays.asList(parms))
2728: + ')');
2729: }
2730:
2731: try {
2732: Implementation impl = activity.getImplementation();
2733: if (!(impl instanceof ToolSet)) {
2734: throw new IllegalArgumentException("Activity "
2735: + activity.getId()
2736: + " does not have a ToolSet implementation");
2737: }
2738:
2739: // For parallel mode toolsets, toolIndex holds the unity-based
2740: // count of tools which have finished. For sequential mode toolsets,
2741: // it holds the zero-based index of the tool with which the workitem
2742: // is currently associated. N.B. Multi-tool activities can result
2743: // in this method being called several times for the same workitem.
2744: int toolCount = ((ToolSet) impl).getTool().length;
2745: int toolIndex = workItem.getToolIndex();
2746:
2747: // If the tool finished normally...
2748: if (appStatus == ToolAgent.FINISHED) {
2749: // Use any results to update process instance attributes as per
2750: // INOUT & OUT parameters.
2751: if (parms != null) {
2752: WorkflowEngineUtilities.applyResults(parms,
2753: processInstance, true);
2754: }
2755:
2756: // Increment the tool index/count.
2757: if (toolIndex < toolCount)
2758: workItem.setToolIndex(++toolIndex);
2759: }
2760:
2761: // Compute new workitem state.
2762: WMWorkItemState state;
2763: boolean allToolsFinished = toolIndex == toolCount;
2764: if (appStatus == ToolAgent.FINISHED
2765: && allToolsFinished
2766: && activity.getFinishMode() != AutomationMode.MANUAL) {
2767:
2768: state = WMWorkItemState.CLOSED_COMPLETED;
2769: } else if (!allToolsFinished
2770: && activity.getToolMode() == ToolMode.PARALLEL) {
2771:
2772: state = WMWorkItemState.OPEN_RUNNING;
2773: } else {
2774: state = WMWorkItemState.OPEN_SUSPENDED;
2775: }
2776:
2777: // Update the work item state.
2778: changeWorkItemState(workflow, activity, processInstance,
2779: workItem, state);
2780: } catch (AttributeReadOnlyException e) {
2781: throw new WMWorkflowException(e);
2782: } catch (RepositoryException e) {
2783: throw new WMWorkflowException(e);
2784: }
2785: }
2786:
2787: public void toolStarted(String processInstanceId, String workItemId)
2788: throws WMWorkflowException {
2789:
2790: setEngine();
2791: ProcessInstance processInstance = findProcessInstance(processInstanceId);
2792: WorkflowProcess workflow = findWorkflow(processInstance);
2793: WorkItem workItem = findWorkItem(processInstanceId, workItemId);
2794: Activity activity = WorkflowUtilities.findActivity(workflow,
2795: workItem.getActivityDefinitionId());
2796:
2797: toolStarted(workflow, activity, processInstance, workItem);
2798: }
2799:
2800: public void toolFinished(String processInstanceId,
2801: String workItemId, int appStatus, Parameter[] parms)
2802: throws WMWorkflowException {
2803:
2804: setEngine();
2805: ProcessInstance processInstance = findProcessInstance(processInstanceId);
2806: WorkflowProcess workflow = findWorkflow(processInstance);
2807: WorkItem workItem = findWorkItem(processInstanceId, workItemId);
2808: Activity activity = WorkflowUtilities.findActivity(workflow,
2809: workItem.getActivityDefinitionId());
2810:
2811: toolFinished(workflow, activity, processInstance, workItem,
2812: appStatus, parms);
2813: }
2814:
2815: public void raiseEvent(ApplicationEvent event,
2816: String[] correlationKeys) throws WMWorkflowException {
2817:
2818: if (correlationKeys.length == 1)
2819: triggerProcess(correlationKeys[0], event);
2820: else {
2821: triggerTransition(event, correlationKeys[0],
2822: correlationKeys[1], correlationKeys[2],
2823: correlationKeys[3]);
2824: }
2825: }
2826:
2827: /**
2828: * Event-triggered process instantiation.
2829: *
2830: * @param processDefinitionId The ID the process to instantiate.
2831: * @param event The inbound application event.
2832: * @throws WMWorkflowException
2833: */
2834: private void triggerProcess(String processDefinitionId,
2835: ApplicationEvent event) throws WMWorkflowException {
2836:
2837: // Find the workflow.
2838: WorkflowProcess workflow = findWorkflow(processDefinitionId);
2839:
2840: // Instantiate the workflow.
2841: ProcessInstance processInstance = _createProcessInstance(
2842: workflow, null, null, true);
2843:
2844: // Extract key values from the event and apply them to the new instance.
2845: WorkflowEngineUtilities.applyResults(workflow.getTrigger()
2846: .getActualParameter(), processInstance, event, _svcMgr
2847: .getApplicationEventBroker());
2848:
2849: // Start the process instance.
2850: startProcess(processInstance);
2851: }
2852:
2853: /**
2854: * Event-triggered process continuation.
2855: *
2856: * @param event The inbound application event.
2857: * @param processDefinitionId The ID the process to instantiate.
2858: * @param processInstanceId The ID of the listening process instance.
2859: * @param activityInstanceId The ID of the listening activity instance.
2860: * @param transitionId The ID of the listening transition.
2861: * @throws WMWorkflowException
2862: */
2863: private void triggerTransition(ApplicationEvent event,
2864: String processDefinitionId, String processInstanceId,
2865: String activityInstanceId, String transitionId)
2866: throws WMWorkflowException {
2867:
2868: setEngine();
2869: try {
2870: if (_logger.isDebugEnabled()) {
2871: _logger.debug("raiseEvent(" + event + ", "
2872: + processInstanceId + ", " + activityInstanceId
2873: + ", " + transitionId + ')');
2874: }
2875:
2876: InstanceRepository instanceRepository = _svcMgr
2877: .getInstanceRepository();
2878:
2879: // Find the target process and activity instances.
2880: ProcessInstance processInstance;
2881: try {
2882: processInstance = instanceRepository
2883: .findProcessInstance(processInstanceId);
2884: } catch (ObjectNotFoundException e) {
2885: _logger.warn("Received application event: " + event
2886: + ", but process instance " + processInstanceId
2887: + " could not be found.");
2888: return;
2889: }
2890: ActivityInstance activityInstance;
2891: try {
2892: activityInstance = instanceRepository
2893: .findActivityInstance(activityInstanceId);
2894: } catch (ObjectNotFoundException e) {
2895: _logger.warn("Received application event: " + event
2896: + ", but activity instance "
2897: + activityInstanceId + " could not be found.");
2898: return;
2899: }
2900:
2901: // The activity instance cannot handle the event unless it is in the
2902: // open.running state.
2903: if (WMActivityInstanceState.valueOf(
2904: activityInstance.getState()).isClosed()) {
2905:
2906: _logger
2907: .warn("Received application event: "
2908: + event
2909: + " but activity instance "
2910: + activityInstanceId
2911: + " is in the closed state. The event will be discarded.");
2912: return;
2913: }
2914:
2915: // Find the workflow, activity and transition definitions.
2916: WorkflowProcess workflow = findWorkflow(processDefinitionId);
2917: Activity activity = WorkflowUtilities.findActivity(
2918: workflow, activityInstance
2919: .getActivityDefinitionId());
2920: Transition transition = (Transition) activity
2921: .getEfferentTransitions().get(transitionId);
2922: if (transition == null) {
2923: throw new WMInvalidProcessDefinitionException(
2924: "Could not find transition with Id '"
2925: + transitionId
2926: + "' in process instance "
2927: + processDefinitionId);
2928: }
2929:
2930: // Extract key values from the event and apply them to the instance.
2931: // N.B. deadline transitions do not pass actual parameters, so we
2932: // only do this for application event-triggered transitions.
2933: if (transition.getEvent() != null) {
2934: WorkflowEngineUtilities.applyResults(workflow
2935: .getTrigger().getActualParameter(),
2936: processInstance, event, _svcMgr
2937: .getApplicationEventBroker());
2938: }
2939:
2940: // Fire the event transition.
2941: EngineContext ctx = EngineContext.pushContext(this ,
2942: workflow, processInstance, activity,
2943: activityInstance, null, event);
2944: try {
2945: WorkflowRunner runner = new WorkflowRunner(_svcMgr, ctx);
2946: runner.executeTransition(transition, activityInstance,
2947: true);
2948: } catch (EvaluatorException e) {
2949: throw new WMWorkflowException(e);
2950: } finally {
2951: EngineContext.popContext();
2952: }
2953: } catch (RepositoryException e) {
2954: throw new WMWorkflowException(e);
2955: }
2956: }
2957:
2958: void cascadeProcessInstanceState(WorkflowProcess workflow,
2959: ProcessInstance processInstance, int processState,
2960: boolean throwProcessException, int activityState,
2961: boolean throwActivityException, int workItemState,
2962: boolean throwWorkItemException, boolean forceTransitions)
2963: throws WMWorkflowException {
2964:
2965: // Nothing to do if the state isn't actually changing.
2966: if (processState != processInstance.getState()
2967: && processState != WMObjectState.DEFAULT_INT) {
2968:
2969: // Ensure that this is a valid transition, throwing an exception
2970: // if required.
2971: WMProcessInstanceState oldState = WMProcessInstanceState
2972: .valueOf(processInstance.getState());
2973: int action = oldState.checkTransition(processState,
2974: throwProcessException && !forceTransitions);
2975: if (action == WMObjectState.ILLEGAL_ACTION
2976: && forceTransitions) {
2977:
2978: action = WMObjectState.FORCED_ACTION;
2979: }
2980:
2981: // If the transition is valid, apply the state change.
2982: if (action != WMObjectState.ILLEGAL_ACTION) {
2983: if (_logger.isDebugEnabled()) {
2984: _logger.debug("Setting process instance '"
2985: + processInstance.getProcessInstanceId()
2986: + "' state to "
2987: + WMProcessInstanceState
2988: .valueOf(processState));
2989: }
2990:
2991: // Look up the workflow if necessary.
2992: if (workflow == null) {
2993: ProcessRepository processRepository = _svcMgr
2994: .getProcessRepository();
2995: String processDefinitionId = processInstance
2996: .getProcessDefinitionId();
2997: try {
2998: workflow = processRepository
2999: .findWorkflowProcess(processDefinitionId);
3000: } catch (RepositoryException e) {
3001: throw new WMInvalidProcessDefinitionException(
3002: processDefinitionId);
3003: }
3004: }
3005:
3006: // Set the process instance state.
3007: int previousState = processInstance.getState();
3008: processInstance.setState(processState);
3009:
3010: // Notify process instance listeners. Cannot notify forced
3011: // actions, because there is no corresponding WfMC-defined life
3012: // cycle event for the transition.
3013: if (action != WMObjectState.FORCED_ACTION) {
3014: _svcMgr.getWorkflowEventBroker()
3015: .fireProcessInstanceEvent(processInstance,
3016: action, workflow, previousState);
3017: }
3018:
3019: // Set the state of activity instances, if this is changing.
3020: if (activityState != WMObjectState.DEFAULT_INT) {
3021: Collection activities = processInstance
3022: .getActivityInstances();
3023: for (Iterator iter = activities.iterator(); iter
3024: .hasNext();) {
3025: ActivityInstance activityInstance = (ActivityInstance) iter
3026: .next();
3027: cascadeActivityInstanceState(workflow, null,
3028: activityInstance, activityState,
3029: throwActivityException, workItemState,
3030: throwWorkItemException,
3031: forceTransitions);
3032: }
3033: }
3034: }
3035: }
3036: }
3037:
3038: // Cascades updates to activity instance state.
3039: // N.B. started, due, and completed dates are always set by WorkflowRunner.
3040: void cascadeActivityInstanceState(WorkflowProcess workflow,
3041: Activity activity, ActivityInstance activityInstance,
3042: int activityState, boolean throwActivityException,
3043: int workItemState, boolean throwWorkItemException,
3044: boolean forceTransitions) throws WMWorkflowException {
3045:
3046: // Nothing to do if the state isn't actually changing.
3047: if (activityState != activityInstance.getState()
3048: && activityState != WMObjectState.DEFAULT_INT) {
3049:
3050: // Ensure that this is a valid transition, throwing an exception
3051: // if required.
3052: WMActivityInstanceState oldState = WMActivityInstanceState
3053: .valueOf(activityInstance.getState());
3054: int action = oldState.checkTransition(activityState,
3055: throwActivityException && !forceTransitions);
3056: if (action == WMObjectState.ILLEGAL_ACTION
3057: && forceTransitions) {
3058: // Report that the activity has started, even if this is not a
3059: // valid state transition according to the WfMC state machine.
3060: // For example, a WAPI call is not permitted the CLOSED -> OPEN
3061: // transition, but the engine does this internally when looping.
3062: action = activityState == WMActivityInstanceState.OPEN_RUNNING_INT ? WMActivityInstanceState.START_ACTION
3063: : WMObjectState.FORCED_ACTION;
3064: }
3065:
3066: // If the transition is valid, apply the state change.
3067: if (action != WMObjectState.ILLEGAL_ACTION) {
3068: if (_logger.isDebugEnabled()) {
3069: _logger.debug("Setting activity instance '"
3070: + activityInstance.getActivityInstanceId()
3071: + "' state to "
3072: + WMActivityInstanceState
3073: .valueOf(activityState));
3074: }
3075:
3076: if (activity == null) {
3077: String activityDefinitionId = activityInstance
3078: .getActivityDefinitionId();
3079: activity = WorkflowUtilities.findActivity(workflow,
3080: activityDefinitionId);
3081: }
3082:
3083: // Set the activity instance state.
3084: int previousState = activityInstance.getState();
3085: activityInstance.setState(activityState);
3086:
3087: // Notify activity instance listeners. Cannot notify forced
3088: // actions, because there is no corresponding WfMC-defined life
3089: // cycle event for the transition.
3090: if (action != WMObjectState.FORCED_ACTION) {
3091: _svcMgr.getWorkflowEventBroker()
3092: .fireActivityInstanceEvent(
3093: activityInstance, action, activity,
3094: previousState);
3095: }
3096:
3097: // Set the state of work items, if this is changing.
3098: if (workItemState != WMObjectState.DEFAULT_INT) {
3099: Collection workItems = activityInstance
3100: .getWorkItems();
3101: for (Iterator iter = workItems.iterator(); iter
3102: .hasNext();) {
3103: cascadeWorkItemState(activity, (WorkItem) iter
3104: .next(), workItemState,
3105: throwWorkItemException,
3106: forceTransitions);
3107: }
3108: }
3109:
3110: // If this activity instance has any associated process
3111: // instances, we must cascade the activity state change to the
3112: // sub-process instances (for those subflow instances where this
3113: // would be a legal state transition).
3114: Implementation activityImpl = activity
3115: .getImplementation();
3116: if (activityImpl instanceof SubFlow) {
3117: SubFlow subFlow = (SubFlow) activityImpl;
3118: if (subFlow.getExecution() != ExecutionType.ASYNCHRONOUS) {
3119: WorkflowProcess subWorkflow = WorkflowUtilities
3120: .findWorkflowProcess(workflow
3121: .getPackage(), subFlow.getId());
3122: int processState = WorkflowEngineUtilities
3123: .activityStateToProcessState(activityState);
3124: for (Iterator iter = activityInstance
3125: .getChildProcessInstances().iterator(); iter
3126: .hasNext();) {
3127:
3128: ProcessInstance childProcInst = (ProcessInstance) iter
3129: .next();
3130: cascadeProcessInstanceState(subWorkflow,
3131: childProcInst, processState, false,
3132: activityState, false,
3133: workItemState, false, false);
3134: }
3135: }
3136: }
3137: }
3138: }
3139: }
3140:
3141: /* package */void recomputeDates(ProcessInstance procInst,
3142: ActivityInstance activityInstance) {
3143:
3144: // If the activity is open and its target date is before that of the
3145: // process instance, or if the process instance activity target date is
3146: // null, update the process instance. Otherwise, or if the activity is
3147: // closed, set the process instance target date to the earliest of the
3148: // open activities.
3149: WMActivityInstanceState state = WMActivityInstanceState
3150: .valueOf(activityInstance.getState());
3151: Date piaTargetDate = procInst.getActivityTargetDate();
3152: Date actTargetDate = activityInstance.getTargetDate();
3153: if (state.isOpen()
3154: && actTargetDate != null
3155: && (piaTargetDate == null || actTargetDate
3156: .before(piaTargetDate))) {
3157:
3158: procInst.setActivityTargetDate(actTargetDate);
3159: } else {
3160: Collection activities = procInst.getActivityInstances();
3161: actTargetDate = null;
3162: for (Iterator iter = activities.iterator(); iter.hasNext();) {
3163: ActivityInstance ai = (ActivityInstance) iter.next();
3164: state = WMActivityInstanceState.valueOf(ai.getState());
3165: if (state.isOpen()) {
3166: // Is this activity's target date is the earliest so far?
3167: Date aitDate = ai.getTargetDate();
3168: if (aitDate != null
3169: && (actTargetDate == null || aitDate
3170: .before(actTargetDate))) {
3171:
3172: actTargetDate = aitDate;
3173: }
3174: }
3175: }
3176: // Apply the earliest activity target date to the process instance.
3177: procInst.setActivityTargetDate(actTargetDate);
3178: }
3179:
3180: // If the activity is open and its due date is before that of the
3181: // process instance, or if the process instance activity due date is
3182: // null, update the process instance. Otherwise, or if the activity is
3183: // closed, set the process instance due date to the earliest of the
3184: // open activities.
3185: Date piaDueDate = procInst.getActivityDueDate();
3186: Date actDueDate = activityInstance.getDueDate();
3187: if (state.isOpen()
3188: && actDueDate != null
3189: && (piaDueDate == null || actDueDate.before(piaDueDate))) {
3190:
3191: procInst.setActivityDueDate(actDueDate);
3192: } else {
3193: Collection activities = procInst.getActivityInstances();
3194: actDueDate = null;
3195: for (Iterator iter = activities.iterator(); iter.hasNext();) {
3196: ActivityInstance ai = (ActivityInstance) iter.next();
3197: state = WMActivityInstanceState.valueOf(ai.getState());
3198: if (state.isOpen()) {
3199: // Is this activity's due date is the earliest so far?
3200: Date aitDate = ai.getDueDate();
3201: if (aitDate != null
3202: && (actDueDate == null || aitDate
3203: .before(actDueDate))) {
3204:
3205: actDueDate = aitDate;
3206: }
3207: }
3208: }
3209: // Apply the earliest activity due date to the process instance.
3210: procInst.setActivityDueDate(actDueDate);
3211: }
3212: }
3213:
3214: private void cascadeWorkItemState(Activity activity,
3215: WorkItem workItem, int workItemState,
3216: boolean throwWorkItemException, boolean forceTransitions)
3217: throws WMWorkflowException {
3218:
3219: // Nothing to do if the state isn't actually changing.
3220: if (workItemState != workItem.getState()
3221: && workItemState != WMObjectState.DEFAULT_INT) {
3222:
3223: // Ensure that this is a valid transition, throwing an exception
3224: // if required.
3225: WMWorkItemState oldState = WMWorkItemState.valueOf(workItem
3226: .getState());
3227: int action = oldState.checkTransition(workItemState,
3228: throwWorkItemException && !forceTransitions);
3229:
3230: // If we're not re-using work items, ignore an illegal state change
3231: // even if the caller's trying to force it. For example, when
3232: // restarting an assigned loopback activity there'll be a new work
3233: // item in the open.notRunning state; the old work items must remain
3234: // in the closed state.
3235: if (action == WMObjectState.ILLEGAL_ACTION
3236: && forceTransitions
3237: && ServerConfig.reuseWorkItems()) {
3238: // Report that the work item has started, even if this is not a
3239: // valid state transition according to the WfMC state machine.
3240: // For example, a WAPI call is not permitted the CLOSED -> OPEN
3241: // transition, but the engine does this internally when looping.
3242: action = workItemState == WMWorkItemState.OPEN_RUNNING_INT ? WMWorkItemState.START_ACTION
3243: : WMObjectState.FORCED_ACTION;
3244: }
3245:
3246: // If the transition is valid, apply the state change.
3247: if (action != WMObjectState.ILLEGAL_ACTION) {
3248: if (_logger.isDebugEnabled()) {
3249: _logger.debug("Setting work item '"
3250: + workItem.getWorkItemId() + "' state to "
3251: + WMWorkItemState.valueOf(workItemState));
3252: }
3253:
3254: // Update started/target/due/completed dates if starting/completing.
3255: switch (workItemState) {
3256: case WMWorkItemState.OPEN_NOTRUNNING_INT:
3257: workItem.setStartedDate(null);
3258: workItem.setTargetDate(null);
3259: workItem.setDueDate(null);
3260: workItem.setCompletedDate(null);
3261: break;
3262: case WMWorkItemState.OPEN_RUNNING_INT:
3263: if (oldState == WMWorkItemState.OPEN_NOTRUNNING)
3264: setWorkItemTrackingDates(activity, workItem);
3265: break;
3266: case WMWorkItemState.CLOSED_COMPLETED_INT:
3267: case WMWorkItemState.CLOSED_TERMINATED_INT:
3268: // Work items should have their completed date set even
3269: // when they're being terminated - marking a manual-
3270: // complete activity complete results in its work items
3271: // being terminated.
3272: workItem.setCompletedDate(new Date());
3273: break;
3274: }
3275:
3276: // Set the work item state.
3277: int previousState = workItem.getState();
3278: workItem.setState(workItemState);
3279:
3280: // Notify work item listeners. Cannot notify forced actions,
3281: // because there is no corresponding WfMC-defined life cycle
3282: // event for the transition.
3283: if (action != WMObjectState.FORCED_ACTION) {
3284: _svcMgr.getWorkflowEventBroker().fireWorkItemEvent(
3285: workItem, action, activity, previousState);
3286: }
3287: }
3288: }
3289: }
3290:
3291: // If activity is auto-start, set the work item start dates to that of the
3292: // activity. Otherwise, set it to the current time. Target & due dates are
3293: // calculated using the performer's calendar (if any) and the user's time
3294: // zone (if defined).
3295: private void setWorkItemTrackingDates(Activity activity,
3296: WorkItem workItem) throws WMWorkflowException {
3297:
3298: ActivityInstance activityInstance = workItem
3299: .getActivityInstance();
3300: Date startedDate = activity.getStartMode() == AutomationMode.MANUAL ? new Date()
3301: : activityInstance.getStartedDate();
3302: Date targetDate;
3303: Date dueDate;
3304: try {
3305: CalendarFactory calendarFactory = _svcMgr
3306: .getCalendarFactory();
3307: String performer = workItem.getPerformer();
3308: String participant = workItem.getParticipant();
3309: targetDate = WorkflowEngineUtilities.calculateTargetDate(
3310: activity, performer, participant, startedDate,
3311: calendarFactory);
3312: dueDate = WorkflowEngineUtilities.calculateDueDate(
3313: activity, performer, participant, startedDate,
3314: calendarFactory);
3315: } catch (RepositoryException e) {
3316: throw new WMWorkflowException(e);
3317: }
3318: workItem.setStartedDate(startedDate);
3319: workItem.setTargetDate(targetDate);
3320: workItem.setDueDate(dueDate);
3321: workItem.setCompletedDate(null);
3322:
3323: // If necessary, adjust the activity tracking dates if they're earlier
3324: // than the work item dates.
3325: if (targetDate != null) {
3326: Date activityTargetDate = activityInstance.getTargetDate();
3327: if (activityTargetDate == null
3328: || activityTargetDate.before(targetDate)) {
3329:
3330: activityInstance.setTargetDate(targetDate);
3331: }
3332: }
3333: if (dueDate != null) {
3334: Date activityDueDate = activityInstance.getDueDate();
3335: if (activityDueDate == null
3336: || activityDueDate.before(dueDate)) {
3337:
3338: activityInstance.setDueDate(dueDate);
3339: }
3340: }
3341: }
3342:
3343: private void createProcessAttributes(
3344: ProcessInstance processInstance, DataField[] dataFields)
3345: throws RepositoryException {
3346:
3347: InstanceRepository instanceRepository = _svcMgr
3348: .getInstanceRepository();
3349: WorkflowEventBroker broker = _svcMgr.getWorkflowEventBroker();
3350: DataConverter dataConverter = _svcMgr.getDataConverter();
3351: for (int i = 0, n = dataFields.length; i < n; i++) {
3352: DataField dataField = dataFields[i];
3353: Type type = dataField.getDataType().getType()
3354: .getImpliedType();
3355:
3356: // Coerce the data to the correct type.
3357: Object initialValue = dataConverter.convertValue(dataField
3358: .getInitialValue(), type.value());
3359:
3360: // Create the attribute instance.
3361: AttributeInstance attr = instanceRepository
3362: .createProcessInstanceAttribute(processInstance
3363: .getProcessInstanceId(), dataField.getId(),
3364: type.value(), initialValue);
3365:
3366: // Notify attribute instance listeners.
3367: broker.fireAttributeInstanceCreated(attr, dataField);
3368: }
3369: }
3370:
3371: private XPDLPackage findPackage(String packageId)
3372: throws WMWorkflowException {
3373:
3374: try {
3375: return _svcMgr.getProcessRepository()
3376: .findPackage(packageId);
3377: } catch (ObjectNotFoundException e) {
3378: throw new WMInvalidProcessDefinitionException(packageId);
3379: } catch (RepositoryException e) {
3380: throw new WMWorkflowException(e);
3381: }
3382: }
3383:
3384: private WorkflowProcess findWorkflow(String processDefinitionId)
3385: throws WMWorkflowException {
3386:
3387: try {
3388: return _svcMgr.getProcessRepository().findWorkflowProcess(
3389: processDefinitionId);
3390: } catch (ObjectNotFoundException e) {
3391: throw new WMInvalidProcessDefinitionException(
3392: processDefinitionId);
3393: } catch (RepositoryException e) {
3394: throw new WMWorkflowException(e);
3395: }
3396: }
3397:
3398: private WorkflowProcess findWorkflow(ProcessInstance processInstance)
3399: throws WMWorkflowException {
3400:
3401: return findWorkflow(processInstance.getProcessDefinitionId());
3402: }
3403:
3404: private ProcessInstance findProcessInstance(String processInstanceId)
3405: throws WMWorkflowException {
3406:
3407: try {
3408: return _svcMgr.getInstanceRepository().findProcessInstance(
3409: processInstanceId);
3410: } catch (ObjectNotFoundException e) {
3411: throw new WMInvalidProcessInstanceException(
3412: processInstanceId);
3413: } catch (RepositoryException e) {
3414: throw new WMWorkflowException(e);
3415: }
3416: }
3417:
3418: private ActivityInstance findActivityInstance(
3419: String activityInstanceId) throws WMWorkflowException {
3420:
3421: try {
3422: return _svcMgr.getInstanceRepository()
3423: .findActivityInstance(activityInstanceId);
3424: } catch (ObjectNotFoundException e) {
3425: throw new WMInvalidActivityInstanceException(
3426: activityInstanceId);
3427: } catch (RepositoryException e) {
3428: throw new WMWorkflowException(e);
3429: }
3430: }
3431:
3432: private WorkItem findWorkItem(String processInstanceId,
3433: String workItemId) throws WMWorkflowException {
3434:
3435: try {
3436: return _svcMgr.getInstanceRepository().findWorkItem(
3437: processInstanceId, workItemId);
3438: } catch (ObjectNotFoundException e) {
3439: throw new WMInvalidWorkItemException(workItemId);
3440: } catch (RepositoryException e) {
3441: throw new WMWorkflowException(e);
3442: }
3443: }
3444: }
|