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