001/**
002 * Copyright 2005-2014 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 */
016package org.kuali.rice.kew.engine;
017
018import static org.junit.Assert.assertEquals;
019import static org.junit.Assert.assertNotNull;
020import static org.junit.Assert.assertTrue;
021
022import java.io.Serializable;
023
024import javax.xml.namespace.QName;
025
026import org.junit.Test;
027import org.kuali.rice.kew.api.KewApiServiceLocator;
028import org.kuali.rice.kew.api.WorkflowDocument;
029import org.kuali.rice.kew.api.WorkflowDocumentFactory;
030import org.kuali.rice.kew.api.WorkflowRuntimeException;
031import org.kuali.rice.kew.engine.node.Branch;
032import org.kuali.rice.kew.engine.node.BranchState;
033import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange;
034import org.kuali.rice.kew.framework.postprocessor.ProcessDocReport;
035import org.kuali.rice.kew.messaging.exceptionhandling.DocumentMessageExceptionHandler;
036import org.kuali.rice.kew.postprocessor.DefaultPostProcessor;
037import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
038import org.kuali.rice.kew.service.KEWServiceLocator;
039import org.kuali.rice.kew.test.KEWTestCase;
040import org.kuali.rice.kew.test.TestUtilities;
041import org.kuali.rice.kew.api.KewApiConstants;
042import org.kuali.rice.ksb.api.KsbApiServiceLocator;
043import org.kuali.rice.ksb.api.bus.support.JavaServiceDefinition;
044import org.kuali.rice.ksb.messaging.service.KSBJavaService;
045import org.kuali.rice.test.BaselineTestCase;
046
047@BaselineTestCase.BaselineMode(BaselineTestCase.Mode.CLEAR_DB)
048public 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}