001/* 002 * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Mariana Cedica 016 */ 017package org.nuxeo.ecm.platform.routing.core.impl; 018 019import java.io.Serializable; 020import java.util.ArrayList; 021import java.util.List; 022import java.util.Map; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026import org.nuxeo.ecm.automation.OperationContext; 027import org.nuxeo.ecm.core.api.CoreSession; 028import org.nuxeo.ecm.core.api.DocumentModel; 029import org.nuxeo.ecm.core.api.IdRef; 030import org.nuxeo.ecm.core.api.IterableQueryResult; 031import org.nuxeo.ecm.core.api.NuxeoException; 032import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner; 033import org.nuxeo.ecm.core.work.AbstractWork; 034import org.nuxeo.ecm.core.work.api.WorkManager; 035import org.nuxeo.ecm.platform.routing.core.api.DocumentRoutingEscalationService; 036import org.nuxeo.ecm.platform.routing.core.impl.GraphNode.EscalationRule; 037import org.nuxeo.runtime.api.Framework; 038 039/** 040 * @since 5.7.2 041 */ 042public class DocumentRoutingEscalationServiceImpl implements DocumentRoutingEscalationService { 043 044 private static Log log = LogFactory.getLog(DocumentRoutingEscalationServiceImpl.class); 045 046 public static final String queryForSuspendedNodesWithEscalation = "Select DISTINCT ecm:uuid from RouteNode WHERE ecm:currentLifeCycleState = 'suspended' " 047 + "AND ( rnode:escalationRules/*1/executed = 0 OR rnode:escalationRules/*1/multipleExecution = 1 )"; 048 049 @Override 050 public List<String> queryForSuspendedNodesWithEscalation(CoreSession session) { 051 final List<String> nodesDocIds = new ArrayList<String>(); 052 new UnrestrictedSessionRunner(session) { 053 @Override 054 public void run() { 055 IterableQueryResult results = session.queryAndFetch(queryForSuspendedNodesWithEscalation, "NXQL"); 056 for (Map<String, Serializable> result : results) { 057 nodesDocIds.add(result.get("ecm:uuid").toString()); 058 log.trace("Inspecting node for escalation rules:" + result.get("ecm:uuid").toString()); 059 } 060 results.close(); 061 } 062 }.runUnrestricted(); 063 return nodesDocIds; 064 } 065 066 @Override 067 public List<EscalationRule> computeEscalationRulesToExecute(GraphNode node) { 068 return node.evaluateEscalationRules(); 069 } 070 071 @Override 072 public void scheduleExecution(EscalationRule rule, CoreSession session) { 073 WorkManager manager = Framework.getLocalService(WorkManager.class); 074 manager.schedule( 075 new EscalationRuleWork(rule.getId(), rule.getNode().getDocument().getId(), session.getRepositoryName()), 076 WorkManager.Scheduling.IF_NOT_SCHEDULED); 077 } 078 079 public static class EscalationRuleWork extends AbstractWork { 080 081 private static final long serialVersionUID = 1L; 082 083 protected String escalationRuleId; 084 085 protected String nodeDocId; 086 087 public static final String CATEGORY = "routingEscalation"; 088 089 public EscalationRuleWork(String escalationRuleId, String nodeDocId, String repositoryName) { 090 super(repositoryName + ":" + nodeDocId + ":escalationRule:" + escalationRuleId); 091 this.repositoryName = repositoryName; 092 this.escalationRuleId = escalationRuleId; 093 this.nodeDocId = nodeDocId; 094 } 095 096 @Override 097 public String getTitle() { 098 return getId(); 099 } 100 101 @Override 102 public String getCategory() { 103 return CATEGORY; 104 } 105 106 @Override 107 public void work() { 108 initSession(); 109 DocumentModel nodeDoc = session.getDocument(new IdRef(nodeDocId)); 110 GraphNode node = nodeDoc.getAdapter(GraphNode.class); 111 if (node == null) { 112 throw new NuxeoException("Can't execute worker '" + getId() + "' : the document '" + nodeDocId 113 + "' can not be adapted to a GraphNode"); 114 } 115 List<EscalationRule> rules = node.getEscalationRules(); 116 EscalationRule rule = null; 117 for (EscalationRule escalationRule : rules) { 118 if (escalationRuleId.equals(escalationRule.getId())) { 119 rule = escalationRule; 120 break; 121 } 122 } 123 if (rule == null) { 124 throw new NuxeoException("Can't execute worker '" + getId() + "' : the rule '" + escalationRuleId 125 + "' was not found on the node '" + nodeDocId + "'"); 126 } 127 OperationContext context = new OperationContext(session); 128 context.putAll(node.getWorkflowContextualInfo(session, true)); 129 context.setInput(context.get("documents")); 130 try { 131 // check to see if the rule wasn't executed meanwhile 132 boolean alreadyExecuted = getExecutionStatus(rule, session); 133 if (alreadyExecuted && !rule.isMultipleExecution()) { 134 log.trace("Rule " + rule.getId() + "on node " + node.getId() + " already executed"); 135 return; 136 } 137 node.executeChain(rule.getChain()); 138 // mark the rule as resolved 139 markRuleAsExecuted(nodeDocId, escalationRuleId, session); 140 } catch (NuxeoException e) { 141 e.addInfo("Error when executing worker: " + getTitle()); 142 throw e; 143 } 144 } 145 146 /** 147 * Used to check the executed status when the escalationRule is run by a worker in a work queue 148 * 149 * @param session 150 */ 151 public boolean getExecutionStatus(EscalationRule rule, CoreSession session) { 152 DocumentModel nodeDoc = session.getDocument(new IdRef(rule.getNode().getDocument().getId())); 153 GraphNode node = nodeDoc.getAdapter(GraphNode.class); 154 List<EscalationRule> rules = node.getEscalationRules(); 155 for (EscalationRule escalationRule : rules) { 156 if (rule.compareTo(escalationRule) == 0) { 157 return escalationRule.isExecuted(); 158 } 159 } 160 return false; 161 } 162 163 } 164 165 private static void markRuleAsExecuted(String nodeDocId, String escalationRuleId, CoreSession session) 166 { 167 DocumentModel nodeDoc = session.getDocument(new IdRef(nodeDocId)); 168 GraphNode node = nodeDoc.getAdapter(GraphNode.class); 169 List<EscalationRule> rules = node.getEscalationRules(); 170 EscalationRule rule = null; 171 for (EscalationRule escalationRule : rules) { 172 if (escalationRuleId.equals(escalationRule.getId())) { 173 rule = escalationRule; 174 break; 175 } 176 } 177 rule.setExecuted(true); 178 session.saveDocument(nodeDoc); 179 } 180}