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