|  1 |     | 
     | 
  |  2 |     | 
     | 
  |  3 |     | 
     | 
  |  4 |     | 
     | 
  |  5 |     | 
     | 
  |  6 |     | 
     | 
  |  7 |     | 
     | 
  |  8 |     | 
     | 
  |  9 |     | 
     | 
  |  10 |     | 
     | 
  |  11 |     | 
     | 
  |  12 |     | 
     | 
  |  13 |     | 
     | 
  |  14 |     | 
     | 
  |  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.doctype.bo.DocumentType;  | 
  |  28 |     | 
   import org.kuali.rice.kew.engine.ActivationContext;  | 
  |  29 |     | 
   import org.kuali.rice.kew.engine.EngineState;  | 
  |  30 |     | 
   import org.kuali.rice.kew.engine.ProcessContext;  | 
  |  31 |     | 
   import org.kuali.rice.kew.engine.RouteContext;  | 
  |  32 |     | 
   import org.kuali.rice.kew.engine.StandardWorkflowEngine;  | 
  |  33 |     | 
   import org.kuali.rice.kew.engine.node.Branch;  | 
  |  34 |     | 
   import org.kuali.rice.kew.engine.node.NoOpNode;  | 
  |  35 |     | 
   import org.kuali.rice.kew.engine.node.NodeJotter;  | 
  |  36 |     | 
   import org.kuali.rice.kew.engine.node.NodeType;  | 
  |  37 |     | 
   import org.kuali.rice.kew.engine.node.Process;  | 
  |  38 |     | 
   import org.kuali.rice.kew.engine.node.RequestsNode;  | 
  |  39 |     | 
   import org.kuali.rice.kew.engine.node.RouteNode;  | 
  |  40 |     | 
   import org.kuali.rice.kew.engine.node.RouteNodeInstance;  | 
  |  41 |     | 
   import org.kuali.rice.kew.engine.node.SimpleNode;  | 
  |  42 |     | 
   import org.kuali.rice.kew.exception.DocumentSimulatedRouteException;  | 
  |  43 |     | 
   import org.kuali.rice.kew.exception.InvalidActionTakenException;  | 
  |  44 |     | 
   import org.kuali.rice.kew.exception.ResourceUnavailableException;  | 
  |  45 |     | 
   import org.kuali.rice.kew.exception.WorkflowRuntimeException;  | 
  |  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.bo.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 |     | 
     | 
  |  71 |     | 
     | 
  |  72 |     | 
     | 
  |  73 |     | 
     | 
  |  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 |     | 
                 | 
  |  92 |    0 |                this.criteria = null;  | 
  |  93 |    0 |                this.results = null;  | 
  |  94 |     | 
           }  | 
  |  95 |     | 
       }  | 
  |  96 |     | 
     | 
  |  97 |     | 
       @Override  | 
  |  98 |     | 
       public void process(Long documentId, Long 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 |     | 
                         | 
  |  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.getRouteHeaderId();  | 
  |  119 |     | 
                         | 
  |  120 |     | 
                         | 
  |  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 |     | 
                                 | 
  |  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 |     | 
     | 
  |  178 |     | 
     | 
  |  179 |     | 
     | 
  |  180 |     | 
     | 
  |  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 |     | 
                     | 
  |  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<Long>());  | 
  |  209 |    0 |            }  | 
  |  210 |    0 |            return isInPath;  | 
  |  211 |     | 
       }  | 
  |  212 |     | 
     | 
  |  213 |     | 
       private boolean isNodeNameInPath(String nodeName, RouteNode node, Set<Long> 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 |     | 
     | 
  |  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 route header id given, cannot simulate a document without a document type name or a route header id."); | 
  |  289 |     | 
               }  | 
  |  290 |    0 |                if (criteria.getXmlContent() == null) { | 
  |  291 |    0 |                        criteria.setXmlContent(""); | 
  |  292 |     | 
               }  | 
  |  293 |    0 |        }  | 
  |  294 |     | 
     | 
  |  295 |     | 
         | 
  |  296 |     | 
     | 
  |  297 |     | 
     | 
  |  298 |     | 
     | 
  |  299 |     | 
     | 
  |  300 |     | 
     | 
  |  301 |     | 
     | 
  |  302 |     | 
       private DocumentRouteHeaderValue createSimulationDocument(Long 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();  | 
  |  315 |    0 |                    document = new DocumentRouteHeaderValue();  | 
  |  316 |    0 |                    context.setDocument(document);  | 
  |  317 |    0 |                    document.setRouteHeaderId(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(Long 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 |     | 
                         | 
  |  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 |     | 
     | 
  |  392 |     | 
     | 
  |  393 |     | 
     | 
  |  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 |     | 
                         | 
  |  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 |     | 
                     | 
  |  447 |    0 |                        return;  | 
  |  448 |     | 
               }  | 
  |  449 |     | 
                 | 
  |  450 |     | 
                 | 
  |  451 |    0 |                Branch defaultBranch = document.getInitialRouteNodeInstances().get(0).getBranch();  | 
  |  452 |     | 
                 | 
  |  453 |    0 |                document.getInitialRouteNodeInstances().clear();  | 
  |  454 |     | 
     | 
  |  455 |    0 |                RouteNodeInstance currentNodeInstance = null;  | 
  |  456 |    0 |                for (RouteNode simulationNode : simulationNodes) { | 
  |  457 |    0 |                            RouteNodeInstance nodeInstance = helper.getNodeFactory().createRouteNodeInstance(document.getRouteHeaderId(), 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 |     | 
         | 
  |  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 |     | 
             | 
  |  491 |    0 |            List<ActionRequestValue> actionRequests = new ArrayList<ActionRequestValue>();  | 
  |  492 |    0 |            for (Iterator iter = actionRequestService.findPendingByDoc(document.getRouteHeaderId()).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);  | 
  |  498 |    0 |            }  | 
  |  499 |     | 
     | 
  |  500 |    0 |            LOG.debug("Simulate Deactivating all pending action requests"); | 
  |  501 |     | 
             | 
  |  502 |    0 |            for (Iterator<ActionRequestValue> iter = actionRequests.iterator(); iter.hasNext();) { | 
  |  503 |    0 |                ActionRequestValue actionRequest = (ActionRequestValue) iter.next();  | 
  |  504 |     | 
                 | 
  |  505 |    0 |                if ( (user.getPrincipalId().equals(actionRequest.getPrincipalId())) && (actionRequest.isActive()) ) { | 
  |  506 |    0 |                        actionRequestService.deactivateRequest(actionTaken, actionRequest, routeContext.getActivationContext());  | 
  |  507 |     | 
               }  | 
  |  508 |     | 
                 | 
  |  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 |     | 
     | 
  |  515 |    0 |            document.markDocumentEnroute();  | 
  |  516 |     | 
     | 
  |  517 |     | 
     | 
  |  518 |     | 
     | 
  |  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.setRouteHeaderId(routeHeader.getRouteHeaderId());  | 
  |  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.getGroupId());  | 
  |  538 |    0 |                            } else{ | 
  |  539 |    0 |                                    throw new IllegalArgumentException("Invalid Recipient type received: " + delegator.getClass().getName()); | 
  |  540 |     | 
                           }  | 
  |  541 |     | 
                   }  | 
  |  542 |     | 
     | 
  |  543 |     | 
                     | 
  |  544 |    0 |                    val.setCurrentIndicator(Boolean.TRUE);  | 
  |  545 |    0 |                    return val;  | 
  |  546 |     | 
       }  | 
  |  547 |     | 
     | 
  |  548 |     | 
             | 
  |  549 |     | 
     | 
  |  550 |     | 
     | 
  |  551 |     | 
     | 
  |  552 |     | 
     | 
  |  553 |     | 
           private Recipient findDelegatorForActionRequests(List<ActionRequestValue> actionRequests) { | 
  |  554 |    0 |                    return KEWServiceLocator.getActionRequestService().findDelegator(actionRequests);  | 
  |  555 |     | 
           }  | 
  |  556 |     | 
     | 
  |  557 |     | 
         | 
  |  558 |     | 
     | 
  |  559 |     | 
     | 
  |  560 |     | 
     | 
  |  561 |     | 
     | 
  |  562 |     | 
     | 
  |  563 |     | 
       @Override  | 
  |  564 |     | 
       protected void saveNode(RouteContext context, RouteNodeInstance nodeInstance) { | 
  |  565 |     | 
                     | 
  |  566 |     | 
     | 
  |  567 |    0 |                if (nodeInstance.getRouteNodeInstanceId() == null) { | 
  |  568 |    0 |                        nodeInstance.setRouteNodeInstanceId(context.getEngineState().getNextSimulationId());  | 
  |  569 |     | 
               }  | 
  |  570 |     | 
                 | 
  |  571 |     | 
                 | 
  |  572 |     | 
                 | 
  |  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 |     | 
   }  |