Source Code Cross Referenced for WorkflowEngine.java in  » Workflow-Engines » obe-1.0 » org » obe » engine » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Workflow Engines » obe 1.0 » org.obe.engine 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.