Coverage Report - org.kuali.rice.kew.messaging.exceptionhandling.ExceptionRoutingServiceImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
ExceptionRoutingServiceImpl
0%
0/117
0%
0/52
3.133
 
 1  
 /*
 2  
  * Copyright 2005-2007 The Kuali Foundation
 3  
  *
 4  
  *
 5  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  * http://www.opensource.org/licenses/ecl2.php
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.kuali.rice.kew.messaging.exceptionhandling;
 18  
 
 19  
 import java.lang.reflect.InvocationTargetException;
 20  
 import java.util.ArrayList;
 21  
 import java.util.Collections;
 22  
 import java.util.List;
 23  
 
 24  
 import org.apache.log4j.MDC;
 25  
 import org.kuali.rice.core.api.exception.RiceRuntimeException;
 26  
 import org.kuali.rice.kew.actionitem.ActionItem;
 27  
 import org.kuali.rice.kew.actionrequest.ActionRequestFactory;
 28  
 import org.kuali.rice.kew.actionrequest.ActionRequestValue;
 29  
 import org.kuali.rice.kew.actionrequest.KimGroupRecipient;
 30  
 import org.kuali.rice.kew.api.WorkflowRuntimeException;
 31  
 import org.kuali.rice.kew.api.action.ActionRequestStatus;
 32  
 import org.kuali.rice.kew.engine.RouteContext;
 33  
 import org.kuali.rice.kew.engine.node.RouteNodeInstance;
 34  
 import org.kuali.rice.kew.exception.InvalidActionTakenException;
 35  
 import org.kuali.rice.kew.exception.RouteManagerException;
 36  
 import org.kuali.rice.kew.exception.WorkflowDocumentExceptionRoutingService;
 37  
 import org.kuali.rice.kew.postprocessor.DocumentRouteStatusChange;
 38  
 import org.kuali.rice.kew.postprocessor.PostProcessor;
 39  
 import org.kuali.rice.kew.postprocessor.ProcessDocReport;
 40  
 import org.kuali.rice.kew.role.RoleRouteModule;
 41  
 import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
 42  
 import org.kuali.rice.kew.service.KEWServiceLocator;
 43  
 import org.kuali.rice.kew.util.KEWConstants;
 44  
 import org.kuali.rice.kew.util.PerformanceLogger;
 45  
 import org.kuali.rice.krad.util.KRADConstants;
 46  
 import org.kuali.rice.ksb.messaging.PersistedMessageBO;
 47  
 import org.kuali.rice.ksb.service.KSBServiceLocator;
 48  
 
 49  
 
 50  0
 public class ExceptionRoutingServiceImpl implements WorkflowDocumentExceptionRoutingService {
 51  
 
 52  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ExceptionRoutingServiceImpl.class);
 53  
 
 54  
     public void placeInExceptionRouting(String errorMessage, PersistedMessageBO persistedMessage, String documentId) throws Exception {
 55  0
                   RouteNodeInstance nodeInstance = null;
 56  0
                   KEWServiceLocator.getRouteHeaderService().lockRouteHeader(documentId, true);
 57  0
                   DocumentRouteHeaderValue document = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
 58  0
                   RouteContext routeContext = establishRouteContext(document, null);
 59  0
                   List<RouteNodeInstance> activeNodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(documentId);
 60  0
                   if (!activeNodeInstances.isEmpty()) {
 61  
                           // take the first active nodeInstance found.
 62  0
                           nodeInstance = activeNodeInstances.get(0);
 63  
                   }
 64  0
                   placeInExceptionRouting(errorMessage, nodeInstance, persistedMessage, routeContext, document, true);
 65  0
           }
 66  
     
 67  
     public void placeInExceptionRouting(Throwable throwable, PersistedMessageBO persistedMessage, String documentId) throws Exception {
 68  0
             placeInExceptionRouting(throwable, persistedMessage, documentId, true);
 69  0
     }
 70  
     
 71  
     /**
 72  
      * In our case here, our last ditch effort to put the document into exception routing will try to do so without invoking
 73  
      * the Post Processor for do route status change to "Exception" status.
 74  
      */
 75  
     public void placeInExceptionRoutingLastDitchEffort(Throwable throwable, PersistedMessageBO persistedMessage, String documentId) throws Exception {
 76  0
             placeInExceptionRouting(throwable, persistedMessage, documentId, false);
 77  0
     }
 78  
     
 79  
     protected void placeInExceptionRouting(Throwable throwable, PersistedMessageBO persistedMessage, String documentId, boolean invokePostProcessor) throws Exception {
 80  0
             KEWServiceLocator.getRouteHeaderService().lockRouteHeader(documentId, true);
 81  0
             DocumentRouteHeaderValue document = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
 82  0
             throwable = unwrapRouteManagerExceptionIfPossible(throwable);
 83  0
         RouteContext routeContext = establishRouteContext(document, throwable);
 84  0
         RouteNodeInstance nodeInstance = routeContext.getNodeInstance();
 85  0
             Throwable cause = determineActualCause(throwable, 0);
 86  0
         String errorMessage = (cause != null && cause.getMessage() != null) ? cause.getMessage() : "";
 87  0
             placeInExceptionRouting(errorMessage, nodeInstance, persistedMessage, routeContext, document, invokePostProcessor);
 88  0
     }
 89  
     
 90  
     protected void placeInExceptionRouting(String errorMessage, RouteNodeInstance nodeInstance, PersistedMessageBO persistedMessage, RouteContext routeContext, DocumentRouteHeaderValue document, boolean invokePostProcessor) throws Exception {
 91  0
             String documentId = document.getDocumentId();
 92  0
         MDC.put("docId", documentId);
 93  0
         PerformanceLogger performanceLogger = new PerformanceLogger(documentId);
 94  
         try {
 95  
 
 96  
             // mark all active requests to initialized and delete the action items
 97  0
             List<ActionRequestValue> actionRequests = KEWServiceLocator.getActionRequestService().findPendingByDoc(documentId);
 98  0
             for (ActionRequestValue actionRequest : actionRequests) {
 99  0
                 if (actionRequest.isActive()) {
 100  0
                     actionRequest.setStatus(ActionRequestStatus.INITIALIZED.getCode());
 101  0
                     for (ActionItem actionItem : actionRequest.getActionItems()) {
 102  0
                         KEWServiceLocator.getActionListService().deleteActionItem(actionItem);
 103  
                     }
 104  0
                     KEWServiceLocator.getActionRequestService().saveActionRequest(actionRequest);
 105  
                 }
 106  
             }
 107  
 
 108  0
             LOG.debug("Generating exception request for doc : " + documentId);
 109  0
             if (errorMessage == null) {
 110  0
                     errorMessage = "";
 111  
             }
 112  0
             if (errorMessage.length() > KEWConstants.MAX_ANNOTATION_LENGTH) {
 113  0
                 errorMessage = errorMessage.substring(0, KEWConstants.MAX_ANNOTATION_LENGTH);
 114  
             }
 115  0
             List<ActionRequestValue> exceptionRequests = new ArrayList<ActionRequestValue>();
 116  0
             if (nodeInstance.getRouteNode().isExceptionGroupDefined()) {
 117  0
                     exceptionRequests = generateExceptionGroupRequests(routeContext);
 118  
             } else {
 119  0
                     exceptionRequests = generateKimExceptionRequests(routeContext);
 120  
             }
 121  0
             if (exceptionRequests.isEmpty()) {
 122  0
                     throw new RiceRuntimeException("Failed to generate exception requests for exception routing!");
 123  
             }
 124  0
             activateExceptionRequests(routeContext, exceptionRequests, errorMessage, invokePostProcessor);
 125  0
             KSBServiceLocator.getMessageQueueService().delete(persistedMessage);
 126  
         } finally {
 127  0
             performanceLogger.log("Time to generate exception request.");
 128  0
             MDC.remove("docId");
 129  0
         }
 130  0
     }
 131  
 
 132  
     protected void notifyStatusChange(DocumentRouteHeaderValue routeHeader, String newStatusCode, String oldStatusCode) throws InvalidActionTakenException {
 133  0
         DocumentRouteStatusChange statusChangeEvent = new DocumentRouteStatusChange(routeHeader.getDocumentId(), routeHeader.getAppDocId(), oldStatusCode, newStatusCode);
 134  
         try {
 135  0
             LOG.debug("Notifying post processor of status change "+oldStatusCode+"->"+newStatusCode);
 136  0
             PostProcessor postProcessor = routeHeader.getDocumentType().getPostProcessor();
 137  0
             ProcessDocReport report = postProcessor.doRouteStatusChange(statusChangeEvent);
 138  0
             if (!report.isSuccess()) {
 139  0
                 LOG.warn(report.getMessage(), report.getProcessException());
 140  0
                 throw new InvalidActionTakenException(report.getMessage());
 141  
             }
 142  0
         } catch (Exception ex) {
 143  0
             LOG.warn(ex, ex);
 144  0
             throw new WorkflowRuntimeException(ex);
 145  0
         }
 146  0
     }
 147  
     
 148  
     protected List<ActionRequestValue> generateExceptionGroupRequests(RouteContext routeContext) {
 149  0
             RouteNodeInstance nodeInstance = routeContext.getNodeInstance();
 150  0
             ActionRequestFactory arFactory = new ActionRequestFactory(routeContext.getDocument(), null);
 151  0
             ActionRequestValue exceptionRequest = arFactory.createActionRequest(KEWConstants.ACTION_REQUEST_COMPLETE_REQ, new Integer(0), new KimGroupRecipient(nodeInstance.getRouteNode().getExceptionWorkgroup()), "Exception Workgroup for route node " + nodeInstance.getName(), KEWConstants.EXCEPTION_REQUEST_RESPONSIBILITY_ID, Boolean.TRUE, "");
 152  0
             return Collections.singletonList(exceptionRequest);
 153  
     }
 154  
     
 155  
     protected List<ActionRequestValue> generateKimExceptionRequests(RouteContext routeContext) throws Exception {
 156  0
             RoleRouteModule roleRouteModule = new RoleRouteModule();
 157  0
             roleRouteModule.setNamespace(KRADConstants.KUALI_RICE_WORKFLOW_NAMESPACE);
 158  0
             roleRouteModule.setResponsibilityTemplateName(KEWConstants.EXCEPTION_ROUTING_RESPONSIBILITY_TEMPLATE_NAME);
 159  0
             List<ActionRequestValue> requests = roleRouteModule.findActionRequests(routeContext);
 160  0
             processExceptionRequests(requests);
 161  0
             return requests;
 162  
     }
 163  
     
 164  
     
 165  
     
 166  
     /**
 167  
      * Takes the given list of Action Requests and ensures their attributes are set properly for exception
 168  
      * routing requests.  Namely, this ensures that all "force action" values are set to "true".
 169  
      */
 170  
     protected void processExceptionRequests(List<ActionRequestValue> exceptionRequests) {
 171  0
             if (exceptionRequests != null) {
 172  0
                     for (ActionRequestValue actionRequest : exceptionRequests) {
 173  0
                             processExceptionRequest(actionRequest);
 174  
                     }
 175  
             }
 176  0
     }
 177  
     
 178  
     /**
 179  
      * Processes a single exception request, ensuring that it's force action flag is set to true and it's node instance is set to null.
 180  
      * It then recurses through any children requests.
 181  
      */
 182  
     protected void processExceptionRequest(ActionRequestValue actionRequest) {
 183  0
             actionRequest.setForceAction(true);
 184  0
             actionRequest.setNodeInstance(null);
 185  0
             processExceptionRequests(actionRequest.getChildrenRequests());
 186  0
     }
 187  
     
 188  
     /**
 189  
      * End IU Customization
 190  
      * @param routeContext
 191  
      * @param exceptionRequests
 192  
      * @param exceptionMessage
 193  
      * @throws Exception
 194  
      */
 195  
     
 196  
     protected void activateExceptionRequests(RouteContext routeContext, List<ActionRequestValue> exceptionRequests, String exceptionMessage, boolean invokePostProcessor) throws Exception {
 197  0
             setExceptionAnnotations(exceptionRequests, exceptionMessage);
 198  
             // TODO is there a reason we reload the document here?
 199  0
             DocumentRouteHeaderValue rh = KEWServiceLocator.getRouteHeaderService().getRouteHeader(routeContext.getDocument().getDocumentId());
 200  0
             String oldStatus = rh.getDocRouteStatus();
 201  0
             rh.setDocRouteStatus(KEWConstants.ROUTE_HEADER_EXCEPTION_CD);
 202  0
             if (invokePostProcessor) {
 203  0
                     notifyStatusChange(rh, KEWConstants.ROUTE_HEADER_EXCEPTION_CD, oldStatus);
 204  
             }
 205  0
             KEWServiceLocator.getRouteHeaderService().saveRouteHeader(rh);
 206  0
             KEWServiceLocator.getActionRequestService().activateRequests(exceptionRequests);
 207  0
     }
 208  
     
 209  
     /**
 210  
      * Sets the exception message as the annotation on the top-level Action Requests
 211  
      */
 212  
     protected void setExceptionAnnotations(List<ActionRequestValue> actionRequests, String exceptionMessage) {
 213  0
             for (ActionRequestValue actionRequest : actionRequests) {
 214  0
                     actionRequest.setAnnotation(exceptionMessage);
 215  
             }
 216  0
     }
 217  
 
 218  
     private Throwable unwrapRouteManagerExceptionIfPossible(Throwable throwable) {
 219  0
             if (throwable instanceof InvocationTargetException) {
 220  0
                     throwable = throwable.getCause();
 221  
             }
 222  0
             if (throwable != null && (! (throwable instanceof RouteManagerException)) && throwable.getCause() instanceof RouteManagerException) {
 223  0
                     throwable = throwable.getCause();
 224  
             }
 225  0
             return throwable;
 226  
     }
 227  
 
 228  
     protected Throwable determineActualCause(Throwable throwable, int depth) {
 229  0
             if (depth >= 10) {
 230  0
                     return throwable;
 231  
             }
 232  0
             if ((throwable instanceof InvocationTargetException) || (throwable instanceof RouteManagerException)) {
 233  0
                     if (throwable.getCause() != null) {
 234  0
                             return determineActualCause(throwable.getCause(), ++depth);
 235  
                     }
 236  
             }
 237  0
             return throwable;
 238  
     }
 239  
     
 240  
     protected RouteContext establishRouteContext(DocumentRouteHeaderValue document, Throwable throwable) {
 241  0
             RouteContext routeContext = new RouteContext();
 242  0
         if (throwable instanceof RouteManagerException) {
 243  0
             RouteManagerException rmException = (RouteManagerException) throwable;
 244  0
             routeContext = rmException.getRouteContext();
 245  0
         } else {
 246  0
                 routeContext.setDocument(document);
 247  0
             List<RouteNodeInstance> activeNodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId());
 248  0
             if (!activeNodeInstances.isEmpty()) {
 249  
                 // take the first active nodeInstance found.
 250  0
                 RouteNodeInstance nodeInstance = (RouteNodeInstance) activeNodeInstances.get(0);
 251  0
                 routeContext.setNodeInstance(nodeInstance);
 252  
             }
 253  
         }
 254  0
         if (routeContext.getNodeInstance() == null) {
 255  
             // get the initial node instance
 256  0
             routeContext.setNodeInstance((RouteNodeInstance) document.getInitialRouteNodeInstances().get(0));
 257  
         }
 258  0
         return routeContext;
 259  
     }
 260  
 }