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