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-2011 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.messaging.exceptionhandling;
 17  
 
 18  
 import java.lang.reflect.InvocationTargetException;
 19  
 import java.util.ArrayList;
 20  
 import java.util.Collections;
 21  
 import java.util.List;
 22  
 
 23  
 import org.apache.log4j.MDC;
 24  
 import org.kuali.rice.core.api.exception.RiceRuntimeException;
 25  
 import org.kuali.rice.kew.actionitem.ActionItem;
 26  
 import org.kuali.rice.kew.actionrequest.ActionRequestFactory;
 27  
 import org.kuali.rice.kew.actionrequest.ActionRequestValue;
 28  
 import org.kuali.rice.kew.actionrequest.KimGroupRecipient;
 29  
 import org.kuali.rice.kew.api.WorkflowRuntimeException;
 30  
 import org.kuali.rice.kew.api.action.ActionRequestStatus;
 31  
 import org.kuali.rice.kew.api.exception.InvalidActionTakenException;
 32  
 import org.kuali.rice.kew.engine.RouteContext;
 33  
 import org.kuali.rice.kew.engine.node.RouteNodeInstance;
 34  
 import org.kuali.rice.kew.exception.RouteManagerException;
 35  
 import org.kuali.rice.kew.exception.WorkflowDocumentExceptionRoutingService;
 36  
 import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange;
 37  
 import org.kuali.rice.kew.framework.postprocessor.PostProcessor;
 38  
 import org.kuali.rice.kew.framework.postprocessor.ProcessDocReport;
 39  
 import org.kuali.rice.kew.role.RoleRouteModule;
 40  
 import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
 41  
 import org.kuali.rice.kew.service.KEWServiceLocator;
 42  
 import org.kuali.rice.kew.api.KewApiConstants;
 43  
 import org.kuali.rice.kew.util.PerformanceLogger;
 44  
 import org.kuali.rice.krad.util.KRADConstants;
 45  
 import org.kuali.rice.ksb.messaging.PersistedMessageBO;
 46  
 import org.kuali.rice.ksb.service.KSBServiceLocator;
 47  
 
 48  
 
 49  0
 public class ExceptionRoutingServiceImpl implements WorkflowDocumentExceptionRoutingService {
 50  
 
 51  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ExceptionRoutingServiceImpl.class);
 52  
 
 53  
     public void placeInExceptionRouting(String errorMessage, PersistedMessageBO persistedMessage, String documentId) throws Exception {
 54  0
                   RouteNodeInstance nodeInstance = null;
 55  0
                   KEWServiceLocator.getRouteHeaderService().lockRouteHeader(documentId, true);
 56  0
                   DocumentRouteHeaderValue document = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
 57  0
                   RouteContext routeContext = establishRouteContext(document, null);
 58  0
                   List<RouteNodeInstance> activeNodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(documentId);
 59  0
                   if (!activeNodeInstances.isEmpty()) {
 60  
                           // take the first active nodeInstance found.
 61  0
                           nodeInstance = activeNodeInstances.get(0);
 62  
                   }
 63  0
                   placeInExceptionRouting(errorMessage, nodeInstance, persistedMessage, routeContext, document, true);
 64  0
           }
 65  
     
 66  
     public void placeInExceptionRouting(Throwable throwable, PersistedMessageBO persistedMessage, String documentId) throws Exception {
 67  0
             placeInExceptionRouting(throwable, persistedMessage, documentId, true);
 68  0
     }
 69  
     
 70  
     /**
 71  
      * In our case here, our last ditch effort to put the document into exception routing will try to do so without invoking
 72  
      * the Post Processor for do route status change to "Exception" status.
 73  
      */
 74  
     public void placeInExceptionRoutingLastDitchEffort(Throwable throwable, PersistedMessageBO persistedMessage, String documentId) throws Exception {
 75  0
             placeInExceptionRouting(throwable, persistedMessage, documentId, false);
 76  0
     }
 77  
     
 78  
     protected void placeInExceptionRouting(Throwable throwable, PersistedMessageBO persistedMessage, String documentId, boolean invokePostProcessor) throws Exception {
 79  0
             KEWServiceLocator.getRouteHeaderService().lockRouteHeader(documentId, true);
 80  0
             DocumentRouteHeaderValue document = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
 81  0
             throwable = unwrapRouteManagerExceptionIfPossible(throwable);
 82  0
         RouteContext routeContext = establishRouteContext(document, throwable);
 83  0
         RouteNodeInstance nodeInstance = routeContext.getNodeInstance();
 84  0
             Throwable cause = determineActualCause(throwable, 0);
 85  0
         String errorMessage = (cause != null && cause.getMessage() != null) ? cause.getMessage() : "";
 86  0
             placeInExceptionRouting(errorMessage, nodeInstance, persistedMessage, routeContext, document, invokePostProcessor);
 87  0
     }
 88  
     
 89  
     protected void placeInExceptionRouting(String errorMessage, RouteNodeInstance nodeInstance, PersistedMessageBO persistedMessage, RouteContext routeContext, DocumentRouteHeaderValue document, boolean invokePostProcessor) throws Exception {
 90  0
             String documentId = document.getDocumentId();
 91  0
         MDC.put("docId", documentId);
 92  0
         PerformanceLogger performanceLogger = new PerformanceLogger(documentId);
 93  
         try {
 94  
 
 95  
             // mark all active requests to initialized and delete the action items
 96  0
             List<ActionRequestValue> actionRequests = KEWServiceLocator.getActionRequestService().findPendingByDoc(documentId);
 97  0
             for (ActionRequestValue actionRequest : actionRequests) {
 98  0
                 if (actionRequest.isActive()) {
 99  0
                     actionRequest.setStatus(ActionRequestStatus.INITIALIZED.getCode());
 100  0
                     for (ActionItem actionItem : actionRequest.getActionItems()) {
 101  0
                         KEWServiceLocator.getActionListService().deleteActionItem(actionItem);
 102  
                     }
 103  0
                     KEWServiceLocator.getActionRequestService().saveActionRequest(actionRequest);
 104  
                 }
 105  
             }
 106  
 
 107  0
             LOG.debug("Generating exception request for doc : " + documentId);
 108  0
             if (errorMessage == null) {
 109  0
                     errorMessage = "";
 110  
             }
 111  0
             if (errorMessage.length() > KewApiConstants.MAX_ANNOTATION_LENGTH) {
 112  0
                 errorMessage = errorMessage.substring(0, KewApiConstants.MAX_ANNOTATION_LENGTH);
 113  
             }
 114  0
             List<ActionRequestValue> exceptionRequests = new ArrayList<ActionRequestValue>();
 115  0
             if (nodeInstance.getRouteNode().isExceptionGroupDefined()) {
 116  0
                     exceptionRequests = generateExceptionGroupRequests(routeContext);
 117  
             } else {
 118  0
                     exceptionRequests = generateKimExceptionRequests(routeContext);
 119  
             }
 120  0
             if (exceptionRequests.isEmpty()) {
 121  0
                     throw new RiceRuntimeException("Failed to generate exception requests for exception routing!");
 122  
             }
 123  0
             activateExceptionRequests(routeContext, exceptionRequests, errorMessage, invokePostProcessor);
 124  0
             KSBServiceLocator.getMessageQueueService().delete(persistedMessage);
 125  
         } finally {
 126  0
             performanceLogger.log("Time to generate exception request.");
 127  0
             MDC.remove("docId");
 128  0
         }
 129  0
     }
 130  
 
 131  
     protected void notifyStatusChange(DocumentRouteHeaderValue routeHeader, String newStatusCode, String oldStatusCode) throws InvalidActionTakenException {
 132  0
         DocumentRouteStatusChange statusChangeEvent = new DocumentRouteStatusChange(routeHeader.getDocumentId(), routeHeader.getAppDocId(), oldStatusCode, newStatusCode);
 133  
         try {
 134  0
             LOG.debug("Notifying post processor of status change "+oldStatusCode+"->"+newStatusCode);
 135  0
             PostProcessor postProcessor = routeHeader.getDocumentType().getPostProcessor();
 136  0
             ProcessDocReport report = postProcessor.doRouteStatusChange(statusChangeEvent);
 137  0
             if (!report.isSuccess()) {
 138  0
                 LOG.warn(report.getMessage(), report.getProcessException());
 139  0
                 throw new InvalidActionTakenException(report.getMessage());
 140  
             }
 141  0
         } catch (Exception ex) {
 142  0
             LOG.warn(ex, ex);
 143  0
             throw new WorkflowRuntimeException(ex);
 144  0
         }
 145  0
     }
 146  
     
 147  
     protected List<ActionRequestValue> generateExceptionGroupRequests(RouteContext routeContext) {
 148  0
             RouteNodeInstance nodeInstance = routeContext.getNodeInstance();
 149  0
             ActionRequestFactory arFactory = new ActionRequestFactory(routeContext.getDocument(), null);
 150  0
             ActionRequestValue exceptionRequest = arFactory.createActionRequest(KewApiConstants.ACTION_REQUEST_COMPLETE_REQ, new Integer(0), new KimGroupRecipient(nodeInstance.getRouteNode().getExceptionWorkgroup()), "Exception Workgroup for route node " + nodeInstance.getName(), KewApiConstants.EXCEPTION_REQUEST_RESPONSIBILITY_ID, Boolean.TRUE, "");
 151  0
             return Collections.singletonList(exceptionRequest);
 152  
     }
 153  
     
 154  
     protected List<ActionRequestValue> generateKimExceptionRequests(RouteContext routeContext) throws Exception {
 155  0
             RoleRouteModule roleRouteModule = new RoleRouteModule();
 156  0
             roleRouteModule.setNamespace(KRADConstants.KUALI_RICE_WORKFLOW_NAMESPACE);
 157  0
             roleRouteModule.setResponsibilityTemplateName(KewApiConstants.EXCEPTION_ROUTING_RESPONSIBILITY_TEMPLATE_NAME);
 158  0
             List<ActionRequestValue> requests = roleRouteModule.findActionRequests(routeContext);
 159  0
             processExceptionRequests(requests);
 160  0
             return requests;
 161  
     }
 162  
     
 163  
     
 164  
     
 165  
     /**
 166  
      * Takes the given list of Action Requests and ensures their attributes are set properly for exception
 167  
      * routing requests.  Namely, this ensures that all "force action" values are set to "true".
 168  
      */
 169  
     protected void processExceptionRequests(List<ActionRequestValue> exceptionRequests) {
 170  0
             if (exceptionRequests != null) {
 171  0
                     for (ActionRequestValue actionRequest : exceptionRequests) {
 172  0
                             processExceptionRequest(actionRequest);
 173  
                     }
 174  
             }
 175  0
     }
 176  
     
 177  
     /**
 178  
      * 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.
 179  
      * It then recurses through any children requests.
 180  
      */
 181  
     protected void processExceptionRequest(ActionRequestValue actionRequest) {
 182  0
             actionRequest.setForceAction(true);
 183  0
             actionRequest.setNodeInstance(null);
 184  0
             processExceptionRequests(actionRequest.getChildrenRequests());
 185  0
     }
 186  
     
 187  
     /**
 188  
      * End IU Customization
 189  
      * @param routeContext
 190  
      * @param exceptionRequests
 191  
      * @param exceptionMessage
 192  
      * @throws Exception
 193  
      */
 194  
     
 195  
     protected void activateExceptionRequests(RouteContext routeContext, List<ActionRequestValue> exceptionRequests, String exceptionMessage, boolean invokePostProcessor) throws Exception {
 196  0
             setExceptionAnnotations(exceptionRequests, exceptionMessage);
 197  
             // TODO is there a reason we reload the document here?
 198  0
             DocumentRouteHeaderValue rh = KEWServiceLocator.getRouteHeaderService().getRouteHeader(routeContext.getDocument().getDocumentId());
 199  0
             String oldStatus = rh.getDocRouteStatus();
 200  0
             rh.setDocRouteStatus(KewApiConstants.ROUTE_HEADER_EXCEPTION_CD);
 201  0
             if (invokePostProcessor) {
 202  0
                     notifyStatusChange(rh, KewApiConstants.ROUTE_HEADER_EXCEPTION_CD, oldStatus);
 203  
             }
 204  0
             KEWServiceLocator.getRouteHeaderService().saveRouteHeader(rh);
 205  0
             KEWServiceLocator.getActionRequestService().activateRequests(exceptionRequests);
 206  0
     }
 207  
     
 208  
     /**
 209  
      * Sets the exception message as the annotation on the top-level Action Requests
 210  
      */
 211  
     protected void setExceptionAnnotations(List<ActionRequestValue> actionRequests, String exceptionMessage) {
 212  0
             for (ActionRequestValue actionRequest : actionRequests) {
 213  0
                     actionRequest.setAnnotation(exceptionMessage);
 214  
             }
 215  0
     }
 216  
 
 217  
     private Throwable unwrapRouteManagerExceptionIfPossible(Throwable throwable) {
 218  0
             if (throwable instanceof InvocationTargetException) {
 219  0
                     throwable = throwable.getCause();
 220  
             }
 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<RouteNodeInstance> activeNodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId());
 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  
 }