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