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 final 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<>(); 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 boolean isIdempotent() { 116 return false; 117 } 118 119 @Override 120 public void work() { 121 openSystemSession(); 122 DocumentModel nodeDoc = session.getDocument(new IdRef(nodeDocId)); 123 GraphNode node = nodeDoc.getAdapter(GraphNode.class); 124 if (node == null) { 125 throw new NuxeoException("Can't execute worker '" + getId() + "' : the document '" + nodeDocId 126 + "' can not be adapted to a GraphNode"); 127 } 128 List<EscalationRule> rules = node.getEscalationRules(); 129 EscalationRule rule = null; 130 for (EscalationRule escalationRule : rules) { 131 if (escalationRuleId.equals(escalationRule.getId())) { 132 rule = escalationRule; 133 break; 134 } 135 } 136 if (rule == null) { 137 throw new NuxeoException("Can't execute worker '" + getId() + "' : the rule '" + escalationRuleId 138 + "' was not found on the node '" + nodeDocId + "'"); 139 } 140 try (OperationContext context = new OperationContext(session)) { 141 context.putAll(node.getWorkflowContextualInfo(session, true)); 142 context.setInput(context.get("documents")); 143 // check to see if the rule wasn't executed meanwhile 144 boolean alreadyExecuted = getExecutionStatus(rule, session); 145 if (alreadyExecuted && !rule.isMultipleExecution()) { 146 log.trace("Rule " + rule.getId() + "on node " + node.getId() + " already executed"); 147 return; 148 } 149 node.executeChain(rule.getChain()); 150 // mark the rule as resolved 151 markRuleAsExecuted(nodeDocId, escalationRuleId, session); 152 } catch (NuxeoException e) { 153 e.addInfo("Error when executing worker: " + getTitle()); 154 throw e; 155 } 156 } 157 158 /** 159 * Used to check the executed status when the escalationRule is run by a worker in a work queue 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 for (EscalationRule escalationRule : rules) { 181 if (escalationRuleId.equals(escalationRule.getId())) { 182 escalationRule.setExecuted(true); 183 break; 184 } 185 } 186 session.saveDocument(nodeDoc); 187 } 188}