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.impl.document;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.List;
021
022import org.apache.commons.collections.CollectionUtils;
023import org.apache.commons.lang.StringUtils;
024import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
025import org.kuali.rice.kew.actionrequest.ActionRequestValue;
026import org.kuali.rice.kew.actionrequest.service.ActionRequestService;
027import org.kuali.rice.kew.actionrequest.service.impl.NotificationSuppression;
028import org.kuali.rice.kew.api.document.DocumentRefreshQueue;
029import org.kuali.rice.kew.api.WorkflowRuntimeException;
030import org.kuali.rice.kew.engine.OrchestrationConfig;
031import org.kuali.rice.kew.engine.OrchestrationConfig.EngineCapability;
032import org.kuali.rice.kew.engine.RouteHelper;
033import org.kuali.rice.kew.engine.node.RouteNodeInstance;
034import org.kuali.rice.kew.engine.node.service.RouteNodeService;
035import org.kuali.rice.kew.service.KEWServiceLocator;
036import org.kuali.rice.kew.util.PerformanceLogger;
037
038
039/**
040 * A service which effectively "refreshes" and requeues a document.  It first deletes any
041 * pending action requests on the documents and then requeues the document for standard routing.
042 * Addionally, it adds duplicate notification suppression state to RouteNodeInstanceS for 
043 * which ActionRequestS will be regenerated. 
044 * 
045 * <p>Intended to be called async and wired that way in server/client spring beans.</p>
046 * 
047 * @author Kuali Rice Team (rice.collab@kuali.org)
048 */
049public class DocumentRefreshQueueImpl implements DocumentRefreshQueue {
050        
051        private RouteHelper helper = new RouteHelper();
052    
053        /**
054         * Requeues a document, and sets notification suppression data
055         * 
056         * @see org.kuali.rice.kew.api.document.DocumentRefreshQueue#refreshDocument(java.lang.String)
057         */
058        @Override
059        public void refreshDocument(String documentId) {
060                if (StringUtils.isBlank(documentId)) {
061            throw new RiceIllegalArgumentException("documentId is null or blank");
062        }
063
064        PerformanceLogger performanceLogger = new PerformanceLogger();
065        KEWServiceLocator.getRouteHeaderService().lockRouteHeader(documentId);
066        Collection<RouteNodeInstance> activeNodes = getRouteNodeService().getActiveNodeInstances(documentId);
067        List<ActionRequestValue> requestsToDelete = new ArrayList<ActionRequestValue>();
068        
069                NotificationSuppression notificationSuppression = new NotificationSuppression();
070                
071        for (RouteNodeInstance nodeInstance : activeNodes) {
072            // only "requeue" if we're dealing with a request activation node
073            if (helper.isRequestActivationNode(nodeInstance.getRouteNode())) {
074                List<ActionRequestValue> deletesForThisNode = 
075                        getActionRequestService().findPendingRootRequestsByDocIdAtRouteNode(documentId, nodeInstance.getRouteNodeInstanceId());
076
077                for (ActionRequestValue deleteForThisNode : deletesForThisNode) {
078                    // check either the request or its first present child request to see if it is system generated
079                    boolean containsRoleOrRuleRequests = deleteForThisNode.isRouteModuleRequest();
080                    if (!containsRoleOrRuleRequests) {
081                        if (CollectionUtils.isNotEmpty(deleteForThisNode.getChildrenRequests())) {
082                            containsRoleOrRuleRequests = deleteForThisNode.getChildrenRequests().get(0).isRouteModuleRequest();
083                        }
084                    }
085
086                    if (containsRoleOrRuleRequests) {
087                        // remove all route or rule system generated requests
088                        requestsToDelete.add(deleteForThisNode);
089
090                        // suppress duplicate notifications
091                                notificationSuppression.addNotificationSuppression(nodeInstance, deleteForThisNode);
092                        }
093                }
094
095                // this will trigger a regeneration of requests
096                nodeInstance.setInitial(true);
097                getRouteNodeService().save(nodeInstance);
098            }
099        }
100        for (ActionRequestValue requestToDelete : requestsToDelete) {
101            getActionRequestService().deleteActionRequestGraphNoOutbox(requestToDelete);
102        }
103        try {
104            OrchestrationConfig config = new OrchestrationConfig(EngineCapability.STANDARD);
105                KEWServiceLocator.getWorkflowEngineFactory().newEngine(config).process(documentId, null);
106        } catch (Exception e) {
107                throw new WorkflowRuntimeException(e);
108        }
109        performanceLogger.log("Time to run DocumentRequeuer for document " + documentId);       
110        }
111
112    private ActionRequestService getActionRequestService() {
113        return KEWServiceLocator.getActionRequestService();
114    }
115    
116    private RouteNodeService getRouteNodeService() {
117        return KEWServiceLocator.getRouteNodeService();
118    }
119}