001 /* 002 * Copyright 2005-2007 The Kuali Foundation 003 * 004 * 005 * Licensed under the Educational Community License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.opensource.org/licenses/ecl2.php 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.kuali.rice.kew.engine; 018 019 import java.io.Serializable; 020 021 import javax.xml.namespace.QName; 022 023 import org.junit.Test; 024 import org.kuali.rice.kew.dto.NetworkIdDTO; 025 import org.kuali.rice.kew.engine.node.Branch; 026 import org.kuali.rice.kew.engine.node.BranchState; 027 import org.kuali.rice.kew.exception.WorkflowRuntimeException; 028 import org.kuali.rice.kew.messaging.MessageServiceNames; 029 import org.kuali.rice.kew.messaging.exceptionhandling.DocumentMessageExceptionHandler; 030 import org.kuali.rice.kew.postprocessor.DefaultPostProcessor; 031 import org.kuali.rice.kew.postprocessor.DocumentRouteStatusChange; 032 import org.kuali.rice.kew.postprocessor.ProcessDocReport; 033 import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue; 034 import org.kuali.rice.kew.service.KEWServiceLocator; 035 import org.kuali.rice.kew.service.WorkflowDocument; 036 import org.kuali.rice.kew.test.KEWTestCase; 037 import org.kuali.rice.kew.test.TestUtilities; 038 import org.kuali.rice.kew.util.KEWConstants; 039 import org.kuali.rice.ksb.messaging.JavaServiceDefinition; 040 import org.kuali.rice.ksb.messaging.service.KSBJavaService; 041 import org.kuali.rice.ksb.service.KSBServiceLocator; 042 043 044 public class StandardWorkflowEngineTest extends KEWTestCase { 045 046 protected void loadTestData() throws Exception { 047 loadXmlFile("EngineConfig.xml"); 048 } 049 050 /** 051 * Tests that the proper state is set up on the root branch in the document 052 * to indicate that both PROCESSED and FINAL callbacks have been made into 053 * the post processor. 054 */ 055 @Test public void testSystemBranchState() throws Exception { 056 // route the document to final 057 WorkflowDocument document = new WorkflowDocument(new NetworkIdDTO("ewestfal"), "SimpleDocType"); 058 document.routeDocument(""); 059 assertTrue("Document should be final.", document.stateIsFinal()); 060 061 // now look at the branch state 062 DocumentRouteHeaderValue routeHeader = KEWServiceLocator.getRouteHeaderService().getRouteHeader(document.getRouteHeaderId()); 063 Branch rootBranch = routeHeader.getRootBranch(); 064 assertNotNull(rootBranch); 065 BranchState processedBranchState = rootBranch.getBranchState(KEWConstants.POST_PROCESSOR_PROCESSED_KEY); 066 BranchState finalBranchState = rootBranch.getBranchState(KEWConstants.POST_PROCESSOR_FINAL_KEY); 067 assertNotNull(processedBranchState); 068 assertNotNull(finalBranchState); 069 assertEquals("true", processedBranchState.getValue()); 070 assertEquals("true", finalBranchState.getValue()); 071 assertEquals(1, TestPostProcessor.processedCount); 072 assertEquals(1, TestPostProcessor.finalCount); 073 } 074 075 /** 076 * Tests that a FINAL document can go into exception routing and recover 077 * properly while only calling the PROCESSED and FINAL callbacks once. 078 */ 079 @Test public void testFinalDocumentExceptionRoutingRecovery() throws Exception { 080 081 // route the document to final 082 WorkflowDocument document = new WorkflowDocument(new NetworkIdDTO("ewestfal"), "SimpleDocType"); 083 document.routeDocument(""); 084 assertTrue("Document should be final.", document.stateIsFinal()); 085 assertEquals(1, TestPostProcessor.processedCount); 086 assertEquals(1, TestPostProcessor.finalCount); 087 088 // now queue up an exploder which should push the document into 089 // exception routing 090 JavaServiceDefinition serviceDef = new JavaServiceDefinition(); 091 serviceDef.setPriority(new Integer(1)); 092 serviceDef.setQueue(true); 093 serviceDef.setRetryAttempts(0); 094 serviceDef.setServiceInterface(KSBJavaService.class.getName()); 095 serviceDef.setServiceName(new QName("KEW", "exploader")); 096 serviceDef.setService(new ImTheExploderProcessor()); 097 098 serviceDef.setMessageExceptionHandler(DocumentMessageExceptionHandler.class.getName()); 099 serviceDef.validate(); 100 KSBServiceLocator.getServiceDeployer().registerService(serviceDef, true); 101 102 KSBJavaService exploderAsService = (KSBJavaService) MessageServiceNames.getServiceAsynchronously(new QName("KEW", "exploader"), KEWServiceLocator.getRouteHeaderService().getRouteHeader(document.getRouteHeaderId())); 103 exploderAsService.invoke(""); 104 // we need to make the exploder a service to get this going again... 105 // SpringServiceLocator.getRouteQueueService().requeueDocument(document.getRouteHeaderId(), 106 // ImTheExploderProcessor.class.getName()); 107 // fail("Should have exploded!!!"); 108 TestUtilities.waitForExceptionRouting(); 109 110 // the document should be in exception routing now 111 document = new WorkflowDocument(new NetworkIdDTO("ewestfal"), document.getRouteHeaderId()); 112 assertTrue("Document should be in exception routing.", document.stateIsException()); 113 assertEquals(1, TestPostProcessor.processedCount); 114 assertEquals(1, TestPostProcessor.finalCount); 115 116 assertTrue("ewestfal should have a complete request.", document.isCompletionRequested()); 117 document.complete(""); 118 119 // the document should be final once again 120 document = new WorkflowDocument(new NetworkIdDTO("ewestfal"), document.getRouteHeaderId()); 121 assertTrue("Document should be final.", document.stateIsFinal()); 122 assertEquals(1, TestPostProcessor.processedCount); 123 assertEquals(1, TestPostProcessor.finalCount); 124 } 125 126 public void tearDown() throws Exception { 127 try { 128 TestPostProcessor.resetProcessedCount(); 129 TestPostProcessor.resetFinalCount(); 130 } finally { 131 super.tearDown(); 132 } 133 } 134 135 public static class TestPostProcessor extends DefaultPostProcessor { 136 137 public static int finalCount = 0; 138 139 public static int processedCount = 0; 140 141 public ProcessDocReport doRouteStatusChange(DocumentRouteStatusChange statusChangeEvent) throws Exception { 142 if (KEWConstants.ROUTE_HEADER_PROCESSED_CD.equals(statusChangeEvent.getNewRouteStatus())) { 143 processedCount++; 144 } else if (KEWConstants.ROUTE_HEADER_FINAL_CD.equals(statusChangeEvent.getNewRouteStatus())) { 145 finalCount++; 146 } 147 return new ProcessDocReport(true); 148 } 149 150 public static void resetProcessedCount() { 151 processedCount = 0; 152 } 153 154 public static void resetFinalCount() { 155 finalCount = 0; 156 } 157 } 158 159 public static class ImTheExploderProcessor implements KSBJavaService { 160 161 public void invoke(Serializable payLoad) { 162 throw new WorkflowRuntimeException("I'm the Exploder!!!"); 163 } 164 165 } 166 167 }