001 /**
002 * Copyright 2005-2011 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
019 import static org.junit.Assert.assertEquals;
020 import static org.junit.Assert.assertNotNull;
021 import static org.junit.Assert.assertTrue;
022
023 import java.util.Collection;
024 import java.util.Iterator;
025 import java.util.List;
026 import java.util.Set;
027
028 import org.junit.Test;
029 import org.kuali.rice.kew.actionrequest.ActionRequestValue;
030 import org.kuali.rice.kew.api.WorkflowDocument;
031 import org.kuali.rice.kew.api.WorkflowDocumentFactory;
032 import org.kuali.rice.kew.api.action.ActionRequestStatus;
033 import org.kuali.rice.kew.engine.node.RouteNodeInstance;
034 import org.kuali.rice.kew.service.KEWServiceLocator;
035 import org.kuali.rice.kew.test.KEWTestCase;
036 import org.kuali.rice.kew.api.KewApiConstants;
037
038 public class ParallelRoutingTest extends KEWTestCase {
039
040 private static final String DOCUMENT_TYPE_NAME = "ParallelDocType";
041 private static final String PARALLEL_EMPTY_DOCUMENT_TYPE_NAME = "ParallelEmptyDocType";
042 private static final String PARALLEL_EMPTY_DOCUMENT_TYPE_2_NAME = "ParallelEmptyDocType2";
043 private static final String ACKNOWLEDGE_1_NODE = "Acknowledge1";
044 private static final String WORKFLOW_DOCUMENT_2_NODE = "WorkflowDocument2";
045 private static final String WORKFLOW_DOCUMENT_3_NODE = "WorkflowDocument3";
046 private static final String JOIN_NODE = "Join";
047 private static final String WORKFLOW_DOCUMENT_FINAL_NODE = "WorkflowDocumentFinal";
048
049 protected void loadTestData() throws Exception {
050 loadXmlFile("EngineConfig.xml");
051 }
052
053 @Test public void testParallelRoute() throws Exception {
054 WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"), DOCUMENT_TYPE_NAME);
055 document.saveDocumentData();
056 assertTrue("Document should be initiated", document.isInitiated());
057 assertEquals("Should be no action requests.", 0, document.getRootActionRequests().size());
058 Collection nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId());
059 assertEquals("Wrong number of active nodes.", 1, nodeInstances.size());
060 document.route("Routing for parallel");
061
062 // should have generated a request to "bmcgough"
063 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("bmcgough"), document.getDocumentId());
064 assertTrue("Document should be enroute", document.isEnroute());
065 List actionRequests = KEWServiceLocator.getActionRequestService().findPendingByDoc(document.getDocumentId());
066 assertEquals("Incorrect pending action requests.", 1, actionRequests.size());
067 ActionRequestValue bRequest = (ActionRequestValue)actionRequests.get(0);
068 assertNotNull("Should have been routed through node instance.", bRequest.getNodeInstance());
069 assertTrue(document.isApprovalRequested());
070
071 document.approve("Approving test");
072
073 // document should split at this point and generate an ack to temay and approves to rkirkend and pmckown
074 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("rkirkend"), document.getDocumentId());
075 assertTrue("Document should be enroute", document.isEnroute());
076 actionRequests = KEWServiceLocator.getActionRequestService().findPendingByDoc(document.getDocumentId());
077 assertEquals("Incorrect pending action requests.", 3, actionRequests.size());
078 boolean isToTemay = false;
079 boolean isToPmckown = false;
080 boolean isToRkirkend = false;
081 for (Iterator iterator = actionRequests.iterator(); iterator.hasNext();) {
082 ActionRequestValue actionRequest = (ActionRequestValue) iterator.next();
083 if (actionRequest.getPrincipalId().equals(getPrincipalIdForName("temay"))) {
084 isToTemay = true;
085 assertEquals("Request should be activated.", ActionRequestStatus.ACTIVATED.getCode(), actionRequest.getStatus());
086 assertEquals("Wrong action requested.", KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, actionRequest.getActionRequested());
087 assertNotNull("Should have been routed through node instance.", actionRequest.getNodeInstance());
088 assertEquals("Invalid node.", ACKNOWLEDGE_1_NODE, actionRequest.getNodeInstance().getRouteNode().getRouteNodeName());
089 }
090 if (actionRequest.getPrincipalId().equals(getPrincipalIdForName("rkirkend"))) {
091 isToRkirkend = true;
092 assertEquals("Request should be activated.", ActionRequestStatus.ACTIVATED.getCode(), actionRequest.getStatus());
093 assertEquals("Wrong action requested.", KewApiConstants.ACTION_REQUEST_APPROVE_REQ, actionRequest.getActionRequested());
094 assertNotNull("Should have been routed through node instance.", actionRequest.getNodeInstance());
095 assertEquals("Invalid node.", WORKFLOW_DOCUMENT_2_NODE, actionRequest.getNodeInstance().getRouteNode().getRouteNodeName());
096 }
097 if (actionRequest.getPrincipalId().equals(getPrincipalIdForName("pmckown"))) {
098 isToPmckown = true;
099 assertEquals("Request should be activated.", ActionRequestStatus.ACTIVATED.getCode(), actionRequest.getStatus());
100 assertEquals("Wrong action requested.", KewApiConstants.ACTION_REQUEST_APPROVE_REQ, actionRequest.getActionRequested());
101 assertNotNull("Should have been routed through node instance.", actionRequest.getNodeInstance());
102 assertEquals("Invalid node.", WORKFLOW_DOCUMENT_3_NODE, actionRequest.getNodeInstance().getRouteNode().getRouteNodeName());
103 }
104 }
105 assertTrue("No request to temay.", isToTemay);
106 assertTrue("No request to pmckown.", isToPmckown);
107 assertTrue("No request to rkirkend.", isToRkirkend);
108
109 // check that we are at both nodes, one in each branch
110 Set<String> nodeNames = document.getNodeNames();
111 assertEquals("Wrong number of node names.", 2, nodeNames.size() );
112 boolean isNode2 = false;
113 boolean isNode3 = false;
114 for (String name : nodeNames) {
115 if (name.equals(WORKFLOW_DOCUMENT_2_NODE)) {
116 isNode2 = true;
117 }
118 if (name.equals(WORKFLOW_DOCUMENT_3_NODE)) {
119 isNode3 = true;
120 }
121 }
122 assertTrue("Not at node2.", isNode2);
123 assertTrue("Not at node3.", isNode3);
124 nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId());
125 assertEquals("Wrong number of active nodes.", 2, nodeInstances.size());
126 Iterator iterator = nodeInstances.iterator();
127 RouteNodeInstance instance1 = (RouteNodeInstance)iterator.next();
128 RouteNodeInstance instance2 = (RouteNodeInstance)iterator.next();
129 assertNotNull("Node should be in branch.", instance1.getBranch());
130 assertNotNull("Node should be in branch.", instance2.getBranch());
131 assertTrue("Branches should be different.", !instance1.getBranch().getBranchId().equals(instance2.getBranch().getBranchId()));
132
133 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("rkirkend"), document.getDocumentId());
134 assertTrue("Should have request.", document.isApprovalRequested());
135 document.approve("Git-r-dun");
136
137 nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId());
138 assertEquals("Wrong number of active nodes.", 2, nodeInstances.size());
139 boolean isAtJoin = false;
140 boolean isAtWD3 = false;
141 for (Iterator iter = nodeInstances.iterator(); iter.hasNext();) {
142 RouteNodeInstance nodeInstance = (RouteNodeInstance) iter.next();
143 if (nodeInstance.getRouteNode().getRouteNodeName().equals(JOIN_NODE)) {
144 assertEquals("Join branch should be split branch.", instance1.getBranch().getParentBranch().getBranchId(), nodeInstance.getBranch().getBranchId());
145 isAtJoin = true;
146 }
147 if (nodeInstance.getRouteNode().getRouteNodeName().equals(WORKFLOW_DOCUMENT_3_NODE)) {
148 isAtWD3 = true;
149 }
150 }
151 assertTrue("Not at join", isAtJoin);
152 assertTrue("Not at WD3", isAtWD3);
153
154 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("pmckown"), document.getDocumentId());
155 assertTrue("Should have request.", document.isApprovalRequested());
156 document.approve("Do it.");
157
158 nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId());
159 assertEquals("Wrong number of active nodes.", 1, nodeInstances.size());
160 boolean isAtWDF = false;
161 for (Iterator iter = nodeInstances.iterator(); iter.hasNext();) {
162 RouteNodeInstance nodeInstance = (RouteNodeInstance) iter.next();
163 if (nodeInstance.getRouteNode().getRouteNodeName().equals(WORKFLOW_DOCUMENT_FINAL_NODE)) {
164 isAtWDF = true;
165 }
166 }
167 assertTrue("Not at WDF", isAtWDF);
168
169 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("xqi"), document.getDocumentId());
170 assertTrue("Should still be enroute.", document.isEnroute());
171 assertTrue("Should have request.", document.isApprovalRequested());
172 document.approve("I'm the last approver");
173
174 assertTrue("Document should be processed.", document.isProcessed());
175 nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId());
176 //commented out because the final RouteNodeInstance is now not active when the doc goes final
177 // assertEquals("Wrong number of active nodes.", 1, nodeInstances.size());
178 // isAtWDF = false;
179 // for (Iterator iter = nodeInstances.iterator(); iter.hasNext();) {
180 // RouteNodeInstance nodeInstance = (RouteNodeInstance) iter.next();
181 // if (nodeInstance.getRouteNode().getRouteNodeName().equals(WORKFLOW_DOCUMENT_FINAL_NODE)) {
182 // isAtWDF = true;
183 // }
184 // }
185 // assertTrue("Not at WDF", isAtWDF);
186
187 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("temay"), document.getDocumentId());
188 assertTrue("Should have request.", document.isAcknowledgeRequested());
189 document.acknowledge("");
190 assertTrue(document.isFinal());
191 }
192
193 /**
194 * Tests that the document route past the join properly when there are parallel branches that don't generate requests.
195 * This was coded in response to a bug found while testing with ERA in order to track it down and fix it.
196 */
197 @Test public void testEmptyParallelBranches() throws Exception {
198
199 WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"), PARALLEL_EMPTY_DOCUMENT_TYPE_NAME);
200 document.saveDocumentData();
201 assertTrue("Document should be initiated", document.isInitiated());
202 assertEquals("Should be no action requests.", 0, document.getRootActionRequests().size());
203 Collection<? extends Object> nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId());
204 assertEquals("Wrong number of active nodes.", 1, nodeInstances.size());
205 document.route("");
206
207 // should have generated a request to "bmcgough"
208 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("bmcgough"), document.getDocumentId());
209 assertTrue("Document should be enroute", document.isEnroute());
210 List actionRequests = KEWServiceLocator.getActionRequestService().findPendingByDoc(document.getDocumentId());
211 assertEquals("Incorrect pending action requests.", 1, actionRequests.size());
212 ActionRequestValue bRequest = (ActionRequestValue)actionRequests.get(0);
213 assertNotNull("Should have been routed through node instance.", bRequest.getNodeInstance());
214 assertTrue(document.isApprovalRequested());
215
216 document.approve("");
217
218 // now the document should have split, passed through nodes in each branch which didn't generate requests,
219 // and then passed the join node and generated requests at WorkflowDocumentFinal
220 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("xqi"), document.getDocumentId());
221 assertTrue("Document should be enroute", document.isEnroute());
222 assertTrue(document.isApprovalRequested());
223
224 }
225
226 /**
227 * This runs the test with the adhoc approvers branch second instead of first
228 *//*
229 public void testEmptyParallelBranchesSwitched() throws Exception {
230
231 WorkflowDocument document = WorkflowDocumentFactory.createDocument(new NetworkIdVO("ewestfal"), PARALLEL_EMPTY_DOCUMENT_TYPE_2_NAME);
232 document.saveDocumentData();
233 assertTrue("Document should be initiated", document.isInitiated());
234 assertEquals("Should be no action requests.", 0, document.getActionRequests().length);
235 assertEquals("Invalid route level.", new Integer(0), document.getRouteHeader().getDocRouteLevel());
236 Collection nodeInstances = SpringServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId());
237 assertEquals("Wrong number of active nodes.", 1, nodeInstances.size());
238 document.route("");
239
240 // should have generated a request to "bmcgough"
241 document = WorkflowDocumentFactory.loadDocument(new NetworkIdVO("bmcgough"), document.getDocumentId());
242 assertTrue("Document should be enroute", document.isEnroute());
243 List actionRequests = TestUtilities.getActionRequestService().findPendingByDoc(document.getDocumentId());
244 assertEquals("Incorrect pending action requests.", 1, actionRequests.size());
245 ActionRequestValue bRequest = (ActionRequestValue)actionRequests.get(0);
246 assertNotNull("Should have been routed through node instance.", bRequest.getRouteNodeInstance());
247 assertTrue(document.isApprovalRequested());
248
249 document.approve("");
250
251 // now the document should have split, passed through nodes in each branch which didn't generate requests,
252 // and then passed the join node and generated requests at WorkflowDocumentFinal
253 document = WorkflowDocumentFactory.loadDocument(new NetworkIdVO("xqi"), document.getDocumentId());
254 assertTrue("Document should be enroute", document.isEnroute());
255 assertTrue(document.isApprovalRequested());
256
257 }*/
258
259 @Test public void testAdhocApproversJoinScenario() throws Exception {
260 WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"), "AdHocApproversDocType");
261 document.route("");
262
263 // should send an approve to bmcgough
264 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("bmcgough"), document.getDocumentId());
265 assertTrue("Bmcgough should have approve request.", document.isApprovalRequested());
266 document.approve("");
267
268 // at this point the document should pass the split, and end up at the WorkflowDocument2 node and the AdHocApproversJoin node
269 // after bypassing the AdHocJoinPoint
270 Set<String> nodeNames = document.getNodeNames();
271 assertEquals("There should be two node names.", 2, nodeNames.size());
272 assertTrue("Should be at WorkflowDocument2 node.", nodeNames.contains("WorkflowDocument2"));
273 assertTrue("Should be at WorkflowDocument2 node.", nodeNames.contains("AdHocApproversJoin"));
274
275 // pmckown has the request at the adhoc approvers node, if we approve as him then the document should _not_ transition out
276 // of it's current nodes
277 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("pmckown"), document.getDocumentId());
278 assertTrue("Pmckown should have approve request.", document.isApprovalRequested());
279 document.approve("");
280
281 // the document should still be at the same nodes
282 nodeNames = document.getNodeNames();
283 assertEquals("There should be two node names.", 2, nodeNames.size());
284 assertTrue("Should be at WorkflowDocument2 node.", nodeNames.contains("WorkflowDocument2"));
285 assertTrue("Should be at WorkflowDocument2 node.", nodeNames.contains("AdHocApproversJoin"));
286
287 // at WorkflowDocument2, rkirkend is the approver, if we approve as him we should end up at the WorkflowDocumentFinal node
288 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("rkirkend"), document.getDocumentId());
289 assertTrue("Rkirkend should have approve request.", document.isApprovalRequested());
290 document.approve("");
291
292 // the document should now be at WorkflowDocumentFinal with a request to xqi
293 nodeNames = document.getNodeNames();
294 assertEquals("There should be one node name.", 1, nodeNames.size());
295 assertTrue("Should be at WorkflowDocumentFinal node.", nodeNames.contains("WorkflowDocumentFinal"));
296
297 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("xqi"), document.getDocumentId());
298 assertTrue("Document should still be enroute.", document.isEnroute());
299 document.approve("");
300 assertTrue("Document should now be final.", document.isFinal());
301
302 }
303
304 }