001/** 002 * Copyright 2005-2017 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.kew.routelog.web; 017 018import org.apache.struts.action.ActionForm; 019import org.apache.struts.action.ActionForward; 020import org.apache.struts.action.ActionMapping; 021import org.kuali.rice.kew.actionrequest.ActionRequestValue; 022import org.kuali.rice.kew.actionrequest.service.ActionRequestService; 023import org.kuali.rice.kew.actiontaken.ActionTakenValue; 024import org.kuali.rice.kew.api.KewApiServiceLocator; 025import org.kuali.rice.kew.api.WorkflowRuntimeException; 026import org.kuali.rice.kew.api.action.ActionRequest; 027import org.kuali.rice.kew.api.action.ActionRequestStatus; 028import org.kuali.rice.kew.api.action.RoutingReportCriteria; 029import org.kuali.rice.kew.api.document.DocumentDetail; 030import org.kuali.rice.kew.api.document.node.RouteNodeInstanceState; 031import org.kuali.rice.kew.api.exception.InvalidActionTakenException; 032import org.kuali.rice.kew.doctype.SecuritySession; 033import org.kuali.rice.kew.doctype.service.DocumentSecurityService; 034import org.kuali.rice.kew.dto.DTOConverter.RouteNodeInstanceLoader; 035import org.kuali.rice.kew.engine.node.Branch; 036import org.kuali.rice.kew.engine.node.NodeState; 037import org.kuali.rice.kew.engine.node.RouteNode; 038import org.kuali.rice.kew.engine.node.RouteNodeInstance; 039import org.kuali.rice.kew.engine.node.service.RouteNodeService; 040import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue; 041import org.kuali.rice.kew.service.KEWServiceLocator; 042import org.kuali.rice.kew.util.Utilities; 043import org.kuali.rice.kew.web.KewKualiAction; 044import org.kuali.rice.krad.UserSession; 045import org.kuali.rice.krad.util.GlobalVariables; 046 047import javax.servlet.http.HttpServletRequest; 048import javax.servlet.http.HttpServletResponse; 049import java.util.ArrayList; 050import java.util.Collection; 051import java.util.Collections; 052import java.util.Comparator; 053import java.util.HashMap; 054import java.util.HashSet; 055import java.util.List; 056import java.util.Map; 057import java.util.Set; 058 059 060/** 061 * A Struts Action used to display the routelog. 062 * 063 * @author Kuali Rice Team (rice.collab@kuali.org) 064 */ 065public class RouteLogAction extends KewKualiAction { 066 067 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(RouteLogAction.class); 068 private static Comparator<ActionRequestValue> ROUTE_LOG_ACTION_REQUEST_SORTER = new Utilities.RouteLogActionRequestSorter(); 069 070 @Override 071 public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 072 073 RouteLogForm rlForm = (RouteLogForm) form; 074 String documentId = null; 075 if (! org.apache.commons.lang.StringUtils.isEmpty(rlForm.getDocumentId())) { 076 documentId = rlForm.getDocumentId(); 077 } else if (! org.apache.commons.lang.StringUtils.isEmpty(rlForm.getDocId())) { 078 documentId =rlForm.getDocId(); 079 } else { 080 throw new WorkflowRuntimeException("No paramater provided to fetch document"); 081 } 082 083 DocumentRouteHeaderValue routeHeader = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId); 084 085 DocumentSecurityService security = KEWServiceLocator.getDocumentSecurityService(); 086 if (!security.routeLogAuthorized(getUserSession().getPrincipalId(), routeHeader, new SecuritySession(GlobalVariables.getUserSession().getPrincipalId()))) { 087 return mapping.findForward("NotAuthorized"); 088 } 089 090 fixActionRequestsPositions(routeHeader); 091 populateRouteLogFormActionRequests(rlForm, routeHeader); 092 093 rlForm.setLookFuture(routeHeader.getDocumentType().getLookIntoFuturePolicy().getPolicyValue().booleanValue()); 094 095 if (rlForm.isShowFuture()) { 096 try { 097 populateRouteLogFutureRequests(rlForm, routeHeader); 098 } catch (Exception e) { 099 String errorMsg = "Unable to determine Future Action Requests"; 100 LOG.info(errorMsg,e); 101 rlForm.setShowFutureError(errorMsg); 102 } 103 } 104 request.setAttribute("routeHeader", routeHeader); 105 106 // check whether action message logging should be enabled, user must 107 // have KIM permission for doc type 108 boolean isAuthorizedToAddRouteLogMessage = KEWServiceLocator.getDocumentTypePermissionService() 109 .canAddRouteLogMessage(GlobalVariables.getUserSession().getPrincipalId(), routeHeader); 110 if (isAuthorizedToAddRouteLogMessage) { 111 rlForm.setEnableLogAction(true); 112 } else { 113 rlForm.setEnableLogAction(false); 114 } 115 116 return super.execute(mapping, rlForm, request, response); 117 } 118 119 @SuppressWarnings("unchecked") 120 public void populateRouteLogFormActionRequests(RouteLogForm rlForm, DocumentRouteHeaderValue routeHeader) { 121 List<ActionRequestValue> rootRequests = getActionRequestService().getRootRequests( 122 routeHeader.getActionRequests()); 123 Collections.sort(rootRequests, ROUTE_LOG_ACTION_REQUEST_SORTER); 124 125 List<ActionRequestValue> rootRequestsForDisplay = new ArrayList<ActionRequestValue>(); 126 127 for (ActionRequestValue actionRequestValue : rootRequests) { 128 rootRequestsForDisplay.add(actionRequestValue.deepCopy(new HashMap<Object, Object>())); 129 } 130 131 rootRequestsForDisplay = switchActionRequestPositionsIfPrimaryDelegatesPresent(rootRequestsForDisplay); 132 int arCount = 0; 133 for ( ActionRequestValue actionRequest : rootRequestsForDisplay ) { 134 if (actionRequest.isPending()) { 135 arCount++; 136 137 if (ActionRequestStatus.INITIALIZED.getCode().equals(actionRequest.getStatus())) { 138 actionRequest.setDisplayStatus("PENDING"); 139 } else if (ActionRequestStatus.ACTIVATED.getCode().equals(actionRequest.getStatus())) { 140 actionRequest.setDisplayStatus("IN ACTION LIST"); 141 } 142 } 143 } 144 rlForm.setRootRequests(rootRequestsForDisplay); 145 rlForm.setPendingActionRequestCount(arCount); 146 } 147 148 @SuppressWarnings("unchecked") 149 private ActionRequestValue switchActionRequestPositionIfPrimaryDelegatePresent( ActionRequestValue actionRequest ) { 150 151 /** 152 * KULRICE-4756 - The main goal here is to fix the regression of what happened in Rice 1.0.2 with the display 153 * of primary delegate requests. The delegate is displayed at the top-most level correctly on action requests 154 * that are "rooted" at a "role" request. 155 * 156 * If they are rooted at a principal or group request, then the display of the primary delegator at the top-most 157 * level does not happen (instead it shows the delegator and you have to expand the request to see the primary 158 * delegate). 159 * 160 * Ultimately, the KAI group and Rice BA need to come up with a specification for how the Route Log should 161 * display delegate information. For now, will fix this so that in the non "role" case, it will put the 162 * primary delegate as the outermost request *except* in the case where there is more than one primary delegate. 163 */ 164 165 if (!actionRequest.isRoleRequest()) { 166 List<ActionRequestValue> primaryDelegateRequests = actionRequest.getPrimaryDelegateRequests(); 167 // only display primary delegate request at top if there is only *one* primary delegate request 168 if ( primaryDelegateRequests.size() != 1) { 169 return actionRequest; 170 } 171 ActionRequestValue primaryDelegateRequest = primaryDelegateRequests.get(0); 172 actionRequest.getChildrenRequests().remove(primaryDelegateRequest); 173 primaryDelegateRequest.setChildrenRequests(actionRequest.getChildrenRequests()); 174 primaryDelegateRequest.setParentActionRequest(actionRequest.getParentActionRequest()); 175 176 actionRequest.setChildrenRequests( new ArrayList<ActionRequestValue>(0) ); 177 actionRequest.setParentActionRequest(primaryDelegateRequest); 178 179 primaryDelegateRequest.getChildrenRequests().add(0, actionRequest); 180 181 for (ActionRequestValue delegateRequest : primaryDelegateRequest.getChildrenRequests()) { 182 delegateRequest.setParentActionRequest(primaryDelegateRequest); 183 } 184 185 return primaryDelegateRequest; 186 } 187 188 return actionRequest; 189 } 190 191 private List<ActionRequestValue> switchActionRequestPositionsIfPrimaryDelegatesPresent( Collection<ActionRequestValue> actionRequests ) { 192 List<ActionRequestValue> results = new ArrayList<ActionRequestValue>( actionRequests.size() ); 193 for ( ActionRequestValue actionRequest : actionRequests ) { 194 results.add( switchActionRequestPositionIfPrimaryDelegatePresent(actionRequest) ); 195 } 196 return results; 197 } 198 199 @SuppressWarnings("unchecked") 200 private void fixActionRequestsPositions(DocumentRouteHeaderValue routeHeader) { 201 for (ActionTakenValue actionTaken : routeHeader.getActionsTaken()) { 202 Collections.sort((List<ActionRequestValue>) actionTaken.getActionRequests(), ROUTE_LOG_ACTION_REQUEST_SORTER); 203 actionTaken.setActionRequests( actionTaken.getActionRequests() ); 204 } 205 } 206 207 /** 208 * executes a simulation of the future routing, and sets the futureRootRequests and futureActionRequestCount 209 * properties on the provided RouteLogForm. 210 * 211 * @param rlForm the RouteLogForm --used in a write-only fashion. 212 * @param document the DocumentRouteHeaderValue for the document whose future routing is being simulated. 213 * @throws Exception 214 */ 215 public void populateRouteLogFutureRequests(RouteLogForm rlForm, DocumentRouteHeaderValue document) throws Exception { 216 217 RoutingReportCriteria reportCriteria = RoutingReportCriteria.Builder.createByDocumentId(document.getDocumentId()).build(); 218 String applicationId = document.getDocumentType().getApplicationId(); 219 220 // gather the IDs for action requests that predate the simulation 221 Set<String> preexistingActionRequestIds = getActionRequestIds(document); 222 223 // run the simulation 224 DocumentDetail documentDetail = KewApiServiceLocator.getWorkflowDocumentActionsService(applicationId).executeSimulation(reportCriteria); 225 226 // fabricate our ActionRequestValueS from the results 227 List<ActionRequestValue> futureActionRequests = 228 reconstituteActionRequestValues(documentDetail, preexistingActionRequestIds); 229 230 Collections.sort(futureActionRequests, ROUTE_LOG_ACTION_REQUEST_SORTER); 231 232 List<ActionRequestValue> futureActionRequestsForDisplay = new ArrayList<ActionRequestValue>(); 233 234 for (ActionRequestValue actionRequestValue : futureActionRequests) { 235 futureActionRequestsForDisplay.add(actionRequestValue.deepCopy(new HashMap<Object, Object>())); 236 } 237 238 futureActionRequestsForDisplay = 239 switchActionRequestPositionsIfPrimaryDelegatesPresent(futureActionRequestsForDisplay); 240 241 int pendingActionRequestCount = 0; 242 for (ActionRequestValue actionRequest: futureActionRequestsForDisplay) { 243 if (actionRequest.isPending()) { 244 pendingActionRequestCount++; 245 246 if (ActionRequestStatus.INITIALIZED.getCode().equals(actionRequest.getStatus())) { 247 actionRequest.setDisplayStatus("PENDING"); 248 } else if (ActionRequestStatus.ACTIVATED.getCode().equals(actionRequest.getStatus())) { 249 actionRequest.setDisplayStatus("IN ACTION LIST"); 250 } 251 } 252 } 253 254 rlForm.setFutureRootRequests(futureActionRequestsForDisplay); 255 rlForm.setFutureActionRequestCount(pendingActionRequestCount); 256 } 257 258 259 /** 260 * This utility method returns a Set of LongS containing the IDs for the ActionRequestValueS associated with 261 * this DocumentRouteHeaderValue. 262 */ 263 @SuppressWarnings("unchecked") 264 private Set<String> getActionRequestIds(DocumentRouteHeaderValue document) { 265 Set<String> actionRequestIds = new HashSet<String>(); 266 267 List<ActionRequestValue> actionRequests = 268 KEWServiceLocator.getActionRequestService().findAllActionRequestsByDocumentId(document.getDocumentId()); 269 270 if (actionRequests != null) { 271 for (ActionRequestValue actionRequest : actionRequests) { 272 if (actionRequest.getActionRequestId() != null) { 273 actionRequestIds.add(actionRequest.getActionRequestId()); 274 } 275 } 276 } 277 return actionRequestIds; 278 } 279 280 /** 281 * This method creates ActionRequestValue objects from the DocumentDetailDTO output from 282 * 283 * @param documentDetail contains the DTOs from which the ActionRequestValues are reconstituted 284 * @param preexistingActionRequestIds this is a Set of ActionRequest IDs that will not be reconstituted 285 * @return the ActionRequestValueS that have been created 286 */ 287 private List<ActionRequestValue> reconstituteActionRequestValues(DocumentDetail documentDetail, 288 Set<String> preexistingActionRequestIds) { 289 290 RouteNodeInstanceFabricator routeNodeInstanceFabricator = 291 new RouteNodeInstanceFabricator(KEWServiceLocator.getRouteNodeService()); 292 293 if (documentDetail.getRouteNodeInstances() != null && !documentDetail.getRouteNodeInstances().isEmpty()) { 294 for (org.kuali.rice.kew.api.document.node.RouteNodeInstance routeNodeInstanceVO : documentDetail.getRouteNodeInstances()) { 295 routeNodeInstanceFabricator.importRouteNodeInstanceDTO(routeNodeInstanceVO); 296 } 297 } 298 299 List<ActionRequest> actionRequestVOs = documentDetail.getActionRequests(); 300 List<ActionRequestValue> futureActionRequests = new ArrayList<ActionRequestValue>(); 301 if (actionRequestVOs != null) { 302 for (ActionRequest actionRequestVO : actionRequestVOs) { 303 if (actionRequestVO != null) { 304 if (!preexistingActionRequestIds.contains(actionRequestVO.getId())) { 305 ActionRequestValue converted = ActionRequestValue.from(actionRequestVO, 306 routeNodeInstanceFabricator); 307 futureActionRequests.add(converted); 308 } 309 } 310 } 311 } 312 return futureActionRequests; 313 } 314 315 private ActionRequestService getActionRequestService() { 316 return (ActionRequestService) KEWServiceLocator.getService(KEWServiceLocator.ACTION_REQUEST_SRV); 317 } 318 319 private UserSession getUserSession() { 320 return GlobalVariables.getUserSession(); 321 } 322 323 /** 324 * Creates dummy RouteNodeInstances based on imported data from RouteNodeInstanceDTOs. 325 * It is then able to vend those RouteNodeInstanceS back by their IDs. 326 * 327 * @author Kuali Rice Team (rice.collab@kuali.org) 328 * 329 */ 330 private static class RouteNodeInstanceFabricator implements RouteNodeInstanceLoader { 331 332 private Map<String,Branch> branches = new HashMap<String, Branch>(); 333 private Map<String, RouteNodeInstance> routeNodeInstances = 334 new HashMap<String, RouteNodeInstance>(); 335 private Map<String,RouteNode> routeNodes = new HashMap<String, RouteNode>(); 336 private Map<String,NodeState> nodeStates = new HashMap<String, NodeState>(); 337 338 private RouteNodeService routeNodeService; 339 340 /** 341 * This constructs a FutureRouteNodeInstanceFabricator, which will generate bogus 342 * RouteNodeInstances for SimulationEngine results 343 * 344 */ 345 public RouteNodeInstanceFabricator(RouteNodeService routeNodeService) { 346 this.routeNodeService = routeNodeService; 347 } 348 349 /** 350 * 351 * This method looks at the given RouteNodeInstanceDTO and imports it (and all it's ancestors) 352 * as dummy RouteNodeInstanceS 353 * 354 * @param nodeInstanceDTO 355 */ 356 public void importRouteNodeInstanceDTO(org.kuali.rice.kew.api.document.node.RouteNodeInstance nodeInstanceDTO) { 357 _importRouteNodeInstanceDTO(nodeInstanceDTO); 358 } 359 360 /** 361 * helper method for {@link #importRouteNodeInstanceDTO(org.kuali.rice.kew.api.document.node.RouteNodeInstance)} which does all 362 * the work. The public method just wraps this one but hides the returned RouteNodeInstance, 363 * which is used for the recursive call to populate the nextNodeInstanceS inside our 364 * RouteNodeInstanceS. 365 * 366 * @param nodeInstanceDTO 367 * @return 368 */ 369 private RouteNodeInstance _importRouteNodeInstanceDTO(org.kuali.rice.kew.api.document.node.RouteNodeInstance nodeInstanceDTO) { 370 if (nodeInstanceDTO == null) { 371 return null; 372 } 373 RouteNodeInstance nodeInstance = new RouteNodeInstance(); 374 nodeInstance.setActive(nodeInstanceDTO.isActive()); 375 376 nodeInstance.setComplete(nodeInstanceDTO.isComplete()); 377 nodeInstance.setDocumentId(nodeInstanceDTO.getDocumentId()); 378 nodeInstance.setInitial(nodeInstanceDTO.isInitial()); 379 380 Branch branch = getBranch(nodeInstanceDTO.getBranchId()); 381 nodeInstance.setBranch(branch); 382 383 if (nodeInstanceDTO.getRouteNodeId() != null) { 384 RouteNode routeNode = routeNodeService.findRouteNodeById(nodeInstanceDTO.getRouteNodeId()); 385 386 if (routeNode == null) { 387 routeNode = getRouteNode(nodeInstanceDTO.getRouteNodeId()); 388 routeNode.setNodeType(nodeInstanceDTO.getName()); 389 } 390 391 nodeInstance.setRouteNode(routeNode); 392 393 if (routeNode.getBranch() != null) { 394 branch.setName(routeNode.getBranch().getName()); 395 } 396 } 397 398 RouteNodeInstance process = getRouteNodeInstance(nodeInstanceDTO.getProcessId()); 399 nodeInstance.setProcess(process); 400 401 nodeInstance.setRouteNodeInstanceId(nodeInstanceDTO.getId()); 402 403 List<NodeState> nodeState = new ArrayList<NodeState>(); 404 if (nodeInstanceDTO.getState() != null) { 405 for (RouteNodeInstanceState stateDTO : nodeInstanceDTO.getState()) { 406 NodeState state = getNodeState(stateDTO.getId()); 407 if (state != null) { 408 state.setKey(stateDTO.getKey()); 409 state.setValue(stateDTO.getValue()); 410 state.setStateId(stateDTO.getId()); 411 state.setNodeInstance(nodeInstance); 412 nodeState.add(state); 413 } 414 } 415 } 416 nodeInstance.setState(nodeState); 417 418 List<RouteNodeInstance> nextNodeInstances = new ArrayList<RouteNodeInstance>(); 419 420 421 for (org.kuali.rice.kew.api.document.node.RouteNodeInstance nextNodeInstanceVO : nodeInstanceDTO.getNextNodeInstances()) { 422 // recurse to populate nextNodeInstances 423 nextNodeInstances.add(_importRouteNodeInstanceDTO(nextNodeInstanceVO)); 424 } 425 nodeInstance.setNextNodeInstances(nextNodeInstances); 426 427 routeNodeInstances.put(nodeInstance.getRouteNodeInstanceId(), nodeInstance); 428 return nodeInstance; 429 } 430 431 /** 432 * This method returns a dummy RouteNodeInstance for the given ID, or null if it hasn't 433 * imported from a RouteNodeInstanceDTO with that ID 434 * 435 * @see org.kuali.rice.kew.dto.DTOConverter.RouteNodeInstanceLoader#load(String) 436 */ 437 @Override 438 public RouteNodeInstance load(String routeNodeInstanceID) { 439 return routeNodeInstances.get(routeNodeInstanceID); 440 } 441 442 443 /** 444 * This method creates bogus BranchES as needed 445 * 446 * @param branchId 447 * @return 448 */ 449 private Branch getBranch(String branchId) { 450 Branch result = null; 451 452 if (branchId != null) { 453 // if branch doesn't exist, create it 454 if (!branches.containsKey(branchId)) { 455 result = new Branch(); 456 result.setBranchId(branchId); 457 branches.put(branchId, result); 458 } else { 459 result = branches.get(branchId); 460 } 461 } 462 return result; 463 } 464 465 /** 466 * This method creates bogus RouteNodeS as needed 467 * 468 * @param routeNodeId 469 * @return 470 */ 471 private RouteNode getRouteNode(String routeNodeId) { 472 RouteNode result = null; 473 474 if (routeNodeId != null) { 475 // if RouteNode doesn't exist, create it 476 if (!routeNodes.containsKey(routeNodeId)) { 477 result = new RouteNode(); 478 result.setRouteNodeId(routeNodeId); 479 routeNodes.put(routeNodeId, result); 480 } else { 481 result = routeNodes.get(routeNodeId); 482 } 483 } 484 return result; 485 } 486 487 /** 488 * This method creates bogus RouteNodeInstanceS as needed 489 * 490 * @param routeNodeInstanceId 491 * @return 492 */ 493 public RouteNodeInstance getRouteNodeInstance(String routeNodeInstanceId) { 494 RouteNodeInstance result = null; 495 496 if (routeNodeInstanceId != null) { 497 // if RouteNodeInstance doesn't exist, create it 498 if (!routeNodeInstances.containsKey(routeNodeInstanceId)) { 499 result = new RouteNodeInstance(); 500 result.setRouteNodeInstanceId(routeNodeInstanceId); 501 routeNodeInstances.put(routeNodeInstanceId, result); 502 } else { 503 result = routeNodeInstances.get(routeNodeInstanceId); 504 } 505 } 506 return result; 507 } 508 509 /** 510 * This method creates bogus NodeStateS as needed 511 * 512 * @param nodeStateId 513 * @return 514 */ 515 private NodeState getNodeState(String nodeStateId) { 516 NodeState result = null; 517 518 if (nodeStateId != null) { 519 // if NodeState doesn't exist, create it 520 if (!nodeStates.containsKey(nodeStateId)) { 521 result = new NodeState(); 522 result.setNodeStateId(nodeStateId); 523 nodeStates.put(nodeStateId, result); 524 } else { 525 result = nodeStates.get(nodeStateId); 526 } 527 } 528 return result; 529 } 530 531 } // end inner class FutureRouteNodeInstanceFabricator 532 533 /** 534 * Logs a new message to the route log for the current document, then refreshes the action taken list to display 535 * back the new message in the route log tab. User must have permission to log a message for the doc type and the 536 * request must be coming from the route log tab display (not the route log page). 537 */ 538 public ActionForward logActionMessageInRouteLog(ActionMapping mapping, ActionForm form, HttpServletRequest request, 539 HttpServletResponse response) throws Exception { 540 RouteLogForm routeLogForm = (RouteLogForm) form; 541 542 String documentId = null; 543 if (!org.apache.commons.lang.StringUtils.isEmpty(routeLogForm.getDocumentId())) { 544 documentId = routeLogForm.getDocumentId(); 545 } else if (!org.apache.commons.lang.StringUtils.isEmpty(routeLogForm.getDocId())) { 546 documentId = routeLogForm.getDocId(); 547 } else { 548 throw new WorkflowRuntimeException("No paramater provided to fetch document"); 549 } 550 551 DocumentRouteHeaderValue routeHeader = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId); 552 553 // check user has permission to add a route log message 554 boolean isAuthorizedToAddRouteLogMessage = KEWServiceLocator.getDocumentTypePermissionService() 555 .canAddRouteLogMessage(GlobalVariables.getUserSession().getPrincipalId(), routeHeader); 556 557 if (!isAuthorizedToAddRouteLogMessage) { 558 throw new InvalidActionTakenException("Principal with name '" 559 + GlobalVariables.getUserSession().getPrincipalName() 560 + "' is not authorized to add route log messages for documents of type '" 561 + routeHeader.getDocumentType().getName()); 562 } 563 564 LOG.info("Logging new action message for user " + GlobalVariables.getUserSession().getPrincipalName() 565 + ", route header " + routeHeader); 566 KEWServiceLocator.getWorkflowDocumentService().logDocumentAction( 567 GlobalVariables.getUserSession().getPrincipalId(), routeHeader, 568 routeLogForm.getNewRouteLogActionMessage()); 569 570 routeLogForm.setNewRouteLogActionMessage(""); 571 572 // retrieve routeHeader again to pull new action taken 573 routeHeader = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId, true); 574 fixActionRequestsPositions(routeHeader); 575 request.setAttribute("routeHeader", routeHeader); 576 577 // be sure to increment the internal nav number for the back button 578 routeLogForm.setInternalNavCount(routeLogForm.getNextNavCount()); 579 580 return mapping.findForward(getDefaultMapping()); 581 } 582 583}