001/* 002 * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0 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 * Contributors: 017 * Mariana Cedica 018 */ 019package org.nuxeo.ecm.platform.routing.core.impl; 020 021import java.io.Serializable; 022import java.util.ArrayList; 023import java.util.List; 024import java.util.Map; 025 026import org.apache.commons.logging.Log; 027import org.apache.commons.logging.LogFactory; 028import org.nuxeo.ecm.automation.OperationContext; 029import org.nuxeo.ecm.automation.OperationException; 030import org.nuxeo.ecm.core.api.CoreSession; 031import org.nuxeo.ecm.core.api.DocumentModel; 032import org.nuxeo.ecm.core.api.IdRef; 033import org.nuxeo.ecm.core.api.IterableQueryResult; 034import org.nuxeo.ecm.core.api.NuxeoException; 035import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner; 036import org.nuxeo.ecm.core.work.AbstractWork; 037import org.nuxeo.ecm.core.work.api.WorkManager; 038import org.nuxeo.ecm.platform.routing.core.api.DocumentRoutingEscalationService; 039import org.nuxeo.ecm.platform.routing.core.impl.GraphNode.EscalationRule; 040import org.nuxeo.runtime.api.Framework; 041 042/** 043 * @since 5.7.2 044 */ 045public class DocumentRoutingEscalationServiceImpl implements DocumentRoutingEscalationService { 046 047 private static Log log = LogFactory.getLog(DocumentRoutingEscalationServiceImpl.class); 048 049 public static final String queryForSuspendedNodesWithEscalation = "Select DISTINCT ecm:uuid from RouteNode WHERE ecm:currentLifeCycleState = 'suspended' " 050 + "AND ( rnode:escalationRules/*1/executed = 0 OR rnode:escalationRules/*1/multipleExecution = 1 )"; 051 052 @Override 053 public List<String> queryForSuspendedNodesWithEscalation(CoreSession session) { 054 final List<String> nodesDocIds = new ArrayList<String>(); 055 new UnrestrictedSessionRunner(session) { 056 @Override 057 public void run() { 058 IterableQueryResult results = session.queryAndFetch(queryForSuspendedNodesWithEscalation, "NXQL"); 059 for (Map<String, Serializable> result : results) { 060 nodesDocIds.add(result.get("ecm:uuid").toString()); 061 log.trace("Inspecting node for escalation rules:" + result.get("ecm:uuid").toString()); 062 } 063 results.close(); 064 } 065 }.runUnrestricted(); 066 return nodesDocIds; 067 } 068 069 @Override 070 public List<EscalationRule> computeEscalationRulesToExecute(GraphNode node) { 071 return node.evaluateEscalationRules(); 072 } 073 074 @Override 075 public void scheduleExecution(EscalationRule rule, CoreSession session) { 076 WorkManager manager = Framework.getService(WorkManager.class); 077 manager.schedule( 078 new EscalationRuleWork(rule.getId(), rule.getNode().getDocument().getId(), session.getRepositoryName()), 079 WorkManager.Scheduling.IF_NOT_SCHEDULED); 080 } 081 082 public static class EscalationRuleWork extends AbstractWork { 083 084 private static final long serialVersionUID = 1L; 085 086 protected String escalationRuleId; 087 088 protected String nodeDocId; 089 090 public static final String CATEGORY = "routingEscalation"; 091 092 public EscalationRuleWork(String escalationRuleId, String nodeDocId, String repositoryName) { 093 super(repositoryName + ":" + nodeDocId + ":escalationRule:" + escalationRuleId); 094 this.repositoryName = repositoryName; 095 this.escalationRuleId = escalationRuleId; 096 this.nodeDocId = nodeDocId; 097 } 098 099 @Override 100 public String getTitle() { 101 return getId(); 102 } 103 104 @Override 105 public String getCategory() { 106 return CATEGORY; 107 } 108 109 @Override 110 public int getRetryCount() { 111 // retry when there is concurrency 112 return 2; 113 } 114 115 @Override 116 public void work() { 117 openSystemSession(); 118 DocumentModel nodeDoc = session.getDocument(new IdRef(nodeDocId)); 119 GraphNode node = nodeDoc.getAdapter(GraphNode.class); 120 if (node == null) { 121 throw new NuxeoException("Can't execute worker '" + getId() + "' : the document '" + nodeDocId 122 + "' can not be adapted to a GraphNode"); 123 } 124 List<EscalationRule> rules = node.getEscalationRules(); 125 EscalationRule rule = null; 126 for (EscalationRule escalationRule : rules) { 127 if (escalationRuleId.equals(escalationRule.getId())) { 128 rule = escalationRule; 129 break; 130 } 131 } 132 if (rule == null) { 133 throw new NuxeoException("Can't execute worker '" + getId() + "' : the rule '" + escalationRuleId 134 + "' was not found on the node '" + nodeDocId + "'"); 135 } 136 try (OperationContext context = new OperationContext(session)) { 137 context.putAll(node.getWorkflowContextualInfo(session, true)); 138 context.setInput(context.get("documents")); 139 // check to see if the rule wasn't executed meanwhile 140 boolean alreadyExecuted = getExecutionStatus(rule, session); 141 if (alreadyExecuted && !rule.isMultipleExecution()) { 142 log.trace("Rule " + rule.getId() + "on node " + node.getId() + " already executed"); 143 return; 144 } 145 node.executeChain(rule.getChain()); 146 // mark the rule as resolved 147 markRuleAsExecuted(nodeDocId, escalationRuleId, session); 148 } catch (NuxeoException e) { 149 e.addInfo("Error when executing worker: " + getTitle()); 150 throw e; 151 } catch (OperationException e) { 152 throw new NuxeoException("Error when executing worker: " + getTitle(), e); 153 } 154 } 155 156 /** 157 * Used to check the executed status when the escalationRule is run by a worker in a work queue 158 * 159 * @param session 160 */ 161 public boolean getExecutionStatus(EscalationRule rule, CoreSession session) { 162 DocumentModel nodeDoc = session.getDocument(new IdRef(rule.getNode().getDocument().getId())); 163 GraphNode node = nodeDoc.getAdapter(GraphNode.class); 164 List<EscalationRule> rules = node.getEscalationRules(); 165 for (EscalationRule escalationRule : rules) { 166 if (rule.compareTo(escalationRule) == 0) { 167 return escalationRule.isExecuted(); 168 } 169 } 170 return false; 171 } 172 173 } 174 175 private static void markRuleAsExecuted(String nodeDocId, String escalationRuleId, CoreSession session) 176 { 177 DocumentModel nodeDoc = session.getDocument(new IdRef(nodeDocId)); 178 GraphNode node = nodeDoc.getAdapter(GraphNode.class); 179 List<EscalationRule> rules = node.getEscalationRules(); 180 EscalationRule rule = null; 181 for (EscalationRule escalationRule : rules) { 182 if (escalationRuleId.equals(escalationRule.getId())) { 183 rule = escalationRule; 184 break; 185 } 186 } 187 rule.setExecuted(true); 188 session.saveDocument(nodeDoc); 189 } 190}