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