Coverage Report - org.kuali.rice.kew.routelog.web.RouteLogAction
 
Classes in this File Line Coverage Branch Coverage Complexity
RouteLogAction
0%
0/130
0%
0/62
3.895
RouteLogAction$RouteNodeInstanceFabricator
0%
0/80
0%
0/32
3.895
 
 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.routelog.web;
 18  
 
 19  
 import java.util.ArrayList;
 20  
 import java.util.Collection;
 21  
 import java.util.Collections;
 22  
 import java.util.Comparator;
 23  
 import java.util.HashMap;
 24  
 import java.util.HashSet;
 25  
 import java.util.List;
 26  
 import java.util.Map;
 27  
 import java.util.Set;
 28  
 
 29  
 import javax.servlet.http.HttpServletRequest;
 30  
 import javax.servlet.http.HttpServletResponse;
 31  
 import javax.xml.namespace.QName;
 32  
 
 33  
 import org.apache.struts.action.ActionForm;
 34  
 import org.apache.struts.action.ActionForward;
 35  
 import org.apache.struts.action.ActionMapping;
 36  
 import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
 37  
 import org.kuali.rice.kew.actionrequest.ActionRequestValue;
 38  
 import org.kuali.rice.kew.actionrequest.service.ActionRequestService;
 39  
 import org.kuali.rice.kew.actiontaken.ActionTakenValue;
 40  
 import org.kuali.rice.kew.doctype.SecuritySession;
 41  
 import org.kuali.rice.kew.doctype.service.DocumentSecurityService;
 42  
 import org.kuali.rice.kew.dto.ActionRequestDTO;
 43  
 import org.kuali.rice.kew.dto.DTOConverter;
 44  
 import org.kuali.rice.kew.dto.DTOConverter.RouteNodeInstanceLoader;
 45  
 import org.kuali.rice.kew.dto.DocumentDetailDTO;
 46  
 import org.kuali.rice.kew.dto.ReportCriteriaDTO;
 47  
 import org.kuali.rice.kew.dto.RouteNodeInstanceDTO;
 48  
 import org.kuali.rice.kew.dto.StateDTO;
 49  
 import org.kuali.rice.kew.engine.node.Branch;
 50  
 import org.kuali.rice.kew.engine.node.NodeState;
 51  
 import org.kuali.rice.kew.engine.node.RouteNode;
 52  
 import org.kuali.rice.kew.engine.node.RouteNodeInstance;
 53  
 import org.kuali.rice.kew.engine.node.service.RouteNodeService;
 54  
 import org.kuali.rice.kew.exception.InvalidActionTakenException;
 55  
 import org.kuali.rice.kew.exception.WorkflowRuntimeException;
 56  
 import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
 57  
 import org.kuali.rice.kew.service.KEWServiceLocator;
 58  
 import org.kuali.rice.kew.service.WorkflowUtility;
 59  
 import org.kuali.rice.kew.util.KEWConstants;
 60  
 import org.kuali.rice.kew.util.Utilities;
 61  
 import org.kuali.rice.kew.web.KewKualiAction;
 62  
 import org.kuali.rice.kns.UserSession;
 63  
 import org.kuali.rice.kns.util.GlobalVariables;
 64  
 
 65  
 
 66  
 /**
 67  
  * A Struts Action used to display the routelog.
 68  
  *
 69  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 70  
  */
 71  0
 public class RouteLogAction extends KewKualiAction {
 72  
 
 73  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(RouteLogAction.class);
 74  0
     private static Comparator<ActionRequestValue> ROUTE_LOG_ACTION_REQUEST_SORTER = new Utilities.RouteLogActionRequestSorter();
 75  
     
 76  
     @Override
 77  
         public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
 78  
 
 79  0
         RouteLogForm rlForm = (RouteLogForm) form;
 80  0
         String documentId = null;
 81  0
         if (! org.apache.commons.lang.StringUtils.isEmpty(rlForm.getDocumentId())) {
 82  0
                 documentId = rlForm.getDocumentId();
 83  0
         } else if (! org.apache.commons.lang.StringUtils.isEmpty(rlForm.getDocId())) {
 84  0
                 documentId =rlForm.getDocId();
 85  
         } else {
 86  0
                 throw new WorkflowRuntimeException("No paramater provided to fetch document");
 87  
         }
 88  
 
 89  0
         DocumentRouteHeaderValue routeHeader = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
 90  
 
 91  0
         DocumentSecurityService security = KEWServiceLocator.getDocumentSecurityService();
 92  0
         if (!security.routeLogAuthorized(getUserSession(), routeHeader, new SecuritySession(GlobalVariables.getUserSession()))) {
 93  0
           return mapping.findForward("NotAuthorized");
 94  
         }
 95  
         
 96  0
         fixActionRequestsPositions(routeHeader);
 97  0
         populateRouteLogFormActionRequests(rlForm, routeHeader);
 98  
 
 99  0
         rlForm.setLookFuture(routeHeader.getDocumentType().getLookIntoFuturePolicy().getPolicyValue().booleanValue());
 100  
 
 101  0
         if (rlForm.isShowFuture()) {
 102  
             try {
 103  0
                 populateRouteLogFutureRequests(rlForm, routeHeader);
 104  0
             } catch (Exception e) {
 105  0
                 String errorMsg = "Unable to determine Future Action Requests";
 106  0
                 LOG.info(errorMsg,e);
 107  0
                 rlForm.setShowFutureError(errorMsg);
 108  0
             }
 109  
         }
 110  0
         request.setAttribute("routeHeader", routeHeader);
 111  
         
 112  
                 // check whether action message logging should be enabled, user must
 113  
                 // have KIM permission for doc type 
 114  0
         boolean isAuthorizedToAddRouteLogMessage = KEWServiceLocator.getDocumentTypePermissionService()
 115  
                                 .canAddRouteLogMessage(GlobalVariables.getUserSession().getPrincipalId(), routeHeader);
 116  0
                 if (isAuthorizedToAddRouteLogMessage) {
 117  0
                         rlForm.setEnableLogAction(true);
 118  
                 } else {
 119  0
                         rlForm.setEnableLogAction(false);
 120  
                 }
 121  
         
 122  0
         return super.execute(mapping, rlForm, request, response);
 123  
     }
 124  
 
 125  
     @SuppressWarnings("unchecked")
 126  
         public void populateRouteLogFormActionRequests(RouteLogForm rlForm, DocumentRouteHeaderValue routeHeader) {
 127  0
         List<ActionRequestValue> rootRequests = getActionRequestService().getRootRequests(routeHeader.getActionRequests());
 128  0
         Collections.sort(rootRequests, ROUTE_LOG_ACTION_REQUEST_SORTER);
 129  0
         rootRequests = switchActionRequestPositionsIfPrimaryDelegatesPresent(rootRequests);
 130  0
         int arCount = 0;
 131  0
         for ( ActionRequestValue actionRequest : rootRequests ) {
 132  0
             if (actionRequest.isPending()) {
 133  0
                 arCount++;
 134  
 
 135  0
                 if (KEWConstants.ACTION_REQUEST_INITIALIZED.equals(actionRequest.getStatus())) {
 136  0
                     actionRequest.setDisplayStatus("PENDING");
 137  0
                 } else if (KEWConstants.ACTION_REQUEST_ACTIVATED.equals(actionRequest.getStatus())) {
 138  0
                     actionRequest.setDisplayStatus("IN ACTION LIST");
 139  
                 }
 140  
             }
 141  
         }
 142  0
         rlForm.setRootRequests(rootRequests);
 143  0
         rlForm.setPendingActionRequestCount(arCount);
 144  0
     }
 145  
 
 146  
     @SuppressWarnings("unchecked")
 147  
         private ActionRequestValue switchActionRequestPositionIfPrimaryDelegatePresent( ActionRequestValue actionRequest ) {
 148  
             
 149  
             /**
 150  
              * KULRICE-4756 - The main goal here is to fix the regression of what happened in Rice 1.0.2 with the display
 151  
              * of primary delegate requests.  The delegate is displayed at the top-most level correctly on action requests
 152  
              * that are "rooted" at a "role" request.
 153  
              * 
 154  
              * If they are rooted at a principal or group request, then the display of the primary delegator at the top-most
 155  
              * level does not happen (instead it shows the delegator and you have to expand the request to see the primary
 156  
              * delegate).
 157  
              * 
 158  
              * Ultimately, the KAI group and Rice BA need to come up with a specification for how the Route Log should
 159  
              * display delegate information.  For now, will fix this so that in the non "role" case, it will put the
 160  
              * primary delegate as the outermost request *except* in the case where there is more than one primary delegate.
 161  
              */
 162  
             
 163  0
             if (!actionRequest.isRoleRequest()) {
 164  0
                     List<ActionRequestValue> primaryDelegateRequests = actionRequest.getPrimaryDelegateRequests();
 165  
                     // only display primary delegate request at top if there is only *one* primary delegate request
 166  0
                     if ( primaryDelegateRequests.size() != 1) {
 167  0
                             return actionRequest;
 168  
                     }
 169  0
                     ActionRequestValue primaryDelegateRequest = primaryDelegateRequests.get(0);
 170  0
                     actionRequest.getChildrenRequests().remove(primaryDelegateRequest);
 171  0
                     primaryDelegateRequest.setChildrenRequests(actionRequest.getChildrenRequests());
 172  0
                     primaryDelegateRequest.setParentActionRequest(actionRequest.getParentActionRequest());
 173  0
                     primaryDelegateRequest.setParentActionRequestId(actionRequest.getParentActionRequestId());
 174  
                     
 175  0
                     actionRequest.setChildrenRequests( new ArrayList<ActionRequestValue>(0) );
 176  0
                     actionRequest.setParentActionRequest(primaryDelegateRequest);
 177  0
                     actionRequest.setParentActionRequestId(primaryDelegateRequest.getActionRequestId());
 178  
                     
 179  0
                     primaryDelegateRequest.getChildrenRequests().add(0, actionRequest);
 180  
                     
 181  0
                     for (ActionRequestValue delegateRequest : primaryDelegateRequest.getChildrenRequests()) {
 182  0
                             delegateRequest.setParentActionRequest(primaryDelegateRequest);
 183  0
                             delegateRequest.setParentActionRequestId(primaryDelegateRequest.getActionRequestId());
 184  
                     }
 185  
                     
 186  0
                     return primaryDelegateRequest;
 187  
             }
 188  
             
 189  0
             return actionRequest;
 190  
     }
 191  
 
 192  
     private List<ActionRequestValue> switchActionRequestPositionsIfPrimaryDelegatesPresent( Collection<ActionRequestValue> actionRequests ) {
 193  0
             List<ActionRequestValue> results = new ArrayList<ActionRequestValue>( actionRequests.size() );
 194  0
             for ( ActionRequestValue actionRequest : actionRequests ) {
 195  0
                         results.add( switchActionRequestPositionIfPrimaryDelegatePresent(actionRequest) );
 196  
             }
 197  0
             return results;
 198  
     }
 199  
     
 200  
     @SuppressWarnings("unchecked")
 201  
     private void fixActionRequestsPositions(DocumentRouteHeaderValue routeHeader) {
 202  0
         for (ActionTakenValue actionTaken : routeHeader.getActionsTaken()) {
 203  0
             Collections.sort((List<ActionRequestValue>) actionTaken.getActionRequests(), ROUTE_LOG_ACTION_REQUEST_SORTER);
 204  0
             actionTaken.setActionRequests( actionTaken.getActionRequests() );
 205  
         }
 206  0
     }
 207  
     
 208  
     /**
 209  
      * executes a simulation of the future routing, and sets the futureRootRequests and futureActionRequestCount
 210  
      * properties on the provided RouteLogForm.
 211  
      * 
 212  
      * @param rlForm the RouteLogForm --used in a write-only fashion.
 213  
      * @param document the DocumentRouteHeaderValue for the document whose future routing is being simulated.
 214  
      * @throws Exception
 215  
      */
 216  
     public void populateRouteLogFutureRequests(RouteLogForm rlForm, DocumentRouteHeaderValue document) throws Exception {
 217  
 
 218  0
         ReportCriteriaDTO reportCriteria = ReportCriteriaDTO.createReportCritByDocId(document.getDocumentId());
 219  0
         String applicationId = document.getDocumentType().getApplicationId();
 220  0
         WorkflowUtility workflowUtility = 
 221  
                 (WorkflowUtility) GlobalResourceLoader.getService(new QName(applicationId, "WorkflowUtilityService"));
 222  
 
 223  
         // gather the IDs for action requests that predate the simulation
 224  0
                 Set<Long> preexistingActionRequestIds = getActionRequestIds(document);
 225  
         
 226  
                 // run the simulation via WorkflowUtility
 227  0
         DocumentDetailDTO documentDetail = workflowUtility.routingReport(reportCriteria);
 228  
 
 229  
         // fabricate our ActionRequestValueS from the results
 230  0
         List<ActionRequestValue> futureActionRequests = 
 231  
                 reconstituteActionRequestValues(documentDetail, preexistingActionRequestIds);
 232  
 
 233  0
         Collections.sort(futureActionRequests, ROUTE_LOG_ACTION_REQUEST_SORTER);
 234  
         
 235  0
         futureActionRequests = switchActionRequestPositionsIfPrimaryDelegatesPresent(futureActionRequests);
 236  
         
 237  0
         int pendingActionRequestCount = 0;
 238  0
         for (ActionRequestValue actionRequest: futureActionRequests) {
 239  0
             if (actionRequest.isPending()) {
 240  0
                 pendingActionRequestCount++;
 241  
 
 242  0
                 if (KEWConstants.ACTION_REQUEST_INITIALIZED.equals(actionRequest.getStatus())) {
 243  0
                     actionRequest.setDisplayStatus("PENDING");
 244  0
                 } else if (KEWConstants.ACTION_REQUEST_ACTIVATED.equals(actionRequest.getStatus())) {
 245  0
                     actionRequest.setDisplayStatus("IN ACTION LIST");
 246  
                 }
 247  
             }
 248  
         }
 249  
 
 250  0
         rlForm.setFutureRootRequests(futureActionRequests);
 251  0
         rlForm.setFutureActionRequestCount(pendingActionRequestCount);
 252  0
     }
 253  
 
 254  
 
 255  
         /**
 256  
          * This utility method returns a Set of LongS containing the IDs for the ActionRequestValueS associated with 
 257  
          * this DocumentRouteHeaderValue. 
 258  
          */
 259  
         @SuppressWarnings("unchecked")
 260  
         private Set<Long> getActionRequestIds(DocumentRouteHeaderValue document) {
 261  0
                 Set<Long> actionRequestIds = new HashSet<Long>();
 262  
 
 263  0
                 List<ActionRequestValue> actionRequests = 
 264  
                         KEWServiceLocator.getActionRequestService().findAllActionRequestsByDocumentId(document.getDocumentId());
 265  
                 
 266  0
                 if (actionRequests != null) {
 267  0
                         for (ActionRequestValue actionRequest : actionRequests) {
 268  0
                                 if (actionRequest.getActionRequestId() != null) {
 269  0
                                         actionRequestIds.add(actionRequest.getActionRequestId());
 270  
                                 }
 271  
                         }
 272  
                 }
 273  0
                 return actionRequestIds;
 274  
         }
 275  
 
 276  
         /**
 277  
          * This method creates ActionRequestValue objects from the DocumentDetailDTO output from 
 278  
          * {@link WorkflowUtility#routingReport(ReportCriteriaDTO)}Report()
 279  
          * 
 280  
          * @param documentDetail contains the DTOs from which the ActionRequestValues are reconstituted
 281  
          * @param preexistingActionRequestIds this is a Set of ActionRequest IDs that will not be reconstituted
 282  
          * @return the ActionRequestValueS that have been created
 283  
          */
 284  
         private List<ActionRequestValue> reconstituteActionRequestValues(DocumentDetailDTO documentDetail,
 285  
                         Set<Long> preexistingActionRequestIds) {
 286  
 
 287  0
         RouteNodeInstanceFabricator routeNodeInstanceFabricator = 
 288  
                     new RouteNodeInstanceFabricator(KEWServiceLocator.getRouteNodeService());
 289  
 
 290  0
         if (documentDetail.getNodeInstances() != null && documentDetail.getNodeInstances().length > 0) {
 291  0
                 for (RouteNodeInstanceDTO routeNodeInstanceVO : documentDetail.getNodeInstances()) {
 292  0
                         routeNodeInstanceFabricator.importRouteNodeInstanceDTO(routeNodeInstanceVO);
 293  
                 }
 294  
                 }
 295  
         
 296  0
         ActionRequestDTO[] actionRequestVOs = documentDetail.getActionRequests();
 297  0
         List<ActionRequestValue> futureActionRequests = new ArrayList<ActionRequestValue>();
 298  0
         if (actionRequestVOs != null) {
 299  0
                         for (ActionRequestDTO actionRequestVO : actionRequestVOs) {
 300  0
                                 if (actionRequestVO != null) {
 301  0
                                         if (!preexistingActionRequestIds.contains(actionRequestVO.getActionRequestId())) {
 302  0
                                                 ActionRequestValue converted = DTOConverter.convertActionRequestDTO(actionRequestVO, routeNodeInstanceFabricator);
 303  0
                                                 futureActionRequests.add(converted);
 304  
                                         }
 305  
                                 }
 306  
                         }
 307  
                 }
 308  0
                 return futureActionRequests;
 309  
         }
 310  
     
 311  
     private ActionRequestService getActionRequestService() {
 312  0
         return (ActionRequestService) KEWServiceLocator.getService(KEWServiceLocator.ACTION_REQUEST_SRV);
 313  
     }
 314  
     
 315  
     private UserSession getUserSession() {
 316  0
         return GlobalVariables.getUserSession();
 317  
     }
 318  
     
 319  
     /**
 320  
      * Creates dummy RouteNodeInstances based on imported data from RouteNodeInstanceDTOs.
 321  
      * It is then able to vend those RouteNodeInstanceS back by their IDs.
 322  
      * 
 323  
      * @author Kuali Rice Team (rice.collab@kuali.org)
 324  
      *
 325  
      */
 326  0
     private static class RouteNodeInstanceFabricator implements RouteNodeInstanceLoader {
 327  
 
 328  0
             private Map<Long,Branch> branches = new HashMap<Long, Branch>();;
 329  0
             private Map<Long,RouteNodeInstance> routeNodeInstances = new HashMap<Long, RouteNodeInstance>();
 330  0
             private Map<Long,RouteNode> routeNodes = new HashMap<Long, RouteNode>();
 331  0
             private Map<Long,NodeState> nodeStates = new HashMap<Long, NodeState>();
 332  
 
 333  
             private RouteNodeService routeNodeService;
 334  
             
 335  
             /**
 336  
                  * This constructs a FutureRouteNodeInstanceFabricator, which will generate bogus
 337  
                  * RouteNodeInstances for SimulationEngine results
 338  
                  * 
 339  
                  */
 340  0
                 public RouteNodeInstanceFabricator(RouteNodeService routeNodeService) {
 341  0
                         this.routeNodeService = routeNodeService;
 342  0
                 }
 343  
 
 344  
                 /**
 345  
                  * 
 346  
                  * This method looks at the given RouteNodeInstanceDTO and imports it (and all it's ancestors)
 347  
                  * as dummy RouteNodeInstanceS
 348  
                  * 
 349  
                  * @param nodeInstanceDTO
 350  
                  */
 351  
                 public void importRouteNodeInstanceDTO(RouteNodeInstanceDTO nodeInstanceDTO) {
 352  0
                         _importRouteNodeInstanceDTO(nodeInstanceDTO);
 353  0
                 }
 354  
                 
 355  
                 /**
 356  
                  * helper method for {@link #importRouteNodeInstanceDTO(RouteNodeInstanceDTO)} which does all
 357  
                  * the work.  The public method just wraps this one but hides the returned RouteNodeInstance,
 358  
                  * which is used for the recursive call to populate the nextNodeInstanceS inside our 
 359  
                  * RouteNodeInstanceS.
 360  
                  * 
 361  
                  * @param nodeInstanceDTO
 362  
                  * @return
 363  
                  */
 364  
             private RouteNodeInstance _importRouteNodeInstanceDTO(RouteNodeInstanceDTO nodeInstanceDTO) {
 365  0
                     if (nodeInstanceDTO == null) {
 366  0
                             return null;
 367  
                     }
 368  0
                     RouteNodeInstance nodeInstance = new RouteNodeInstance();
 369  0
                     nodeInstance.setActive(nodeInstanceDTO.isActive());
 370  
 
 371  0
                     nodeInstance.setComplete(nodeInstanceDTO.isComplete());
 372  0
                     nodeInstance.setDocumentId(nodeInstanceDTO.getDocumentId());
 373  0
                     nodeInstance.setInitial(nodeInstanceDTO.isInitial());
 374  
 
 375  0
                     Branch branch = getBranch(nodeInstanceDTO.getBranchId());
 376  0
                     nodeInstance.setBranch(branch);
 377  
 
 378  0
                     if (nodeInstanceDTO.getRouteNodeId() != null) {
 379  0
                             RouteNode routeNode = routeNodeService.findRouteNodeById(nodeInstanceDTO.getRouteNodeId());
 380  
 
 381  0
                             if (routeNode == null) {
 382  0
                                     routeNode = getRouteNode(nodeInstanceDTO.getRouteNodeId());
 383  0
                                     routeNode.setNodeType(nodeInstanceDTO.getName());
 384  
                             }
 385  
 
 386  0
                             nodeInstance.setRouteNode(routeNode);
 387  
 
 388  0
                             if (routeNode.getBranch() != null) {
 389  0
                                 branch.setName(routeNode.getBranch().getName());
 390  
                         } 
 391  
                     }
 392  
 
 393  0
                     RouteNodeInstance process = getRouteNodeInstance(nodeInstanceDTO.getProcessId());
 394  0
                     nodeInstance.setProcess(process);
 395  
 
 396  0
                     nodeInstance.setRouteNodeInstanceId(nodeInstanceDTO.getRouteNodeInstanceId());
 397  0
                     DTOConverter.convertState(null);
 398  
 
 399  0
                     List<NodeState> nodeState = new ArrayList<NodeState>();
 400  0
                     if (nodeInstanceDTO.getState() != null) {
 401  0
                                 for (StateDTO stateDTO : nodeInstanceDTO.getState()) {
 402  0
                                         NodeState state = getNodeState(stateDTO.getStateId());
 403  0
                                         if (state != null) {
 404  0
                                                 state.setKey(stateDTO.getKey());
 405  0
                                                 state.setValue(stateDTO.getValue());
 406  0
                                                 state.setStateId(stateDTO.getStateId());
 407  0
                                                 state.setNodeInstance(nodeInstance);
 408  0
                                                 nodeState.add(state);
 409  
                                         }
 410  
                                 }
 411  
                         }
 412  0
                     nodeInstance.setState(nodeState);
 413  
 
 414  0
                     List<RouteNodeInstance> nextNodeInstances = new ArrayList<RouteNodeInstance>();
 415  0
                     nodeInstance.setNextNodeInstances(nextNodeInstances);
 416  
 
 417  0
                     for (RouteNodeInstanceDTO nextNodeInstanceVO : nodeInstanceDTO.getNextNodes()) {
 418  
                             // recurse to populate nextNodeInstances
 419  0
                             nextNodeInstances.add(_importRouteNodeInstanceDTO(nextNodeInstanceVO));
 420  
                     }
 421  
 
 422  0
                     routeNodeInstances.put(nodeInstance.getRouteNodeInstanceId(), nodeInstance);
 423  0
                     return nodeInstance;
 424  
             }
 425  
             
 426  
                 /**
 427  
                  * This method returns a dummy RouteNodeInstance for the given ID, or null if it hasn't
 428  
                  * imported from a RouteNodeInstanceDTO with that ID
 429  
                  * 
 430  
                  * @see org.kuali.rice.kew.dto.DTOConverter.RouteNodeInstanceLoader#load(java.lang.Long)
 431  
                  */
 432  
                 @Override
 433  
                 public RouteNodeInstance load(Long routeNodeInstanceID) {
 434  0
                         return routeNodeInstances.get(routeNodeInstanceID);
 435  
                 }
 436  
 
 437  
 
 438  
             /**
 439  
              * This method creates bogus BranchES as needed
 440  
              * 
 441  
              * @param branchId
 442  
              * @return
 443  
              */
 444  
             private Branch getBranch(Long branchId) {
 445  0
                     Branch result = null;
 446  
 
 447  0
                     if (branchId != null) {
 448  
                             // if branch doesn't exist, create it
 449  0
                             if (!branches.containsKey(branchId)) {
 450  0
                                     result = new Branch();
 451  0
                                     result.setBranchId(branchId);
 452  0
                                     branches.put(branchId, result);
 453  
                             } else {
 454  0
                                     result = branches.get(branchId);
 455  
                             }
 456  
                     }
 457  0
                     return result;
 458  
             }
 459  
 
 460  
             /**
 461  
              * This method creates bogus RouteNodeS as needed
 462  
              * 
 463  
              * @param routeNodeId
 464  
              * @return
 465  
              */
 466  
             private RouteNode getRouteNode(Long routeNodeId) {
 467  0
                     RouteNode result = null;
 468  
 
 469  0
                     if (routeNodeId != null) {
 470  
                             // if RouteNode doesn't exist, create it
 471  0
                             if (!routeNodes.containsKey(routeNodeId)) {
 472  0
                                     result = new RouteNode();
 473  0
                                     result.setRouteNodeId(routeNodeId);
 474  0
                                     routeNodes.put(routeNodeId, result);
 475  
                             } else {
 476  0
                                     result = routeNodes.get(routeNodeId);
 477  
                             }
 478  
                     }
 479  0
                     return result;
 480  
             }
 481  
 
 482  
             /**
 483  
              * This method creates bogus RouteNodeInstanceS as needed
 484  
              * 
 485  
              * @param routeNodeInstanceId
 486  
              * @return
 487  
              */
 488  
             public RouteNodeInstance getRouteNodeInstance(Long routeNodeInstanceId) {
 489  0
                     RouteNodeInstance result = null;
 490  
 
 491  0
                     if (routeNodeInstanceId != null) {
 492  
                             // if RouteNodeInstance doesn't exist, create it
 493  0
                             if (!routeNodeInstances.containsKey(routeNodeInstanceId)) {
 494  0
                                     result = new RouteNodeInstance();
 495  0
                                     result.setRouteNodeInstanceId(routeNodeInstanceId);
 496  0
                                     routeNodeInstances.put(routeNodeInstanceId, result);
 497  
                             } else {
 498  0
                                     result = routeNodeInstances.get(routeNodeInstanceId);
 499  
                             }
 500  
                     }
 501  0
                     return result;
 502  
             }
 503  
 
 504  
             /**
 505  
              * This method creates bogus NodeStateS as needed
 506  
              * 
 507  
              * @param nodeStateId
 508  
              * @return
 509  
              */
 510  
             private NodeState getNodeState(Long nodeStateId) {
 511  0
                     NodeState result = null;
 512  
 
 513  0
                     if (nodeStateId != null) {
 514  
                             // if NodeState doesn't exist, create it
 515  0
                             if (!nodeStates.containsKey(nodeStateId)) {
 516  0
                                     result = new NodeState();
 517  0
                                     result.setNodeStateId(nodeStateId);
 518  0
                                     nodeStates.put(nodeStateId, result);
 519  
                             } else {
 520  0
                                     result = nodeStates.get(nodeStateId);
 521  
                             }
 522  
                     }
 523  0
                     return result;
 524  
             }
 525  
 
 526  
     } // end inner class FutureRouteNodeInstanceFabricator
 527  
 
 528  
     /**
 529  
      * Logs a new message to the route log for the current document, then refreshes the action taken list to display
 530  
      * back the new message in the route log tab. User must have permission to log a message for the doc type and the
 531  
      * request must be coming from the route log tab display (not the route log page).
 532  
      */
 533  
         public ActionForward logActionMessageInRouteLog(ActionMapping mapping, ActionForm form, HttpServletRequest request,
 534  
                         HttpServletResponse response) throws Exception {
 535  0
                 RouteLogForm routeLogForm = (RouteLogForm) form;
 536  
 
 537  0
                 String documentId = null;
 538  0
                 if (!org.apache.commons.lang.StringUtils.isEmpty(routeLogForm.getDocumentId())) {
 539  0
                         documentId = routeLogForm.getDocumentId();
 540  0
                 } else if (!org.apache.commons.lang.StringUtils.isEmpty(routeLogForm.getDocId())) {
 541  0
                         documentId = routeLogForm.getDocId();
 542  
                 } else {
 543  0
                         throw new WorkflowRuntimeException("No paramater provided to fetch document");
 544  
                 }
 545  
                 
 546  0
                 DocumentRouteHeaderValue routeHeader = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
 547  
                 
 548  
                 // check user has permission to add a route log message
 549  0
                 boolean isAuthorizedToAddRouteLogMessage = KEWServiceLocator.getDocumentTypePermissionService()
 550  
                                 .canAddRouteLogMessage(GlobalVariables.getUserSession().getPrincipalId(), routeHeader);
 551  
 
 552  0
                 if (!isAuthorizedToAddRouteLogMessage) {
 553  0
                         throw new InvalidActionTakenException("Principal with name '"
 554  
                                         + GlobalVariables.getUserSession().getPrincipalName()
 555  
                                         + "' is not authorized to add route log messages for documents of type '"
 556  
                                         + routeHeader.getDocumentType().getName());
 557  
                 }
 558  
 
 559  0
                 LOG.info("Logging new action message for user " + GlobalVariables.getUserSession().getPrincipalName()
 560  
                                 + ", route header " + routeHeader);
 561  0
                 KEWServiceLocator.getWorkflowDocumentService().logDocumentAction(
 562  
                                 GlobalVariables.getUserSession().getPrincipalId(), routeHeader,
 563  
                                 routeLogForm.getNewRouteLogActionMessage());
 564  
 
 565  0
                 routeLogForm.setNewRouteLogActionMessage("");
 566  
 
 567  
                 // retrieve routeHeader again to pull new action taken
 568  0
                 routeHeader = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId, true);
 569  0
                 fixActionRequestsPositions(routeHeader);
 570  0
                 request.setAttribute("routeHeader", routeHeader);
 571  
 
 572  0
                 return mapping.findForward(getDefaultMapping());
 573  
         }
 574  
     
 575  
 }