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 <[email protected]> 018 * slacoin <[email protected]> 019 */ 020package org.nuxeo.ecm.automation.core.trace; 021 022import static org.nuxeo.ecm.automation.core.Constants.LF; 023 024import java.io.BufferedWriter; 025import java.io.IOException; 026import java.io.Writer; 027import java.util.Arrays; 028import java.util.Calendar; 029import java.util.HashMap; 030import java.util.LinkedList; 031import java.util.List; 032import java.util.Map; 033 034import org.apache.commons.logging.Log; 035import org.apache.commons.logging.LogFactory; 036import org.nuxeo.ecm.automation.OperationContext; 037import org.nuxeo.ecm.automation.OperationType; 038import org.nuxeo.ecm.automation.core.impl.InvokableMethod; 039import org.nuxeo.ecm.automation.core.scripting.Expression; 040 041/** 042 * @since 5.7.3 043 */ 044public class Call { 045 046 private static final Log log = LogFactory.getLog(Call.class); 047 048 /** 049 * Black listing of mvel expressions which should not be evaluated by the Automation traces for debugging purpose 050 * 051 * @since 8.1 052 */ 053 public static final String[] MVEL_BLACK_LIST_EXPR = new String[] { "getNextId" }; 054 055 protected final String chainId; 056 057 protected final String aliases; 058 059 protected final OperationType type; 060 061 protected final List<Trace> nested = new LinkedList<Trace>(); 062 063 protected final Details details; 064 065 protected Call(OperationType chain, OperationType op, Details details) { 066 type = op; 067 chainId = chain.getId(); 068 aliases = Arrays.toString(chain.getAliases()); 069 this.details = details; 070 } 071 072 public Call(OperationType chain, OperationType op) { 073 this(chain, op, new Details()); 074 } 075 076 public Call(OperationType chain, OperationContext context, OperationType type, InvokableMethod method, 077 Map<String, Object> parms) { 078 this(chain, type, new Details(context, method, parms)); 079 } 080 081 protected static class Details { 082 083 protected final Map<String, Object> parameters = new HashMap<>(); 084 085 protected final Map<String, Object> variables = new HashMap<>(); 086 087 protected final InvokableMethod method; 088 089 protected final Object input; 090 091 protected Object output; 092 093 protected Details() { 094 method = null; 095 input = null; 096 } 097 098 protected Details(OperationContext context, InvokableMethod method, Map<String, Object> parms) { 099 this.method = method; 100 input = context.getInput(); 101 variables.putAll(context); 102 parms.forEach(new Evaluator(context)::inject); 103 } 104 105 protected class Evaluator { 106 107 protected final OperationContext context; 108 109 protected Evaluator(OperationContext context) { 110 this.context = context; 111 } 112 113 protected void inject(String key, Object value) { 114 if (!(value instanceof Expression)) { 115 parameters.put(key, value); 116 return; 117 } 118 Expression exp = (Expression) value; 119 for (String mvelExpr : MVEL_BLACK_LIST_EXPR) { 120 if (exp.getExpr().contains(mvelExpr)) { 121 parameters.put(key, new ExpressionParameter(key, 122 String.format("Cannot be evaluated in traces when using '%s' expression", mvelExpr))); 123 return; 124 } 125 } 126 try { 127 parameters.put(key, new ExpressionParameter(key, exp.eval(context))); 128 return; 129 } catch (RuntimeException e) { 130 log.warn("Cannot evaluate mvel expression for parameter: " + key, e); 131 } 132 } 133 } 134 135 } 136 137 /** 138 * @since 7.1 139 */ 140 public static class ExpressionParameter { 141 142 protected final String parameterId; 143 144 protected final Object parameterValue; 145 146 public ExpressionParameter(String parameterId, Object parameterValue) { 147 this.parameterId = parameterId; 148 this.parameterValue = parameterValue; 149 } 150 151 public Object getParameterValue() { 152 return parameterValue; 153 } 154 155 public String getParameterId() { 156 157 return parameterId; 158 } 159 } 160 161 public OperationType getType() { 162 return type; 163 } 164 165 public InvokableMethod getMethod() { 166 return details.method; 167 } 168 169 public Map<String, Object> getParameters() { 170 return details.parameters; 171 } 172 173 public Map<String, Object> getVariables() { 174 return details.variables; 175 } 176 177 public Object getInput() { 178 return details.input; 179 } 180 181 public Object getOutput() { 182 return details.output; 183 } 184 185 public List<Trace> getNested() { 186 return nested; 187 } 188 189 public String getChainId() { 190 return chainId; 191 } 192 193 public String getAliases() { 194 return aliases; 195 } 196 197 /** 198 * @since 9.3 199 */ 200 public void print(BufferedWriter writer) throws IOException { 201 try { 202 writer.append(LF); 203 writer.append(LF); 204 writer.append("****** " + getType().getId() + " ******"); 205 writer.append(LF); 206 writer.append("Chain ID: "); 207 writer.append(getChainId()); 208 if (getAliases() != null) { 209 writer.append(LF); 210 writer.append("Chain Aliases: "); 211 writer.append(getAliases()); 212 } 213 writer.append(LF); 214 writer.append("Class: "); 215 writer.append(getType().getType().getSimpleName()); 216 writer.append(LF); 217 writer.append("Method: '"); 218 writer.append(getMethod().getMethod().getName()); 219 writer.append("' | Input Type: "); 220 writer.append(getMethod().getConsume().getName()); 221 writer.append(" | Output Type: "); 222 writer.append(getMethod().getProduce().getName()); 223 writer.append(LF); 224 writer.append("Input: "); 225 writer.append(getInput() == null ? "null" : getInput().toString()); 226 if (!getParameters().isEmpty()) { 227 writer.append(LF); 228 writer.append("Parameters "); 229 for (String parameter : getParameters().keySet()) { 230 writer.append(" | "); 231 writer.append("Name: "); 232 writer.append(parameter); 233 writer.append(", Value: "); 234 Object value = getParameters().get(parameter); 235 if (value instanceof Call.ExpressionParameter) { 236 value = String.format("Expr:(id=%s | value=%s)", 237 ((Call.ExpressionParameter) getParameters().get(parameter)).getParameterId(), 238 ((Call.ExpressionParameter) getParameters().get(parameter)).getParameterValue()); 239 } 240 writer.append(value.toString()); 241 } 242 } 243 if (!getVariables().isEmpty()) { 244 writer.append(LF); 245 writer.append("Context Variables"); 246 for (String keyVariable : getVariables().keySet()) { 247 writer.append(" | "); 248 writer.append("Key: "); 249 writer.append(keyVariable); 250 writer.append(", Value: "); 251 Object variable = getVariables().get(keyVariable); 252 if (variable instanceof Calendar) { 253 writer.append(((Calendar) variable).getTime().toString()); 254 } else { 255 writer.append(variable == null ? "null" : variable.toString()); 256 } 257 } 258 } 259 if (!getNested().isEmpty()) { 260 printHeading("start sub chain", writer); 261 for (Trace trace : getNested()) { 262 writer.append(LF); 263 trace.print(writer); 264 writer.append(LF); 265 } 266 printHeading("end sub chain", writer); 267 } 268 } catch (IOException e) { 269 log.error("Nuxeo TracePrinter cannot write traces output", e); 270 } 271 } 272 273 protected void printHeading(String heading, BufferedWriter writer) throws IOException { 274 writer.append(LF + LF + "****** " + heading + " ******"); 275 } 276}