View Javadoc

1   /**
2    * Copyright 2005-2012 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.actionrequest.service.impl;
17  
18  import java.util.ArrayList;
19  import java.util.Collection;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Set;
27  
28  import org.apache.commons.lang.ObjectUtils;
29  import org.apache.commons.lang.StringUtils;
30  import org.apache.log4j.Logger;
31  import org.kuali.rice.core.api.config.CoreConfigHelper;
32  import org.kuali.rice.core.api.exception.RiceRuntimeException;
33  import org.kuali.rice.core.api.config.property.ConfigContext;
34  import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator;
35  import org.kuali.rice.kew.actionitem.ActionItem;
36  import org.kuali.rice.kew.actionlist.service.ActionListService;
37  import org.kuali.rice.kew.actionrequest.ActionRequestValue;
38  import org.kuali.rice.kew.actionrequest.Recipient;
39  import org.kuali.rice.kew.actionrequest.dao.ActionRequestDAO;
40  import org.kuali.rice.kew.actionrequest.service.ActionRequestService;
41  import org.kuali.rice.kew.api.document.DocumentRefreshQueue;
42  import org.kuali.rice.kew.actiontaken.ActionTakenValue;
43  import org.kuali.rice.kew.actiontaken.service.ActionTakenService;
44  import org.kuali.rice.kew.api.action.ActionRequestPolicy;
45  import org.kuali.rice.kew.api.action.ActionRequestStatus;
46  import org.kuali.rice.kew.api.action.RecipientType;
47  import org.kuali.rice.kew.doctype.bo.DocumentType;
48  import org.kuali.rice.kew.engine.ActivationContext;
49  import org.kuali.rice.kew.engine.node.RouteNodeInstance;
50  import org.kuali.rice.kew.exception.WorkflowServiceErrorException;
51  import org.kuali.rice.kew.exception.WorkflowServiceErrorImpl;
52  import org.kuali.rice.kew.messaging.MessageServiceNames;
53  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
54  import org.kuali.rice.kew.routeheader.service.RouteHeaderService;
55  import org.kuali.rice.kew.routemodule.RouteModule;
56  import org.kuali.rice.kew.service.KEWServiceLocator;
57  import org.kuali.rice.kew.util.FutureRequestDocumentStateManager;
58  import org.kuali.rice.kew.api.KewApiConstants;
59  import org.kuali.rice.kew.util.PerformanceLogger;
60  import org.kuali.rice.kew.util.ResponsibleParty;
61  import org.kuali.rice.kim.api.identity.principal.Principal;
62  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
63  import org.kuali.rice.krad.util.KRADConstants;
64  
65  
66  /**
67   * Default implementation of the {@link ActionRequestService}.
68   *
69   * @author Kuali Rice Team (rice.collab@kuali.org)
70   */
71  public class ActionRequestServiceImpl implements ActionRequestService {
72      private static final Logger LOG = Logger.getLogger(ActionRequestServiceImpl.class);
73  
74      private ActionRequestDAO actionRequestDAO;
75  
76      public ActionRequestValue findByActionRequestId(String actionRequestId) {
77          return getActionRequestDAO().getActionRequestByActionRequestId(actionRequestId);
78      }
79  
80      public Map<String, String> getActionsRequested(DocumentRouteHeaderValue routeHeader, String principalId, boolean completeAndApproveTheSame) {
81      	return getActionsRequested(principalId, routeHeader.getActionRequests(), completeAndApproveTheSame);
82      }
83      
84      /**
85       * Returns a Map of actions that are requested for the given principalId in the given list of action requests.
86       * @param principalId
87       * @param actionRequests
88       * @param completeAndApproveTheSame
89       * @return
90       */
91      protected Map<String, String> getActionsRequested(String principalId, List<ActionRequestValue> actionRequests, boolean completeAndApproveTheSame) {
92      	Map<String, String> actionsRequested = new HashMap<String, String>();
93          actionsRequested.put(KewApiConstants.ACTION_REQUEST_FYI_REQ, "false");
94          actionsRequested.put(KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, "false");
95          actionsRequested.put(KewApiConstants.ACTION_REQUEST_APPROVE_REQ, "false");
96          actionsRequested.put(KewApiConstants.ACTION_REQUEST_COMPLETE_REQ, "false");
97      	String topActionRequested = KewApiConstants.ACTION_REQUEST_FYI_REQ;
98          for (ActionRequestValue actionRequest : actionRequests) {
99              // we are getting the full list of requests here, so no need to look at role requests, if we did this then
100             // we could get a "false positive" for "all approve" roles where only part of the request graph is marked
101             // as "done"
102             if (!RecipientType.ROLE.getCode().equals(actionRequest.getRecipientTypeCd()) &&
103                     actionRequest.isRecipientRoutedRequest(principalId) && actionRequest.isActive()) {
104                 int actionRequestComparison = ActionRequestValue.compareActionCode(actionRequest.getActionRequested(), topActionRequested, completeAndApproveTheSame);
105                 if (actionRequest.isFYIRequest() && actionRequestComparison >= 0) {
106                     actionsRequested.put(KewApiConstants.ACTION_REQUEST_FYI_REQ, "true");
107                 } else if (actionRequest.isAcknowledgeRequest() && actionRequestComparison >= 0) {
108                     actionsRequested.put(KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, "true");
109                     actionsRequested.put(KewApiConstants.ACTION_REQUEST_FYI_REQ, "false");
110                     topActionRequested = actionRequest.getActionRequested();
111                 } else if (actionRequest.isApproveRequest() && actionRequestComparison >= 0) {
112                     actionsRequested.put(KewApiConstants.ACTION_REQUEST_APPROVE_REQ, "true");
113                     actionsRequested.put(KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, "false");
114                     actionsRequested.put(KewApiConstants.ACTION_REQUEST_FYI_REQ, "false");
115                     topActionRequested = actionRequest.getActionRequested();
116                 } else if (actionRequest.isCompleteRequst() && actionRequestComparison >= 0) {
117                 	actionsRequested.put(KewApiConstants.ACTION_REQUEST_COMPLETE_REQ, "true");
118                 	actionsRequested.put(KewApiConstants.ACTION_REQUEST_APPROVE_REQ, "false");
119                     actionsRequested.put(KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, "false");
120                     actionsRequested.put(KewApiConstants.ACTION_REQUEST_FYI_REQ, "false");
121                 	if (completeAndApproveTheSame) {
122                 		actionsRequested.put(KewApiConstants.ACTION_REQUEST_APPROVE_REQ, "true");
123                 	}
124                     topActionRequested = actionRequest.getActionRequested();
125                 }
126             }
127         }
128         return actionsRequested;
129     }
130 
131     public ActionRequestValue initializeActionRequestGraph(ActionRequestValue actionRequest,
132             DocumentRouteHeaderValue document, RouteNodeInstance nodeInstance) {
133         if (actionRequest.getParentActionRequest() != null) {
134             LOG.warn("-->A non parent action request from doc " + document.getDocumentId());
135             actionRequest = KEWServiceLocator.getActionRequestService().getRoot(actionRequest);
136         }
137         propagatePropertiesToRequestGraph(actionRequest, document, nodeInstance);
138         return actionRequest;
139     }
140 
141     private void propagatePropertiesToRequestGraph(ActionRequestValue actionRequest, DocumentRouteHeaderValue document,
142             RouteNodeInstance nodeInstance) {
143         setPropertiesToRequest(actionRequest, document, nodeInstance);
144         for (ActionRequestValue actionRequestValue : actionRequest.getChildrenRequests())
145         {
146             propagatePropertiesToRequestGraph(actionRequestValue, document, nodeInstance);
147         }
148     }
149 
150     private void setPropertiesToRequest(ActionRequestValue actionRequest, DocumentRouteHeaderValue document,
151             RouteNodeInstance nodeInstance) {
152         actionRequest.setDocumentId(document.getDocumentId());
153         actionRequest.setDocVersion(document.getDocVersion());
154         actionRequest.setRouteLevel(document.getDocRouteLevel());
155         actionRequest.setNodeInstance(nodeInstance);
156         actionRequest.setStatus(ActionRequestStatus.INITIALIZED.getCode());
157     }
158 
159 
160 
161     public void activateRequests(Collection actionRequests) {
162         activateRequests(actionRequests, new ActivationContext(!ActivationContext.CONTEXT_IS_SIMULATION));
163     }
164 
165     public void activateRequests(Collection actionRequests, boolean simulate) {
166         activateRequests(actionRequests, new ActivationContext(simulate));
167     }
168 
169     public void activateRequests(Collection actionRequests, ActivationContext activationContext) {
170         if (actionRequests == null) {
171             return;
172         }
173         PerformanceLogger performanceLogger = null;
174         if ( LOG.isInfoEnabled() ) {
175         	performanceLogger = new PerformanceLogger();
176         }
177         activationContext.setGeneratedActionItems(new ArrayList<ActionItem>());
178         activateRequestsInternal(actionRequests, activationContext);
179         if (!activationContext.isSimulation()) {
180             KEWServiceLocator.getNotificationService().notify(ActionItem.to(activationContext.getGeneratedActionItems()));
181         }
182         if ( LOG.isInfoEnabled() ) {
183         	performanceLogger.log("Time to " + (activationContext.isSimulation() ? "simulate activation of " : "activate ")
184         			+ actionRequests.size() + " action requests.");
185         }
186         if ( LOG.isDebugEnabled() ) {
187         	LOG.debug("Generated " + activationContext.getGeneratedActionItems().size() + " action items.");
188         }
189     }
190 
191     public void activateRequest(ActionRequestValue actionRequest) {
192         activateRequests(Collections.singletonList(actionRequest), new ActivationContext(!ActivationContext.CONTEXT_IS_SIMULATION));
193     }
194 
195     public void activateRequest(ActionRequestValue actionRequest, boolean simulate) {
196         activateRequests(Collections.singletonList(actionRequest), new ActivationContext(simulate));
197     }
198 
199     public void activateRequest(ActionRequestValue actionRequest, ActivationContext activationContext) {
200         activateRequests(Collections.singletonList(actionRequest), activationContext);
201     }
202 
203     public List activateRequestNoNotification(ActionRequestValue actionRequest, boolean simulate) {
204         return activateRequestNoNotification(actionRequest, new ActivationContext(simulate));
205     }
206 
207     public List activateRequestNoNotification(ActionRequestValue actionRequest, ActivationContext activationContext) {
208         activationContext.setGeneratedActionItems(new ArrayList<ActionItem>());
209         activateRequestInternal(actionRequest, activationContext);
210         return activationContext.getGeneratedActionItems();
211     }
212 
213     /**
214      * Internal helper method for activating a Collection of action requests and their children. Maintains an accumulator
215      * for generated action items.
216      * @param actionRequests
217      * @param activationContext
218      */
219     private void activateRequestsInternal(Collection actionRequests, ActivationContext activationContext) {
220         if (actionRequests == null) {
221             return;
222         }
223         List<?> actionRequestList = new ArrayList<Object>(actionRequests);
224         for (int i = 0; i < actionRequestList.size(); i++) {
225         	activateRequestInternal((ActionRequestValue) actionRequestList.get(i), activationContext);
226         }
227     }
228 
229     /**
230      * Internal helper method for activating a single action requests and it's children. Maintains an accumulator for
231      * generated action items.
232      */
233     private void activateRequestInternal(ActionRequestValue actionRequest, ActivationContext activationContext) {
234         PerformanceLogger performanceLogger = null;
235         if ( LOG.isInfoEnabled() ) {
236         	performanceLogger = new PerformanceLogger();
237         }
238         if (actionRequest == null || actionRequest.isActive() || actionRequest.isDeactivated()) {
239             return;
240         }
241         processResponsibilityId(actionRequest);
242         if (deactivateOnActionAlreadyTaken(actionRequest, activationContext)) {
243             return;
244         }
245         if (deactivateOnInactiveGroup(actionRequest, activationContext)) {
246             return;
247         }
248         if (deactivateOnEmptyGroup(actionRequest, activationContext)) {
249         	return;
250         }
251         actionRequest.setStatus(ActionRequestStatus.ACTIVATED.getCode());
252         if (!activationContext.isSimulation()) {
253             saveActionRequest(actionRequest);
254             activationContext.getGeneratedActionItems().addAll(generateActionItems(actionRequest, activationContext));
255         }
256         activateRequestsInternal(actionRequest.getChildrenRequests(), activationContext);
257         activateRequestInternal(actionRequest.getParentActionRequest(), activationContext);
258         if ( LOG.isInfoEnabled() ) {
259         	if (activationContext.isSimulation()) {
260                 performanceLogger.log("Time to simulate activation of request.");
261 	        } else {
262 	            performanceLogger.log("Time to activate action request with id " + actionRequest.getActionRequestId());
263 	        }
264         }
265     }
266 
267     /**
268      * Generates ActionItems for the given ActionRequest and returns the List of generated Action Items.
269      *
270      * @param actionRequest
271      * @param activationContext
272      * @return the List of generated ActionItems
273      */
274     private List<ActionItem> generateActionItems(ActionRequestValue actionRequest, ActivationContext activationContext) {
275     	if ( LOG.isDebugEnabled() ) {
276     		LOG.debug("generating the action items for request " + actionRequest.getActionRequestId());
277     	}
278         List<ActionItem> actionItems = new ArrayList<ActionItem>();
279         if (!actionRequest.isPrimaryDelegator()) {
280             if (actionRequest.isGroupRequest()) {
281                 List<String> principalIds =  KimApiServiceLocator.getGroupService().getMemberPrincipalIds(actionRequest.getGroupId());
282                 actionItems.addAll(createActionItemsForPrincipals(actionRequest, principalIds));
283             } else if (actionRequest.isUserRequest()) {
284                 ActionItem actionItem = getActionListService().createActionItemForActionRequest(actionRequest);
285                 actionItems.add(actionItem);
286             }
287         }
288         if (!activationContext.isSimulation()) {
289             for (ActionItem actionItem: actionItems) {
290             	if ( LOG.isDebugEnabled() ) {
291             		LOG.debug("Saving action item: " + actionItems);
292             	}
293                 getActionListService().saveActionItem(actionItem);
294             }
295         } else {
296         	actionRequest.getSimulatedActionItems().addAll(actionItems);
297         }
298         return actionItems;
299     }
300 
301     private List<ActionItem> createActionItemsForPrincipals(ActionRequestValue actionRequest, List<String> principalIds) {
302         List<ActionItem> actionItems = new ArrayList<ActionItem>();
303         for (String principalId: principalIds) {
304 
305             ActionItem actionItem = getActionListService().createActionItemForActionRequest(actionRequest);
306             actionItem.setPrincipalId(principalId);
307             actionItem.setRoleName(actionRequest.getQualifiedRoleName());
308 
309             //KULRICE-3307 Prevent workflow from attempting to activate requests for null principals
310             String ignoreUnknownPrincipalIdsValue = ConfigContext.getCurrentContextConfig().getProperty(KewApiConstants.WORKFLOW_ACTION_IGNORE_UNKOWN_PRINCIPAL_IDS);
311             boolean ignoreUnknownPrincipalIds = Boolean.parseBoolean(ignoreUnknownPrincipalIdsValue);
312 
313             if(principalId==null && ignoreUnknownPrincipalIds)
314             {
315                 LOG.warn("Ignoring action item with actionRequestID of " + actionRequest.getActionRequestId()  + " due to null principalId.");
316             }
317             else
318             {
319                 if(principalId==null)
320                 {
321                     IllegalArgumentException e = new IllegalArgumentException("Exception thrown when trying to add action item with null principalId");
322                     LOG.error(e);
323                     throw e;
324                 }
325                 else
326                 {
327                     actionItems.add(actionItem);
328                 }
329             }
330         }
331         return actionItems;
332     }
333 
334     private void processResponsibilityId(ActionRequestValue actionRequest) {
335     	if (actionRequest.getResolveResponsibility()) {
336 	        String responsibilityId = actionRequest.getResponsibilityId();
337 	        try {
338 	            RouteModule routeModule = KEWServiceLocator.getRouteModuleService().findRouteModule(actionRequest);
339 	            if (responsibilityId != null && actionRequest.isRouteModuleRequest()) {
340 	            	if ( LOG.isDebugEnabled() ) {
341 	            		LOG.debug("Resolving responsibility id for action request id=" + actionRequest.getActionRequestId()
342 	                        + " and responsibility id=" + actionRequest.getResponsibilityId());
343 	            	}
344 	                ResponsibleParty responsibleParty = routeModule.resolveResponsibilityId(actionRequest.getResponsibilityId());
345 	                if (responsibleParty == null) {
346 	                    return;
347 	                }
348 	                if (responsibleParty.getPrincipalId() != null) {
349 	                    Principal user = KimApiServiceLocator.getIdentityService()
350 	                            .getPrincipal(responsibleParty.getPrincipalId());
351 	                    actionRequest.setPrincipalId(user.getPrincipalId());
352 	                } else if (responsibleParty.getGroupId() != null) {
353 	                	actionRequest.setGroupId(responsibleParty.getGroupId());
354 	                } else if (responsibleParty.getRoleName() != null) {
355 	                    actionRequest.setRoleName(responsibleParty.getRoleName());
356 	                }
357 	            }
358 	        } catch (Exception e) {
359 	            LOG.error("Exception thrown when trying to resolve responsibility id " + responsibilityId, e);
360 	            throw new RuntimeException(e);
361 	        }
362     	}
363     }
364 
365     protected boolean deactivateOnActionAlreadyTaken(ActionRequestValue actionRequestToActivate,
366             ActivationContext activationContext) {
367 
368         FutureRequestDocumentStateManager futureRequestStateMngr = null;
369 
370         if (actionRequestToActivate.isGroupRequest()) {
371             futureRequestStateMngr = new FutureRequestDocumentStateManager(actionRequestToActivate.getRouteHeader(), actionRequestToActivate.getGroup());
372         } else if (actionRequestToActivate.isUserRequest()) {
373             futureRequestStateMngr = new FutureRequestDocumentStateManager(actionRequestToActivate.getRouteHeader(), actionRequestToActivate.getPrincipalId());
374         } else {
375             return false;
376         }
377 
378         if (futureRequestStateMngr.isReceiveFutureRequests()) {
379             return false;
380         }
381         if (!actionRequestToActivate.getForceAction() || futureRequestStateMngr.isDoNotReceiveFutureRequests()) {
382             ActionTakenValue previousActionTaken = null;
383             if (!activationContext.isSimulation()) {
384                 previousActionTaken = getActionTakenService().getPreviousAction(actionRequestToActivate);
385             } else {
386                 previousActionTaken = getActionTakenService().getPreviousAction(actionRequestToActivate,
387                         activationContext.getSimulatedActionsTaken());
388             }
389             if (previousActionTaken != null) {
390                 if ( LOG.isDebugEnabled() ) {
391                 	LOG.debug("found a satisfying action taken so setting this request done.  Action Request Id "
392                             + actionRequestToActivate.getActionRequestId());
393                 }
394                 // set up the delegation for an action taken if this is a delegate request and the delegate has
395                 // already taken action.
396                 if (!previousActionTaken.isForDelegator() && actionRequestToActivate.getParentActionRequest() != null) {
397                     previousActionTaken.setDelegator(actionRequestToActivate.getParentActionRequest().getRecipient());
398                     if (!activationContext.isSimulation()) {
399                         getActionTakenService().saveActionTaken(previousActionTaken);
400                     }
401                 }
402                 deactivateRequest(previousActionTaken, actionRequestToActivate, null, activationContext);
403                 return true;
404             }
405         }
406         if ( LOG.isDebugEnabled() ) {
407         	LOG.debug("Forcing action for action request " + actionRequestToActivate.getActionRequestId());
408         }
409         return false;
410     }
411     
412     /**
413      * Checks if the action request which is being activated has a group with no members.  If this is the case then it will immediately
414      * initiate de-activation on the request since a group with no members will result in no action items being generated so should be
415      * effectively skipped.
416      */
417     protected boolean deactivateOnEmptyGroup(ActionRequestValue actionRequestToActivate, ActivationContext activationContext) {
418     	if (actionRequestToActivate.isGroupRequest()) {
419     		 if (KimApiServiceLocator.getGroupService().getMemberPrincipalIds(actionRequestToActivate.getGroup().getId()).isEmpty()) {
420     			 deactivateRequest(null, actionRequestToActivate, null, activationContext);
421     			 return true;
422          	}
423     	}
424     	return false;
425     }
426 
427     /**
428      * Checks if the action request which is being activated is being assigned to an inactive group.  If this is the case and if the FailOnInactiveGroup 
429      * policy is set to false then it will immediately initiate de-activation on the request
430      */
431     protected boolean deactivateOnInactiveGroup(ActionRequestValue actionRequestToActivate, ActivationContext activationContext) {
432         if (actionRequestToActivate.isGroupRequest()) {
433             if (!actionRequestToActivate.getGroup().isActive() && !actionRequestToActivate.getRouteHeader().getDocumentType().getFailOnInactiveGroup().getPolicyValue()) {
434                 deactivateRequest(null, actionRequestToActivate, null, activationContext);
435                 return true;
436             }
437         }
438         return false;
439     }
440     
441     public void deactivateRequest(ActionTakenValue actionTaken, ActionRequestValue actionRequest) {
442         deactivateRequest(actionTaken, actionRequest, null, new ActivationContext(!ActivationContext.CONTEXT_IS_SIMULATION));
443     }
444 
445     public void deactivateRequest(ActionTakenValue actionTaken, ActionRequestValue actionRequest, boolean simulate) {
446         deactivateRequest(actionTaken, actionRequest, null, new ActivationContext(simulate));
447     }
448 
449     public void deactivateRequest(ActionTakenValue actionTaken, ActionRequestValue actionRequest,
450             ActivationContext activationContext) {
451         deactivateRequest(actionTaken, actionRequest, null, activationContext);
452     }
453 
454     public void deactivateRequests(ActionTakenValue actionTaken, List actionRequests) {
455         deactivateRequests(actionTaken, actionRequests, null,
456                 new ActivationContext(!ActivationContext.CONTEXT_IS_SIMULATION));
457     }
458 
459     public void deactivateRequests(ActionTakenValue actionTaken, List actionRequests, boolean simulate) {
460         deactivateRequests(actionTaken, actionRequests, null, new ActivationContext(simulate));
461     }
462 
463     public void deactivateRequests(ActionTakenValue actionTaken, List actionRequests, ActivationContext activationContext) {
464         deactivateRequests(actionTaken, actionRequests, null, activationContext);
465     }
466 
467     private void deactivateRequests(ActionTakenValue actionTaken, Collection actionRequests,
468             ActionRequestValue deactivationRequester, ActivationContext activationContext) {
469         if (actionRequests == null) {
470             return;
471         }
472         for (Iterator iterator = actionRequests.iterator(); iterator.hasNext();) {
473             ActionRequestValue actionRequest = (ActionRequestValue) iterator.next();
474             deactivateRequest(actionTaken, actionRequest, deactivationRequester, activationContext);
475         }
476     }
477 
478     private void deactivateRequest(ActionTakenValue actionTaken, ActionRequestValue actionRequest,
479             ActionRequestValue deactivationRequester, ActivationContext activationContext) {
480         if (actionRequest == null || actionRequest.isDeactivated()
481                 || haltForAllApprove(actionRequest, deactivationRequester)) {
482             return;
483         }
484         actionRequest.setStatus(ActionRequestStatus.DONE.getCode());
485         actionRequest.setActionTaken(actionTaken);
486         if (actionTaken != null) {
487             actionTaken.getActionRequests().add(actionRequest);
488         }
489         if (!activationContext.isSimulation()) {
490             getActionRequestDAO().saveActionRequest(actionRequest);
491             deleteActionItems(actionRequest);
492         }
493         deactivateRequests(actionTaken, actionRequest.getChildrenRequests(), actionRequest, activationContext);
494         deactivateRequest(actionTaken, actionRequest.getParentActionRequest(), actionRequest, activationContext);
495     }
496 
497     /**
498      * Returns true if we are dealing with an 'All Approve' request, the requester of the deactivation is a child of the
499      * 'All Approve' request, and all of the children have not been deactivated. If all of the children are already
500      * deactivated or a non-child request initiated deactivation, then this method returns false. false otherwise.
501      * @param actionRequest
502      * @param deactivationRequester
503      * @return
504      */
505     private boolean haltForAllApprove(ActionRequestValue actionRequest, ActionRequestValue deactivationRequester) {
506         if (ActionRequestPolicy.ALL.getCode().equals(actionRequest.getApprovePolicy())
507                 && actionRequest.hasChild(deactivationRequester)) {
508             boolean allDeactivated = true;
509             for (ActionRequestValue childRequest : actionRequest.getChildrenRequests())
510             {
511                 if (!(allDeactivated = childRequest.isDeactivated()))
512                 {
513                     return true;
514                 }
515             }
516         }
517         return false;
518     }
519 
520     public List<ActionRequestValue> getRootRequests(Collection<ActionRequestValue> actionRequests) {
521     	Set<ActionRequestValue> unsavedRequests = new HashSet<ActionRequestValue>();
522     	Map<String, ActionRequestValue> requestMap = new HashMap<String, ActionRequestValue>();
523     	for (ActionRequestValue actionRequest1 : actionRequests)
524     	{
525     		ActionRequestValue actionRequest = (ActionRequestValue) actionRequest1;
526     		ActionRequestValue rootRequest = getRoot(actionRequest);
527     		if (rootRequest.getActionRequestId() != null)
528     		{
529     			requestMap.put(rootRequest.getActionRequestId(), rootRequest);
530     		} else
531     		{
532     			unsavedRequests.add(rootRequest);
533     		}
534     	}
535     	List<ActionRequestValue> requests = new ArrayList<ActionRequestValue>();
536     	requests.addAll(requestMap.values());
537     	requests.addAll(unsavedRequests);
538     	return requests;
539     }
540 
541     public ActionRequestValue getRoot(ActionRequestValue actionRequest) {
542         if (actionRequest == null) {
543             return null;
544         }
545         if (actionRequest.getParentActionRequest() != null) {
546             return getRoot(actionRequest.getParentActionRequest());
547         }
548         return actionRequest;
549     }
550     
551     /**
552      * Returns all pending requests for a given routing identity
553      * @param documentId the id of the document header being routed
554      * @return a List of all pending ActionRequestValues for the document
555      */
556     public List<ActionRequestValue> findAllPendingRequests(String documentId) {
557     	ActionRequestDAO arDAO = getActionRequestDAO();
558         List<ActionRequestValue> pendingArs = arDAO.findByStatusAndDocId(ActionRequestStatus.ACTIVATED.getCode(), documentId);
559         return pendingArs;
560     }
561 
562     public List findAllValidRequests(String principalId, String documentId, String requestCode) {
563         ActionRequestDAO arDAO = getActionRequestDAO();
564         Collection pendingArs = arDAO.findByStatusAndDocId(ActionRequestStatus.ACTIVATED.getCode(), documentId);
565         return findAllValidRequests(principalId, pendingArs, requestCode);
566     }
567 
568     public List findAllValidRequests(String principalId, Collection actionRequests, String requestCode) {
569         List matchedArs = new ArrayList();
570         List<String> arGroups = KimApiServiceLocator.getGroupService().getGroupIdsByPrincipalId(principalId);
571         return filterActionRequestsByCode((List<ActionRequestValue>)actionRequests, principalId, arGroups, requestCode);
572     }
573     
574     /**
575 	 * Filters action requests based on if they occur after the given requestCode, and if they relate to 
576 	 * the given principal
577 	 * @param actionRequests the List of ActionRequestValues to filter
578 	 * @param principalId the id of the principal to find active requests for
579 	 * @param principalGroupIds List of group ids that the principal belongs to
580 	 * @param requestCode the request code for all ActionRequestValues to be after
581 	 * @return the filtered List of ActionRequestValues
582 	 */
583 	public List<ActionRequestValue> filterActionRequestsByCode(List<ActionRequestValue> actionRequests, String principalId, List<String> principalGroupIds, String requestCode) {
584 		List<ActionRequestValue> filteredActionRequests = new ArrayList<ActionRequestValue>();
585 		
586 		List<String> arGroups = null;
587         for (ActionRequestValue ar : actionRequests) {
588             if (ActionRequestValue.compareActionCode(ar.getActionRequested(), requestCode, true) > 0) {
589                 continue;
590             }
591             if (ar.isUserRequest() && principalId.equals(ar.getPrincipalId())) {
592             	filteredActionRequests.add(ar);
593             } else if (ar.isGroupRequest() && principalGroupIds != null && !principalGroupIds.isEmpty()) {
594             	for (String groupId : principalGroupIds) {
595             		if (groupId.equals(ar.getGroupId())) {
596             			filteredActionRequests.add(ar);
597             		}
598             	}
599             }
600         }
601 		
602 		return filteredActionRequests;
603 	}
604 
605     public void updateActionRequestsForResponsibilityChange(Set<String> responsibilityIds) {
606     	PerformanceLogger performanceLogger = null;
607     	if ( LOG.isInfoEnabled() ) {
608     		performanceLogger = new PerformanceLogger();
609     	}
610         Collection documentsAffected = getRouteHeaderService().findPendingByResponsibilityIds(responsibilityIds);
611         String cacheWaitValue = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsString(KewApiConstants.KEW_NAMESPACE, KRADConstants.DetailTypes.RULE_DETAIL_TYPE, KewApiConstants.RULE_CACHE_REQUEUE_DELAY);
612         Long cacheWait = KewApiConstants.DEFAULT_CACHE_REQUEUE_WAIT_TIME;
613         if (!org.apache.commons.lang.StringUtils.isEmpty(cacheWaitValue)) {
614             try {
615                 cacheWait = Long.valueOf(cacheWaitValue);
616             } catch (NumberFormatException e) {
617                 LOG.warn("Cache wait time is not a valid number: " + cacheWaitValue);
618             }
619         }
620         if ( LOG.isInfoEnabled() ) {
621         	LOG.info("Scheduling requeue of " + documentsAffected.size() + " documents, affected by " + responsibilityIds.size()
622                     + " responsibility changes.  Installing a processing wait time of " + cacheWait
623                     + " milliseconds to avoid stale rule cache.");
624         }
625         for (Object aDocumentsAffected : documentsAffected)
626         {
627             String documentId = (String) aDocumentsAffected;
628 
629              String applicationId = null;
630              DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByDocumentId(documentId);
631                     
632              if (documentType != null) {
633                 applicationId = documentType.getApplicationId();
634              }
635 
636             if (applicationId == null)
637             {
638                 applicationId = CoreConfigHelper.getApplicationId();
639             }
640             if(documentType.getRegenerateActionRequestsOnChange().getPolicyValue()) {
641                 DocumentRefreshQueue documentRequeuer = MessageServiceNames.getDocumentRequeuerService(applicationId,
642                         documentId, cacheWait);
643                 documentRequeuer.refreshDocument(documentId);
644             }
645         }
646         if ( LOG.isInfoEnabled() ) {
647         	performanceLogger.log("Time to updateActionRequestsForResponsibilityChange");
648         }
649     }
650 
651     /**
652      * Deletes an action request and all of its action items following the graph down through the action request's
653      * children. This method should be invoked on a top-level action request.
654      */
655     public void deleteActionRequestGraph(ActionRequestValue actionRequest) {
656         deleteActionItems(actionRequest);
657         if (actionRequest.getActionTakenId() != null) {
658             ActionTakenValue actionTaken = getActionTakenService().findByActionTakenId(actionRequest.getActionTakenId());
659 
660             if(actionTaken != null){//iu patch
661             getActionTakenService().delete(actionTaken);
662             }//iu patch
663            
664         }
665         getActionRequestDAO().delete(actionRequest.getActionRequestId());
666         for (ActionRequestValue child: actionRequest.getChildrenRequests()) {
667             deleteActionRequestGraph(child);
668         }
669     }
670 
671     /**
672      * Deletes the action items for the action request
673      * @param actionRequest the action request whose action items to delete
674      */
675     private void deleteActionItems(ActionRequestValue actionRequest) {
676     	List<ActionItem> actionItems = actionRequest.getActionItems();
677     	if ( LOG.isDebugEnabled() ) {
678     		LOG.debug("deleting " + actionItems.size() + " action items for action request: " + actionRequest);
679     	}
680         for (ActionItem actionItem: actionItems) {
681         	if ( LOG.isDebugEnabled() ) {
682         		LOG.debug("deleting action item: " + actionItem);
683         	}
684             getActionListService().deleteActionItem(actionItem);
685         }
686     }
687 
688 
689     public List<ActionRequestValue> findByDocumentIdIgnoreCurrentInd(String documentId) {
690         return getActionRequestDAO().findByDocumentIdIgnoreCurrentInd(documentId);
691     }
692 
693     public List<ActionRequestValue> findAllActionRequestsByDocumentId(String documentId) {
694         return getActionRequestDAO().findAllByDocId(documentId);
695     }
696 
697     public List<ActionRequestValue> findAllRootActionRequestsByDocumentId(String documentId) {
698         return getActionRequestDAO().findAllRootByDocId(documentId);
699     }
700 
701     public List<ActionRequestValue> findPendingByActionRequestedAndDocId(String actionRequestedCd, String documentId) {
702         return getActionRequestDAO().findPendingByActionRequestedAndDocId(actionRequestedCd, documentId);
703     }
704 
705     /**
706      * @see org.kuali.rice.kew.actionrequest.service.ActionRequestService#getPrincipalIdsWithPendingActionRequestByActionRequestedAndDocId(java.lang.String, java.lang.String)
707      */
708     public List<String> getPrincipalIdsWithPendingActionRequestByActionRequestedAndDocId(String actionRequestedCd, String documentId) {
709     	List<String> principalIds = new ArrayList<String>();
710     	List<ActionRequestValue> actionRequests = findPendingByActionRequestedAndDocId(actionRequestedCd, documentId);
711 		for(ActionRequestValue actionRequest: actionRequests){
712 			if(actionRequest.isUserRequest()){
713 				principalIds.add(actionRequest.getPrincipalId());
714 			} else if(actionRequest.isGroupRequest()){
715 				principalIds.addAll(
716 						KimApiServiceLocator.getGroupService().getMemberPrincipalIds(actionRequest.getGroupId()));
717 			}
718 		}
719     	return principalIds;
720     }
721 
722     public List<ActionRequestValue> findPendingByDocIdAtOrBelowRouteLevel(String documentId, Integer routeLevel) {
723         return getActionRequestDAO().findPendingByDocIdAtOrBelowRouteLevel(documentId, routeLevel);
724     }
725 
726     public List<ActionRequestValue> findPendingRootRequestsByDocId(String documentId) {
727         return getRootRequests(findPendingByDoc(documentId));
728     }
729 
730     public List<ActionRequestValue> findPendingRootRequestsByDocIdAtRouteNode(String documentId, String nodeInstanceId) {
731         return getActionRequestDAO().findPendingRootRequestsByDocIdAtRouteNode(documentId, nodeInstanceId);
732     }
733 
734     public List<ActionRequestValue> findRootRequestsByDocIdAtRouteNode(String documentId, String nodeInstanceId) {
735         return getActionRequestDAO().findRootRequestsByDocIdAtRouteNode(documentId, nodeInstanceId);
736     }
737 
738     public List<ActionRequestValue> findPendingRootRequestsByDocIdAtOrBelowRouteLevel(String documentId, Integer routeLevel) {
739         return getActionRequestDAO().findPendingRootRequestsByDocIdAtOrBelowRouteLevel(documentId, routeLevel);
740     }
741 
742     public List<ActionRequestValue> findPendingRootRequestsByDocIdAtRouteLevel(String documentId, Integer routeLevel) {
743         return getActionRequestDAO().findPendingRootRequestsByDocIdAtRouteLevel(documentId, routeLevel);
744     }
745 
746     public List<ActionRequestValue> findPendingRootRequestsByDocumentType(String documentTypeId) {
747         return getActionRequestDAO().findPendingRootRequestsByDocumentType(documentTypeId);
748     }
749 
750     public void saveActionRequest(ActionRequestValue actionRequest) {
751         if (actionRequest.isGroupRequest()) {
752             if (!actionRequest.getGroup().isActive() && actionRequest.getRouteHeader().getDocumentType().getFailOnInactiveGroup().getPolicyValue()) {
753         		throw new RiceRuntimeException("Attempted to save an action request with an inactive group.");
754         	}
755         }
756         getActionRequestDAO().saveActionRequest(actionRequest);
757     }
758 
759     public List<ActionRequestValue> findPendingByDoc(String documentId) {
760         return getActionRequestDAO().findAllPendingByDocId(documentId);
761     }
762 
763     public List<ActionRequestValue> findPendingByDocRequestCdRouteLevel(String documentId, String requestCode, Integer routeLevel) {
764         List<ActionRequestValue> requests = new ArrayList<ActionRequestValue>();
765         for (Object object : getActionRequestDAO().findAllPendingByDocId(documentId))
766         {
767             ActionRequestValue actionRequest = (ActionRequestValue) object;
768             if (ActionRequestValue.compareActionCode(actionRequest.getActionRequested(), requestCode, true) > 0)
769             {
770                 continue;
771             }
772             if (actionRequest.getRouteLevel().intValue() == routeLevel.intValue())
773             {
774                 requests.add(actionRequest);
775             }
776         }
777         return requests;
778     }
779 
780     public List<ActionRequestValue> findPendingByDocRequestCdNodeName(String documentId, String requestCode, String nodeName) {
781         List<ActionRequestValue> requests = new ArrayList<ActionRequestValue>();
782         for (Object object : getActionRequestDAO().findAllPendingByDocId(documentId))
783         {
784             ActionRequestValue actionRequest = (ActionRequestValue) object;
785             if (ActionRequestValue.compareActionCode(actionRequest.getActionRequested(), requestCode, true) > 0)
786             {
787                 continue;
788             }
789             if (actionRequest.getNodeInstance() != null && actionRequest.getNodeInstance().getName().equals(nodeName))
790             {
791                 requests.add(actionRequest);
792             }
793         }
794         return requests;
795     }
796 
797     public List findActivatedByGroup(String groupId) {
798         return getActionRequestDAO().findActivatedByGroup(groupId);
799     }
800 
801     private ActionListService getActionListService() {
802         return (ActionListService) KEWServiceLocator.getActionListService();
803     }
804 
805     private ActionTakenService getActionTakenService() {
806         return (ActionTakenService) KEWServiceLocator.getActionTakenService();
807     }
808 
809     public ActionRequestDAO getActionRequestDAO() {
810         return actionRequestDAO;
811     }
812 
813     public void setActionRequestDAO(ActionRequestDAO actionRequestDAO) {
814         this.actionRequestDAO = actionRequestDAO;
815     }
816 
817     private RouteHeaderService getRouteHeaderService() {
818         return (RouteHeaderService) KEWServiceLocator.getService(KEWServiceLocator.DOC_ROUTE_HEADER_SRV);
819     }
820 
821     public List<ActionRequestValue> findByStatusAndDocId(String statusCd, String documentId) {
822         return getActionRequestDAO().findByStatusAndDocId(statusCd, documentId);
823     }
824 
825     public void alterActionRequested(List actionRequests, String actionRequestCd) {
826         for (Object actionRequest1 : actionRequests)
827         {
828             ActionRequestValue actionRequest = (ActionRequestValue) actionRequest1;
829 
830             actionRequest.setActionRequested(actionRequestCd);
831             for (ActionItem item : actionRequest.getActionItems())
832             {
833                 item.setActionRequestCd(actionRequestCd);
834             }
835 
836             saveActionRequest(actionRequest);
837         }
838     }
839 
840     // TODO this still won't work in certain cases when checking from the root
841     public boolean isDuplicateRequest(ActionRequestValue actionRequest) {
842         List<ActionRequestValue> requests = findAllRootActionRequestsByDocumentId(actionRequest.getDocumentId());
843         for (ActionRequestValue existingRequest : requests) {
844             if (existingRequest.getStatus().equals(ActionRequestStatus.DONE.getCode())
845                     && existingRequest.getRouteLevel().equals(actionRequest.getRouteLevel())
846                     && ObjectUtils.equals(existingRequest.getPrincipalId(), actionRequest.getPrincipalId())
847                     && ObjectUtils.equals(existingRequest.getGroupId(), actionRequest.getGroupId())
848                     && ObjectUtils.equals(existingRequest.getRoleName(), actionRequest.getRoleName())
849                     && ObjectUtils.equals(existingRequest.getQualifiedRoleName(), actionRequest.getQualifiedRoleName())
850                     && existingRequest.getActionRequested().equals(actionRequest.getActionRequested())) {
851                 return true;
852             }
853         }
854         return false;
855     }
856 
857     public Recipient findDelegator(List actionRequests) {
858         Recipient delegator = null;
859         String requestCode = KewApiConstants.ACTION_REQUEST_FYI_REQ;
860         for (Object actionRequest1 : actionRequests)
861         {
862             ActionRequestValue actionRequest = (ActionRequestValue) actionRequest1;
863             ActionRequestValue delegatorRequest = findDelegatorRequest(actionRequest);
864             if (delegatorRequest != null)
865             {
866                 if (ActionRequestValue.compareActionCode(delegatorRequest.getActionRequested(), requestCode, true) >= 0)
867                 {
868                     delegator = delegatorRequest.getRecipient();
869                     requestCode = delegatorRequest.getActionRequested();
870                 }
871             }
872         }
873         return delegator;
874     }
875 
876     public Recipient findDelegator(ActionRequestValue actionRequest) {
877         ActionRequestValue delegatorRequest = findDelegatorRequest(actionRequest);
878         Recipient delegator = null;
879         if (delegatorRequest != null) {
880             delegator = delegatorRequest.getRecipient();
881         }
882         return delegator;
883     }
884 
885     public ActionRequestValue findDelegatorRequest(ActionRequestValue actionRequest) {
886         ActionRequestValue parentRequest = actionRequest.getParentActionRequest();
887         if (parentRequest != null && !(parentRequest.isUserRequest() || parentRequest.isGroupRequest())) {
888             parentRequest = findDelegatorRequest(parentRequest);
889         }
890         return parentRequest;
891     }
892 
893     public void deleteByDocumentId(String documentId) {
894         actionRequestDAO.deleteByDocumentId(documentId);
895     }
896 
897     public void deleteByActionRequestId(String actionRequestId) {
898         actionRequestDAO.delete(actionRequestId);
899     }
900 
901     public void validateActionRequest(ActionRequestValue actionRequest) {
902         LOG.debug("Enter validateActionRequest(..)");
903         List<WorkflowServiceErrorImpl> errors = new ArrayList<WorkflowServiceErrorImpl>();
904 
905         String actionRequestCd = actionRequest.getActionRequested();
906         if (actionRequestCd == null || actionRequestCd.trim().equals("")) {
907             errors.add(new WorkflowServiceErrorImpl("ActionRequest cd null.", "actionrequest.actionrequestcd.empty",
908                     actionRequest.getActionRequestId().toString()));
909         } else if (!KewApiConstants.ACTION_REQUEST_CD.containsKey(actionRequestCd)) {
910             errors.add(new WorkflowServiceErrorImpl("ActionRequest cd invalid.", "actionrequest.actionrequestcd.invalid",
911                     actionRequest.getActionRequestId().toString()));
912         }
913 
914         String documentId = actionRequest.getDocumentId();
915         if (documentId == null || StringUtils.isEmpty(documentId)) {
916         	errors.add(new WorkflowServiceErrorImpl("ActionRequest Document id empty.", "actionrequest.documentid.empty",
917                     actionRequest.getActionRequestId().toString()));
918         } else if (getRouteHeaderService().getRouteHeader(documentId) == null) {
919             errors.add(new WorkflowServiceErrorImpl("ActionRequest Document id invalid.",
920                     "actionrequest.documentid.invalid", actionRequest.getActionRequestId().toString()));
921         }
922 
923         String actionRequestStatus = actionRequest.getStatus();
924         if (actionRequestStatus == null || actionRequestStatus.trim().equals("")) {
925             errors.add(new WorkflowServiceErrorImpl("ActionRequest status null.", "actionrequest.actionrequeststatus.empty",
926                     actionRequest.getActionRequestId().toString()));
927         } else if (ActionRequestStatus.fromCode(actionRequestStatus) == null) {
928             errors.add(new WorkflowServiceErrorImpl("ActionRequest status invalid.",
929                     "actionrequest.actionrequeststatus.invalid", actionRequest.getActionRequestId().toString()));
930         }
931 
932         if (actionRequest.getResponsibilityId() == null) {
933             errors.add(new WorkflowServiceErrorImpl("ActionRequest responsibility id null.",
934                     "actionrequest.responsibilityid.empty", actionRequest.getActionRequestId().toString()));
935         }
936 
937         Integer priority = actionRequest.getPriority();
938         if (priority == null) {
939             errors.add(new WorkflowServiceErrorImpl("ActionRequest priority null.", "actionrequest.priority.empty",
940                     actionRequest.getActionRequestId().toString()));
941         }
942 
943         // if(actionRequest.getRouteMethodName() == null || actionRequest.getRouteMethodName().trim().equals("")){
944         // errors.add(new WorkflowServiceErrorImpl("ActionRequest route method name null.",
945         // "actionrequest.routemethodname.empty", actionRequest.getActionRequestId().toString()));
946         // }
947 
948         Integer routeLevel = actionRequest.getRouteLevel();
949         if (routeLevel == null) {
950             errors.add(new WorkflowServiceErrorImpl("ActionRequest route level null.", "actionrequest.routelevel.empty",
951                     actionRequest.getActionRequestId().toString()));
952         } else if (routeLevel < -1) {
953             errors.add(new WorkflowServiceErrorImpl("ActionRequest route level invalid.",
954                     "actionrequest.routelevel.invalid", actionRequest.getActionRequestId().toString()));
955         }
956 
957         Integer version = actionRequest.getDocVersion();
958         if (version == null) {
959             errors.add(new WorkflowServiceErrorImpl("ActionRequest doc version null.", "actionrequest.docversion.empty",
960                     actionRequest.getActionRequestId().toString()));
961         }
962 
963         if (actionRequest.getCreateDate() == null) {
964             errors.add(new WorkflowServiceErrorImpl("ActionRequest create date null.", "actionrequest.createdate.empty",
965                     actionRequest.getActionRequestId().toString()));
966         }
967 
968         String recipientType = actionRequest.getRecipientTypeCd();
969         if (recipientType != null && !recipientType.trim().equals("")) {
970             if (recipientType.equals(KewApiConstants.WORKGROUP)) {
971                 String workgroupId = actionRequest.getGroupId();
972                 if (workgroupId == null) {
973                     errors.add(new WorkflowServiceErrorImpl("ActionRequest workgroup null.",
974                             "actionrequest.workgroup.empty", actionRequest.getActionRequestId().toString()));
975                 } else if (KimApiServiceLocator.getGroupService().getGroup(workgroupId) == null) {
976                     errors.add(new WorkflowServiceErrorImpl("ActionRequest workgroup invalid.",
977                             "actionrequest.workgroup.invalid", actionRequest.getActionRequestId().toString()));
978                 }
979 
980             }
981             if (recipientType.equals(KewApiConstants.PERSON)) {
982                 String principalId = actionRequest.getPrincipalId();
983                 if (principalId == null || principalId.trim().equals("")) {
984                     errors.add(new WorkflowServiceErrorImpl("ActionRequest person id null.", "actionrequest.persosn.empty",
985                             actionRequest.getActionRequestId().toString()));
986                 } else {
987                 	Principal principal = KimApiServiceLocator.getIdentityService().getPrincipal(principalId);
988                 	if (principal == null) {
989                 		errors.add(new WorkflowServiceErrorImpl("ActionRequest person id invalid.",
990                 				"actionrequest.personid.invalid", actionRequest.getActionRequestId().toString()));
991                 	}
992                 }
993 
994                 if (recipientType.equals(KewApiConstants.ROLE)
995                         && (actionRequest.getRoleName() == null || actionRequest.getRoleName().trim().equals(""))) {
996                     errors.add(new WorkflowServiceErrorImpl("ActionRequest role name null.", "actionrequest.rolename.null",
997                             actionRequest.getActionRequestId().toString()));
998                 }
999             }
1000             LOG.debug("Exit validateActionRequest(..) ");
1001             if (!errors.isEmpty()) {
1002                 throw new WorkflowServiceErrorException("ActionRequest Validation Error", errors);
1003             }
1004         }
1005     }
1006 
1007     public List getDelegateRequests(ActionRequestValue actionRequest) {
1008         List<ActionRequestValue> delegateRequests = new ArrayList<ActionRequestValue>();
1009         List requests = getTopLevelRequests(actionRequest);
1010         for (Object request : requests)
1011         {
1012             ActionRequestValue parentActionRequest = (ActionRequestValue) request;
1013             delegateRequests.addAll(parentActionRequest.getChildrenRequests());
1014         }
1015         return delegateRequests;
1016     }
1017 
1018     public List getTopLevelRequests(ActionRequestValue actionRequest) {
1019         List<ActionRequestValue> topLevelRequests = new ArrayList<ActionRequestValue>();
1020         if (actionRequest.isRoleRequest()) {
1021             topLevelRequests.addAll(actionRequest.getChildrenRequests());
1022         } else {
1023             topLevelRequests.add(actionRequest);
1024         }
1025         return topLevelRequests;
1026     }
1027 
1028     public boolean isValidActionRequestCode(String actionRequestCode) {
1029         return actionRequestCode != null && KewApiConstants.ACTION_REQUEST_CODES.containsKey(actionRequestCode);
1030     }
1031 
1032     public boolean doesPrincipalHaveRequest(String principalId, String documentId) {
1033         if (getActionRequestDAO().doesDocumentHaveUserRequest(principalId, documentId)) {
1034             return true;
1035         }
1036         // TODO since we only store the workgroup id for workgroup requests, if the user is in a workgroup that has a request
1037         // than we need get all the requests with workgroup ids and see if our user is in that group
1038         List<String> groupIds = getActionRequestDAO().getRequestGroupIds(documentId);
1039         for (String groupId : groupIds) {
1040             if (KimApiServiceLocator.getGroupService().isMemberOfGroup(principalId, groupId)) {
1041                 return true;
1042             }
1043         }
1044         return false;
1045     }
1046 
1047 }