001: /*
002: * Copyright 2005-2006 The Kuali Foundation.
003: *
004: *
005: * Licensed under the Educational Community License, Version 1.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.opensource.org/licenses/ecl1.php
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package edu.iu.uis.eden.engine.simulation;
018:
019: import java.io.ByteArrayInputStream;
020: import java.io.ByteArrayOutputStream;
021: import java.io.IOException;
022: import java.io.ObjectInputStream;
023: import java.io.ObjectOutputStream;
024: import java.io.Serializable;
025: import java.sql.Timestamp;
026: import java.util.ArrayList;
027: import java.util.Collections;
028: import java.util.HashSet;
029: import java.util.Iterator;
030: import java.util.List;
031: import java.util.Set;
032:
033: import org.apache.log4j.MDC;
034:
035: import edu.iu.uis.eden.EdenConstants;
036: import edu.iu.uis.eden.KEWServiceLocator;
037: import edu.iu.uis.eden.actionrequests.ActionRequestService;
038: import edu.iu.uis.eden.actionrequests.ActionRequestValue;
039: import edu.iu.uis.eden.actiontaken.ActionTakenValue;
040: import edu.iu.uis.eden.doctype.DocumentType;
041: import edu.iu.uis.eden.engine.ActivationContext;
042: import edu.iu.uis.eden.engine.EngineState;
043: import edu.iu.uis.eden.engine.ProcessContext;
044: import edu.iu.uis.eden.engine.RouteContext;
045: import edu.iu.uis.eden.engine.RouteHelper;
046: import edu.iu.uis.eden.engine.StandardWorkflowEngine;
047: import edu.iu.uis.eden.engine.node.NoOpNode;
048: import edu.iu.uis.eden.engine.node.NodeJotter;
049: import edu.iu.uis.eden.engine.node.Process;
050: import edu.iu.uis.eden.engine.node.RouteNode;
051: import edu.iu.uis.eden.engine.node.RouteNodeInstance;
052: import edu.iu.uis.eden.exception.DocumentSimulatedRouteException;
053: import edu.iu.uis.eden.exception.EdenUserNotFoundException;
054: import edu.iu.uis.eden.exception.InvalidActionTakenException;
055: import edu.iu.uis.eden.routeheader.DocumentRouteHeaderValue;
056: import edu.iu.uis.eden.user.Recipient;
057: import edu.iu.uis.eden.user.WorkflowUser;
058: import edu.iu.uis.eden.util.PerformanceLogger;
059: import edu.iu.uis.eden.util.Utilities;
060: import edu.iu.uis.eden.util.Utilities.RouteLogActionRequestSorter;
061: import edu.iu.uis.eden.workgroup.Workgroup;
062:
063: /**
064: * A WorkflowEngine implementation which runs simulations. This object is not thread-safe
065: * and therefore a new instance needs to be instantiated on every use.
066: *
067: * @author ewestfal
068: */
069: public class SimulationEngine extends StandardWorkflowEngine {
070:
071: private SimulationCriteria criteria;
072: private SimulationResults results = new SimulationResults();
073: private RouteHelper helper = new RouteHelper();
074:
075: public SimulationResults runSimulation(SimulationCriteria criteria)
076: throws Exception {
077: this .criteria = criteria;
078: validateCriteria(criteria);
079: process(criteria.getDocumentId(), null);
080: return results;
081: }
082:
083: public void process(Long documentId, Long nodeInstanceId)
084: throws InvalidActionTakenException,
085: EdenUserNotFoundException, DocumentSimulatedRouteException {
086: RouteContext context = RouteContext.createNewRouteContext();
087: try {
088: ActivationContext activationContext = new ActivationContext(
089: ActivationContext.CONTEXT_IS_SIMULATION);
090: activationContext.setActionsToPerform(!criteria
091: .getActionsToTake().isEmpty());
092: context.setActivationContext(activationContext);
093: context.setEngineState(new EngineState());
094: DocumentRouteHeaderValue document = createSimulationDocument(
095: documentId, criteria, context);
096: if ((criteria.isDocumentSimulation())
097: && ((document.isProcessed()) || (document.isFinal()))) {
098: results.setDocument(document);
099: return;
100: }
101: routeDocumentIfNecessary(document, criteria, context);
102: results.setDocument(document);
103: documentId = document.getRouteHeaderId();
104: MDC.put("docID", documentId);
105: PerformanceLogger perfLog = new PerformanceLogger(
106: documentId);
107: try {
108: LOG.info("Processing document for Simulation: "
109: + documentId);
110: List activeNodeInstances = getRouteNodeService()
111: .getActiveNodeInstances(document);
112: List nodeInstancesToProcess = determineNodeInstancesToProcess(
113: activeNodeInstances, criteria
114: .getDestinationNodeName());
115:
116: context.setDocument(document);
117: // TODO set document content
118: context.setEngineState(new EngineState());
119: ProcessContext processContext = new ProcessContext(
120: true, nodeInstancesToProcess);
121: while (!nodeInstancesToProcess.isEmpty()) {
122: RouteNodeInstance nodeInstance = (RouteNodeInstance) nodeInstancesToProcess
123: .remove(0);
124: NodeJotter.jotNodeInstance(context.getDocument(),
125: nodeInstance);
126: context.setNodeInstance(nodeInstance);
127: processContext = processNodeInstance(context,
128: helper);
129: if (!hasReachedCompletion(processContext, context
130: .getEngineState().getGeneratedRequests(),
131: nodeInstance, criteria)) {
132: if (processContext.isComplete()) {
133: if (!processContext.getNextNodeInstances()
134: .isEmpty()) {
135: nodeInstancesToProcess
136: .addAll(processContext
137: .getNextNodeInstances());
138: }
139: context
140: .getActivationContext()
141: .getSimulatedActionsTaken()
142: .addAll(
143: processPotentialActionsTaken(
144: context, document,
145: nodeInstance,
146: criteria));
147: }
148: } else {
149: context
150: .getActivationContext()
151: .getSimulatedActionsTaken()
152: .addAll(
153: processPotentialActionsTaken(
154: context, document,
155: nodeInstance, criteria));
156: }
157: }
158: List simulatedActionRequests = context.getEngineState()
159: .getGeneratedRequests();
160: Collections
161: .sort(
162: simulatedActionRequests,
163: new Utilities().new RouteLogActionRequestSorter());
164: results
165: .setSimulatedActionRequests(simulatedActionRequests);
166: results.setSimulatedActionsTaken(context
167: .getActivationContext()
168: .getSimulatedActionsTaken());
169: } catch (InvalidActionTakenException e) {
170: throw e;
171: } catch (EdenUserNotFoundException e) {
172: throw e;
173: } catch (Exception e) {
174: String errorMsg = "Error running simulation for document "
175: + ((criteria.isDocumentSimulation()) ? "id "
176: + documentId.toString() : "type "
177: + criteria.getDocumentTypeName());
178: LOG.error(errorMsg, e);
179: throw new DocumentSimulatedRouteException(errorMsg, e);
180: } finally {
181: perfLog.log("Time to run simulation.");
182: RouteContext.clearCurrentRouteContext();
183: MDC.remove("docID");
184: }
185: } finally {
186: RouteContext.releaseCurrentRouteContext();
187: }
188: }
189:
190: /**
191: * If there are multiple paths, we need to figure out which ones we need to follow for blanket approval.
192: * This method will throw an exception if a node with the given name could not be located in the routing path.
193: * This method is written in such a way that it should be impossible for there to be an infinate loop, even if
194: * there is extensive looping in the node graph.
195: */
196: private List determineNodeInstancesToProcess(
197: List activeNodeInstances, String nodeName)
198: throws InvalidActionTakenException {
199: if (Utilities.isEmpty(nodeName)) {
200: return activeNodeInstances;
201: }
202: List nodeInstancesToProcess = new ArrayList();
203: for (Iterator iterator = activeNodeInstances.iterator(); iterator
204: .hasNext();) {
205: RouteNodeInstance nodeInstance = (RouteNodeInstance) iterator
206: .next();
207: if (nodeName.equals(nodeInstance.getName())) {
208: // one of active node instances is node instance to stop at
209: return new ArrayList();
210: } else {
211: if (isNodeNameInPath(nodeName, nodeInstance)) {
212: nodeInstancesToProcess.add(nodeInstance);
213: }
214: }
215: }
216: if (nodeInstancesToProcess.size() == 0) {
217: throw new InvalidActionTakenException(
218: "Could not locate a node with the given name in the blanket approval path '"
219: + nodeName
220: + "'. "
221: + "The document is probably already passed the specified node or does not contain the node.");
222: }
223: return nodeInstancesToProcess;
224: }
225:
226: private boolean isNodeNameInPath(String nodeName,
227: RouteNodeInstance nodeInstance) {
228: boolean isInPath = false;
229: for (Iterator iterator = nodeInstance.getRouteNode()
230: .getNextNodes().iterator(); iterator.hasNext();) {
231: RouteNode nextNode = (RouteNode) iterator.next();
232: isInPath = isInPath
233: || isNodeNameInPath(nodeName, nextNode,
234: new HashSet());
235: }
236: return isInPath;
237: }
238:
239: private boolean isNodeNameInPath(String nodeName, RouteNode node,
240: Set inspected) {
241: boolean isInPath = !inspected.contains(node.getRouteNodeId())
242: && node.getRouteNodeName().equals(nodeName);
243: inspected.add(node.getRouteNodeId());
244: if (helper.isSubProcessNode(node)) {
245: Process subProcess = node.getDocumentType()
246: .getNamedProcess(node.getRouteNodeName());
247: RouteNode subNode = subProcess.getInitialRouteNode();
248: isInPath = isInPath
249: || isNodeNameInPath(nodeName, subNode, inspected);
250: }
251: for (Iterator iterator = node.getNextNodes().iterator(); iterator
252: .hasNext();) {
253: RouteNode nextNode = (RouteNode) iterator.next();
254: isInPath = isInPath
255: || isNodeNameInPath(nodeName, nextNode, inspected);
256: }
257: return isInPath;
258: }
259:
260: private boolean hasReachedCompletion(ProcessContext processContext,
261: List actionRequests, RouteNodeInstance nodeInstance,
262: SimulationCriteria criteria)
263: throws EdenUserNotFoundException {
264: if (!criteria.getDestinationRecipients().isEmpty()) {
265: for (Iterator iterator = actionRequests.iterator(); iterator
266: .hasNext();) {
267: ActionRequestValue request = (ActionRequestValue) iterator
268: .next();
269: for (Iterator userIt = criteria
270: .getDestinationRecipients().iterator(); userIt
271: .hasNext();) {
272: Recipient recipient = (Recipient) userIt.next();
273: if (request.isRecipientRoutedRequest(recipient)) {
274: return true;
275: }
276: }
277: }
278: }
279: String nodeName = criteria.getDestinationNodeName();
280: return (Utilities.isEmpty(nodeName)
281: && processContext.isComplete() && processContext
282: .getNextNodeInstances().isEmpty())
283: || nodeInstance.getRouteNode().getRouteNodeName()
284: .equals(nodeName);
285: }
286:
287: private List processPotentialActionsTaken(
288: RouteContext routeContext,
289: DocumentRouteHeaderValue routeHeader,
290: RouteNodeInstance justProcessedNode,
291: SimulationCriteria criteria)
292: throws EdenUserNotFoundException {
293: List actionsTaken = new ArrayList();
294: List requestsToCheck = new ArrayList();
295: requestsToCheck.addAll(routeContext.getEngineState()
296: .getGeneratedRequests());
297: requestsToCheck.addAll(routeHeader.getActionRequests());
298: List pendingActionRequestValues = getCriteriaActionsToDoByNodeName(
299: requestsToCheck, justProcessedNode.getName());
300: List actionsToTakeForNode = generateActionsToTakeForNode(
301: justProcessedNode.getName(), routeHeader, criteria,
302: pendingActionRequestValues);
303:
304: for (Iterator iter = actionsToTakeForNode.iterator(); iter
305: .hasNext();) {
306: ActionTakenValue actionTaken = (ActionTakenValue) iter
307: .next();
308: KEWServiceLocator.getActionRequestService()
309: .deactivateRequests(actionTaken,
310: pendingActionRequestValues,
311: routeContext.getActivationContext());
312: actionsTaken.add(actionTaken);
313: // routeContext.getActivationContext().getSimulatedActionsTaken().add(actionTaken);
314: }
315: return actionsTaken;
316: }
317:
318: private List generateActionsToTakeForNode(String nodeName,
319: DocumentRouteHeaderValue routeHeader,
320: SimulationCriteria criteria, List pendingActionRequests)
321: throws EdenUserNotFoundException {
322: List actions = new ArrayList();
323: if ((criteria.getActionsToTake() != null)
324: && (!criteria.getActionsToTake().isEmpty())) {
325: for (Iterator iter = criteria.getActionsToTake().iterator(); iter
326: .hasNext();) {
327: SimulationActionToTake simAction = (SimulationActionToTake) iter
328: .next();
329: if (nodeName.equals(simAction.getNodeName())) {
330: actions
331: .add(createDummyActionTaken(
332: routeHeader,
333: simAction.getUser(),
334: simAction.getActionToPerform(),
335: findDelegatorForActionRequests(pendingActionRequests)));
336: }
337: }
338: }
339: return actions;
340: }
341:
342: private List getCriteriaActionsToDoByNodeName(
343: List generatedRequests, String nodeName) {
344: List requests = new ArrayList();
345: for (Iterator iterator = generatedRequests.iterator(); iterator
346: .hasNext();) {
347: ActionRequestValue request = (ActionRequestValue) iterator
348: .next();
349: if ((request.isPending())
350: && (nodeName.equals(request.getNodeInstance()
351: .getName()))) {
352: requests.add(request);
353: }
354: }
355: return requests;
356: }
357:
358: /*private void simulateDocumentType(SimulationCriteria criteria) throws Exception {
359: DocumentType documentType = SpringServiceLocator.getDocumentTypeService().findByName(criteria.getDocumentTypeName());
360: if (documentType == null) {
361: throw new DocumentTypeNotFoundException("Could not locate document type for the given name '" + criteria.getDocumentTypeName() + "'");
362: }
363: if (criteria.getRuleTemplateNames().isEmpty()) {
364: throw new IllegalArgumentException("Must specify at least one rule template name to report against.");
365: }
366: List nodes = findRouteNodesForTemplate(documentType, criteria.getRuleTemplateNames());
367: RouteContext context = RouteContext.getCurrentRouteContext();
368: try {
369: context.setSimulation(true);
370: context.setEngineState(new EngineState());
371: context.setDocument(createSimulationDocument(criteria.getDocumentId(), criteria));
372: context.setDocumentContent(new StandardDocumentContent(criteria.getXmlContent(), context));
373: context.setDoNotSendApproveNotificationEmails(true);
374: results.setDocument(context.getDocument());
375: Branch simulationBranch = null;
376: for (Iterator iterator = nodes.iterator(); iterator.hasNext(); ) {
377: RouteNode node = (RouteNode) iterator.next();
378: context.setNodeInstance(createSimulationNodeInstance(context, node));
379: // for simulation, we'll have one branch
380: if (simulationBranch == null) {
381: simulationBranch = createSimulationBranch(context);
382: }
383: context.getNodeInstance().setBranch(simulationBranch);
384: RouteModule routeModule = SpringServiceLocator.getRouteModuleService().findRouteModule(node);
385: results.getSimulatedActionRequests().addAll(initializeActionRequests(context, routeModule.findActionRequests(context)));
386: }
387: } finally {
388: RouteContext.clearCurrentRouteContext();
389: }
390:
391: }*/
392:
393: private void validateCriteria(SimulationCriteria criteria) {
394: if (criteria.getDocumentId() == null
395: && Utilities.isEmpty(criteria.getDocumentTypeName())) {
396: throw new IllegalArgumentException(
397: "No document type name or route header id given, cannot simulate a document without a document type name or a route header id.");
398: }
399: if (criteria.getXmlContent() == null) {
400: criteria.setXmlContent("");
401: }
402: }
403:
404: /**
405: * Creates the document to run the simulation against by loading it from the database or creating a fake document for
406: * simulation purposes depending on the passed simulation criteria.
407: *
408: * If the documentId is available, we load the document from the database, otherwise we create one based on the given
409: * DocumentType and xml content.
410: */
411: private DocumentRouteHeaderValue createSimulationDocument(
412: Long documentId, SimulationCriteria criteria,
413: RouteContext context) {
414: DocumentRouteHeaderValue document = null;
415: if (criteria.isDocumentSimulation()) {
416: document = getDocumentForSimulation(documentId);
417: if (!Utilities.isEmpty(criteria.getXmlContent())) {
418: document.setDocContent(criteria.getXmlContent());
419: }
420: // document = getRouteHeaderService().getRouteHeader(documentId);
421: } else if (criteria.isDocumentTypeSimulation()) {
422: DocumentType documentType = KEWServiceLocator
423: .getDocumentTypeService().findByName(
424: criteria.getDocumentTypeName());
425: if (documentType == null) {
426: throw new IllegalArgumentException(
427: "Specified document type could not be found for name '"
428: + criteria.getDocumentTypeName() + "'");
429: }
430: documentId = context.getEngineState().getNextSimulationId();
431: document = new DocumentRouteHeaderValue();
432: context.setDocument(document);
433: document.setRouteHeaderId(documentId);
434: document.setCreateDate(new Timestamp(System
435: .currentTimeMillis()));
436: document.setDocContent(criteria.getXmlContent());
437: document.setDocRouteLevel(new Integer(0));
438: document
439: .setDocumentTypeId(documentType.getDocumentTypeId());
440: document
441: .setDocRouteStatus(EdenConstants.ROUTE_HEADER_INITIATED_CD);
442: initializeDocument(document);
443: installSimulationNodeInstances(context, criteria);
444: }
445: if (document == null) {
446: throw new IllegalArgumentException(
447: "Workflow simulation engine could not locate document with id "
448: + documentId);
449: }
450: return document;
451: }
452:
453: private DocumentRouteHeaderValue getDocumentForSimulation(
454: Long documentId) {
455: DocumentRouteHeaderValue document = getRouteHeaderService()
456: .getRouteHeader(documentId);
457: return (DocumentRouteHeaderValue) deepCopy(document);
458: }
459:
460: private Serializable deepCopy(Serializable src) {
461: Serializable obj = null;
462: if (src != null) {
463: ObjectOutputStream oos = null;
464: ObjectInputStream ois = null;
465: try {
466: ByteArrayOutputStream serializer = new ByteArrayOutputStream();
467: oos = new ObjectOutputStream(serializer);
468: oos.writeObject(src);
469:
470: ByteArrayInputStream deserializer = new ByteArrayInputStream(
471: serializer.toByteArray());
472: ois = new ObjectInputStream(deserializer);
473: obj = (Serializable) ois.readObject();
474: } catch (IOException e) {
475: throw new RuntimeException(
476: "unable to complete deepCopy from src '"
477: + src.toString() + "'", e);
478: } catch (ClassNotFoundException e) {
479: throw new RuntimeException(
480: "unable to complete deepCopy from src '"
481: + src.toString() + "'", e);
482: } finally {
483: try {
484: if (oos != null) {
485: oos.close();
486: }
487: if (ois != null) {
488: ois.close();
489: }
490: } catch (IOException e) {
491: // ignoring this IOException, since the streams are going to be abandoned now anyway
492: }
493: }
494: }
495: return obj;
496: }
497:
498: private void routeDocumentIfNecessary(
499: DocumentRouteHeaderValue document,
500: SimulationCriteria criteria, RouteContext routeContext)
501: throws InvalidActionTakenException {
502: if (criteria.getRoutingUser() != null) {
503: ActionTakenValue action = createDummyActionTaken(document,
504: criteria.getRoutingUser(),
505: EdenConstants.ACTION_TAKEN_ROUTED_CD, null);
506: routeContext.getActivationContext()
507: .getSimulatedActionsTaken().add(action);
508: simulateDocumentRoute(action, document, criteria
509: .getRoutingUser(), routeContext);
510: }
511: }
512:
513: /**
514: * Looks at the rule templates and/or the startNodeName and creates the appropriate node instances to run simulation against.
515: * After creating the node instances, it hooks them all together and installs a "terminal" simulation node to stop the simulation
516: * node at the end of the simulation.
517: */
518: private void installSimulationNodeInstances(RouteContext context,
519: SimulationCriteria criteria) {
520: DocumentRouteHeaderValue document = context.getDocument();
521: RouteNodeInstance initialNodeInstance = (RouteNodeInstance) document
522: .getInitialRouteNodeInstance(0);
523: List simulationNodes = new ArrayList();
524: if (!criteria.getNodeNames().isEmpty()) {
525: for (Iterator iterator = criteria.getNodeNames().iterator(); iterator
526: .hasNext();) {
527: String nodeName = (String) iterator.next();
528: LOG.debug("Installing simulation starting node '"
529: + nodeName + "'");
530: List nodes = KEWServiceLocator.getRouteNodeService()
531: .getFlattenedNodes(document.getDocumentType(),
532: true);
533: boolean foundNode = false;
534: for (Iterator iterator2 = nodes.iterator(); iterator2
535: .hasNext();) {
536: RouteNode node = (RouteNode) iterator2.next();
537: if (node.getRouteNodeName().equals(nodeName)) {
538: simulationNodes.add(node);
539: foundNode = true;
540: break;
541: }
542: }
543: if (!foundNode) {
544: throw new IllegalArgumentException(
545: "Could not find node on the document type for the given name '"
546: + nodeName + "'");
547: }
548: }
549: } else if (!criteria.getRuleTemplateNames().isEmpty()) {
550: List nodes = KEWServiceLocator
551: .getRouteNodeService()
552: .getFlattenedNodes(document.getDocumentType(), true);
553: for (Iterator iterator = criteria.getRuleTemplateNames()
554: .iterator(); iterator.hasNext();) {
555: String ruleTemplateName = (String) iterator.next();
556: boolean foundNode = false;
557: for (Iterator iterator2 = nodes.iterator(); iterator2
558: .hasNext();) {
559: RouteNode node = (RouteNode) iterator2.next();
560: String routeMethodName = node.getRouteMethodName();
561: if (node.isFlexRM()
562: && ruleTemplateName.equals(routeMethodName)) {
563: simulationNodes.add(node);
564: foundNode = true;
565: break;
566: }
567: }
568: if (!foundNode) {
569: throw new IllegalArgumentException(
570: "Could not find node on the document type with the given rule template name '"
571: + ruleTemplateName + "'");
572: }
573: }
574: } else {
575: // can we assume we want to use all the nodes?
576: List nodes = KEWServiceLocator
577: .getRouteNodeService()
578: .getFlattenedNodes(document.getDocumentType(), true);
579: for (Iterator iterator2 = nodes.iterator(); iterator2
580: .hasNext();) {
581: RouteNode node = (RouteNode) iterator2.next();
582: simulationNodes.add(node);
583: }
584: }
585: // hook all of the simulation nodes together
586: RouteNodeInstance currentNodeInstance = initialNodeInstance;
587: for (Iterator iterator = simulationNodes.iterator(); iterator
588: .hasNext();) {
589: RouteNode simulationNode = (RouteNode) iterator.next();
590: RouteNodeInstance nodeInstance = helper
591: .getNodeFactory()
592: .createRouteNodeInstance(
593: document.getRouteHeaderId(), simulationNode);
594: nodeInstance.setBranch(initialNodeInstance.getBranch());
595: currentNodeInstance.addNextNodeInstance(nodeInstance);
596: saveNode(context, currentNodeInstance);
597: currentNodeInstance = nodeInstance;
598: }
599: installSimulationTerminationNode(context, document
600: .getDocumentType(), currentNodeInstance);
601: }
602:
603: private void installSimulationTerminationNode(RouteContext context,
604: DocumentType documentType,
605: RouteNodeInstance lastNodeInstance) {
606: RouteNode terminationNode = new RouteNode();
607: terminationNode.setDocumentType(documentType);
608: terminationNode.setDocumentTypeId(documentType
609: .getDocumentTypeId());
610: terminationNode.setNodeType(NoOpNode.class.getName());
611: terminationNode.setRouteNodeName("SIMULATION_TERMINATION_NODE");
612: RouteNodeInstance terminationNodeInstance = helper
613: .getNodeFactory().createRouteNodeInstance(
614: lastNodeInstance.getDocumentId(),
615: terminationNode);
616: terminationNodeInstance.setBranch(lastNodeInstance.getBranch());
617: lastNodeInstance.addNextNodeInstance(terminationNodeInstance);
618: saveNode(context, lastNodeInstance);
619: }
620:
621: // below is fairly a copy of RouteDocumentAction... but actions have to be faked for now
622: private void simulateDocumentRoute(ActionTakenValue actionTaken,
623: DocumentRouteHeaderValue document, WorkflowUser user,
624: RouteContext routeContext)
625: throws InvalidActionTakenException {
626: ActionRequestService actionRequestService = KEWServiceLocator
627: .getActionRequestService();
628: // TODO delyea - deep copy below
629: List actionRequests = new ArrayList();
630: for (Iterator iter = actionRequestService.findPendingByDoc(
631: document.getRouteHeaderId()).iterator(); iter.hasNext();) {
632: ActionRequestValue arv = (ActionRequestValue) iter.next();
633: actionRequests.add((ActionRequestValue) deepCopy(arv));
634: }
635: // actionRequests.addAll(actionRequestService.findPendingByDoc(document.getRouteHeaderId()));
636: LOG.debug("Simulate Deactivating all pending action requests");
637: // deactivate any requests for the user that routed the document.
638: for (Iterator iter = actionRequests.iterator(); iter.hasNext();) {
639: ActionRequestValue actionRequest = (ActionRequestValue) iter
640: .next();
641: // requests generated to the user who is routing the document should be deactivated
642: if ((user.getWorkflowId().equals(actionRequest
643: .getWorkflowId()))
644: && (actionRequest.isActive())) {
645: actionRequestService.deactivateRequest(actionTaken,
646: actionRequest, routeContext
647: .getActivationContext());
648: }
649: // requests generated by a save action should be deactivated
650: else if (EdenConstants.SAVED_REQUEST_RESPONSIBILITY_ID
651: .equals(actionRequest.getResponsibilityId())) {
652: actionRequestService.deactivateRequest(actionTaken,
653: actionRequest, routeContext
654: .getActivationContext());
655: }
656: }
657:
658: // String oldStatus = document.getDocRouteStatus();
659: document.markDocumentEnroute();
660: // String newStatus = document.getDocRouteStatus();
661: // notifyStatusChange(newStatus, oldStatus);
662: // getRouteHeaderService().saveRouteHeader(document);
663: }
664:
665: private ActionTakenValue createDummyActionTaken(
666: DocumentRouteHeaderValue routeHeader,
667: WorkflowUser userToPerformAction, String actionToPerform,
668: Recipient delegator) {
669: ActionTakenValue val = new ActionTakenValue();
670: val.setActionTaken(actionToPerform);
671: if (EdenConstants.ACTION_TAKEN_ROUTED_CD
672: .equals(actionToPerform)) {
673: val.setActionTaken(EdenConstants.ACTION_TAKEN_COMPLETED_CD);
674: }
675: val.setAnnotation("");
676: val.setDocVersion(routeHeader.getDocVersion());
677: val.setRouteHeaderId(routeHeader.getRouteHeaderId());
678: val.setWorkflowId(userToPerformAction.getWorkflowUserId()
679: .getWorkflowId());
680: if (delegator instanceof WorkflowUser) {
681: val.setDelegatorWorkflowId(((WorkflowUser) delegator)
682: .getWorkflowUserId().getWorkflowId());
683: } else if (delegator instanceof Workgroup) {
684: val.setDelegatorWorkgroupId(((Workgroup) delegator)
685: .getWorkflowGroupId().getGroupId());
686: }
687: val.setRouteHeader(routeHeader);
688: val.setCurrentIndicator(Boolean.TRUE);
689: return val;
690: }
691:
692: /**
693: * Used by actions taken
694: *
695: * Returns the highest priority delegator in the list of action requests.
696: */
697: private Recipient findDelegatorForActionRequests(List actionRequests)
698: throws EdenUserNotFoundException {
699: return KEWServiceLocator.getActionRequestService()
700: .findDelegator(actionRequests);
701: }
702:
703: /**
704: * Executes a "saveNode" for the simulation engine, this does not actually save the document, but rather
705: * assigns it some simulation ids.
706: *
707: * Resolves KULRICE-368
708: */
709: @Override
710: protected void saveNode(RouteContext context,
711: RouteNodeInstance nodeInstance) {
712: // we shold be in simulation mode here
713:
714: // if we are in simulation mode, lets go ahead and assign some id
715: // values to our beans
716: for (Iterator iterator = nodeInstance.getNextNodeInstances()
717: .iterator(); iterator.hasNext();) {
718: RouteNodeInstance routeNodeInstance = (RouteNodeInstance) iterator
719: .next();
720: if (routeNodeInstance.getRouteNodeInstanceId() == null) {
721: routeNodeInstance.setRouteNodeInstanceId(context
722: .getEngineState().getNextSimulationId());
723: }
724: }
725: if (nodeInstance.getProcess() != null
726: && nodeInstance.getProcess().getRouteNodeInstanceId() == null) {
727: nodeInstance.getProcess().setRouteNodeInstanceId(
728: context.getEngineState().getNextSimulationId());
729: }
730: if (nodeInstance.getBranch() != null
731: && nodeInstance.getBranch().getBranchId() == null) {
732: nodeInstance.getBranch().setBranchId(
733: context.getEngineState().getNextSimulationId());
734: }
735: }
736:
737: /*private Branch createSimulationBranch(RouteContext context) {
738: Branch branch = helper.getNodeFactory().createBranch("SIMULATION", null, context.getNodeInstance());
739: branch.setBranchId(context.getEngineState().getNextSimulationId());
740: return branch;
741: }
742:
743: private RouteNodeInstance createSimulationNodeInstance(RouteContext context, RouteNode node) {
744: RouteNodeInstance nodeInstance = helper.getNodeFactory().createRouteNodeInstance(context.getDocument().getRouteHeaderId(), node);
745: nodeInstance.setRouteNodeInstanceId(context.getEngineState().getNextSimulationId());
746: return nodeInstance;
747: }
748:
749: private List findRouteNodesForTemplate(DocumentType documentType, List ruleTemplateNames) {
750: List routeNodes = new ArrayList();
751: List flattenedRouteNodes = SpringServiceLocator.getRouteNodeService().getFlattenedNodes(documentType, true);
752: for (Iterator iterator = ruleTemplateNames.iterator(); iterator.hasNext(); ) {
753: String ruleTemplateName = (String) iterator.next();
754: boolean foundNode = false;
755: for (Iterator iterator2 = flattenedRouteNodes.iterator(); iterator2.hasNext(); ) {
756: RouteNode node = (RouteNode) iterator2.next();
757: if (node.isFlexRM() && ruleTemplateName.equals(node.getRouteMethodName())) {
758: routeNodes.add(node);
759: foundNode = true;
760: break;
761: }
762: }
763: if (!foundNode) {
764: throw new IllegalArgumentException("Could not locate route node with rule template '"+ruleTemplateName+"' on Document Type '"+documentType.getName());
765: }
766: }
767: return routeNodes;
768: }
769:
770: private List initializeActionRequests(RouteContext context, List actionRequests) {
771: if (actionRequests == null) {
772: return new ArrayList();
773: }
774: for (Iterator iterator = actionRequests.iterator(); iterator.hasNext(); ) {
775: ActionRequestValue actionRequest = (ActionRequestValue) iterator.next();
776: SpringServiceLocator.getActionRequestService().initializeActionRequestGraph(actionRequest, context.getDocument(), context.getNodeInstance());
777: }
778: return actionRequests;
779: }*/
780:
781: /**
782: * ByteArrayOutputStream implementation that doesnŐt synchronize methods and
783: * doesnŐt copy the data on toByteArray().
784: */
785: // public class FastByteArrayOutputStream extends OutputStream {
786: // /**
787: // * Buffer and size
788: // */
789: // protected byte[] buf = null;
790: //
791: // protected int size = 0;
792: //
793: // /**
794: // * Constructs a stream with buffer capacity size 5K
795: // */
796: // public FastByteArrayOutputStream() {
797: // this(5 * 1024);
798: // }
799: //
800: // /**
801: // * Constructs a stream with the given initial size
802: // */
803: // public FastByteArrayOutputStream(int initSize) {
804: // this.size = 0;
805: // this.buf = new byte[initSize];
806: // }
807: //
808: // /**
809: // * Ensures that we have a large enough buffer for the given size.
810: // */
811: // private void verifyBufferSize(int sz) {
812: // if (sz > buf.length) {
813: // byte[] old = buf;
814: // buf = new byte[Math.max(sz, 2 * buf.length)];
815: // System.arraycopy(old, 0, buf, 0, old.length);
816: // old = null;
817: // }
818: // }
819: //
820: // public int getSize() {
821: // return size;
822: // }
823: //
824: // /**
825: // * Returns the byte array containing the written data. Note that this
826: // * array will almost always be larger than the amount of data actually
827: // * written.
828: // */
829: // public byte[] getByteArray() {
830: // return buf;
831: // }
832: //
833: // public final void write(byte b[]) {
834: // verifyBufferSize(size + b.length);
835: // System.arraycopy(b, 0, buf, size, b.length);
836: // size += b.length;
837: // }
838: //
839: // public final void write(byte b[], int off, int len) {
840: // verifyBufferSize(size + len);
841: // System.arraycopy(b, off, buf, size, len);
842: // size += len;
843: // }
844: //
845: // public final void write(int b) {
846: // verifyBufferSize(size + 1);
847: // buf[size++] = (byte) b;
848: // }
849: //
850: // public void reset() {
851: // size = 0;
852: // }
853: //
854: // /**
855: // * Returns a ByteArrayInputStream for reading back the written data
856: // */
857: // public InputStream getInputStream() {
858: // return new FastByteArrayInputStream(buf, size);
859: // }
860: //
861: // }
862: }
|