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