001/**
002 * Copyright 2005-2014 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.role.service.impl;
017
018import org.apache.commons.collections.CollectionUtils;
019import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
020import org.kuali.rice.kew.actionrequest.ActionRequestValue;
021import org.kuali.rice.kew.api.KewApiServiceLocator;
022import org.kuali.rice.kew.api.action.RolePokerQueue;
023import org.kuali.rice.kew.api.document.DocumentProcessingQueue;
024import org.kuali.rice.kew.api.rule.RoleName;
025import org.kuali.rice.kew.doctype.bo.DocumentType;
026import org.kuali.rice.kew.engine.RouteContext;
027import org.kuali.rice.kew.engine.node.RouteNodeInstance;
028import org.kuali.rice.kew.role.service.RoleService;
029import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
030import org.kuali.rice.kew.rule.FlexRM;
031import org.kuali.rice.kew.rule.bo.RuleTemplateAttributeBo;
032import org.kuali.rice.kew.rule.bo.RuleTemplateBo;
033import org.kuali.rice.kew.service.KEWServiceLocator;
034
035import java.util.ArrayList;
036import java.util.Collection;
037import java.util.HashSet;
038import java.util.List;
039import java.util.Set;
040
041
042/**
043 *
044 * @author Kuali Rice Team (rice.collab@kuali.org)
045 */
046public class RoleServiceImpl implements RoleService {
047
048        private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(RoleServiceImpl.class);
049
050    public void reResolveRole(DocumentType documentType, String roleName) {
051        String infoString = "documentType="+(documentType == null ? null : documentType.getName())+", role="+roleName;
052        if (documentType == null ||
053                org.apache.commons.lang.StringUtils.isEmpty(roleName)) {
054            throw new IllegalArgumentException("Cannot pass null or empty arguments to reResolveQualifiedRole: "+infoString);
055        }
056        LOG.debug("Re-resolving role asynchronously for "+infoString);
057        Set<String> documentIds = new HashSet<String>();
058        findAffectedDocuments(documentType, roleName, null, documentIds);
059        LOG.debug(documentIds.size()+" documents were affected by this re-resolution, requeueing with the RolePokerQueue");
060        for (String documentId : documentIds) {
061            String applicationId = KEWServiceLocator.getRouteHeaderService().getApplicationIdByDocumentId(documentId);
062            RolePokerQueue rolePokerQueue = KewApiServiceLocator.getRolePokerQueue(documentId, applicationId);
063                rolePokerQueue.reResolveRole(documentId, roleName);
064                }
065    }
066
067    public void reResolveQualifiedRole(DocumentType documentType, String roleName, String qualifiedRoleNameLabel) {
068        String infoString = "documentType="+(documentType == null ? null : documentType.getName())+", role="+roleName+", qualifiedRole="+qualifiedRoleNameLabel;
069        if (documentType == null ||
070                org.apache.commons.lang.StringUtils.isEmpty(roleName) ||
071                org.apache.commons.lang.StringUtils.isEmpty(qualifiedRoleNameLabel)) {
072            throw new IllegalArgumentException("Cannot pass null or empty arguments to reResolveQualifiedRole: "+infoString);
073        }
074        LOG.debug("Re-resolving qualified role asynchronously for "+infoString);
075        Set<String> documentIds = new HashSet<String>();
076        findAffectedDocuments(documentType, roleName, qualifiedRoleNameLabel, documentIds);
077        LOG.debug(documentIds.size()+" documents were affected by this re-resolution, requeueing with the RolePokerQueue");
078        for (String documentId : documentIds) {
079            String applicationId = KEWServiceLocator.getRouteHeaderService().getApplicationIdByDocumentId(documentId);
080            RolePokerQueue rolePokerQueue = KewApiServiceLocator.getRolePokerQueue(documentId, applicationId);
081                rolePokerQueue.reResolveQualifiedRole(documentId, roleName, qualifiedRoleNameLabel);
082                }
083    }
084
085    /**
086     *
087     * route level and then filters in the approriate ones.
088     */
089    public void reResolveQualifiedRole(DocumentRouteHeaderValue routeHeader, String roleName, String qualifiedRoleNameLabel) {
090        String infoString = "routeHeader="+(routeHeader == null ? null : routeHeader.getDocumentId())+", role="+roleName+", qualifiedRole="+qualifiedRoleNameLabel;
091        if (routeHeader == null ||
092                org.apache.commons.lang.StringUtils.isEmpty(roleName) ||
093                org.apache.commons.lang.StringUtils.isEmpty(qualifiedRoleNameLabel)) {
094            throw new IllegalArgumentException("Cannot pass null arguments to reResolveQualifiedRole: "+infoString);
095        }
096        LOG.debug("Re-resolving qualified role synchronously for "+infoString);
097        List<RouteNodeInstance> nodeInstances = findNodeInstances(routeHeader, roleName);
098        int requestsGenerated = 0;
099        if (!nodeInstances.isEmpty()) {
100            deletePendingRoleRequests(routeHeader.getDocumentId(), roleName, qualifiedRoleNameLabel);
101            for (RouteNodeInstance nodeInstance : nodeInstances) {
102                RuleTemplateBo ruleTemplate = nodeInstance.getRouteNode().getRuleTemplate();
103                FlexRM flexRM = new FlexRM();
104                        RouteContext context = RouteContext.getCurrentRouteContext();
105                        context.setDocument(routeHeader);
106                        context.setNodeInstance(nodeInstance);
107                        try {
108                                List<ActionRequestValue> actionRequests = flexRM.getActionRequests(routeHeader, nodeInstance, ruleTemplate.getName());
109                    for (ActionRequestValue actionRequest : actionRequests) {
110                                        if (roleName.equals(actionRequest.getRoleName()) && qualifiedRoleNameLabel.equals(actionRequest.getQualifiedRoleNameLabel())) {
111                                                actionRequest = KEWServiceLocator.getActionRequestService().initializeActionRequestGraph(actionRequest, routeHeader, nodeInstance);
112                                                KEWServiceLocator.getActionRequestService().saveActionRequest(actionRequest);
113                                                requestsGenerated++;
114                                        }
115                                }
116                        } catch (Exception e) {
117                                RouteContext.clearCurrentRouteContext();
118                        }
119
120            }
121        }
122        LOG.debug("Generated "+requestsGenerated+" action requests after re-resolve: "+infoString);
123        requeueDocument(routeHeader);
124    }
125
126    public void reResolveRole(DocumentRouteHeaderValue routeHeader, String roleName) {
127        String infoString = "routeHeader="+(routeHeader == null ? null : routeHeader.getDocumentId())+", role="+roleName;
128        if (routeHeader == null ||
129                org.apache.commons.lang.StringUtils.isEmpty(roleName)) {
130            throw new RiceIllegalArgumentException("Cannot pass null arguments to reResolveQualifiedRole: "+infoString);
131        }
132        LOG.debug("Re-resolving role synchronously for "+infoString);
133        List<RouteNodeInstance> nodeInstances = findNodeInstances(routeHeader, roleName);
134        int requestsGenerated = 0;
135        if (!nodeInstances.isEmpty()) {
136            deletePendingRoleRequests(routeHeader.getDocumentId(), roleName, null);
137            for (RouteNodeInstance nodeInstance : nodeInstances) {
138                RuleTemplateBo ruleTemplate = nodeInstance.getRouteNode().getRuleTemplate();
139                FlexRM flexRM = new FlexRM();
140                        RouteContext context = RouteContext.getCurrentRouteContext();
141                        context.setDocument(routeHeader);
142                        context.setNodeInstance(nodeInstance);
143                        try {
144                                List<ActionRequestValue> actionRequests = flexRM.getActionRequests(routeHeader, nodeInstance, ruleTemplate.getName());
145                    for (ActionRequestValue actionRequest : actionRequests) {
146                                        if (roleName.equals(actionRequest.getRoleName())) {
147                                                actionRequest = KEWServiceLocator.getActionRequestService().initializeActionRequestGraph(actionRequest, routeHeader, nodeInstance);
148                                                KEWServiceLocator.getActionRequestService().saveActionRequest(actionRequest);
149                                                requestsGenerated++;
150                                        }
151                                }
152                        } finally {
153                                RouteContext.clearCurrentRouteContext();
154                        }
155            }
156        }
157        LOG.debug("Generated "+requestsGenerated+" action requests after re-resolve: "+infoString);
158        requeueDocument(routeHeader);
159    }
160
161    // search the document type and all its children
162    private void findAffectedDocuments(DocumentType documentType, String roleName, String qualifiedRoleNameLabel, Set<String> documentIds) {
163        List<ActionRequestValue> pendingRequests = KEWServiceLocator.getActionRequestService().findPendingRootRequestsByDocumentType(documentType.getDocumentTypeId());
164        for (ActionRequestValue actionRequest : pendingRequests) {
165                        if (roleName.equals(actionRequest.getRoleName()) &&
166                                        (qualifiedRoleNameLabel == null || qualifiedRoleNameLabel.equals(actionRequest.getQualifiedRoleNameLabel()))) {
167                                documentIds.add(actionRequest.getDocumentId());
168                        }
169                }
170        for (DocumentType childDocumentType : documentType.getChildrenDocTypes()) {
171                        findAffectedDocuments(childDocumentType, roleName, qualifiedRoleNameLabel, documentIds);
172                }
173    }
174
175    private void deletePendingRoleRequests(String documentId, String roleName, String qualifiedRoleNameLabel) {
176        List<ActionRequestValue> pendingRequests = KEWServiceLocator.getActionRequestService().findPendingByDoc(documentId);
177        pendingRequests = KEWServiceLocator.getActionRequestService().getRootRequests(pendingRequests);
178        List<ActionRequestValue> requestsToDelete = new ArrayList<ActionRequestValue>();
179        for (ActionRequestValue actionRequest : pendingRequests) {
180            if (roleName.equals(actionRequest.getRoleName()) &&
181                        (qualifiedRoleNameLabel == null || qualifiedRoleNameLabel.equals(actionRequest.getQualifiedRoleNameLabel()))) {
182                requestsToDelete.add(actionRequest);
183            }
184        }
185        LOG.debug("Deleting "+requestsToDelete.size()+" action requests for roleName="+roleName+", qualifiedRoleNameLabel="+qualifiedRoleNameLabel);
186        for (ActionRequestValue actionRequest : requestsToDelete) {
187            KEWServiceLocator.getActionRequestService().deleteActionRequestGraphNoOutbox(actionRequest);
188        }
189    }
190
191    private List<RouteNodeInstance> findNodeInstances(DocumentRouteHeaderValue routeHeader, String roleName) {
192        List<RouteNodeInstance> nodeInstances = new ArrayList<RouteNodeInstance>();
193        Collection<RouteNodeInstance> activeNodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(routeHeader.getDocumentId());
194        if (CollectionUtils.isEmpty(activeNodeInstances)) {
195            throw new IllegalStateException("Document does not currently have any active nodes so re-resolving is not legal.");
196        }
197        for (RouteNodeInstance activeNodeInstance : activeNodeInstances) {
198            RuleTemplateBo template = activeNodeInstance.getRouteNode().getRuleTemplate();
199            if (templateHasRole(template, roleName)) {
200                nodeInstances.add(activeNodeInstance);
201            }
202        }
203        if (nodeInstances.isEmpty()) {
204            throw new IllegalStateException("Could not locate given role to re-resolve: " + roleName);
205        }
206        return nodeInstances;
207    }
208
209    private boolean templateHasRole(RuleTemplateBo template, String roleName) {
210        List<RuleTemplateAttributeBo> templateAttributes = template.getRuleTemplateAttributes();
211        for (RuleTemplateAttributeBo templateAttribute : templateAttributes) {
212            List<RoleName> roleNames = KEWServiceLocator.getWorkflowRuleAttributeMediator().getRoleNames(templateAttribute);
213            for (RoleName role : roleNames) {
214                if (role.getLabel().equals(roleName)) {
215                    return true;
216                }
217            }
218        }
219        return false;
220    }
221
222    protected void requeueDocument(DocumentRouteHeaderValue document) {
223        String applicationId = document.getDocumentType().getApplicationId();
224        DocumentProcessingQueue documentProcessingQueue = KewApiServiceLocator.getDocumentProcessingQueue(document.getDocumentId(), applicationId);
225        documentProcessingQueue.process(document.getDocumentId());
226    }
227
228}