001    /**
002     * Copyright 2005-2012 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.actionrequest.service.impl;
017    
018    import java.util.ArrayList;
019    import java.util.Collection;
020    import java.util.Collections;
021    import java.util.HashMap;
022    import java.util.HashSet;
023    import java.util.Iterator;
024    import java.util.List;
025    import java.util.Map;
026    import java.util.Set;
027    
028    import org.apache.commons.lang.ObjectUtils;
029    import org.apache.commons.lang.StringUtils;
030    import org.apache.log4j.Logger;
031    import org.kuali.rice.core.api.config.CoreConfigHelper;
032    import org.kuali.rice.core.api.exception.RiceRuntimeException;
033    import org.kuali.rice.core.api.config.property.ConfigContext;
034    import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator;
035    import org.kuali.rice.kew.actionitem.ActionItem;
036    import org.kuali.rice.kew.actionlist.service.ActionListService;
037    import org.kuali.rice.kew.actionrequest.ActionRequestValue;
038    import org.kuali.rice.kew.actionrequest.Recipient;
039    import org.kuali.rice.kew.actionrequest.dao.ActionRequestDAO;
040    import org.kuali.rice.kew.actionrequest.service.ActionRequestService;
041    import org.kuali.rice.kew.api.document.DocumentRefreshQueue;
042    import org.kuali.rice.kew.actiontaken.ActionTakenValue;
043    import org.kuali.rice.kew.actiontaken.service.ActionTakenService;
044    import org.kuali.rice.kew.api.action.ActionRequestPolicy;
045    import org.kuali.rice.kew.api.action.ActionRequestStatus;
046    import org.kuali.rice.kew.api.action.RecipientType;
047    import org.kuali.rice.kew.doctype.bo.DocumentType;
048    import org.kuali.rice.kew.engine.ActivationContext;
049    import org.kuali.rice.kew.engine.node.RouteNodeInstance;
050    import org.kuali.rice.kew.exception.WorkflowServiceErrorException;
051    import org.kuali.rice.kew.exception.WorkflowServiceErrorImpl;
052    import org.kuali.rice.kew.messaging.MessageServiceNames;
053    import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
054    import org.kuali.rice.kew.routeheader.service.RouteHeaderService;
055    import org.kuali.rice.kew.routemodule.RouteModule;
056    import org.kuali.rice.kew.service.KEWServiceLocator;
057    import org.kuali.rice.kew.util.FutureRequestDocumentStateManager;
058    import org.kuali.rice.kew.api.KewApiConstants;
059    import org.kuali.rice.kew.util.PerformanceLogger;
060    import org.kuali.rice.kew.util.ResponsibleParty;
061    import org.kuali.rice.kim.api.identity.principal.Principal;
062    import org.kuali.rice.kim.api.services.KimApiServiceLocator;
063    import org.kuali.rice.krad.util.KRADConstants;
064    
065    
066    /**
067     * Default implementation of the {@link ActionRequestService}.
068     *
069     * @author Kuali Rice Team (rice.collab@kuali.org)
070     */
071    public class ActionRequestServiceImpl implements ActionRequestService {
072        private static final Logger LOG = Logger.getLogger(ActionRequestServiceImpl.class);
073    
074        private ActionRequestDAO actionRequestDAO;
075    
076        public ActionRequestValue findByActionRequestId(String actionRequestId) {
077            return getActionRequestDAO().getActionRequestByActionRequestId(actionRequestId);
078        }
079    
080        public Map<String, String> getActionsRequested(DocumentRouteHeaderValue routeHeader, String principalId, boolean completeAndApproveTheSame) {
081            return getActionsRequested(principalId, routeHeader.getActionRequests(), completeAndApproveTheSame);
082        }
083        
084        /**
085         * Returns a Map of actions that are requested for the given principalId in the given list of action requests.
086         * @param principalId
087         * @param actionRequests
088         * @param completeAndApproveTheSame
089         * @return
090         */
091        protected Map<String, String> getActionsRequested(String principalId, List<ActionRequestValue> actionRequests, boolean completeAndApproveTheSame) {
092            Map<String, String> actionsRequested = new HashMap<String, String>();
093            actionsRequested.put(KewApiConstants.ACTION_REQUEST_FYI_REQ, "false");
094            actionsRequested.put(KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, "false");
095            actionsRequested.put(KewApiConstants.ACTION_REQUEST_APPROVE_REQ, "false");
096            actionsRequested.put(KewApiConstants.ACTION_REQUEST_COMPLETE_REQ, "false");
097            String topActionRequested = KewApiConstants.ACTION_REQUEST_FYI_REQ;
098            for (ActionRequestValue actionRequest : actionRequests) {
099                // 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    }