View Javadoc
1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.engine;
17  
18  import org.junit.Test;
19  import org.kuali.rice.kew.actionrequest.ActionRequestValue;
20  import org.kuali.rice.kew.api.KewApiConstants;
21  import org.kuali.rice.kew.api.WorkflowDocument;
22  import org.kuali.rice.kew.api.WorkflowDocumentFactory;
23  import org.kuali.rice.kew.api.action.ActionRequestStatus;
24  import org.kuali.rice.kew.engine.node.RouteNodeInstance;
25  import org.kuali.rice.kew.service.KEWServiceLocator;
26  import org.kuali.rice.kew.test.KEWTestCase;
27  
28  import java.util.Collection;
29  import java.util.Iterator;
30  import java.util.List;
31  import java.util.Set;
32  
33  import static org.junit.Assert.*;
34  
35  public class ParallelRoutingTest extends KEWTestCase {
36      
37      private static final String DOCUMENT_TYPE_NAME = "ParallelDocType";
38      private static final String PARALLEL_EMPTY_DOCUMENT_TYPE_NAME = "ParallelEmptyDocType";
39      private static final String PARALLEL_EMPTY_DOCUMENT_TYPE_2_NAME = "ParallelEmptyDocType2";
40      private static final String ACKNOWLEDGE_1_NODE = "Acknowledge1";
41      private static final String WORKFLOW_DOCUMENT_2_NODE = "WorkflowDocument2";
42      private static final String WORKFLOW_DOCUMENT_3_NODE = "WorkflowDocument3";
43      private static final String JOIN_NODE = "Join";
44      private static final String WORKFLOW_DOCUMENT_FINAL_NODE = "WorkflowDocumentFinal";
45  	
46      protected void loadTestData() throws Exception {
47          loadXmlFile("EngineConfig.xml");
48      }
49  
50      @Test public void testParallelRoute() throws Exception {
51          WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"), DOCUMENT_TYPE_NAME);
52          document.saveDocumentData();
53          assertTrue("Document should be initiated", document.isInitiated());
54          assertEquals("Should be no action requests.", 0, document.getRootActionRequests().size());
55          Collection<RouteNodeInstance> nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId());
56          assertEquals("Wrong number of active nodes.", 1, nodeInstances.size());
57          document.route("Routing for parallel");
58          
59          // should have generated a request to "bmcgough"
60          document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("bmcgough"), document.getDocumentId());
61          assertTrue("Document should be enroute", document.isEnroute());
62          List<ActionRequestValue> actionRequests = KEWServiceLocator.getActionRequestService().findPendingByDoc(document.getDocumentId());
63          assertEquals("Incorrect pending action requests.", 1, actionRequests.size());
64          ActionRequestValue bRequest = actionRequests.get(0);
65          assertNotNull("Should have been routed through node instance.", bRequest.getNodeInstance());
66          assertTrue(document.isApprovalRequested());
67          
68          document.approve("Approving test");
69          
70          // document should split at this point and generate an ack to temay and approves to rkirkend and pmckown
71          document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("rkirkend"), document.getDocumentId());
72          assertTrue("Document should be enroute", document.isEnroute());
73          actionRequests = KEWServiceLocator.getActionRequestService().findPendingByDoc(document.getDocumentId());
74          assertEquals("Incorrect pending action requests.", 3, actionRequests.size());
75          boolean isToTemay = false;
76          boolean isToPmckown = false;
77          boolean isToRkirkend = false;
78          for (Iterator iterator = actionRequests.iterator(); iterator.hasNext();) {
79              ActionRequestValue actionRequest = (ActionRequestValue) iterator.next();
80              if (actionRequest.getPrincipalId().equals(getPrincipalIdForName("temay"))) {
81                  isToTemay = true;
82                  assertEquals("Request should be activated.", ActionRequestStatus.ACTIVATED.getCode(), actionRequest.getStatus());
83                  assertEquals("Wrong action requested.", KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, actionRequest.getActionRequested());
84                  assertNotNull("Should have been routed through node instance.", actionRequest.getNodeInstance());
85                  assertEquals("Invalid node.", ACKNOWLEDGE_1_NODE, actionRequest.getNodeInstance().getRouteNode().getRouteNodeName());
86              }
87              if (actionRequest.getPrincipalId().equals(getPrincipalIdForName("rkirkend"))) {
88                  isToRkirkend = true;
89                  assertEquals("Request should be activated.", ActionRequestStatus.ACTIVATED.getCode(), actionRequest.getStatus());
90                  assertEquals("Wrong action requested.", KewApiConstants.ACTION_REQUEST_APPROVE_REQ, actionRequest.getActionRequested());
91                  assertNotNull("Should have been routed through node instance.", actionRequest.getNodeInstance());
92                  assertEquals("Invalid node.", WORKFLOW_DOCUMENT_2_NODE, actionRequest.getNodeInstance().getRouteNode().getRouteNodeName());
93              }
94              if (actionRequest.getPrincipalId().equals(getPrincipalIdForName("pmckown"))) {
95                  isToPmckown = true;
96                  assertEquals("Request should be activated.", ActionRequestStatus.ACTIVATED.getCode(), actionRequest.getStatus());
97                  assertEquals("Wrong action requested.", KewApiConstants.ACTION_REQUEST_APPROVE_REQ, actionRequest.getActionRequested());
98                  assertNotNull("Should have been routed through node instance.", actionRequest.getNodeInstance());
99                  assertEquals("Invalid node.", WORKFLOW_DOCUMENT_3_NODE, actionRequest.getNodeInstance().getRouteNode().getRouteNodeName());
100             }
101         }
102         assertTrue("No request to temay.", isToTemay);
103         assertTrue("No request to pmckown.", isToPmckown);
104         assertTrue("No request to rkirkend.", isToRkirkend);
105         
106         // check that we are at both nodes, one in each branch
107         Set<String> nodeNames = document.getNodeNames();
108         assertEquals("Wrong number of node names.", 2, nodeNames.size() );
109         boolean isNode2 = false;
110         boolean isNode3 = false;
111         for (String name : nodeNames) {
112             if (name.equals(WORKFLOW_DOCUMENT_2_NODE)) {
113                 isNode2 = true;
114             }
115             if (name.equals(WORKFLOW_DOCUMENT_3_NODE)) {
116                 isNode3 = true;
117             }
118         }
119         assertTrue("Not at node2.", isNode2);
120         assertTrue("Not at node3.", isNode3);
121         nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId());
122         assertEquals("Wrong number of active nodes.", 2, nodeInstances.size());
123         Iterator<RouteNodeInstance> iterator = nodeInstances.iterator();
124         RouteNodeInstance instance1 = (RouteNodeInstance)iterator.next();
125         RouteNodeInstance instance2 = (RouteNodeInstance)iterator.next();
126         assertNotNull("Node should be in branch.", instance1.getBranch());
127         assertNotNull("Node should be in branch.", instance2.getBranch());
128         assertTrue("Branches should be different.", !instance1.getBranch().getBranchId().equals(instance2.getBranch().getBranchId()));
129         
130         document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("rkirkend"), document.getDocumentId());
131         assertTrue("Should have request.", document.isApprovalRequested());
132         document.approve("Git-r-dun");
133 
134         nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId());
135         assertEquals("Wrong number of active nodes.", 2, nodeInstances.size());
136         boolean isAtJoin = false;
137         boolean isAtWD3 = false;
138         for (RouteNodeInstance nodeInstance : nodeInstances) {
139             if (nodeInstance.getRouteNode().getRouteNodeName().equals(JOIN_NODE)) {
140                 assertEquals("Join branch should be split branch.", instance1.getBranch().getParentBranch().getBranchId(), nodeInstance.getBranch().getBranchId());
141                 isAtJoin = true;
142             }
143             if (nodeInstance.getRouteNode().getRouteNodeName().equals(WORKFLOW_DOCUMENT_3_NODE)) {
144                 isAtWD3 = true;
145             }
146         }
147         assertTrue("Not at join", isAtJoin);
148         assertTrue("Not at WD3", isAtWD3);
149         
150         document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("pmckown"), document.getDocumentId());
151         assertTrue("Should have request.", document.isApprovalRequested());
152         document.approve("Do it.");
153         
154         nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId());
155         assertEquals("Wrong number of active nodes.", 1, nodeInstances.size());
156         boolean isAtWDF = false;
157         for (RouteNodeInstance nodeInstance : nodeInstances) {
158             if (nodeInstance.getRouteNode().getRouteNodeName().equals(WORKFLOW_DOCUMENT_FINAL_NODE)) {
159                 isAtWDF = true;
160             }
161         }
162         assertTrue("Not at WDF", isAtWDF);
163         
164         document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("xqi"), document.getDocumentId());
165         assertTrue("Should still be enroute.", document.isEnroute());
166         assertTrue("Should have request.", document.isApprovalRequested());
167         document.approve("I'm the last approver");
168         
169         assertTrue("Document should be processed.", document.isProcessed());
170         nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId());
171         assertEquals("The doc is processed so no node instances should be active", 0, nodeInstances.size());
172 
173         document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("temay"), document.getDocumentId());
174         assertTrue("Should have request.", document.isAcknowledgeRequested());
175         document.acknowledge("");
176         assertTrue(document.isFinal());
177     }
178     
179     /**
180      * Tests that the document route past the join properly when there are parallel branches that don't generate requests.
181      * This was coded in response to a bug found while testing with ERA in order to track it down and fix it.
182      */
183     @Test public void testEmptyParallelBranches() throws Exception {
184         
185         WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"), PARALLEL_EMPTY_DOCUMENT_TYPE_NAME);
186         document.saveDocumentData();
187         assertTrue("Document should be initiated", document.isInitiated());
188         assertEquals("Should be no action requests.", 0, document.getRootActionRequests().size());
189         Collection<? extends Object> nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId());
190         assertEquals("Wrong number of active nodes.", 1, nodeInstances.size());
191         document.route("");
192         
193         // should have generated a request to "bmcgough"
194         document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("bmcgough"), document.getDocumentId());
195         assertTrue("Document should be enroute", document.isEnroute());
196         List<ActionRequestValue> actionRequests = KEWServiceLocator.getActionRequestService().findPendingByDoc(document.getDocumentId());
197         assertEquals("Incorrect pending action requests.", 1, actionRequests.size());
198         ActionRequestValue bRequest = actionRequests.get(0);
199         assertNotNull("Should have been routed through node instance.", bRequest.getNodeInstance());
200         assertTrue(document.isApprovalRequested());
201         
202         document.approve("");
203         
204         // now the document should have split, passed through nodes in each branch which didn't generate requests,
205         // and then passed the join node and generated requests at WorkflowDocumentFinal
206         document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("xqi"), document.getDocumentId());
207         assertTrue("Document should be enroute", document.isEnroute());
208         assertTrue(document.isApprovalRequested());
209         
210     }
211     
212     /**
213      * This runs the test with the adhoc approvers branch second instead of first
214      *//*
215     public void testEmptyParallelBranchesSwitched() throws Exception {
216         
217         WorkflowDocument document = WorkflowDocumentFactory.createDocument(new NetworkIdVO("ewestfal"), PARALLEL_EMPTY_DOCUMENT_TYPE_2_NAME);
218         document.saveDocumentData();
219         assertTrue("Document should be initiated", document.isInitiated());
220         assertEquals("Should be no action requests.", 0, document.getActionRequests().length);
221         assertEquals("Invalid route level.", new Integer(0), document.getRouteHeader().getDocRouteLevel());
222         Collection nodeInstances = SpringServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId());
223         assertEquals("Wrong number of active nodes.", 1, nodeInstances.size());
224         document.route("");
225         
226         // should have generated a request to "bmcgough"
227         document = WorkflowDocumentFactory.loadDocument(new NetworkIdVO("bmcgough"), document.getDocumentId());
228         assertTrue("Document should be enroute", document.isEnroute());
229         List actionRequests = TestUtilities.getActionRequestService().findPendingByDoc(document.getDocumentId());
230         assertEquals("Incorrect pending action requests.", 1, actionRequests.size());
231         ActionRequestValue bRequest = (ActionRequestValue)actionRequests.get(0);
232         assertNotNull("Should have been routed through node instance.", bRequest.getRouteNodeInstance());
233         assertTrue(document.isApprovalRequested());
234         
235         document.approve("");
236         
237         // now the document should have split, passed through nodes in each branch which didn't generate requests,
238         // and then passed the join node and generated requests at WorkflowDocumentFinal
239         document = WorkflowDocumentFactory.loadDocument(new NetworkIdVO("xqi"), document.getDocumentId());
240         assertTrue("Document should be enroute", document.isEnroute());
241         assertTrue(document.isApprovalRequested());
242         
243     }*/
244     
245     @Test public void testAdhocApproversJoinScenario() throws Exception {
246         WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"), "AdHocApproversDocType");
247         document.route("");
248         
249         // should send an approve to bmcgough
250         document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("bmcgough"), document.getDocumentId());
251         assertTrue("Bmcgough should have approve request.", document.isApprovalRequested());
252         document.approve("");
253         
254         // at this point the document should pass the split, and end up at the WorkflowDocument2 node and the AdHocApproversJoin node
255         // after bypassing the AdHocJoinPoint
256         Set<String> nodeNames = document.getNodeNames();
257         assertEquals("There should be two node names.", 2, nodeNames.size());
258         assertTrue("Should be at WorkflowDocument2 node.", nodeNames.contains("WorkflowDocument2"));
259         assertTrue("Should be at WorkflowDocument2 node.", nodeNames.contains("AdHocApproversJoin"));
260         
261         // pmckown has the request at the adhoc approvers node, if we approve as him then the document should _not_ transition out
262         // of it's current nodes
263         document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("pmckown"), document.getDocumentId());
264         assertTrue("Pmckown should have approve request.", document.isApprovalRequested());
265         document.approve("");
266         
267         // the document should still be at the same nodes
268         nodeNames = document.getNodeNames();
269         assertEquals("There should be two node names.", 2, nodeNames.size());
270         assertTrue("Should be at WorkflowDocument2 node.", nodeNames.contains("WorkflowDocument2"));
271         assertTrue("Should be at WorkflowDocument2 node.", nodeNames.contains("AdHocApproversJoin"));
272     
273         // at WorkflowDocument2, rkirkend is the approver, if we approve as him we should end up at the WorkflowDocumentFinal node
274         document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("rkirkend"), document.getDocumentId());
275         assertTrue("Rkirkend should have approve request.", document.isApprovalRequested());
276         document.approve("");
277         
278         // the document should now be at WorkflowDocumentFinal with a request to xqi
279         nodeNames = document.getNodeNames();
280         assertEquals("There should be one node name.", 1, nodeNames.size());
281         assertTrue("Should be at WorkflowDocumentFinal node.", nodeNames.contains("WorkflowDocumentFinal"));
282         
283         document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("xqi"), document.getDocumentId());
284         assertTrue("Document should still be enroute.", document.isEnroute());
285         document.approve("");
286         assertTrue("Document should now be final.", document.isFinal());
287         
288     }
289     
290 }