001    /**
002     * Copyright 2005-2013 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     */
016    package org.kuali.rice.kew.superuser.web;
017    
018    import java.util.Collection;
019    import java.util.Collections;
020    import java.util.Comparator;
021    import java.util.Iterator;
022    import java.util.List;
023    
024    import javax.servlet.http.HttpServletRequest;
025    import javax.servlet.http.HttpServletResponse;
026    import javax.xml.namespace.QName;
027    
028    import org.apache.commons.lang.ArrayUtils;
029    import org.apache.commons.lang.StringUtils;
030    import org.apache.struts.action.ActionForm;
031    import org.apache.struts.action.ActionForward;
032    import org.apache.struts.action.ActionMapping;
033    import org.kuali.rice.kew.actionrequest.ActionRequestValue;
034    import org.kuali.rice.kew.api.KewApiConstants;
035    import org.kuali.rice.kew.api.KewApiServiceLocator;
036    import org.kuali.rice.kew.api.WorkflowDocumentFactory;
037    import org.kuali.rice.kew.api.action.ActionRequestType;
038    import org.kuali.rice.kew.api.action.AdHocRevoke;
039    import org.kuali.rice.kew.api.action.DocumentActionParameters;
040    import org.kuali.rice.kew.api.action.ReturnPoint;
041    import org.kuali.rice.kew.api.action.WorkflowDocumentActionsService;
042    import org.kuali.rice.kew.api.document.node.RouteNodeInstance;
043    import org.kuali.rice.kew.api.document.WorkflowDocumentService;
044    import org.kuali.rice.kew.api.exception.WorkflowException;
045    import org.kuali.rice.kew.doctype.bo.DocumentType;
046    import org.kuali.rice.kew.exception.WorkflowServiceErrorException;
047    import org.kuali.rice.kew.exception.WorkflowServiceErrorImpl;
048    import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
049    import org.kuali.rice.kew.service.KEWServiceLocator;
050    import org.kuali.rice.kew.api.KewApiConstants;
051    import org.kuali.rice.kew.web.AppSpecificRouteRecipient;
052    import org.kuali.rice.kew.web.KewKualiAction;
053    import org.kuali.rice.kim.api.group.GroupService;
054    import org.kuali.rice.kim.api.identity.principal.Principal;
055    import org.kuali.rice.kim.api.services.KimApiServiceLocator;
056    import org.kuali.rice.krad.UserSession;
057    import org.kuali.rice.krad.exception.ValidationException;
058    import org.kuali.rice.krad.util.GlobalVariables;
059    import org.kuali.rice.krad.util.KRADConstants;
060    import org.kuali.rice.krad.util.ObjectUtils;
061    import org.kuali.rice.ksb.api.KsbApiServiceLocator;
062    
063    /**
064     * A Struts Action which provides super user functionality.
065     * 
066     * @author Kuali Rice Team (rice.collab@kuali.org)
067     */
068    public class SuperUserAction extends KewKualiAction {
069        private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SuperUserAction.class);
070        public static final String UNAUTHORIZED = "authorizationFailure";
071    
072        //public ActionForward start(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
073        //  defaultDispatch(mapping, form, request, response);
074        //}
075    
076        @Override
077        public ActionForward execute(ActionMapping mapping, ActionForm form,
078                HttpServletRequest request, HttpServletResponse response)
079                throws Exception {
080            initForm(request, form);
081            return super.execute(mapping, form, request, response);
082        }
083    
084        @Override
085        public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request,
086                HttpServletResponse response) throws Exception {
087            ((SuperUserForm) form).getActionRequests().clear();
088            initForm(request, form);
089            return defaultDispatch(mapping, form, request, response);
090        }
091    
092        public ActionForward displaySuperUserDocument(ActionMapping mapping, ActionForm form, HttpServletRequest request,
093                HttpServletResponse response) throws Exception {
094            SuperUserForm superUserForm = (SuperUserForm) form;
095            superUserForm.setDocHandlerUrl(KewApiConstants.DOC_HANDLER_REDIRECT_PAGE + "?docId="
096                    + superUserForm.getDocumentId() + "&" + KewApiConstants.COMMAND_PARAMETER + "="
097                    + KewApiConstants.SUPERUSER_COMMAND);
098            return defaultDispatch(mapping, form, request, response);
099        }
100    
101        public ActionForward routeLevelApprove(ActionMapping mapping, ActionForm form, HttpServletRequest request,
102                HttpServletResponse response) throws Exception {
103            LOG.info("entering routeLevelApprove()...");
104            SuperUserForm superUserForm = (SuperUserForm) form;
105            String documentId = superUserForm.getRouteHeader().getDocumentId();
106            WorkflowDocumentActionsService documentActions = getWorkflowDocumentActionsService(documentId);
107            DocumentActionParameters parameters = DocumentActionParameters.create(documentId, getUserSession(request)
108                    .getPrincipalId(), superUserForm.getAnnotation());
109    
110            documentActions.superUserNodeApprove(parameters, superUserForm.isRunPostProcessorLogic(),
111                    superUserForm.getDestNodeName());
112            saveDocumentMessage("general.routing.superuser.routeLevelApproved", request, superUserForm.getDocumentId(),
113                    null);
114            LOG.info("exiting routeLevelApprove()...");
115            superUserForm.getActionRequests().clear();
116            initForm(request, form);
117            return defaultDispatch(mapping, form, request, response);
118        }
119    
120        public ActionForward approve(ActionMapping mapping, ActionForm form, HttpServletRequest request,
121                HttpServletResponse response) throws Exception {
122            LOG.info("entering approve() ...");
123            SuperUserForm superUserForm = (SuperUserForm) form;
124            String documentId = superUserForm.getRouteHeader().getDocumentId();
125            WorkflowDocumentActionsService documentActions = getWorkflowDocumentActionsService(documentId);
126            DocumentActionParameters parameters = DocumentActionParameters.create(documentId, getUserSession(request)
127                    .getPrincipalId(), superUserForm.getAnnotation());
128            documentActions.superUserBlanketApprove(parameters, superUserForm.isRunPostProcessorLogic());
129            saveDocumentMessage("general.routing.superuser.approved", request, superUserForm.getDocumentId(), null);
130            LOG.info("exiting approve() ...");
131            superUserForm.getActionRequests().clear();
132            initForm(request, form);
133            return defaultDispatch(mapping, form, request, response);
134        }
135    
136        public ActionForward disapprove(ActionMapping mapping, ActionForm form, HttpServletRequest request,
137                HttpServletResponse response) throws Exception {
138            LOG.info("entering disapprove() ...");
139            SuperUserForm superUserForm = (SuperUserForm) form;
140            String documentId = superUserForm.getRouteHeader().getDocumentId();
141            WorkflowDocumentActionsService documentActions = getWorkflowDocumentActionsService(documentId);
142            DocumentActionParameters parameters = DocumentActionParameters.create(documentId, getUserSession(request)
143                    .getPrincipalId(), superUserForm.getAnnotation());
144            documentActions.superUserDisapprove(parameters, superUserForm.isRunPostProcessorLogic());
145            saveDocumentMessage("general.routing.superuser.disapproved", request, superUserForm.getDocumentId(), null);
146            LOG.info("exiting disapprove() ...");
147            superUserForm.getActionRequests().clear();
148            initForm(request, form);
149            return defaultDispatch(mapping, form, request, response);
150        }
151    
152        public ActionForward cancel(ActionMapping mapping, ActionForm form, HttpServletRequest request,
153                HttpServletResponse response) throws Exception {
154            LOG.info("entering cancel() ...");
155            SuperUserForm superUserForm = (SuperUserForm) form;
156            String documentId = superUserForm.getRouteHeader().getDocumentId();
157            WorkflowDocumentActionsService documentActions = getWorkflowDocumentActionsService(documentId);
158            DocumentActionParameters parameters = DocumentActionParameters.create(documentId, getUserSession(request)
159                    .getPrincipalId(), superUserForm.getAnnotation());
160            documentActions.superUserCancel(parameters, superUserForm.isRunPostProcessorLogic());
161            saveDocumentMessage("general.routing.superuser.canceled", request, superUserForm.getDocumentId(), null);
162            LOG.info("exiting cancel() ...");
163            superUserForm.getActionRequests().clear();
164            initForm(request, form);
165            return defaultDispatch(mapping, form, request, response);
166        }
167    
168        public ActionForward returnToPreviousNode(ActionMapping mapping, ActionForm form, HttpServletRequest request,
169                HttpServletResponse response) throws Exception {
170            LOG.info("entering returnToPreviousNode() ...");
171            SuperUserForm superUserForm = (SuperUserForm) form;
172            String documentId = superUserForm.getRouteHeader().getDocumentId();
173            WorkflowDocumentActionsService documentActions = getWorkflowDocumentActionsService(documentId);
174            DocumentActionParameters parameters = DocumentActionParameters.create(documentId, getUserSession(request)
175                    .getPrincipalId(), superUserForm.getAnnotation());
176            documentActions.superUserReturnToPreviousNode(parameters, superUserForm.isRunPostProcessorLogic(),
177                    ReturnPoint.create(superUserForm.getReturnDestNodeName()));
178            saveDocumentMessage("general.routing.returnedToPreviousNode", request, "document", superUserForm
179                    .getReturnDestNodeName().toString());
180            LOG.info("exiting returnToPreviousRouteLevel() ...");
181            superUserForm.getActionRequests().clear();
182            initForm(request, form);
183            return defaultDispatch(mapping, form, request, response);
184        }
185    
186        public ActionForward actionRequestApprove(ActionMapping mapping, ActionForm form, HttpServletRequest request,
187                HttpServletResponse response) throws Exception {
188            LOG.info("entering actionRequestApprove() ...");
189            SuperUserForm superUserForm = (SuperUserForm) form;
190    
191            // Retrieve the relevant arguments from the "methodToCall" parameter.
192            String methodToCallAttr = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
193            superUserForm.setActionTakenRecipientCode(StringUtils.substringBetween(methodToCallAttr,
194                    KRADConstants.METHOD_TO_CALL_PARM1_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM1_RIGHT_DEL));
195            superUserForm.setActionTakenNetworkId(StringUtils.substringBetween(methodToCallAttr,
196                    KRADConstants.METHOD_TO_CALL_PARM2_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM2_RIGHT_DEL));
197            superUserForm.setActionTakenWorkGroupId(StringUtils.substringBetween(methodToCallAttr,
198                    KRADConstants.METHOD_TO_CALL_PARM4_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM4_RIGHT_DEL));
199            superUserForm.setActionTakenActionRequestId(StringUtils.substringBetween(methodToCallAttr,
200                    KRADConstants.METHOD_TO_CALL_PARM5_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM5_RIGHT_DEL));
201    
202            LOG.debug("Routing super user action request approve action");
203            boolean runPostProcessorLogic = ArrayUtils.contains(superUserForm.getActionRequestRunPostProcessorCheck(),
204                    superUserForm.getActionTakenActionRequestId());
205            String documentId = superUserForm.getRouteHeader().getDocumentId();
206            WorkflowDocumentActionsService documentActions = getWorkflowDocumentActionsService(documentId);
207            DocumentActionParameters parameters = DocumentActionParameters.create(documentId, getUserSession(request)
208                    .getPrincipalId(), superUserForm.getAnnotation());
209            documentActions.superUserTakeRequestedAction(parameters, runPostProcessorLogic,
210                    superUserForm.getActionTakenActionRequestId());
211            String messageString;
212            String actionReqest = StringUtils.substringBetween(methodToCallAttr,
213                    KRADConstants.METHOD_TO_CALL_PARM6_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM6_RIGHT_DEL);
214            if (actionReqest.equalsIgnoreCase("acknowledge")) {
215                messageString = "general.routing.superuser.actionRequestAcknowledged";
216            } else if (actionReqest.equalsIgnoreCase("FYI")) {
217                messageString = "general.routing.superuser.actionRequestFYI";
218            } else if (actionReqest.equalsIgnoreCase("complete")) {
219                messageString = "general.routing.superuser.actionRequestCompleted";
220            } else if (actionReqest.equalsIgnoreCase("approved")) {
221                messageString = "general.routing.superuser.actionRequestApproved";
222            } else {
223                messageString = "general.routing.superuser.actionRequestApproved";
224            }
225            saveDocumentMessage(messageString, request, superUserForm.getDocumentId(),
226                    superUserForm.getActionTakenActionRequestId());
227            LOG.info("exiting actionRequestApprove() ...");
228            superUserForm.getActionRequests().clear();
229            initForm(request, form);
230    
231            // If the action request was also an app specific request, remove it from the app specific route recipient list.
232            int removalIndex = findAppSpecificRecipientIndex(superUserForm, superUserForm.getActionTakenActionRequestId());
233    
234            if (removalIndex >= 0) {
235                superUserForm.getAppSpecificRouteList().remove(removalIndex);
236            }
237    
238            return defaultDispatch(mapping, form, request, response);
239        }
240    
241        /**
242         * Finds the index in the app specific route recipient list of the recipient whose routing was
243         * handled by the given action request.
244         * 
245         * @param superUserForm The SuperUserForm currently being processed.
246         * @param actionRequestId The ID of the action request that handled the routing of the app
247         *        specific recipient that is being removed.
248         * @return The index of the app specific route recipient that was handled by the given action
249         *         request, or -1 if no such recipient was found.
250         */
251        private int findAppSpecificRecipientIndex(SuperUserForm superUserForm, String actionRequestId) {
252            int tempIndex = 0;
253            for (Iterator<?> appRouteIter = superUserForm.getAppSpecificRouteList().iterator(); appRouteIter.hasNext();) {
254                    String tempActnReqId = ((AppSpecificRouteRecipient) appRouteIter.next()).getActionRequestId();
255                    if (StringUtils.equals(tempActnReqId, actionRequestId)) {
256                            return tempIndex;
257                    }
258                    tempIndex++;
259            }
260            return -1;
261        }
262    
263        public ActionForward initForm(HttpServletRequest request, ActionForm form) throws Exception {
264            request.setAttribute("Constants", getServlet().getServletContext().getAttribute("KewApiConstants"));
265            SuperUserForm superUserForm = (SuperUserForm) form;
266            DocumentRouteHeaderValue routeHeader = KEWServiceLocator.getRouteHeaderService().getRouteHeader(
267                    superUserForm.getDocumentId());
268            if(ObjectUtils.isNull(routeHeader)) {
269                throw new ValidationException("No route header ID found.  Try searching for the document again using the super user document search.");
270            }
271            superUserForm.setRouteHeader(routeHeader);
272            String principalId = getUserSession(request).getPrincipalId();
273            boolean isAuthorized = KEWServiceLocator.getDocumentTypePermissionService().canAdministerRouting(principalId,
274                    routeHeader.getDocumentType());
275            superUserForm.setAuthorized(isAuthorized);
276            if (!isAuthorized) {
277                saveDocumentMessage("general.routing.superuser.notAuthorized", request, superUserForm.getDocumentId(), null);
278                return null;
279            }
280    
281            superUserForm.setFutureNodeNames(KEWServiceLocator.getRouteNodeService().findFutureNodeNames(
282                    routeHeader.getDocumentId()));
283    
284            Collection actionRequests = KEWServiceLocator.getActionRequestService().findPendingByDoc(
285                    routeHeader.getDocumentId());
286            Iterator requestIterator = actionRequests.iterator();
287            while (requestIterator.hasNext()) {
288                ActionRequestValue req = (ActionRequestValue) requestIterator.next();
289                // if (KewApiConstants.ACTION_REQUEST_APPROVE_REQ.equalsIgnoreCase(req.getActionRequested())) {
290                superUserForm.getActionRequests().add(req);
291                // }
292            }
293    
294            superUserForm.setDocId(superUserForm.getDocumentId());
295            if (superUserForm.getDocId() != null) {
296                superUserForm.setWorkflowDocument(WorkflowDocumentFactory.loadDocument(getUserSession(request)
297                        .getPrincipalId(), superUserForm.getDocId()));
298                superUserForm.establishVisibleActionRequestCds();
299            }
300    
301            return null;
302        }
303    
304        private void saveDocumentMessage(String messageKey, HttpServletRequest request, String subVariable1,
305                String subVariable2) {
306            if (subVariable2 == null) {
307                GlobalVariables.getMessageMap().putInfo("document", messageKey, subVariable1);
308            } else {
309                GlobalVariables.getMessageMap().putInfo("document", messageKey, subVariable1, subVariable2);
310            }
311        }
312    
313        public ActionForward routeToAppSpecificRecipient(ActionMapping mapping, ActionForm form,
314                HttpServletRequest request, HttpServletResponse response) throws Exception {
315            SuperUserForm superUserForm = (SuperUserForm) form;
316    
317            //super.routeToAppSpecificRecipient(mapping, form, request, response);
318            //WorkflowRoutingForm routingForm = (WorkflowRoutingForm) form;
319            String routeType = StringUtils.substringBetween(
320                    (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE),
321                    KRADConstants.METHOD_TO_CALL_PARM1_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM1_RIGHT_DEL);
322            AppSpecificRouteRecipient recipient = null;
323            if (KewApiConstants.PERSON.equals(routeType)) {
324                recipient = superUserForm.getAppSpecificRouteRecipient();
325                recipient.setActionRequested(superUserForm.getAppSpecificRouteActionRequestCd());
326                superUserForm.setAppSpecificPersonId(recipient.getId());
327            } else {
328                recipient = superUserForm.getAppSpecificRouteRecipient2();
329                recipient.setActionRequested(superUserForm.getAppSpecificRouteActionRequestCd2());
330                superUserForm.setAppSpecificWorkgroupId(recipient.getId());
331            }
332    
333            validateAppSpecificRoute(recipient);
334    
335            // Make sure that the requested action is still available.
336            superUserForm.establishVisibleActionRequestCds();
337            if (superUserForm.getAppSpecificRouteActionRequestCds().get(recipient.getActionRequested()) == null) {
338                GlobalVariables.getMessageMap().putError("appSpecificRouteRecipient" +
339                        ((KewApiConstants.WORKGROUP.equals(recipient.getType())) ? "2" : "") + ".id",
340                        "appspecificroute.actionrequested.invalid");
341    
342                throw new ValidationException("The requested action of '" + recipient.getActionRequested()
343                        + "' is no longer available for this document");
344            }
345    
346            try {
347                String routeNodeName = getAdHocRouteNodeName(superUserForm.getWorkflowDocument().getDocumentId());
348                //if (KewApiConstants.PERSON.equals(recipient.getType())) {
349                if (KewApiConstants.PERSON.equals(routeType)) {
350                    String recipientPrincipalId = KEWServiceLocator.getIdentityHelperService().getIdForPrincipalName(
351                            recipient.getId());
352                    superUserForm.getWorkflowDocument().adHocToPrincipal(
353                            ActionRequestType.fromCode(recipient.getActionRequested()), routeNodeName,
354                            superUserForm.getAnnotation(), recipientPrincipalId, "", true);
355                } else {
356                    String recipientGroupId = KEWServiceLocator.getIdentityHelperService().getIdForGroupName(
357                            recipient.getNamespaceCode(), recipient.getId());
358                    superUserForm.getWorkflowDocument().adHocToGroup(
359                            ActionRequestType.fromCode(recipient.getActionRequested()), routeNodeName,
360                            superUserForm.getAnnotation(), recipientGroupId, "", true);
361                }
362            } catch (Exception e) {
363                LOG.error("Error generating app specific route request", e);
364                throw new WorkflowServiceErrorException("AppSpecific Route Error", new WorkflowServiceErrorImpl(
365                        "AppSpecific Route Error", "appspecificroute.systemerror"));
366            }
367    
368            superUserForm.getActionRequests().clear();
369            initForm(request, form);
370    
371            // Retrieve the ID of the latest action request and store it with the app specific route recipient.
372            ActionRequestValue latestActnReq = getLatestActionRequest(superUserForm);
373            if (latestActnReq != null) {
374                recipient.setActionRequestId(latestActnReq.getActionRequestId());
375            }
376            // Add the recipient to the list.
377            superUserForm.getAppSpecificRouteList().add(recipient);
378            superUserForm.resetAppSpecificRoute();
379    
380            return start(mapping, form, request, response);
381        }
382    
383        /**
384         * Searches the current action requests list for the most recent request, which is the one with
385         * the highest ID.
386         * @param superUserForm The SuperUserForm currently being processed.
387         * @return The action request on the form with the highest ID, or null if no action requests
388         *         exist in the list.
389         */
390        private ActionRequestValue getLatestActionRequest(SuperUserForm superUserForm) {
391            ActionRequestValue latestActnReq = null;
392    //      long latestId = -1;
393            
394            // FIXME: KULRICE-5201 required the following refactor since action request ids are no longer numeric (and in any case the assumption that
395            // they are strictly ordinal by time of creation may be false)
396            List<ActionRequestValue> actionRequests = superUserForm.getActionRequests();
397            
398            if (actionRequests != null && actionRequests.size() > 0) {
399                    Collections.sort(actionRequests, new Comparator<ActionRequestValue>() {
400            
401                                    @Override
402                                    // Should should by date in descending order
403                                    public int compare(ActionRequestValue o1, ActionRequestValue o2) {
404                                            if (o1 == null && o2 == null)
405                                                    return 0;
406                                            if (o1 == null)
407                                                    return -1;
408                                            if (o2 == null)
409                                                    return 1;
410                                            
411                                            if (o1.getCreateDate() == null && o2.getCreateDate() == null)
412                                                    return 0;
413                                            if (o1.getCreateDate() == null)
414                                                    return -1;
415                                            if (o2.getCreateDate() == null)
416                                                    return 1;
417            
418                                            return o2.getCreateDate().compareTo(o1.getCreateDate());
419                                    }
420                    
421                    });
422    
423                    // If the list above is sorted in descending order then the first item should be the most recent
424                    latestActnReq = actionRequests.get(0);
425            }
426            
427            // TODO: As part of KULRICE-5329 this change above needs to be verified and compared with code below
428    //      // Search the list for the action request with the highest action request value.
429    //      for (Iterator<?> actnReqIter = superUserForm.getActionRequests().iterator(); actnReqIter.hasNext();) {
430    //              ActionRequestValue tmpActnReq = (ActionRequestValue) actnReqIter.next();
431    //              if (tmpActnReq.getActionRequestId().longValue() > latestId) {
432    //                      latestActnReq = tmpActnReq;
433    //                      latestId = tmpActnReq.getActionRequestId().longValue();
434    //              }
435    //      }
436            return latestActnReq;
437        }
438    
439        /**
440         * Removes an existing AppSpecificRouteRecipient from the list.
441         */
442        public ActionForward removeAppSpecificRecipient(ActionMapping mapping, ActionForm form, HttpServletRequest request,
443                HttpServletResponse response) throws Exception {
444            SuperUserForm superUserForm = (SuperUserForm) form;
445            // Make sure a valid route recipient index was specified in the "methodToCall" attribute.
446            String strIndex = StringUtils.substringBetween(
447                    (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE),
448                    KRADConstants.METHOD_TO_CALL_PARM1_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM1_RIGHT_DEL);
449            if (StringUtils.isBlank(strIndex)) {
450                throw new WorkflowException("No adhoc route recipient index specified");
451            }
452            int removeIndex = Integer.parseInt(strIndex);
453            if (removeIndex < 0 || removeIndex >= superUserForm.getAppSpecificRouteList().size()) {
454                throw new WorkflowException("Invalid adhoc route recipient index specified");
455            }
456            // Remove the specified recipient from the routing, based on the recipient's ID and the ID of the action request that handled the recipient.
457            AppSpecificRouteRecipient removedRec = (AppSpecificRouteRecipient) superUserForm.getAppSpecificRouteList().get(
458                    removeIndex);
459            if (removedRec.getActionRequestId() != null) {
460                superUserForm.getWorkflowDocument().revokeAdHocRequestById(removedRec.getActionRequestId().toString(), "");
461            } else {
462                AdHocRevoke adHocRevoke = null;
463                // Set the ID according to whether the recipient is a person or a group.
464                if (KewApiConstants.PERSON.equals(removedRec.getType())) {
465                    adHocRevoke = AdHocRevoke.createRevokeFromPrincipal(KEWServiceLocator.getIdentityHelperService()
466                            .getIdForPrincipalName(removedRec.getId()));
467                } else {
468                    adHocRevoke = AdHocRevoke.createRevokeFromGroup(KEWServiceLocator.getIdentityHelperService()
469                            .getIdForGroupName(removedRec.getNamespaceCode(), removedRec.getId()));
470                }
471                superUserForm.getWorkflowDocument().revokeAdHocRequests(adHocRevoke, "");
472            }
473            superUserForm.getAppSpecificRouteList().remove(removeIndex);
474    
475            superUserForm.getActionRequests().clear();
476            initForm(request, form);
477            return start(mapping, form, request, response);
478        }
479    
480        private WorkflowDocumentActionsService getWorkflowDocumentActionsService(String documentId) {
481            DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByDocumentId(documentId);
482            String applicationId = documentType.getApplicationId();
483            QName serviceName = new QName(KewApiConstants.Namespaces.KEW_NAMESPACE_2_0,
484                    KewApiConstants.ServiceNames.WORKFLOW_DOCUMENT_ACTIONS_SERVICE_SOAP);
485            WorkflowDocumentActionsService service = (WorkflowDocumentActionsService) KsbApiServiceLocator.getServiceBus()
486                    .getService(serviceName, applicationId);
487            if (service == null) {
488                service = KewApiServiceLocator.getWorkflowDocumentActionsService();
489            }
490            return service;
491        }
492    
493        protected void validateAppSpecificRoute(AppSpecificRouteRecipient recipient) {
494            if (recipient.getId() == null || recipient.getId().trim().equals("")) {
495                GlobalVariables.getMessageMap().putError("appSpecificRouteRecipient" +
496                        ((KewApiConstants.WORKGROUP.equals(recipient.getType())) ? "2" : "") + ".id",
497                        "appspecificroute.recipient.required");
498            } else {
499                if (KewApiConstants.PERSON.equals(recipient.getType())) {
500                    Principal principal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(
501                            recipient.getId());
502                    if (principal == null) {
503                        LOG.error("App Specific user recipient not found");
504                        GlobalVariables.getMessageMap().putError("appSpecificRouteRecipient.id",
505                                "appspecificroute.user.invalid");
506                    }
507                } else if (KewApiConstants.WORKGROUP.equals(recipient.getType())) {
508                    //if (getIdentityManagementService().getGroup(recipient.getId()) == null) {
509                    if (getGroupService().getGroupByNamespaceCodeAndName(recipient.getNamespaceCode(), recipient.getId()) == null) {
510                        GlobalVariables.getMessageMap().putError("appSpecificRouteRecipient2.id",
511                                "appspecificroute.workgroup.invalid");
512                    }
513                }
514            }
515            if (GlobalVariables.getMessageMap().hasErrors()) {
516                throw new ValidationException("AppSpecific Route validation Errors");
517            }
518    
519        }
520    
521        protected String getAdHocRouteNodeName(String documentId) throws WorkflowException {
522            WorkflowDocumentService workflowDocumentService = KewApiServiceLocator.getWorkflowDocumentService();
523            List<RouteNodeInstance> nodeInstances = workflowDocumentService.getActiveRouteNodeInstances(documentId);
524            if (nodeInstances == null || nodeInstances.isEmpty()) {
525                nodeInstances = workflowDocumentService.getTerminalRouteNodeInstances(documentId);
526            }
527            if (nodeInstances == null || nodeInstances.isEmpty()) {
528                throw new WorkflowException("Could not locate a node on the document to send the ad hoc request to.");
529            }
530            return nodeInstances.get(0).getName();
531        }
532    
533        private GroupService getGroupService() {
534            return KimApiServiceLocator.getGroupService();
535        }
536    
537        public static UserSession getUserSession(HttpServletRequest request) {
538            return GlobalVariables.getUserSession();
539        }
540    
541    }