1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package org.kuali.rice.kew.engine;
17  
18  import org.apache.log4j.MDC;
19  import org.kuali.rice.core.framework.parameter.ParameterService;
20  import org.kuali.rice.kew.actionrequest.ActionRequestValue;
21  import org.kuali.rice.kew.api.doctype.IllegalDocumentTypeException;
22  import org.kuali.rice.kew.api.exception.InvalidActionTakenException;
23  import org.kuali.rice.kew.api.exception.WorkflowException;
24  import org.kuali.rice.kew.engine.node.Branch;
25  import org.kuali.rice.kew.engine.node.BranchState;
26  import org.kuali.rice.kew.engine.node.ProcessDefinitionBo;
27  import org.kuali.rice.kew.engine.node.ProcessResult;
28  import org.kuali.rice.kew.engine.node.RouteNodeInstance;
29  import org.kuali.rice.kew.engine.node.RouteNodeUtils;
30  import org.kuali.rice.kew.engine.node.service.RouteNodeService;
31  import org.kuali.rice.kew.engine.transition.Transition;
32  import org.kuali.rice.kew.engine.transition.TransitionEngine;
33  import org.kuali.rice.kew.engine.transition.TransitionEngineFactory;
34  import org.kuali.rice.kew.exception.RouteManagerException;
35  import org.kuali.rice.kew.framework.postprocessor.AfterProcessEvent;
36  import org.kuali.rice.kew.framework.postprocessor.BeforeProcessEvent;
37  import org.kuali.rice.kew.framework.postprocessor.DocumentLockingEvent;
38  import org.kuali.rice.kew.framework.postprocessor.DocumentRouteLevelChange;
39  import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange;
40  import org.kuali.rice.kew.framework.postprocessor.PostProcessor;
41  import org.kuali.rice.kew.framework.postprocessor.ProcessDocReport;
42  import org.kuali.rice.kew.postprocessor.DefaultPostProcessor;
43  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
44  import org.kuali.rice.kew.routeheader.service.RouteHeaderService;
45  import org.kuali.rice.kew.service.KEWServiceLocator;
46  import org.kuali.rice.kew.api.KewApiConstants;
47  import org.kuali.rice.kew.util.PerformanceLogger;
48  import org.kuali.rice.krad.util.KRADConstants;
49  
50  import java.util.ArrayList;
51  import java.util.Collection;
52  import java.util.Iterator;
53  import java.util.LinkedList;
54  import java.util.List;
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  public class StandardWorkflowEngine implements WorkflowEngine {
65  
66  	private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(StandardWorkflowEngine.class);
67  
68  	protected final RouteHelper helper = new RouteHelper();
69  	protected RouteNodeService routeNodeService;
70      protected RouteHeaderService routeHeaderService;
71      protected ParameterService parameterService;
72      protected OrchestrationConfig config;
73  
74      public StandardWorkflowEngine() {}
75  
76  	protected StandardWorkflowEngine(RouteNodeService routeNodeService, RouteHeaderService routeHeaderService, 
77  	        ParameterService parameterService, OrchestrationConfig config) {
78  	    this.routeNodeService = routeNodeService;
79  	    this.routeHeaderService = routeHeaderService;
80  	    this.parameterService = parameterService;
81  	    this.config = config;
82  	}
83  
84  
85  
86  
87  
88  	public boolean isRunPostProcessorLogic() {
89  	    return this.config.isRunPostProcessorLogic();
90  	}
91  
92  	public void process(String documentId, String nodeInstanceId) throws Exception {
93  		if (documentId == null) {
94  			throw new IllegalArgumentException("Cannot process a null document id.");
95  		}
96  		MDC.put("docId", documentId);
97  		boolean success = true;
98  		RouteContext context = RouteContext.createNewRouteContext();
99  		try {
100 			if ( LOG.isInfoEnabled() ) {
101 				LOG.info("Aquiring lock on document " + documentId);
102 			}
103 			KEWServiceLocator.getRouteHeaderService().lockRouteHeader(documentId, true);
104 			if ( LOG.isInfoEnabled() ) {
105 				LOG.info("Aquired lock on document " + documentId);
106 			}
107 
108 			DocumentRouteHeaderValue document = getRouteHeaderService().getRouteHeader(documentId);
109 			context.setDocument(document);
110 			lockAdditionalDocuments(document);
111 
112 			if ( LOG.isInfoEnabled() ) {
113 				LOG.info("Processing document: " + documentId + " : " + nodeInstanceId);
114 			}
115 
116 			try {
117 	            document = notifyPostProcessorBeforeProcess(document, nodeInstanceId);
118 	            context.setDocument(document);
119             } catch (Exception e) {
120                 LOG.warn("Problems contacting PostProcessor before engine process", e);
121                 throw new RouteManagerException("Problems contacting PostProcessor:  " + e.getMessage());
122             }
123             if (!document.isRoutable()) {
124 				LOG.debug("Document not routable so returning with doing no action");
125 				return;
126 			}
127 			List<RouteNodeInstance> nodeInstancesToProcess = new LinkedList<RouteNodeInstance>();
128 			if (nodeInstanceId == null) {
129 				
130 				nodeInstancesToProcess.addAll(RouteNodeUtils.getActiveNodeInstances(document));
131 			} else {
132 				RouteNodeInstance instanceNode = RouteNodeUtils.findRouteNodeInstanceById(nodeInstanceId,document);
133 				if (instanceNode == null) {
134 					throw new IllegalArgumentException("Invalid node instance id: " + nodeInstanceId);
135 				}
136 				nodeInstancesToProcess.add(instanceNode);
137 			}
138 
139 			context.setEngineState(new EngineState());
140 			ProcessContext processContext = new ProcessContext(true, nodeInstancesToProcess);
141 			try {
142 				while (!nodeInstancesToProcess.isEmpty()) {
143 					context.setNodeInstance((RouteNodeInstance) nodeInstancesToProcess.remove(0));
144 					processContext = processNodeInstance(context, helper);
145 					if (processContext.isComplete() && !processContext.getNextNodeInstances().isEmpty()) {
146 						nodeInstancesToProcess.addAll(processContext.getNextNodeInstances());
147 					}
148 				}
149 				context.setDocument(nodePostProcess(context));
150 			} catch (Exception e) {
151 				success = false;
152 				
153 				
154 				throw new RouteManagerException(e, context);
155 			}
156 		} finally {
157 			if ( LOG.isInfoEnabled() ) {
158 				LOG.info((success ? "Successfully processed" : "Failed to process") + " document: " + documentId + " : " + nodeInstanceId);
159 			}
160 			try {
161 	            notifyPostProcessorAfterProcess(context.getDocument(), nodeInstanceId, success);
162             } catch (Exception e) {
163                 LOG.warn("Problems contacting PostProcessor after engine process", e);
164                 throw new RouteManagerException("Problems contacting PostProcessor", e, context);
165             }
166 			RouteContext.clearCurrentRouteContext();
167 			MDC.remove("docId");
168 		}
169 	}
170 
171 	protected ProcessContext processNodeInstance(RouteContext context, RouteHelper helper) throws Exception {
172 		RouteNodeInstance nodeInstance = context.getNodeInstance();
173 		if ( LOG.isDebugEnabled() ) {
174 			LOG.debug("Processing node instance: " + nodeInstance.getRouteNode().getRouteNodeName());
175 		}
176 		if (checkAssertions(context)) {
177 			
178 			return new ProcessContext();
179 		}
180 		TransitionEngine transitionEngine = TransitionEngineFactory.createTransitionEngine(nodeInstance);
181 		ProcessResult processResult = transitionEngine.isComplete(context);
182 		nodeInstance.setInitial(false);
183 
184 		
185 		
186 		if (processResult.isComplete()) {
187 			if ( LOG.isDebugEnabled() ) {
188 				LOG.debug("Routing node has completed: " + nodeInstance.getRouteNode().getRouteNodeName());
189 			}
190 
191 			context.getEngineState().getCompleteNodeInstances().add(nodeInstance.getRouteNodeInstanceId());
192 			List nextNodeCandidates = invokeTransition(context, context.getNodeInstance(), processResult, transitionEngine);
193 
194 			
195 			
196 			
197 			
198 			
199 			List<RouteNodeInstance> nodesToActivate = new ArrayList<RouteNodeInstance>();
200 			if (!nextNodeCandidates.isEmpty()) {
201 				
202 				
203 				
204 				ArrayList<RouteNodeInstance> nextNodeInstances = new ArrayList<RouteNodeInstance>();
205 
206 				for (Iterator nextIt = nextNodeCandidates.iterator(); nextIt.hasNext();) {
207 					RouteNodeInstance nextNodeInstance = (RouteNodeInstance) nextIt.next();
208 					transitionEngine = TransitionEngineFactory.createTransitionEngine(nextNodeInstance);
209 					RouteNodeInstance currentNextNodeInstance = nextNodeInstance;
210 					nextNodeInstance = transitionEngine.transitionTo(nextNodeInstance, context);
211 					
212 					
213 					if (!currentNextNodeInstance.equals(nextNodeInstance)) {
214 						currentNextNodeInstance.getPreviousNodeInstances().remove(nodeInstance);
215 					}
216 					
217 					
218 					
219 					
220 					
221 					
222 					
223 					
224 					
225 					nextNodeInstance.getPreviousNodeInstances().remove(nodeInstance);
226 					nextNodeInstances.add(nextNodeInstance);
227 					handleBackwardCompatibility(context, nextNodeInstance);
228 					
229 					notifyNodeChange(context, nextNodeInstance);
230 					nodesToActivate.add(nextNodeInstance);
231  					
232  				}
233  				
234 				for (RouteNodeInstance nextNodeInstance : nextNodeInstances) {
235 					nodeInstance.addNextNodeInstance(nextNodeInstance);
236 				}
237  			}
238  
239  			
240 			nodeInstance.setComplete(true);
241 			nodeInstance.setActive(false);
242 			
243 			for (RouteNodeInstance nodeToActivate : nodesToActivate) {
244 				nodeToActivate.setActive(true);
245 			}
246 		} else {
247 		    nodeInstance.setComplete(false);
248         }
249 
250 		saveNode(context, nodeInstance);
251 		return new ProcessContext(nodeInstance.isComplete(), nodeInstance.getNextNodeInstances());
252 	}
253 
254 	
255 
256 
257 
258 
259 
260 
261 	private boolean checkAssertions(RouteContext context) throws Exception {
262 		if (context.getNodeInstance().isComplete()) {
263 			if ( LOG.isDebugEnabled() ) {
264 				LOG.debug("The node has already been completed: " + context.getNodeInstance().getRouteNode().getRouteNodeName());
265 			}
266 			return true;
267 		}
268 		if (isRunawayProcessDetected(context.getEngineState())) {
269 
270 			throw new WorkflowException("Detected runaway process.");
271 		}
272 		return false;
273 	}
274 
275 	
276 
277 
278 
279 
280 
281 
282 
283 
284 
285 
286 
287 
288 	
289 
290 
291 
292 
293 
294 
295 
296 
297 
298 
299 
300 
301 
302 	private List invokeTransition(RouteContext context, RouteNodeInstance nodeInstance, ProcessResult processResult, TransitionEngine transitionEngine) throws Exception {
303 		List nextNodeInstances = nodeInstance.getNextNodeInstances();
304 		if (nextNodeInstances.isEmpty()) {
305 			Transition result = transitionEngine.transitionFrom(context, processResult);
306 			nextNodeInstances = result.getNextNodeInstances();
307 			if (nextNodeInstances.isEmpty() && nodeInstance.isInProcess()) {
308 				transitionEngine = TransitionEngineFactory.createTransitionEngine(nodeInstance.getProcess());
309 				context.setNodeInstance(nodeInstance);
310 				nextNodeInstances = invokeTransition(context, nodeInstance.getProcess(), processResult, transitionEngine);
311 			}
312 		}
313 		return nextNodeInstances;
314 	}
315 
316 	
317 
318 
319 
320 
321 
322 
323 
324 
325 
326 
327 
328 
329 
330 
331 private void notifyNodeChange(RouteContext context, RouteNodeInstance nextNodeInstance) throws Exception {
332 		if (!context.isSimulation()) {
333 			RouteNodeInstance nodeInstance = context.getNodeInstance();
334 			
335 			String nextStatus = nodeInstance.getRouteNode().getNextDocStatus();
336 			if (nextStatus != null && nextStatus.length() > 0){
337 				context.getDocument().updateAppDocStatus(nextStatus);
338 			}
339 
340 			DocumentRouteLevelChange event = new DocumentRouteLevelChange(context.getDocument().getDocumentId(), context.getDocument().getAppDocId(), CompatUtils.getLevelForNode(context.getDocument().getDocumentType(), context.getNodeInstance()
341 					.getRouteNode().getRouteNodeName()), CompatUtils.getLevelForNode(context.getDocument().getDocumentType(), nextNodeInstance.getRouteNode().getRouteNodeName()), nodeInstance.getRouteNode().getRouteNodeName(), nextNodeInstance
342 					.getRouteNode().getRouteNodeName(), nodeInstance.getRouteNodeInstanceId(), nextNodeInstance.getRouteNodeInstanceId());
343 			context.setDocument(notifyPostProcessor(context.getDocument(), nodeInstance, event));
344 		}
345 	}
346 
347 	private void handleBackwardCompatibility(RouteContext context, RouteNodeInstance nextNodeInstance) {
348 		context.getDocument().setDocRouteLevel(new Integer(context.getDocument().getDocRouteLevel().intValue() + 1)); 
349 																														
350 																														
351 																														
352 																														
353 																														
354 		saveDocument(context);
355 	}
356 
357 	private void saveDocument(RouteContext context) {
358 		if (!context.isSimulation()) {
359 			getRouteHeaderService().saveRouteHeader(context.getDocument());
360 		}
361 	}
362 
363 	private void saveBranch(RouteContext context, Branch branch) {
364 		if (!context.isSimulation()) {
365 			KEWServiceLocator.getRouteNodeService().save(branch);
366 		}
367 	}
368 
369 	protected void saveNode(RouteContext context, RouteNodeInstance nodeInstance) {
370 		if (!context.isSimulation()) {
371 			getRouteNodeService().save(nodeInstance);
372 		} else {
373 			
374 			
375 			for (Iterator<RouteNodeInstance> iterator = nodeInstance.getNextNodeInstances().iterator(); iterator.hasNext();) {
376 				RouteNodeInstance routeNodeInstance = (RouteNodeInstance) iterator.next();
377 				if (routeNodeInstance.getRouteNodeInstanceId() == null) {
378 					routeNodeInstance.setRouteNodeInstanceId(context.getEngineState().getNextSimulationId());
379 				}
380 			}
381 			if (nodeInstance.getProcess() != null && nodeInstance.getProcess().getRouteNodeInstanceId() == null) {
382 				nodeInstance.getProcess().setRouteNodeInstanceId(context.getEngineState().getNextSimulationId());
383 			}
384 			if (nodeInstance.getBranch() != null && nodeInstance.getBranch().getBranchId() == null) {
385 				nodeInstance.getBranch().setBranchId(context.getEngineState().getNextSimulationId());
386 			}
387 		}
388 	}
389 
390 	
391 	
392 	protected DocumentRouteHeaderValue nodePostProcess(RouteContext context) throws InvalidActionTakenException {
393 		DocumentRouteHeaderValue document = context.getDocument();
394 		Collection<RouteNodeInstance> activeNodes = getRouteNodeService().getActiveNodeInstances(document.getDocumentId());
395 		boolean moreNodes = false;
396 		for (Iterator<RouteNodeInstance> iterator = activeNodes.iterator(); iterator.hasNext();) {
397 			RouteNodeInstance nodeInstance = (RouteNodeInstance) iterator.next();
398 			moreNodes = moreNodes || !nodeInstance.isComplete();
399 		}
400 		List pendingRequests = KEWServiceLocator.getActionRequestService().findPendingByDoc(document.getDocumentId());
401 		boolean activeApproveRequests = false;
402 		boolean activeAckRequests = false;
403 		for (Iterator iterator = pendingRequests.iterator(); iterator.hasNext();) {
404 			ActionRequestValue request = (ActionRequestValue) iterator.next();
405 			activeApproveRequests = request.isApproveOrCompleteRequest() || activeApproveRequests;
406 			activeAckRequests = request.isAcknowledgeRequest() || activeAckRequests;
407 		}
408 		
409 		if (!document.isProcessed() && (!moreNodes || !activeApproveRequests)) {
410 			if ( LOG.isDebugEnabled() ) {
411 				LOG.debug("No more nodes for this document " + document.getDocumentId());
412 			}
413 			
414 			checkDefaultApprovalPolicy(document);
415 
416 			LOG.debug("Marking document processed");
417 			DocumentRouteStatusChange event = new DocumentRouteStatusChange(document.getDocumentId(), document.getAppDocId(), document.getDocRouteStatus(), KewApiConstants.ROUTE_HEADER_PROCESSED_CD);
418 			document.markDocumentProcessed();
419 			
420 			notifyPostProcessor(context, event);
421 		}
422 
423 		
424 		
425 		if (document.isProcessed()) {
426 			DocumentRouteStatusChange event = new DocumentRouteStatusChange(document.getDocumentId(), document.getAppDocId(), document.getDocRouteStatus(), KewApiConstants.ROUTE_HEADER_FINAL_CD);
427 			List actionRequests = KEWServiceLocator.getActionRequestService().findPendingByDoc(document.getDocumentId());
428 			if (actionRequests.isEmpty()) {
429 				document.markDocumentFinalized();
430 				
431 				notifyPostProcessor(context, event);
432 			} else {
433 				boolean markFinalized = true;
434 				for (Iterator iter = actionRequests.iterator(); iter.hasNext();) {
435 					ActionRequestValue actionRequest = (ActionRequestValue) iter.next();
436 					if (KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ.equals(actionRequest.getActionRequested())) {
437 						markFinalized = false;
438 					}
439 				}
440 				if (markFinalized) {
441 					document.markDocumentFinalized();
442 					
443 					this.notifyPostProcessor(context, event);
444 				}
445 			}
446 		}
447 		saveDocument(context);
448 		return document;
449 	}
450 
451 	
452 
453 
454 
455 
456 
457 
458 
459 	private void checkDefaultApprovalPolicy(DocumentRouteHeaderValue document) throws RouteManagerException {
460 		if (!document.getDocumentType().getDefaultApprovePolicy().getPolicyValue().booleanValue()) {
461 			LOG.debug("Checking if any requests have been generated for the document");
462 			List requests = KEWServiceLocator.getActionRequestService().findAllActionRequestsByDocumentId(document.getDocumentId());
463 			boolean approved = false;
464 			for (Iterator iter = requests.iterator(); iter.hasNext();) {
465 				ActionRequestValue actionRequest = (ActionRequestValue) iter.next();
466 				if (actionRequest.isApproveOrCompleteRequest() && actionRequest.isDone()) { 
467 																							
468 																							
469 																							
470 																							
471 																							
472 																							
473 					LOG.debug("Found at least one processed approve request so document can be approved");
474 					approved = true;
475 					break;
476 				}
477 			}
478 			if (!approved) {
479 				LOG.debug("Document requires at least one request and none are present");
480 				
481 				throw new RouteManagerException("Document should have generated at least one approval request.");
482 			}
483 		}
484 	}
485 
486 	private DocumentRouteHeaderValue notifyPostProcessor(RouteContext context, DocumentRouteStatusChange event) {
487 		DocumentRouteHeaderValue document = context.getDocument();
488 		if (context.isSimulation()) {
489 			return document;
490 		}
491 		if (hasContactedPostProcessor(context, event)) {
492 			return document;
493 		}
494 		String documentId = event.getDocumentId();
495 		PerformanceLogger performanceLogger = new PerformanceLogger(documentId);
496 		ProcessDocReport processReport = null;
497 		PostProcessor postProc = null;
498         try {
499             
500             if (!isRunPostProcessorLogic()) {
501                 postProc = new DefaultPostProcessor();
502             } else {
503                 postProc = document.getDocumentType().getPostProcessor();
504             }
505         } catch (Exception e) {
506             LOG.error("Error retrieving PostProcessor for document " + document.getDocumentId(), e);
507             throw new RouteManagerException("Error retrieving PostProcessor for document " + document.getDocumentId(), e);
508         }
509 		try {
510 			processReport = postProc.doRouteStatusChange(event);
511 		} catch (Exception e) {
512 			LOG.error("Error notifying post processor", e);
513 			throw new RouteManagerException(KewApiConstants.POST_PROCESSOR_FAILURE_MESSAGE, e);
514 		} finally {
515 			performanceLogger.log("Time to notifyPostProcessor of event " + event.getDocumentEventCode() + ".");
516 		}
517 
518 		if (!processReport.isSuccess()) {
519 			LOG.warn("PostProcessor failed to process document: " + processReport.getMessage());
520 			throw new RouteManagerException(KewApiConstants.POST_PROCESSOR_FAILURE_MESSAGE + processReport.getMessage());
521 		}
522 		return document;
523 	}
524 
525 	
526 
527 
528 
529 
530 
531 
532 
533 
534 
535 	private boolean hasContactedPostProcessor(RouteContext context, DocumentRouteStatusChange event) {
536 		
537 		
538 		Branch rootBranch = context.getDocument().getRootBranch();
539 		String key = null;
540 		if (KewApiConstants.ROUTE_HEADER_PROCESSED_CD.equals(event.getNewRouteStatus())) {
541 			key = KewApiConstants.POST_PROCESSOR_PROCESSED_KEY;
542 		} else if (KewApiConstants.ROUTE_HEADER_FINAL_CD.equals(event.getNewRouteStatus())) {
543 			key = KewApiConstants.POST_PROCESSOR_FINAL_KEY;
544 		} else {
545 			return false;
546 		}
547 		BranchState branchState = null;
548 		if (rootBranch != null) {
549 		    branchState = rootBranch.getBranchState(key);
550 		} else {
551 		    return false;
552 		}
553 		if (branchState == null) {
554 			branchState = new BranchState();
555 			branchState.setKey(key);
556 			branchState.setValue("true");
557 			rootBranch.addBranchState(branchState);
558 			saveBranch(context, rootBranch);
559 			return false;
560 		}
561 		return "true".equals(branchState.getValue());
562 	}
563 
564 	
565 
566 
567 
568 
569 
570 
571 	private DocumentRouteHeaderValue notifyPostProcessor(DocumentRouteHeaderValue document, RouteNodeInstance nodeInstance, DocumentRouteLevelChange event) {
572 		getRouteHeaderService().saveRouteHeader(document);
573 		ProcessDocReport report = null;
574 		try {
575 	        PostProcessor postProcessor = null;
576 	        
577 	        if (!isRunPostProcessorLogic()) {
578 	            postProcessor = new DefaultPostProcessor();
579 	        } else {
580 	            postProcessor = document.getDocumentType().getPostProcessor();
581 	        }
582 			report = postProcessor.doRouteLevelChange(event);
583 		} catch (Exception e) {
584 			LOG.warn("Problems contacting PostProcessor", e);
585 			throw new RouteManagerException("Problems contacting PostProcessor:  " + e.getMessage());
586 		}
587 		document = getRouteHeaderService().getRouteHeader(document.getDocumentId());
588 		if (!report.isSuccess()) {
589 			LOG.error("PostProcessor rejected route level change::" + report.getMessage(), report.getProcessException());
590 			throw new RouteManagerException("Route Level change failed in post processor::" + report.getMessage());
591 		}
592 		return document;
593 	}
594 
595     
596 
597 
598 
599 	private DocumentRouteHeaderValue notifyPostProcessorBeforeProcess(DocumentRouteHeaderValue document, String nodeInstanceId) {
600 	    return notifyPostProcessorBeforeProcess(document, nodeInstanceId, new BeforeProcessEvent(document.getDocumentId(),document.getAppDocId(),nodeInstanceId));
601 	}
602 
603     
604 
605 
606 
607     private DocumentRouteHeaderValue notifyPostProcessorBeforeProcess(DocumentRouteHeaderValue document, String nodeInstanceId, BeforeProcessEvent event) {
608         ProcessDocReport report = null;
609         try {
610             PostProcessor postProcessor = null;
611             
612             if (!isRunPostProcessorLogic()) {
613                 postProcessor = new DefaultPostProcessor();
614             } else {
615                 postProcessor = document.getDocumentType().getPostProcessor();
616             }
617             report = postProcessor.beforeProcess(event);
618         } catch (Exception e) {
619             LOG.warn("Problems contacting PostProcessor", e);
620             throw new RouteManagerException("Problems contacting PostProcessor:  " + e.getMessage());
621         }
622         document = getRouteHeaderService().getRouteHeader(document.getDocumentId());
623         if (!report.isSuccess()) {
624             LOG.error("PostProcessor rejected route level change::" + report.getMessage(), report.getProcessException());
625             throw new RouteManagerException("Route Level change failed in post processor::" + report.getMessage());
626         }
627         return document;
628     }
629 
630     protected void lockAdditionalDocuments(DocumentRouteHeaderValue document) throws Exception {
631 		DocumentLockingEvent lockingEvent = new DocumentLockingEvent(document.getDocumentId(), document.getAppDocId());
632 		
633 		PostProcessor postProcessor = null;
634         
635         if (!isRunPostProcessorLogic()) {
636             postProcessor = new DefaultPostProcessor();
637         } else {
638             postProcessor = document.getDocumentType().getPostProcessor();
639         }
640         List<String> documentIdsToLock = postProcessor.getDocumentIdsToLock(lockingEvent);
641         if (documentIdsToLock != null && !documentIdsToLock.isEmpty()) {
642         	for (String documentId : documentIdsToLock) {
643         		if ( LOG.isInfoEnabled() ) {
644     				LOG.info("Aquiring additional lock on document " + documentId);
645     			}
646         		getRouteHeaderService().lockRouteHeader(documentId, true);
647         		if ( LOG.isInfoEnabled() ) {
648         			LOG.info("Aquired lock on document " + documentId);
649         		}
650         	}
651         }
652 	}
653 
654     
655 
656 
657 
658     private DocumentRouteHeaderValue notifyPostProcessorAfterProcess(DocumentRouteHeaderValue document, String nodeInstanceId, boolean successfullyProcessed) {
659     	if (document == null) {
660     		
661     		return null;
662     	}
663         return notifyPostProcessorAfterProcess(document, nodeInstanceId, new AfterProcessEvent(document.getDocumentId(),document.getAppDocId(),nodeInstanceId,successfullyProcessed));
664     }
665 
666     
667 
668 
669 
670     private DocumentRouteHeaderValue notifyPostProcessorAfterProcess(DocumentRouteHeaderValue document, String nodeInstanceId, AfterProcessEvent event) {
671         ProcessDocReport report = null;
672         try {
673             PostProcessor postProcessor = null;
674             
675             if (!isRunPostProcessorLogic()) {
676                 postProcessor = new DefaultPostProcessor();
677             } else {
678                 postProcessor = document.getDocumentType().getPostProcessor();
679             }
680             report = postProcessor.afterProcess(event);
681         } catch (Exception e) {
682             throw new RouteManagerException("Problems contacting PostProcessor.",e);
683         }
684         document = getRouteHeaderService().getRouteHeader(document.getDocumentId());
685         if (!report.isSuccess()) {
686             LOG.error("PostProcessor rejected route level change::" + report.getMessage(), report.getProcessException());
687             throw new RouteManagerException("Route Level change failed in post processor::" + report.getMessage());
688         }
689         return document;
690     }
691 
692 	
693 
694 
695 
696 	public void initializeDocument(DocumentRouteHeaderValue document) {
697 		
698 		
699 		
700 		
701 		
702 		
703 		RouteContext context = new RouteContext();
704 		context.setDocument(document);
705 		if (context.getEngineState() == null) {
706 			context.setEngineState(new EngineState());
707 		}
708 		ProcessDefinitionBo process = document.getDocumentType().getPrimaryProcess();
709 		if (process == null || process.getInitialRouteNode() == null) {
710 		    if (process == null) {
711 		        throw new IllegalDocumentTypeException("DocumentType '" + document.getDocumentType().getName() + "' has no primary process configured!");
712 		    }
713 			return;
714 		}
715 		RouteNodeInstance nodeInstance = helper.getNodeFactory().createRouteNodeInstance(document.getDocumentId(), process.getInitialRouteNode());
716 		nodeInstance.setActive(true);
717 		helper.getNodeFactory().createBranch(KewApiConstants.PRIMARY_BRANCH_NAME, null, nodeInstance);
718 		document.getInitialRouteNodeInstances().add(nodeInstance);
719 		saveNode(context, nodeInstance);
720 	}
721 
722     private boolean isRunawayProcessDetected(EngineState engineState) throws NumberFormatException {
723 	    String maxNodesConstant = getParameterService().getParameterValueAsString(KewApiConstants.KEW_NAMESPACE, KRADConstants.DetailTypes.ALL_DETAIL_TYPE, KewApiConstants.MAX_NODES_BEFORE_RUNAWAY_PROCESS);
724 	    int maxNodes = (org.apache.commons.lang.StringUtils.isEmpty(maxNodesConstant)) ? 50 : Integer.valueOf(maxNodesConstant);
725 	    return engineState.getCompleteNodeInstances().size() > maxNodes;
726 	}
727 
728     protected RouteNodeService getRouteNodeService() {
729 		return routeNodeService;
730 	}
731 
732 	protected RouteHeaderService getRouteHeaderService() {
733 		return routeHeaderService;
734 	}
735 
736     protected ParameterService getParameterService() {
737 		return parameterService;
738 	}
739 
740     public void setRouteNodeService(RouteNodeService routeNodeService) {
741         this.routeNodeService = routeNodeService;
742     }
743 
744     public void setRouteHeaderService(RouteHeaderService routeHeaderService) {
745         this.routeHeaderService = routeHeaderService;
746     }
747 
748     public void setParameterService(ParameterService parameterService) {
749         this.parameterService = parameterService;
750     }
751 }