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