Coverage Report - org.kuali.rice.kew.messaging.exceptionhandling.ExceptionRoutingServiceImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
ExceptionRoutingServiceImpl
0%
0/118
0%
0/50
3.067
 
 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.Iterator;
 23  
 import java.util.List;
 24  
 
 25  
 import org.apache.log4j.MDC;
 26  
 import org.kuali.rice.core.exception.RiceRuntimeException;
 27  
 import org.kuali.rice.core.util.ExceptionUtils;
 28  
 import org.kuali.rice.kew.actionitem.ActionItem;
 29  
 import org.kuali.rice.kew.actionrequest.ActionRequestFactory;
 30  
 import org.kuali.rice.kew.actionrequest.ActionRequestValue;
 31  
 import org.kuali.rice.kew.actionrequest.KimGroupRecipient;
 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.exception.WorkflowRuntimeException;
 38  
 import org.kuali.rice.kew.postprocessor.DocumentRouteStatusChange;
 39  
 import org.kuali.rice.kew.postprocessor.PostProcessor;
 40  
 import org.kuali.rice.kew.postprocessor.ProcessDocReport;
 41  
 import org.kuali.rice.kew.role.RoleRouteModule;
 42  
 import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
 43  
 import org.kuali.rice.kew.service.KEWServiceLocator;
 44  
 import org.kuali.rice.kew.util.KEWConstants;
 45  
 import org.kuali.rice.kew.util.PerformanceLogger;
 46  
 import org.kuali.rice.kns.util.KNSConstants;
 47  
 import org.kuali.rice.ksb.messaging.PersistedMessage;
 48  
 import org.kuali.rice.ksb.service.KSBServiceLocator;
 49  
 
 50  
 
 51  0
 public class ExceptionRoutingServiceImpl implements WorkflowDocumentExceptionRoutingService {
 52  
 
 53  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ExceptionRoutingServiceImpl.class);
 54  
 
 55  
     public void placeInExceptionRouting(String errorMessage, PersistedMessage persistedMessage, Long routeHeaderId) throws Exception {
 56  0
                   RouteNodeInstance nodeInstance = null;
 57  0
                   KEWServiceLocator.getRouteHeaderService().lockRouteHeader(routeHeaderId, true);
 58  0
                   DocumentRouteHeaderValue document = KEWServiceLocator.getRouteHeaderService().getRouteHeader(routeHeaderId);
 59  0
                   RouteContext routeContext = establishRouteContext(document, null);
 60  0
                   List activeNodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(routeHeaderId);
 61  0
                   if (!activeNodeInstances.isEmpty()) {
 62  
                           // take the first active nodeInstance found.
 63  0
                           nodeInstance = (RouteNodeInstance) activeNodeInstances.get(0);
 64  
                   }
 65  0
                   placeInExceptionRouting(errorMessage, nodeInstance, persistedMessage, routeContext, document, true);
 66  0
           }
 67  
     
 68  
     public void placeInExceptionRouting(Throwable throwable, PersistedMessage persistedMessage, Long routeHeaderId) throws Exception {
 69  0
             placeInExceptionRouting(throwable, persistedMessage, routeHeaderId, true);
 70  0
     }
 71  
     
 72  
     /**
 73  
      * In our case here, our last ditch effort to put the document into exception routing will try to do so without invoking
 74  
      * the Post Processor for do route status change to "Exception" status.
 75  
      */
 76  
     public void placeInExceptionRoutingLastDitchEffort(Throwable throwable, PersistedMessage persistedMessage, Long routeHeaderId) throws Exception {
 77  0
             placeInExceptionRouting(throwable, persistedMessage, routeHeaderId, false);
 78  0
     }
 79  
     
 80  
     protected void placeInExceptionRouting(Throwable throwable, PersistedMessage persistedMessage, Long routeHeaderId, boolean invokePostProcessor) throws Exception {
 81  0
             KEWServiceLocator.getRouteHeaderService().lockRouteHeader(routeHeaderId, true);
 82  0
             DocumentRouteHeaderValue document = KEWServiceLocator.getRouteHeaderService().getRouteHeader(routeHeaderId);
 83  0
             throwable = unwrapRouteManagerExceptionIfPossible(throwable);
 84  0
         RouteContext routeContext = establishRouteContext(document, throwable);
 85  0
         RouteNodeInstance nodeInstance = routeContext.getNodeInstance();
 86  0
             Throwable cause = determineActualCause(throwable, 0);
 87  0
         String errorMessage = (cause != null && cause.getMessage() != null) ? cause.getMessage() : "";
 88  0
             placeInExceptionRouting(errorMessage, nodeInstance, persistedMessage, routeContext, document, invokePostProcessor);
 89  0
     }
 90  
     
 91  
     protected void placeInExceptionRouting(String errorMessage, RouteNodeInstance nodeInstance, PersistedMessage persistedMessage, RouteContext routeContext, DocumentRouteHeaderValue document, boolean invokePostProcessor) throws Exception {
 92  0
             Long routeHeaderId = document.getRouteHeaderId();
 93  0
         MDC.put("docId", routeHeaderId);
 94  0
         PerformanceLogger performanceLogger = new PerformanceLogger(routeHeaderId);
 95  
         try {
 96  
 
 97  
             // mark all active requests to initialized and delete the action items
 98  0
             List actionRequests = KEWServiceLocator.getActionRequestService().findPendingByDoc(routeHeaderId);
 99  0
             for (Iterator iter = actionRequests.iterator(); iter.hasNext();) {
 100  0
                 ActionRequestValue actionRequest = (ActionRequestValue) iter.next();
 101  0
                 if (actionRequest.isActive()) {
 102  0
                     actionRequest.setStatus(KEWConstants.ACTION_REQUEST_INITIALIZED);
 103  0
                     for (Iterator iterator = actionRequest.getActionItems().iterator(); iterator.hasNext();) {
 104  0
                         KEWServiceLocator.getActionListService().deleteActionItem((ActionItem) iterator.next());
 105  
                     }
 106  0
                     KEWServiceLocator.getActionRequestService().saveActionRequest(actionRequest);
 107  
                 }
 108  0
             }
 109  
 
 110  0
             LOG.debug("Generating exception request for doc : " + routeHeaderId);
 111  0
             if (errorMessage == null) {
 112  0
                     errorMessage = "";
 113  
             }
 114  0
             if (errorMessage.length() > KEWConstants.MAX_ANNOTATION_LENGTH) {
 115  0
                 errorMessage = errorMessage.substring(0, KEWConstants.MAX_ANNOTATION_LENGTH);
 116  
             }
 117  0
             List<ActionRequestValue> exceptionRequests = new ArrayList<ActionRequestValue>();
 118  0
             if (nodeInstance.getRouteNode().isExceptionGroupDefined()) {
 119  0
                     exceptionRequests = generateExceptionGroupRequests(routeContext);
 120  
             } else {
 121  0
                     exceptionRequests = generateKimExceptionRequests(routeContext);
 122  
             }
 123  0
             if (exceptionRequests.isEmpty()) {
 124  0
                     throw new RiceRuntimeException("Failed to generate exception requests for exception routing!");
 125  
             }
 126  0
             activateExceptionRequests(routeContext, exceptionRequests, errorMessage, invokePostProcessor);
 127  0
             KSBServiceLocator.getRouteQueueService().delete(persistedMessage);
 128  0
         } finally {
 129  0
             performanceLogger.log("Time to generate exception request.");
 130  0
             MDC.remove("docId");
 131  0
         }
 132  0
     }
 133  
 
 134  
     protected void notifyStatusChange(DocumentRouteHeaderValue routeHeader, String newStatusCode, String oldStatusCode) throws InvalidActionTakenException {
 135  0
         DocumentRouteStatusChange statusChangeEvent = new DocumentRouteStatusChange(routeHeader.getRouteHeaderId(), routeHeader.getAppDocId(), oldStatusCode, newStatusCode);
 136  
         try {
 137  0
             LOG.debug("Notifying post processor of status change "+oldStatusCode+"->"+newStatusCode);
 138  0
             PostProcessor postProcessor = routeHeader.getDocumentType().getPostProcessor();
 139  0
             ProcessDocReport report = postProcessor.doRouteStatusChange(statusChangeEvent);
 140  0
             if (!report.isSuccess()) {
 141  0
                 LOG.warn(report.getMessage(), report.getProcessException());
 142  0
                 throw new InvalidActionTakenException(report.getMessage());
 143  
             }
 144  0
         } catch (Exception ex) {
 145  0
             LOG.warn(ex, ex);
 146  0
             throw new WorkflowRuntimeException(ex);
 147  0
         }
 148  0
     }
 149  
     
 150  
     protected List<ActionRequestValue> generateExceptionGroupRequests(RouteContext routeContext) {
 151  0
             RouteNodeInstance nodeInstance = routeContext.getNodeInstance();
 152  0
             ActionRequestFactory arFactory = new ActionRequestFactory(routeContext.getDocument(), nodeInstance);
 153  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, "");
 154  0
             return Collections.singletonList(exceptionRequest);
 155  
     }
 156  
     
 157  
     protected List<ActionRequestValue> generateKimExceptionRequests(RouteContext routeContext) throws Exception {
 158  0
             RoleRouteModule roleRouteModule = new RoleRouteModule();
 159  0
             roleRouteModule.setNamespace(KNSConstants.KUALI_RICE_WORKFLOW_NAMESPACE);
 160  0
             roleRouteModule.setResponsibilityTemplateName(KEWConstants.EXCEPTION_ROUTING_RESPONSIBILITY_TEMPLATE_NAME);
 161  0
             List<ActionRequestValue> requests = roleRouteModule.findActionRequests(routeContext);
 162  0
             processExceptionRequests(requests);
 163  0
             return requests;
 164  
     }
 165  
     
 166  
     
 167  
     
 168  
     /**
 169  
      * Takes the given list of Action Requests and ensures their attributes are set properly for exception
 170  
      * routing requests.  Namely, this ensures that all "force action" values are set to "true".
 171  
      */
 172  
     protected void processExceptionRequests(List<ActionRequestValue> exceptionRequests) {
 173  0
             if (exceptionRequests != null) {
 174  0
                     for (ActionRequestValue actionRequest : exceptionRequests) {
 175  0
                             processExceptionRequest(actionRequest);
 176  
                     }
 177  
             }
 178  0
     }
 179  
     
 180  
     /**
 181  
      * Processes a single exception request, ensuring that it's force action flag is set to true.
 182  
      * It then recurses through any children requests.
 183  
      */
 184  
     protected void processExceptionRequest(ActionRequestValue actionRequest) {
 185  0
             actionRequest.setForceAction(true);
 186  0
             processExceptionRequests(actionRequest.getChildrenRequests());
 187  0
     }
 188  
     
 189  
     /**
 190  
      * End IU Customization
 191  
      * @param routeContext
 192  
      * @param exceptionRequests
 193  
      * @param exceptionMessage
 194  
      * @throws Exception
 195  
      */
 196  
     
 197  
     protected void activateExceptionRequests(RouteContext routeContext, List<ActionRequestValue> exceptionRequests, String exceptionMessage, boolean invokePostProcessor) throws Exception {
 198  0
             setExceptionAnnotations(exceptionRequests, exceptionMessage);
 199  
             // TODO is there a reason we reload the document here?
 200  0
             DocumentRouteHeaderValue rh = KEWServiceLocator.getRouteHeaderService().getRouteHeader(routeContext.getDocument().getRouteHeaderId());
 201  0
             String oldStatus = rh.getDocRouteStatus();
 202  0
             rh.setDocRouteStatus(KEWConstants.ROUTE_HEADER_EXCEPTION_CD);
 203  0
             if (invokePostProcessor) {
 204  0
                     notifyStatusChange(rh, KEWConstants.ROUTE_HEADER_EXCEPTION_CD, oldStatus);
 205  
             }
 206  0
             KEWServiceLocator.getRouteHeaderService().saveRouteHeader(rh);
 207  0
             KEWServiceLocator.getActionRequestService().activateRequests(exceptionRequests);
 208  0
     }
 209  
     
 210  
     /**
 211  
      * Sets the exception message as the annotation on the top-level Action Requests
 212  
      */
 213  
     protected void setExceptionAnnotations(List<ActionRequestValue> actionRequests, String exceptionMessage) {
 214  0
             for (ActionRequestValue actionRequest : actionRequests) {
 215  0
                     actionRequest.setAnnotation(exceptionMessage);
 216  
             }
 217  0
     }
 218  
 
 219  
     private Throwable unwrapRouteManagerExceptionIfPossible(Throwable throwable) {
 220  0
             throwable = ExceptionUtils.unwrapActualCause(throwable);
 221  0
             if (throwable != null && (! (throwable instanceof RouteManagerException)) && throwable.getCause() instanceof RouteManagerException) {
 222  0
                     throwable = throwable.getCause();
 223  
             }
 224  0
             return throwable;
 225  
     }
 226  
 
 227  
     protected Throwable determineActualCause(Throwable throwable, int depth) {
 228  0
             if (depth >= 10) {
 229  0
                     return throwable;
 230  
             }
 231  0
             if ((throwable instanceof InvocationTargetException) || (throwable instanceof RouteManagerException)) {
 232  0
                     if (throwable.getCause() != null) {
 233  0
                             return determineActualCause(throwable.getCause(), ++depth);
 234  
                     }
 235  
             }
 236  0
             return throwable;
 237  
     }
 238  
     
 239  
     protected RouteContext establishRouteContext(DocumentRouteHeaderValue document, Throwable throwable) {
 240  0
             RouteContext routeContext = new RouteContext();
 241  0
         if (throwable instanceof RouteManagerException) {
 242  0
             RouteManagerException rmException = (RouteManagerException) throwable;
 243  0
             routeContext = rmException.getRouteContext();
 244  0
         } else {
 245  0
                 routeContext.setDocument(document);
 246  0
             List activeNodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getRouteHeaderId());
 247  0
             if (!activeNodeInstances.isEmpty()) {
 248  
                 // take the first active nodeInstance found.
 249  0
                 RouteNodeInstance nodeInstance = (RouteNodeInstance) activeNodeInstances.get(0);
 250  0
                 routeContext.setNodeInstance(nodeInstance);
 251  
             }
 252  
         }
 253  0
         if (routeContext.getNodeInstance() == null) {
 254  
             // get the initial node instance
 255  0
             routeContext.setNodeInstance((RouteNodeInstance) document.getInitialRouteNodeInstances().get(0));
 256  
         }
 257  0
         return routeContext;
 258  
     }
 259  
 }