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