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