Coverage Report - org.kuali.rice.kew.engine.simulation.SimulationEngine
 
Classes in this File Line Coverage Branch Coverage Complexity
SimulationEngine
0%
0/310
0%
0/200
6.318
 
 1  
 /**
 2  
  * Copyright 2005-2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.kew.engine.simulation;
 17  
 
 18  
 import org.apache.log4j.MDC;
 19  
 import org.kuali.rice.core.framework.parameter.ParameterService;
 20  
 import org.kuali.rice.kew.actionitem.ActionItem;
 21  
 import org.kuali.rice.kew.actionrequest.ActionRequestValue;
 22  
 import org.kuali.rice.kew.actionrequest.KimGroupRecipient;
 23  
 import org.kuali.rice.kew.actionrequest.KimPrincipalRecipient;
 24  
 import org.kuali.rice.kew.actionrequest.Recipient;
 25  
 import org.kuali.rice.kew.actionrequest.service.ActionRequestService;
 26  
 import org.kuali.rice.kew.actiontaken.ActionTakenValue;
 27  
 import org.kuali.rice.kew.api.WorkflowRuntimeException;
 28  
 import org.kuali.rice.kew.api.exception.DocumentSimulatedRouteException;
 29  
 import org.kuali.rice.kew.doctype.bo.DocumentType;
 30  
 import org.kuali.rice.kew.engine.ActivationContext;
 31  
 import org.kuali.rice.kew.engine.EngineState;
 32  
 import org.kuali.rice.kew.engine.OrchestrationConfig;
 33  
 import org.kuali.rice.kew.engine.ProcessContext;
 34  
 import org.kuali.rice.kew.engine.RouteContext;
 35  
 import org.kuali.rice.kew.engine.StandardWorkflowEngine;
 36  
 import org.kuali.rice.kew.engine.node.Branch;
 37  
 import org.kuali.rice.kew.engine.node.NoOpNode;
 38  
 import org.kuali.rice.kew.engine.node.NodeJotter;
 39  
 import org.kuali.rice.kew.engine.node.NodeType;
 40  
 import org.kuali.rice.kew.engine.node.ProcessDefinitionBo;
 41  
 import org.kuali.rice.kew.engine.node.RequestsNode;
 42  
 import org.kuali.rice.kew.engine.node.RouteNode;
 43  
 import org.kuali.rice.kew.engine.node.RouteNodeInstance;
 44  
 import org.kuali.rice.kew.engine.node.SimpleNode;
 45  
 import org.kuali.rice.kew.engine.node.service.RouteNodeService;
 46  
 import org.kuali.rice.kew.api.exception.InvalidActionTakenException;
 47  
 import org.kuali.rice.kew.api.exception.ResourceUnavailableException;
 48  
 import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
 49  
 import org.kuali.rice.kew.routeheader.service.RouteHeaderService;
 50  
 import org.kuali.rice.kew.service.KEWServiceLocator;
 51  
 import org.kuali.rice.kew.api.KewApiConstants;
 52  
 import org.kuali.rice.kew.util.PerformanceLogger;
 53  
 import org.kuali.rice.kew.util.Utilities;
 54  
 import org.kuali.rice.kim.api.group.Group;
 55  
 import org.kuali.rice.kim.api.identity.Person;
 56  
 
 57  
 import java.io.ByteArrayInputStream;
 58  
 import java.io.ByteArrayOutputStream;
 59  
 import java.io.IOException;
 60  
 import java.io.ObjectInputStream;
 61  
 import java.io.ObjectOutputStream;
 62  
 import java.io.Serializable;
 63  
 import java.sql.Timestamp;
 64  
 import java.util.ArrayList;
 65  
 import java.util.Collections;
 66  
 import java.util.HashSet;
 67  
 import java.util.Iterator;
 68  
 import java.util.List;
 69  
 import java.util.Set;
 70  
 
 71  
 
 72  
 /**
 73  
  * A WorkflowEngine implementation which runs simulations.  This object is not thread-safe
 74  
  * and therefore a new instance needs to be instantiated on every use.
 75  
  *
 76  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 77  
  */
 78  
 public class SimulationEngine extends StandardWorkflowEngine implements SimulationWorkflowEngine {
 79  
 
 80  
     public SimulationEngine () {
 81  0
         super();
 82  0
     }
 83  
     public SimulationEngine(RouteNodeService routeNodeService, RouteHeaderService routeHeaderService,
 84  
             ParameterService parameterService, OrchestrationConfig config) {
 85  0
         super(routeNodeService, routeHeaderService, parameterService, config);
 86  0
     }
 87  
 
 88  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SimulationEngine.class);
 89  
 
 90  
         private SimulationCriteria criteria;
 91  
     private SimulationResults results;
 92  
 
 93  
     @Override
 94  
     public SimulationResults runSimulation(SimulationCriteria criteria) throws Exception {
 95  
         try {
 96  0
             this.criteria = criteria;
 97  0
             this.results = new SimulationResults();
 98  0
             validateCriteria(criteria);
 99  0
             process(criteria.getDocumentId(), null);
 100  0
             return results;
 101  
         } finally {
 102  
             //nulling out the results & criteria since these really should only be local variables.
 103  0
             this.criteria = null;
 104  0
             this.results = null;
 105  
         }
 106  
     }
 107  
 
 108  
     @Override
 109  
     public void process(String documentId, String nodeInstanceId) throws InvalidActionTakenException, DocumentSimulatedRouteException {
 110  0
             RouteContext context = RouteContext.createNewRouteContext();
 111  
             try {
 112  0
                     ActivationContext activationContext = new ActivationContext(ActivationContext.CONTEXT_IS_SIMULATION);
 113  0
                     if (criteria.isActivateRequests() == null) {
 114  0
                         activationContext.setActivateRequests(!criteria.getActionsToTake().isEmpty());
 115  
                     } else {
 116  0
                         activationContext.setActivateRequests(criteria.isActivateRequests().booleanValue());
 117  
                     }
 118  0
                     context.setActivationContext(activationContext);
 119  0
                     context.setEngineState(new EngineState());
 120  
                     // suppress policy errors when running a simulation for the purposes of display on the route log
 121  0
                     RequestsNode.setSupressPolicyErrors(context);
 122  0
                     DocumentRouteHeaderValue document = createSimulationDocument(documentId, criteria, context);
 123  0
             document.setInitiatorWorkflowId("simulation");
 124  0
                     if ( (criteria.isDocumentSimulation()) && ( (document.isProcessed()) || (document.isFinal()) ) ) {
 125  0
                             results.setDocument(document);
 126  
                             return;
 127  
                     }
 128  0
                     routeDocumentIfNecessary(document, criteria, context);
 129  0
                     results.setDocument(document);
 130  0
                     documentId = document.getDocumentId();
 131  
                     
 132  
                     // detect if MDC already has docId param (to avoid nuking it below)
 133  0
                     boolean mdcHadDocId = MDC.get("docId") != null;
 134  0
                     if (!mdcHadDocId) { MDC.put("docId", documentId); }
 135  
                     
 136  0
                     PerformanceLogger perfLog = new PerformanceLogger(documentId);
 137  
                     try {
 138  0
                         if ( LOG.isInfoEnabled() ) {
 139  0
                             LOG.info("Processing document for Simulation: " + documentId);
 140  
                         }
 141  0
                             List<RouteNodeInstance> activeNodeInstances = getRouteNodeService().getActiveNodeInstances(document);
 142  0
                             List<RouteNodeInstance> nodeInstancesToProcess = determineNodeInstancesToProcess(activeNodeInstances, criteria.getDestinationNodeName());
 143  
 
 144  0
                             context.setDocument(document);
 145  
                             // TODO set document content
 146  0
                             context.setEngineState(new EngineState());
 147  0
                             ProcessContext processContext = new ProcessContext(true, nodeInstancesToProcess);
 148  0
                             while (! nodeInstancesToProcess.isEmpty()) {
 149  0
                                     RouteNodeInstance nodeInstance = (RouteNodeInstance)nodeInstancesToProcess.remove(0);
 150  0
                                     if ( !nodeInstance.isActive() ) {
 151  0
                                             continue;
 152  
                                     }
 153  0
                                     NodeJotter.jotNodeInstance(context.getDocument(), nodeInstance);
 154  0
                                     context.setNodeInstance(nodeInstance);
 155  0
                                     processContext = processNodeInstance(context, helper);
 156  0
                                     if (!hasReachedCompletion(processContext, context.getEngineState().getGeneratedRequests(), nodeInstance, criteria)) {
 157  0
                                             if (processContext.isComplete()) {
 158  0
                                                     if (!processContext.getNextNodeInstances().isEmpty()) {
 159  0
                                                             nodeInstancesToProcess.addAll(processContext.getNextNodeInstances());
 160  
                                                     }
 161  0
                                                     context.getActivationContext().getSimulatedActionsTaken().addAll(processPotentialActionsTaken(context, document, nodeInstance, criteria));
 162  
                                             }
 163  
                                     } else {
 164  0
                                             context.getActivationContext().getSimulatedActionsTaken().addAll(processPotentialActionsTaken(context, document, nodeInstance, criteria));
 165  
                                     }
 166  0
                             }
 167  0
                             List simulatedActionRequests = context.getEngineState().getGeneratedRequests();
 168  0
                             Collections.sort(simulatedActionRequests, new Utilities.RouteLogActionRequestSorter());
 169  0
                             results.setSimulatedActionRequests(simulatedActionRequests);
 170  0
                             results.setSimulatedActionsTaken(context.getActivationContext().getSimulatedActionsTaken());
 171  0
             } catch (InvalidActionTakenException e) {
 172  0
                 throw e;
 173  0
             } catch (Exception e) {
 174  0
                 String errorMsg = "Error running simulation for document " + ((criteria.isDocumentSimulation()) ? "id " + documentId.toString() : "type " + criteria.getDocumentTypeName());
 175  0
                 LOG.error(errorMsg,e);
 176  0
                 throw new DocumentSimulatedRouteException(errorMsg, e);
 177  
                     } finally {
 178  0
                             perfLog.log("Time to run simulation.");
 179  0
                             RouteContext.clearCurrentRouteContext();
 180  
                             
 181  0
                             if (!mdcHadDocId) { MDC.remove("docID"); }
 182  
                     }
 183  
             } finally {
 184  0
                     RouteContext.releaseCurrentRouteContext();
 185  0
             }
 186  0
     }
 187  
 
 188  
     /**
 189  
      * If there are multiple paths, we need to figure out which ones we need to follow for blanket approval.
 190  
      * This method will throw an exception if a node with the given name could not be located in the routing path.
 191  
      * This method is written in such a way that it should be impossible for there to be an infinate loop, even if
 192  
      * there is extensive looping in the node graph.
 193  
      */
 194  
     private List<RouteNodeInstance> determineNodeInstancesToProcess(List<RouteNodeInstance> activeNodeInstances, String nodeName) throws InvalidActionTakenException {
 195  0
         if (org.apache.commons.lang.StringUtils.isEmpty(nodeName)) {
 196  0
             return activeNodeInstances;
 197  
         }
 198  0
         List<RouteNodeInstance> nodeInstancesToProcess = new ArrayList<RouteNodeInstance>();
 199  0
         for (RouteNodeInstance nodeInstance : activeNodeInstances) {
 200  0
             if (nodeName.equals(nodeInstance.getName())) {
 201  
                 // one of active node instances is node instance to stop at
 202  0
                 return new ArrayList<RouteNodeInstance>();
 203  
             } else {
 204  0
                 if (isNodeNameInPath(nodeName, nodeInstance)) {
 205  0
                     nodeInstancesToProcess.add(nodeInstance);
 206  
                 }
 207  
             }
 208  
         }
 209  0
         if (nodeInstancesToProcess.size() == 0) {
 210  0
             throw new InvalidActionTakenException("Could not locate a node with the given name in the blanket approval path '" + nodeName + "'.  " +
 211  
                     "The document is probably already passed the specified node or does not contain the node.");
 212  
         }
 213  0
         return nodeInstancesToProcess;
 214  
     }
 215  
 
 216  
     private boolean isNodeNameInPath(String nodeName, RouteNodeInstance nodeInstance) {
 217  0
         boolean isInPath = false;
 218  0
         for (Iterator<RouteNode> iterator = nodeInstance.getRouteNode().getNextNodes().iterator(); iterator.hasNext();) {
 219  0
             RouteNode nextNode = (RouteNode) iterator.next();
 220  0
             isInPath = isInPath || isNodeNameInPath(nodeName, nextNode, new HashSet<String>());
 221  0
         }
 222  0
         return isInPath;
 223  
     }
 224  
 
 225  
     private boolean isNodeNameInPath(String nodeName, RouteNode node, Set<String> inspected) {
 226  0
         boolean isInPath = !inspected.contains(node.getRouteNodeId()) && node.getRouteNodeName().equals(nodeName);
 227  0
         inspected.add(node.getRouteNodeId());
 228  0
         if (helper.isSubProcessNode(node)) {
 229  0
             ProcessDefinitionBo subProcess = node.getDocumentType().getNamedProcess(node.getRouteNodeName());
 230  0
             RouteNode subNode = subProcess.getInitialRouteNode();
 231  0
             isInPath = isInPath || isNodeNameInPath(nodeName, subNode, inspected);
 232  
         }
 233  0
         for (Iterator<RouteNode> iterator = node.getNextNodes().iterator(); iterator.hasNext();) {
 234  0
             RouteNode nextNode = (RouteNode) iterator.next();
 235  0
             isInPath = isInPath || isNodeNameInPath(nodeName, nextNode, inspected);
 236  0
         }
 237  0
         return isInPath;
 238  
     }
 239  
 
 240  
     private boolean hasReachedCompletion(ProcessContext processContext, List actionRequests, RouteNodeInstance nodeInstance, SimulationCriteria criteria) {
 241  0
         if (!criteria.getDestinationRecipients().isEmpty()) {
 242  0
             for (Iterator iterator = actionRequests.iterator(); iterator.hasNext();) {
 243  0
                 ActionRequestValue request = (ActionRequestValue) iterator.next();
 244  0
                 for (Iterator<Recipient> userIt = criteria.getDestinationRecipients().iterator(); userIt.hasNext();) {
 245  0
                     Recipient recipient = (Recipient) userIt.next();
 246  0
                     if (request.isRecipientRoutedRequest(recipient)) {
 247  0
                         if ( (org.apache.commons.lang.StringUtils.isEmpty(criteria.getDestinationNodeName())) || (criteria.getDestinationNodeName().equals(request.getNodeInstance().getName())) ) {
 248  0
                             return true;
 249  
                         }
 250  
                     }
 251  0
                 }
 252  0
             }
 253  
         }
 254  0
         return (org.apache.commons.lang.StringUtils.isEmpty(criteria.getDestinationNodeName()) && processContext.isComplete() && processContext.getNextNodeInstances().isEmpty())
 255  
             || nodeInstance.getRouteNode().getRouteNodeName().equals(criteria.getDestinationNodeName());
 256  
     }
 257  
 
 258  
     private List<ActionTakenValue> processPotentialActionsTaken(RouteContext routeContext, DocumentRouteHeaderValue routeHeader, RouteNodeInstance justProcessedNode, SimulationCriteria criteria) {
 259  0
             List<ActionTakenValue> actionsTaken = new ArrayList<ActionTakenValue>();
 260  0
             List requestsToCheck = new ArrayList();
 261  0
             requestsToCheck.addAll(routeContext.getEngineState().getGeneratedRequests());
 262  0
         requestsToCheck.addAll(routeHeader.getActionRequests());
 263  0
             List<ActionRequestValue> pendingActionRequestValues = getCriteriaActionsToDoByNodeName(requestsToCheck, justProcessedNode.getName());
 264  0
         List<ActionTakenValue> actionsToTakeForNode = generateActionsToTakeForNode(justProcessedNode.getName(), routeHeader, criteria, pendingActionRequestValues);
 265  
 
 266  0
         for (ActionTakenValue actionTaken : actionsToTakeForNode)
 267  
         {
 268  0
             KEWServiceLocator.getActionRequestService().deactivateRequests(actionTaken, pendingActionRequestValues, routeContext.getActivationContext());
 269  0
             actionsTaken.add(actionTaken);
 270  
 //            routeContext.getActivationContext().getSimulatedActionsTaken().add(actionTaken);
 271  
         }
 272  0
             return actionsTaken;
 273  
     }
 274  
 
 275  
     private List<ActionTakenValue> generateActionsToTakeForNode(String nodeName, DocumentRouteHeaderValue routeHeader, SimulationCriteria criteria, List<ActionRequestValue> pendingActionRequests) {
 276  0
         List<ActionTakenValue> actions = new ArrayList<ActionTakenValue>();
 277  0
         if ( (criteria.getActionsToTake() != null) && (!criteria.getActionsToTake().isEmpty()) ) {
 278  0
             for (SimulationActionToTake simAction : criteria.getActionsToTake()) {
 279  0
                 if (nodeName.equals(simAction.getNodeName())) {
 280  0
                     actions.add(createDummyActionTaken(routeHeader, simAction.getUser(), simAction.getActionToPerform(), findDelegatorForActionRequests(pendingActionRequests)));
 281  
                 }
 282  
             }
 283  
         }
 284  0
         return actions;
 285  
     }
 286  
 
 287  
     private List<ActionRequestValue> getCriteriaActionsToDoByNodeName(List generatedRequests, String nodeName) {
 288  0
             List<ActionRequestValue> requests = new ArrayList<ActionRequestValue>();
 289  0
         for (Iterator iterator = generatedRequests.iterator(); iterator.hasNext();) {
 290  0
             ActionRequestValue request = (ActionRequestValue) iterator.next();
 291  0
             if ( (request.isPending()) && request.getNodeInstance() != null && nodeName.equals(request.getNodeInstance().getName())) {
 292  0
                     requests.add(request);
 293  
             }
 294  0
         }
 295  0
         return requests;
 296  
     }
 297  
 
 298  
     private void validateCriteria(SimulationCriteria criteria) {
 299  0
             if (criteria.getDocumentId() == null && org.apache.commons.lang.StringUtils.isEmpty(criteria.getDocumentTypeName())) {
 300  0
                 throw new IllegalArgumentException("No document type name or document id given, cannot simulate a document without a document type name or a document id.");
 301  
             }
 302  0
             if (criteria.getXmlContent() == null) {
 303  0
                     criteria.setXmlContent("");
 304  
             }
 305  0
     }
 306  
 
 307  
     /**
 308  
      * Creates the document to run the simulation against by loading it from the database or creating a fake document for
 309  
      * simulation purposes depending on the passed simulation criteria.
 310  
      *
 311  
      * If the documentId is available, we load the document from the database, otherwise we create one based on the given
 312  
      * DocumentType and xml content.
 313  
      */
 314  
     private DocumentRouteHeaderValue createSimulationDocument(String documentId, SimulationCriteria criteria, RouteContext context) {
 315  0
             DocumentRouteHeaderValue document = null;
 316  0
             if (criteria.isDocumentSimulation()) {
 317  0
             document = getDocumentForSimulation(documentId);
 318  0
             if (!org.apache.commons.lang.StringUtils.isEmpty(criteria.getXmlContent())) {
 319  0
                 document.setDocContent(criteria.getXmlContent());
 320  
             }
 321  0
             } else if (criteria.isDocumentTypeSimulation()) {
 322  0
                 DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByName(criteria.getDocumentTypeName());
 323  0
                 if (documentType == null) {
 324  0
                         throw new IllegalArgumentException("Specified document type could not be found for name '"+criteria.getDocumentTypeName()+"'");
 325  
                 }
 326  0
                 documentId = context.getEngineState().getNextSimulationId().toString();
 327  0
                 document = new DocumentRouteHeaderValue();
 328  0
                 context.setDocument(document);
 329  0
                 document.setDocumentId(documentId);
 330  0
                 document.setCreateDate(new Timestamp(System.currentTimeMillis()));
 331  0
                 document.setDocContent(criteria.getXmlContent());
 332  0
                 document.setDocRouteLevel(new Integer(0));
 333  0
                 document.setDocumentTypeId(documentType.getDocumentTypeId());
 334  0
                     document.setDocRouteStatus(KewApiConstants.ROUTE_HEADER_INITIATED_CD);
 335  0
                     initializeDocument(document);
 336  
         }
 337  0
         if (document == null) {
 338  0
                 throw new IllegalArgumentException("Workflow simulation engine could not locate document with id "+documentId);
 339  
         }
 340  0
         for (ActionRequestValue actionRequest : document.getActionRequests()) {
 341  0
                 actionRequest = (ActionRequestValue) deepCopy(actionRequest);
 342  0
                 document.getSimulatedActionRequests().add(actionRequest);
 343  0
                 for (ActionItem actionItem : actionRequest.getActionItems()) {
 344  0
                         actionRequest.getSimulatedActionItems().add((ActionItem) deepCopy(actionItem));
 345  
                 }
 346  
         }
 347  0
         context.setDocument(document);
 348  0
         installSimulationNodeInstances(context, criteria);
 349  0
                 return document;
 350  
     }
 351  
 
 352  
     private DocumentRouteHeaderValue getDocumentForSimulation(String documentId) {
 353  0
         DocumentRouteHeaderValue document = getRouteHeaderService().getRouteHeader(documentId);
 354  0
         return (DocumentRouteHeaderValue)deepCopy(document);
 355  
     }
 356  
 
 357  
     private Serializable deepCopy(Serializable src) {
 358  0
         Serializable obj = null;
 359  0
         if (src != null) {
 360  0
             ObjectOutputStream oos = null;
 361  0
             ObjectInputStream ois = null;
 362  
             try {
 363  0
                 ByteArrayOutputStream serializer = new ByteArrayOutputStream();
 364  0
                 oos = new ObjectOutputStream(serializer);
 365  0
                 oos.writeObject(src);
 366  
 
 367  0
                 ByteArrayInputStream deserializer = new ByteArrayInputStream(serializer.toByteArray());
 368  0
                 ois = new ObjectInputStream(deserializer);
 369  0
                 obj = (Serializable) ois.readObject();
 370  
             }
 371  0
             catch (IOException e) {
 372  0
                 throw new RuntimeException("unable to complete deepCopy from src '" + src.toString() + "'", e);
 373  
             }
 374  0
             catch (ClassNotFoundException e) {
 375  0
                 throw new RuntimeException("unable to complete deepCopy from src '" + src.toString() + "'", e);
 376  
             }
 377  
             finally {
 378  0
                 try {
 379  0
                     if (oos != null) {
 380  0
                         oos.close();
 381  
                     }
 382  0
                     if (ois != null) {
 383  0
                         ois.close();
 384  
                     }
 385  
                 }
 386  0
                 catch (IOException e) {
 387  
                     // ignoring this IOException, since the streams are going to be abandoned now anyway
 388  0
                 }
 389  0
             }
 390  
         }
 391  0
         return obj;
 392  
     }
 393  
 
 394  
     private void routeDocumentIfNecessary(DocumentRouteHeaderValue document, SimulationCriteria criteria, RouteContext routeContext) throws InvalidActionTakenException {
 395  0
             if (criteria.getRoutingUser() != null) {
 396  0
             ActionTakenValue action = createDummyActionTaken(document, criteria.getRoutingUser(), KewApiConstants.ACTION_TAKEN_ROUTED_CD, null);
 397  0
                     routeContext.getActivationContext().getSimulatedActionsTaken().add(action);
 398  0
             simulateDocumentRoute(action, document, criteria.getRoutingUser(), routeContext);
 399  
             }
 400  0
     }
 401  
 
 402  
     /**
 403  
      * Looks at the rule templates and/or the startNodeName and creates the appropriate node instances to run simulation against.
 404  
      * After creating the node instances, it hooks them all together and installs a "terminal" simulation node to stop the simulation
 405  
      * node at the end of the simulation.
 406  
      */
 407  
     private void installSimulationNodeInstances(RouteContext context, SimulationCriteria criteria) {
 408  0
             DocumentRouteHeaderValue document = context.getDocument();
 409  0
             List<RouteNode> simulationNodes = new ArrayList<RouteNode>();
 410  0
             if (!criteria.getNodeNames().isEmpty()) {
 411  0
                     for (String nodeName : criteria.getNodeNames()) {
 412  0
                                 if ( LOG.isDebugEnabled() ) {
 413  0
                                     LOG.debug("Installing simulation starting node '"+nodeName+"'");
 414  
                                 }
 415  0
                             List<RouteNode> nodes = KEWServiceLocator.getRouteNodeService().getFlattenedNodes(document.getDocumentType(), true);
 416  0
                             boolean foundNode = false;
 417  0
                             for (RouteNode node : nodes) {
 418  0
                                         if (node.getRouteNodeName().equals(nodeName)) {
 419  0
                                                 simulationNodes.add(node);
 420  0
                                                 foundNode = true;
 421  0
                                                 break;
 422  
                                         }
 423  
                                 }
 424  0
                             if (!foundNode) {
 425  0
                                     throw new IllegalArgumentException("Could not find node on the document type for the given name '"+nodeName+"'");
 426  
                             }
 427  0
                     }
 428  0
             } else if (!criteria.getRuleTemplateNames().isEmpty()) {
 429  0
                     List<RouteNode> nodes = KEWServiceLocator.getRouteNodeService().getFlattenedNodes(document.getDocumentType(), true);
 430  0
                     for (String ruleTemplateName : criteria.getRuleTemplateNames()) {
 431  0
                                 boolean foundNode = false;
 432  0
                                 for (RouteNode node : nodes) {
 433  0
                                         String routeMethodName = node.getRouteMethodName();
 434  0
                                         if (node.isFlexRM() && ruleTemplateName.equals(routeMethodName)) {
 435  0
                                                 simulationNodes.add(node);
 436  0
                                                 foundNode = true;
 437  0
                                                 break;
 438  
                                         }
 439  0
                                 }
 440  0
                                 if (!foundNode) {
 441  0
                                     throw new IllegalArgumentException("Could not find node on the document type with the given rule template name '"+ruleTemplateName+"'");
 442  
                             }
 443  0
                         }
 444  0
             } else if (criteria.isFlattenNodes()) {
 445  
                     // if they want to flatten the nodes, we will essentially process all simple nodes that are defined on the DocumentType
 446  0
             List<RouteNode> nodes = KEWServiceLocator.getRouteNodeService().getFlattenedNodes(document.getDocumentType(), true);
 447  0
             for ( RouteNode node : nodes ) {
 448  
                 try {
 449  0
                         if ( NodeType.fromNode( node ).isTypeOf( SimpleNode.class ) 
 450  
                                         && !NodeType.fromNode( node ).isTypeOf( NoOpNode.class ) ) {
 451  0
                             simulationNodes.add(node);
 452  
                         }
 453  0
                 } catch (ResourceUnavailableException ex) {
 454  0
                                         LOG.warn( "Unable to determine node type in simulator: " + ex.getMessage() );
 455  0
                                 }
 456  
             }
 457  0
             } else {
 458  
                 // in this case, we want to let the document proceed from it's current active node
 459  0
                     return;
 460  
             }
 461  
             
 462  
             // hook all of the simulation nodes together
 463  0
             Branch defaultBranch = document.getInitialRouteNodeInstances().get(0).getBranch();
 464  
             // clear out the initial route node instances, we are going to build a new node path based on what we want to simulate
 465  0
             document.getInitialRouteNodeInstances().clear();
 466  
 
 467  0
             RouteNodeInstance currentNodeInstance = null;//initialNodeInstance;
 468  0
             for (RouteNode simulationNode : simulationNodes) {
 469  0
                         RouteNodeInstance nodeInstance = helper.getNodeFactory().createRouteNodeInstance(document.getDocumentId(), simulationNode);
 470  0
                         nodeInstance.setBranch(defaultBranch);
 471  0
                         if (currentNodeInstance == null) {
 472  0
                                 document.getInitialRouteNodeInstances().add(nodeInstance);
 473  0
                                 nodeInstance.setActive(true);
 474  0
                                 saveNode(context, nodeInstance);
 475  
                         } else {
 476  0
                                 currentNodeInstance.addNextNodeInstance(nodeInstance);
 477  0
                                 saveNode(context, currentNodeInstance);
 478  
                         }
 479  0
                         currentNodeInstance = nodeInstance;
 480  0
                 }
 481  0
             installSimulationTerminationNode(context, document.getDocumentType(), currentNodeInstance);
 482  0
     }
 483  
 
 484  
     private void installSimulationTerminationNode(RouteContext context, DocumentType documentType, RouteNodeInstance lastNodeInstance) {
 485  0
             RouteNode terminationNode = new RouteNode();
 486  0
             terminationNode.setDocumentType(documentType);
 487  0
             terminationNode.setDocumentTypeId(documentType.getDocumentTypeId());
 488  0
             terminationNode.setNodeType(NoOpNode.class.getName());
 489  0
             terminationNode.setRouteNodeName("SIMULATION_TERMINATION_NODE");
 490  0
             RouteNodeInstance terminationNodeInstance = helper.getNodeFactory().createRouteNodeInstance(lastNodeInstance.getDocumentId(), terminationNode);
 491  0
             terminationNodeInstance.setBranch(lastNodeInstance.getBranch());
 492  0
             lastNodeInstance.addNextNodeInstance(terminationNodeInstance);
 493  0
             saveNode(context, lastNodeInstance);
 494  0
     }
 495  
 
 496  
     // below is pretty much a copy of RouteDocumentAction... but actions have to be faked for now
 497  
     private void simulateDocumentRoute(ActionTakenValue actionTaken, DocumentRouteHeaderValue document, Person user, RouteContext routeContext) throws InvalidActionTakenException {
 498  0
         if (document.isRouted()) {
 499  0
             throw new WorkflowRuntimeException("Document can not simulate a route if it has already been routed");
 500  
         }
 501  0
             ActionRequestService actionRequestService = KEWServiceLocator.getActionRequestService();
 502  
         // TODO delyea - deep copy below
 503  0
         List<ActionRequestValue> actionRequests = new ArrayList<ActionRequestValue>();
 504  0
         for (Iterator iter = actionRequestService.findPendingByDoc(document.getDocumentId()).iterator(); iter.hasNext();) {
 505  0
             ActionRequestValue arv = (ActionRequestValue) deepCopy( (ActionRequestValue) iter.next() );
 506  0
             for (ActionItem actionItem : arv.getActionItems()) {
 507  0
                         arv.getSimulatedActionItems().add((ActionItem) deepCopy(actionItem));
 508  
                 }
 509  0
             actionRequests.add(arv);//(ActionRequestValue)deepCopy(arv));
 510  0
         }
 511  
 //        actionRequests.addAll(actionRequestService.findPendingByDoc(document.getDocumentId()));
 512  0
         LOG.debug("Simulate Deactivating all pending action requests");
 513  
         // deactivate any requests for the user that routed the document.
 514  0
         for (Iterator<ActionRequestValue> iter = actionRequests.iterator(); iter.hasNext();) {
 515  0
             ActionRequestValue actionRequest = (ActionRequestValue) iter.next();
 516  
             // requests generated to the user who is routing the document should be deactivated
 517  0
             if ( (user.getPrincipalId().equals(actionRequest.getPrincipalId())) && (actionRequest.isActive()) ) {
 518  0
                     actionRequestService.deactivateRequest(actionTaken, actionRequest, routeContext.getActivationContext());
 519  
             }
 520  
             // requests generated by a save action should be deactivated
 521  0
             else if (KewApiConstants.SAVED_REQUEST_RESPONSIBILITY_ID.equals(actionRequest.getResponsibilityId())) {
 522  0
                     actionRequestService.deactivateRequest(actionTaken, actionRequest, routeContext.getActivationContext());
 523  
             }
 524  0
         }
 525  
 
 526  
 //        String oldStatus = document.getDocRouteStatus();
 527  0
         document.markDocumentEnroute();
 528  
 //        String newStatus = document.getDocRouteStatus();
 529  
 //        notifyStatusChange(newStatus, oldStatus);
 530  
 //        getRouteHeaderService().saveRouteHeader(document);
 531  0
     }
 532  
 
 533  
     private ActionTakenValue createDummyActionTaken(DocumentRouteHeaderValue routeHeader, Person userToPerformAction, String actionToPerform, Recipient delegator) {
 534  0
         ActionTakenValue val = new ActionTakenValue();
 535  0
         val.setActionTaken(actionToPerform);
 536  0
         if (KewApiConstants.ACTION_TAKEN_ROUTED_CD.equals(actionToPerform)) {
 537  0
             val.setActionTaken(KewApiConstants.ACTION_TAKEN_COMPLETED_CD);
 538  
         }
 539  0
                 val.setAnnotation("");
 540  0
                 val.setDocVersion(routeHeader.getDocVersion());
 541  0
                 val.setDocumentId(routeHeader.getDocumentId());
 542  0
                 val.setPrincipalId(userToPerformAction.getPrincipalId());
 543  
 
 544  0
                 if (delegator != null) {
 545  0
                         if (delegator instanceof KimPrincipalRecipient) {
 546  0
                                 val.setDelegatorPrincipalId(((KimPrincipalRecipient) delegator).getPrincipalId());
 547  0
                         } else if (delegator instanceof KimGroupRecipient) {
 548  0
                                 Group group = ((KimGroupRecipient) delegator).getGroup();
 549  0
                                 val.setDelegatorGroupId(group.getId());
 550  0
                         } else{
 551  0
                                 throw new IllegalArgumentException("Invalid Recipient type received: " + delegator.getClass().getName());
 552  
                         }
 553  
                 }
 554  
 
 555  
                 //val.setRouteHeader(routeHeader);
 556  0
                 val.setCurrentIndicator(Boolean.TRUE);
 557  0
                 return val;
 558  
     }
 559  
 
 560  
         /**
 561  
          * Used by actions taken
 562  
          *
 563  
          * Returns the highest priority delegator in the list of action requests.
 564  
          */
 565  
         private Recipient findDelegatorForActionRequests(List<ActionRequestValue> actionRequests) {
 566  0
                 return KEWServiceLocator.getActionRequestService().findDelegator(actionRequests);
 567  
         }
 568  
 
 569  
     /**
 570  
      * Executes a "saveNode" for the simulation engine, this does not actually save the document, but rather
 571  
      * assigns it some simulation ids.
 572  
      *
 573  
      * Resolves KULRICE-368
 574  
      */
 575  
     @Override
 576  
     protected void saveNode(RouteContext context, RouteNodeInstance nodeInstance) {
 577  
                 // we shold be in simulation mode here
 578  
 
 579  0
             if (nodeInstance.getRouteNodeInstanceId() == null) {
 580  0
                     nodeInstance.setRouteNodeInstanceId(context.getEngineState().getNextSimulationId());
 581  
             }
 582  
             
 583  
             // if we are in simulation mode, lets go ahead and assign some id
 584  
             // values to our beans
 585  0
             for (Iterator<RouteNodeInstance> iterator = nodeInstance.getNextNodeInstances().iterator(); iterator.hasNext();) {
 586  0
                     RouteNodeInstance routeNodeInstance = (RouteNodeInstance) iterator.next();
 587  0
                     if (routeNodeInstance.getRouteNodeInstanceId() == null) {
 588  0
                             routeNodeInstance.setRouteNodeInstanceId(context.getEngineState().getNextSimulationId());
 589  
                     }
 590  0
             }
 591  0
             if (nodeInstance.getProcess() != null && nodeInstance.getProcess().getRouteNodeInstanceId() == null) {
 592  0
                     nodeInstance.getProcess().setRouteNodeInstanceId(context.getEngineState().getNextSimulationId());
 593  
             }
 594  0
             if (nodeInstance.getBranch() != null && nodeInstance.getBranch().getBranchId() == null) {
 595  0
                     nodeInstance.getBranch().setBranchId(context.getEngineState().getNextSimulationId());
 596  
             }
 597  0
     }
 598  
 
 599  
 }