001: /*
002: * Copyright 2005-2007 The Kuali Foundation.
003: *
004: *
005: * Licensed under the Educational Community License, Version 1.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/ecl1.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 edu.iu.uis.eden.routemanager;
018:
019: import java.util.Collection;
020:
021: import javax.xml.namespace.QName;
022:
023: import org.junit.Test;
024: import org.kuali.workflow.test.WorkflowTestCase;
025:
026: import edu.iu.uis.eden.KEWServiceLocator;
027: import edu.iu.uis.eden.clientapp.WorkflowDocument;
028: import edu.iu.uis.eden.clientapp.WorkflowInfo;
029: import edu.iu.uis.eden.clientapp.vo.ActionRequestVO;
030: import edu.iu.uis.eden.clientapp.vo.NetworkIdVO;
031: import edu.iu.uis.eden.clientapp.vo.RouteNodeInstanceVO;
032: import edu.iu.uis.eden.exception.InvalidActionTakenException;
033: import edu.iu.uis.eden.exception.WorkflowException;
034: import edu.iu.uis.eden.messaging.KEWXMLService;
035: import edu.iu.uis.eden.messaging.MessageServiceNames;
036: import edu.iu.uis.eden.routeheader.DocumentRouteHeaderValue;
037: import edu.iu.uis.eden.test.TestUtilities;
038:
039: public class ExceptionRoutingTest extends WorkflowTestCase {
040:
041: protected void loadTestData() throws Exception {
042: loadXmlFile("RouteManagerConfig.xml");
043: }
044:
045: protected void setUpTransaction() throws Exception {
046: super .setUpTransaction();
047: // reset these static constants, otherwise they will cause problems between test runs
048: ExceptionRoutingTestPostProcessor.THROW_DO_ACTION_TAKEN_EXCEPTION = false;
049: ExceptionRoutingTestPostProcessor.THROW_ROUTE_DELETE_ROUTE_HEADER_EXCEPTION = false;
050: ExceptionRoutingTestPostProcessor.THROW_ROUTE_STATUS_CHANGE_EXCEPTION = false;
051: ExceptionRoutingTestPostProcessor.THROW_ROUTE_STATUS_LEVEL_EXCEPTION = false;
052: ExceptionRoutingTestPostProcessor.TRANSITIONED_OUT_OF_EXCEPTION_ROUTING = false;
053: }
054:
055: @Test
056: public void testSequentialExceptionRouting() throws Exception {
057: WorkflowDocument doc = new WorkflowDocument(new NetworkIdVO(
058: "rkirkend"), "ExceptionRoutingSequentialDoc");
059: try {
060: doc.routeDocument("");
061: fail("should have thrown routing exception");
062: } catch (Exception e) {
063: }
064:
065: TestUtilities.getExceptionThreader().join();//this is necessary to ensure that the exception request will be generated.
066:
067: WorkflowInfo info = new WorkflowInfo();
068: ActionRequestVO[] actionRequests = info.getActionRequests(doc
069: .getRouteHeaderId());
070:
071: assertEquals("Should be a single exception request", 1,
072: actionRequests.length);
073: for (int i = 0; i < actionRequests.length; i++) {
074: ActionRequestVO actionRequest = actionRequests[i];
075: assertTrue("Request should be an exception request.",
076: actionRequest.isExceptionRequest());
077: assertTrue("Complete should be requested", actionRequest
078: .isCompleteRequest());
079: assertTrue("Request should be a workgroup request",
080: actionRequest.isWorkgroupRequest());
081: assertEquals(
082: "Request should be to 'ExceptionRoutingWorkgroup'",
083: "ExceptionRoutingWorkgroup", actionRequest
084: .getWorkgroupVO().getWorkgroupName());
085: assertNotNull("annotation cannot be null", actionRequest
086: .getAnnotation());
087: assertFalse("annotation cannot be empty", ""
088: .equals(actionRequest.getAnnotation()));
089: }
090:
091: doc = new WorkflowDocument(new NetworkIdVO("rkirkend"), doc
092: .getRouteHeaderId());
093: assertTrue("Document should be in exception status", doc
094: .stateIsException());
095: }
096:
097: @Test
098: public void testInvalidActionsInExceptionRouting() throws Exception {
099: WorkflowDocument doc = new WorkflowDocument(new NetworkIdVO(
100: "rkirkend"), "ExceptionRoutingSequentialDoc");
101: try {
102: doc.routeDocument("");
103: fail("should have thrown routing exception");
104: } catch (Exception e) {
105: log.info("Expected exception occurred: " + e);
106: }
107:
108: TestUtilities.getExceptionThreader().join();//this is necessary to ensure that the exception request will be generated.
109:
110: doc = new WorkflowDocument(new NetworkIdVO("rkirkend"), doc
111: .getRouteHeaderId());
112: assertTrue("Document should be in exception status", doc
113: .stateIsException());
114:
115: try {
116: doc
117: .routeDocument("routing a document that is in exception routing");
118: fail("Succeeded in routing document that is in exception routing");
119: } catch (InvalidActionTakenException iate) {
120: log.info("Expected exception occurred: " + iate);
121: } catch (WorkflowException we) {
122: fail("Attempt at routing document in exception routing succeeded, when it should have been an InvalidActionTakenException");
123: }
124: }
125:
126: @Test
127: public void testParallelExceptionRouting() throws Exception {
128: WorkflowDocument doc = new WorkflowDocument(new NetworkIdVO(
129: "user1"), "ExceptionRoutingParallelDoc");
130: doc.routeDocument("");
131: doc = new WorkflowDocument(new NetworkIdVO("ewestfal"), doc
132: .getRouteHeaderId());
133: assertTrue("User should have an approve request", doc
134: .isApprovalRequested());
135: doc = new WorkflowDocument(new NetworkIdVO("bmcgough"), doc
136: .getRouteHeaderId());
137: assertTrue("User should have an approve request", doc
138: .isApprovalRequested());
139: RouteNodeInstanceVO[] nodes = new WorkflowInfo()
140: .getActiveNodeInstances(doc.getRouteHeaderId());
141:
142: // at this point we should be at RouteNode1 and RouteNode3
143: assertEquals("There should be two active nodes", 2,
144: nodes.length);
145: TestUtilities.assertAtNode(doc, "RouteNode1");
146: TestUtilities.assertAtNode(doc, "RouteNode3");
147:
148: try {
149: doc.approve("");
150: fail("should have generated routing exception");
151: } catch (Exception e) {
152: }
153:
154: TestUtilities.getExceptionThreader().join();//this is necessary to ensure that the exception request will be generated.
155: WorkflowInfo info = new WorkflowInfo();
156: ActionRequestVO[] actionRequests = info.getActionRequests(doc
157: .getRouteHeaderId());
158: RouteNodeInstanceVO routeNode1 = null;
159: for (RouteNodeInstanceVO nodeInstanceVO : nodes) {
160: if (nodeInstanceVO.getName().equals("RouteNode1")) {
161: routeNode1 = nodeInstanceVO;
162: }
163: }
164: assertNotNull("Could not locate the routeNode1 node instance.",
165: routeNode1);
166:
167: boolean hasCompleteRequest = false;
168: for (int i = 0; i < actionRequests.length; i++) {
169: ActionRequestVO actionRequest = actionRequests[i];
170: if (actionRequest.isCompleteRequest()) {
171: assertTrue("Complete should be requested",
172: actionRequest.isCompleteRequest());
173: assertTrue("Request should be a workgroup request",
174: actionRequest.isWorkgroupRequest());
175: assertNull(
176: "For exception routing, node instance should have a null id.",
177: actionRequest.getNodeInstanceId());
178: //assertEquals("Node instance id should be id of routeNode1", routeNode1.getRouteNodeInstanceId(), actionRequest.getNodeInstanceId());
179: // routeMethod name should be null as well
180: assertNull("Exception request routeMethodName wrong",
181: actionRequest.getRouteMethodName());
182: assertEquals(
183: "Request should be to 'ExceptionRoutingWorkgroup'",
184: "ExceptionRoutingWorkgroup", actionRequest
185: .getWorkgroupVO().getWorkgroupName());
186: hasCompleteRequest = true;
187: }
188: }
189: assertTrue("Document should have had a complete request",
190: hasCompleteRequest);
191: ExplodingRuleAttribute.dontExplode = true;
192:
193: //there should be a single action item to our member of the exception workgroup
194: Collection actionItems = KEWServiceLocator
195: .getActionListService().findByRouteHeaderId(
196: doc.getRouteHeaderId());
197: assertEquals(
198: "There should only be action items for the member of our exception workgroup",
199: 1, actionItems.size());
200:
201: doc = new WorkflowDocument(new NetworkIdVO("user3"), doc
202: .getRouteHeaderId());
203: assertTrue(
204: "Document should be routing for completion to member of exception workgroup",
205: doc.isCompletionRequested());
206: assertTrue("Document should be in exception status", doc
207: .stateIsException());
208: doc.complete("");
209:
210: doc = new WorkflowDocument(new NetworkIdVO("bmcgough"), doc
211: .getRouteHeaderId());
212: doc.approve("");
213:
214: doc = new WorkflowDocument(new NetworkIdVO("ewestfal"), doc
215: .getRouteHeaderId());
216: doc.approve("");
217:
218: doc = new WorkflowDocument(new NetworkIdVO("rkirkend"), doc
219: .getRouteHeaderId());
220: doc.approve("");
221:
222: doc = new WorkflowDocument(new NetworkIdVO("jhopf"), doc
223: .getRouteHeaderId());
224: doc.approve("");
225:
226: assertTrue("Document should be final", doc.stateIsFinal());
227: }
228:
229: /**
230: * this tests that the document appropriately gets to exception routing if there is a
231: * problem when transitioning out of first node
232: *
233: * @throws Exception
234: */
235: @Test
236: public void testExceptionInTransitionFromStart() throws Exception {
237:
238: WorkflowDocument doc = new WorkflowDocument(new NetworkIdVO(
239: "rkirkend"), "AdhocTransitionTestDocument");
240: //blow chunks transitioning out of adhoc to the first route node
241: ExceptionRoutingTestPostProcessor.THROW_ROUTE_STATUS_LEVEL_EXCEPTION = true;
242:
243: try {
244: doc.routeDocument("");
245: fail("We should be in exception routing");
246: } catch (Exception e) {
247: }
248:
249: TestUtilities.getExceptionThreader().join();//this is necessary to ensure that the exception request will be generated.
250: doc = new WorkflowDocument(new NetworkIdVO("rkirkend"), doc
251: .getRouteHeaderId());
252: assertTrue("document should be in exception routing", doc
253: .stateIsException());
254: }
255:
256: /**
257: * Test to verify the fix for KULWF-669.
258: *
259: * This tests that if we requeue an exception document (through the RouteQueueService) that it doesn't transition
260: * out of exception routing. Then check that, if we complete it, it properly transitions out of exception routing.
261: */
262: @Test
263: public void testRequeueOfExceptionDocument() throws Exception {
264: WorkflowDocument document = new WorkflowDocument(
265: new NetworkIdVO("rkirkend"),
266: "AdhocTransitionTestDocument");
267: document.routeDocument("");
268: assertFalse("Document should not be in exception routing.",
269: document.stateIsException());
270:
271: // in fact, at this point it should be routed to jhopf
272: document = new WorkflowDocument(new NetworkIdVO("jhopf"),
273: document.getRouteHeaderId());
274: assertTrue("Jhopf should have an approve.", document
275: .isApprovalRequested());
276:
277: // let's tell it to blow up on level change
278: ExceptionRoutingTestPostProcessor.THROW_ROUTE_STATUS_CHANGE_EXCEPTION = true;
279: try {
280: document.approve("");
281: fail("We should be in exception routing");
282: } catch (Exception e) {
283: }
284:
285: TestUtilities.waitForExceptionRouting();
286: document = new WorkflowDocument(new NetworkIdVO("rkirkend"),
287: document.getRouteHeaderId());
288: assertTrue("document should be in exception routing", document
289: .stateIsException());
290:
291: // now requeue the document it should stay at exception routing and the status change callback should not
292: // indicate a transition out of exception routing (this is to make sure it's not going out of exception
293: // routing and then right back in)
294: ExceptionRoutingTestPostProcessor.THROW_ROUTE_STATUS_CHANGE_EXCEPTION = false;
295: assertFalse(
296: "Should not have transitioned out of exception routing yet.",
297: ExceptionRoutingTestPostProcessor.TRANSITIONED_OUT_OF_EXCEPTION_ROUTING);
298: // the requeue here should happen synchronously because we are using the SynchronousRouteQueue
299: DocumentRouteHeaderValue routeHeaderValue = KEWServiceLocator
300: .getRouteHeaderService().getRouteHeader(
301: document.getRouteHeaderId());
302: QName documentServiceName = new QName(routeHeaderValue
303: .getDocumentType().getMessageEntity(),
304: MessageServiceNames.DOCUMENT_ROUTING_SERVICE);
305: KEWXMLService routeDocumentMessageService = (KEWXMLService) MessageServiceNames
306: .getServiceAsynchronously(documentServiceName,
307: routeHeaderValue);
308: routeDocumentMessageService.invoke(String.valueOf(document
309: .getRouteHeaderId()));
310:
311: // SpringServiceLocator.getMessageHelper().sendMessage(MessageServiceNames.DOCUMENT_ROUTING_SERVICE, String.valueOf(document.getRouteHeaderId()), routeHeaderValue);
312:
313: // the document should still be in exception routing
314: document = new WorkflowDocument(new NetworkIdVO("rkirkend"),
315: document.getRouteHeaderId());
316: assertTrue("document should be in exception routing", document
317: .stateIsException());
318: assertFalse(
319: "document shouldn't have transitioned out of exception routing.",
320: ExceptionRoutingTestPostProcessor.TRANSITIONED_OUT_OF_EXCEPTION_ROUTING);
321:
322: // now turn status change exceptions off and complete the exception request
323: ExceptionRoutingTestPostProcessor.THROW_ROUTE_STATUS_CHANGE_EXCEPTION = false;
324: assertTrue("rkirkend should be in the exception workgroup.",
325: document.isCompletionRequested());
326: document.complete("Completing out of exception routing.");
327:
328: // Note: The behavior here will be a bit different then in a real setting because in these tests the route queue is synchronous so jhopf's original
329: // Approve never actually took place because the transaction was rolled back (because of the exception in the post process). Therefore, we still
330: // need to take action as him again to push the document to FINAL
331: document = new WorkflowDocument(new NetworkIdVO("jhopf"),
332: document.getRouteHeaderId());
333: assertTrue(document.isApprovalRequested());
334: document.approve("");
335:
336: // document should now be FINAL
337: assertTrue("Document should be FINAL.", document.stateIsFinal());
338:
339: // the status change out of exception routing should have happened
340: assertTrue(
341: "Document should have transitioned out of exception routing.",
342: ExceptionRoutingTestPostProcessor.TRANSITIONED_OUT_OF_EXCEPTION_ROUTING);
343: }
344:
345: }
|