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