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-2.1.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 * vpasquier <vpasquier@nuxeo.com> 016 * slacoin <slacoin@nuxeo.com> 017 */ 018package org.nuxeo.ecm.automation.core.trace; 019 020import java.util.Arrays; 021import java.util.HashMap; 022import java.util.Map; 023import java.util.concurrent.TimeUnit; 024import java.util.regex.PatternSyntaxException; 025 026import org.apache.commons.logging.Log; 027import org.apache.commons.logging.LogFactory; 028import org.nuxeo.ecm.automation.OperationCallback; 029import org.nuxeo.ecm.automation.OperationChain; 030import org.nuxeo.ecm.automation.OperationType; 031import org.nuxeo.runtime.api.Framework; 032 033import com.google.common.cache.Cache; 034import com.google.common.cache.CacheBuilder; 035 036/** 037 * @since 5.7.3 The Automation tracer factory service. 038 */ 039public class TracerFactory implements TracerFactoryMBean { 040 041 public static final String AUTOMATION_TRACE_PROPERTY = "org.nuxeo.automation.trace"; 042 043 public static final String AUTOMATION_TRACE_PRINTABLE_PROPERTY = "org.nuxeo.automation.trace.printable"; 044 045 protected static final Integer CACHE_CONCURRENCY_LEVEL = 10; 046 047 protected static final Integer CACHE_MAXIMUM_SIZE = 1000; 048 049 protected static final Integer CACHE_TIMEOUT = 10; 050 051 private static final Log log = LogFactory.getLog(TracerFactory.class); 052 053 protected String printableTraces; 054 055 protected Cache<String, ChainTraces> tracesCache; 056 057 protected boolean recording; 058 059 protected Trace lastError; 060 061 public TracerFactory() { 062 tracesCache = CacheBuilder.newBuilder().concurrencyLevel(CACHE_CONCURRENCY_LEVEL).maximumSize( 063 CACHE_MAXIMUM_SIZE).expireAfterWrite(CACHE_TIMEOUT, TimeUnit.MINUTES).build(); 064 recording = Framework.isBooleanPropertyTrue(AUTOMATION_TRACE_PROPERTY); 065 printableTraces = Framework.getProperty(AUTOMATION_TRACE_PRINTABLE_PROPERTY, "*"); 066 } 067 068 protected static class ChainTraces { 069 070 protected OperationType chain; 071 072 protected Map<Integer, Trace> traces = new HashMap<Integer, Trace>(); 073 074 protected ChainTraces(OperationType chain) { 075 this.chain = chain; 076 } 077 078 protected String add(Trace trace) { 079 final int index = Integer.valueOf(traces.size()); 080 traces.put(Integer.valueOf(index), trace); 081 return formatKey(trace.chain, index); 082 } 083 084 protected Trace getTrace(int index) { 085 return traces.get(index); 086 } 087 088 protected void removeTrace(int index) { 089 traces.remove(index); 090 } 091 092 protected void clear() { 093 traces.clear(); 094 } 095 096 protected int size() { 097 return traces.size(); 098 } 099 100 } 101 102 /** 103 * If trace mode is enabled, instantiate {@link Tracer}. If not, instantiate {@link TracerLite}. 104 */ 105 public OperationCallback newTracer(String operationTypeId) { 106 if (recording) { 107 return new Tracer(this, printable(operationTypeId)); 108 } 109 return new TracerLite(this); 110 } 111 112 protected Boolean printable(String operationTypeId) { 113 if (!"*".equals(printableTraces)) { 114 try { 115 String[] printableTraces = this.printableTraces.split(","); 116 return Arrays.asList(printableTraces).contains(operationTypeId); 117 } catch (PatternSyntaxException e) { 118 StringBuilder stringBuilder = new StringBuilder(); 119 stringBuilder.append("The property "); 120 stringBuilder.append(AUTOMATION_TRACE_PRINTABLE_PROPERTY); 121 stringBuilder.append(":"); 122 stringBuilder.append(printableTraces); 123 stringBuilder.append(" is wrongly set. All automation traces are printable."); 124 log.info(stringBuilder.toString(), e); 125 return true; 126 } 127 } 128 return true; 129 } 130 131 public String recordTrace(Trace trace) { 132 String chainId = trace.chain.getId(); 133 ChainTraces chainTraces = tracesCache.getIfPresent(chainId); 134 if (chainTraces == null) { 135 tracesCache.put(chainId, new ChainTraces(trace.chain)); 136 } 137 if (trace.getError() != null) { 138 lastError = trace; 139 } 140 chainTraces = tracesCache.getIfPresent(chainId); 141 if (chainTraces.size() != 0) { 142 chainTraces.removeTrace(1); 143 } 144 return tracesCache.getIfPresent(chainId).add(trace); 145 } 146 147 public Trace getTrace(OperationChain chain, int index) { 148 return tracesCache.getIfPresent(chain.getId()).getTrace(index); 149 } 150 151 /** 152 * @param key The name of the chain. 153 * @return The last trace of the given chain. 154 */ 155 public Trace getTrace(String key) { 156 return getTrace(key, -1); 157 } 158 159 public Trace getTrace(String key, int index) { 160 ChainTraces chainTrace = tracesCache.getIfPresent(key); 161 if (chainTrace == null) { 162 return null; 163 } 164 if (index < 0) { 165 index = chainTrace.traces.size() - 1; 166 } 167 return tracesCache.getIfPresent(key).getTrace(index); 168 } 169 170 public Trace getLastErrorTrace() { 171 return lastError; 172 } 173 174 public void clearTrace(OperationChain chain, int index) { 175 tracesCache.getIfPresent(chain).removeTrace(Integer.valueOf(index)); 176 } 177 178 public void clearTrace(OperationChain chain) { 179 tracesCache.invalidate(chain); 180 } 181 182 @Override 183 public void clearTraces() { 184 tracesCache.invalidateAll(); 185 } 186 187 protected static String formatKey(OperationType chain, int index) { 188 return String.format("%s:%s", chain.getId(), index); 189 } 190 191 public void onTrace(Trace popped) { 192 recordTrace(popped); 193 } 194 195 @Override 196 public boolean toggleRecording() { 197 return recording = !recording; 198 } 199 200 @Override 201 public boolean getRecordingState() { 202 return recording; 203 } 204 205 @Override 206 public String getPrintableTraces() { 207 return printableTraces; 208 } 209 210 @Override 211 public String setPrintableTraces(String printableTraces) { 212 this.printableTraces = printableTraces; 213 return printableTraces; 214 } 215}