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.io.BufferedWriter;
023import java.io.ByteArrayOutputStream;
024import java.io.IOException;
025import java.io.OutputStream;
026import java.io.OutputStreamWriter;
027import java.io.Writer;
028import java.nio.charset.Charset;
029import java.util.Arrays;
030import java.util.Calendar;
031import java.util.List;
032
033import org.apache.commons.logging.Log;
034import org.apache.commons.logging.LogFactory;
035
036/**
037 * @since 5.7.3
038 */
039public class TracePrinter {
040
041    private static final Log log = LogFactory.getLog(TracePrinter.class);
042
043    private static final String LF = System.getProperty("line.separator");
044
045    protected final BufferedWriter writer;
046
047
048    public TracePrinter(Writer writer) {
049        this.writer = new BufferedWriter(writer);
050    }
051
052    public TracePrinter(OutputStream out) {
053        this(new OutputStreamWriter(out));
054    }
055
056    protected void printHeading(String heading) throws IOException {
057        writer.append(LF+ LF + "****** " + heading + " ******");
058    }
059
060    protected void printCalls(List<Call> calls) throws IOException {
061        String tabs = "\t";
062        for (Call call : calls) {
063            writer.append(tabs);
064            writer.append(call.getType().getType().getName());
065            writer.append(LF);
066            tabs += "\t";
067        }
068    }
069
070    public void print(Trace trace) throws IOException {
071        printHeading("chain");
072        if (trace.error != null) {
073            writer.append(LF);
074            if (trace.getParent() != null) {
075                writer.append("Parent Chain ID: ");
076                writer.append(trace.getParent().getChainId());
077                writer.append(LF);
078            }
079            writer.append("Name: ");
080            writer.append(trace.getChain().getId());
081            if (trace.getChain().getAliases() != null && trace.getChain().getAliases().length > 0) {
082                writer.append(LF);
083                writer.append("Aliases: ");
084                writer.append(Arrays.toString(trace.getChain().getAliases()));
085            }
086            writer.append(LF);
087            writer.append("Exception: ");
088            writer.append(trace.error.getClass().getSimpleName());
089            writer.append(LF);
090            writer.append("Caught error: ");
091            writer.append(trace.error.getMessage());
092            writer.append(LF);
093            writer.append("Caused by: ");
094            writer.append(trace.error.toString());
095        } else {
096            writer.append(LF);
097            if (trace.getParent() != null) {
098                writer.append("Parent Chain ID: ");
099                writer.append(trace.getParent().getChainId());
100                writer.append(LF);
101            }
102            writer.append("Name: ");
103            writer.append(trace.getChain().getId());
104            writer.append(LF);
105            writer.append("Produced output type: ");
106            writer.append(trace.output == null ? "Void" : trace.output.getClass().getSimpleName());
107        }
108        writer.append(LF);
109        writer.append("****** Hierarchy calls ******");
110        writer.append(LF);
111        printCalls(trace.calls);
112        print(trace.calls);
113        writer.flush();
114    }
115
116    public void print(List<Call> calls) throws IOException {
117        for (Call call : calls) {
118            print(call);
119        }
120    }
121
122    public void print(Call call) throws IOException {
123        printCall(call);
124    }
125
126    public void printCall(Call call) {
127        try {
128            writer.append(LF);
129            writer.append(LF);
130            writer.append("****** " + call.getType().getId() + " ******");
131            writer.append(LF);
132            writer.append("Chain ID: ");
133            writer.append(call.getChainId());
134            if (call.getAliases() != null) {
135                writer.append(LF);
136                writer.append("Chain Aliases: ");
137                writer.append(call.getAliases());
138            }
139            writer.append(LF);
140            writer.append("Class: ");
141            writer.append(call.getType().getType().getSimpleName());
142            writer.append(LF);
143            writer.append("Method: '");
144            writer.append(call.getMethod().getMethod().getName());
145            writer.append("' | Input Type: ");
146            writer.append(call.getMethod().getConsume().getName());
147            writer.append(" | Output Type: ");
148            writer.append(call.getMethod().getProduce().getName());
149            writer.append(LF);
150            writer.append("Input: ");
151            writer.append(call.getInput() == null ? "null" : call.getInput().toString());
152            if (!call.getParameters().isEmpty()) {
153                writer.append(LF);
154                writer.append("Parameters ");
155                for (String parameter : call.getParameters().keySet()) {
156                    writer.append(" | ");
157                    writer.append("Name: ");
158                    writer.append(parameter);
159                    writer.append(", Value: ");
160                    Object value = call.getParameters().get(parameter);
161                    if (value instanceof Call.ExpressionParameter) {
162                        value = String.format("Expr:(id=%s | value=%s)",
163                                ((Call.ExpressionParameter) call.getParameters().get(parameter)).getParameterId(),
164                                ((Call.ExpressionParameter) call.getParameters().get(parameter)).getParameterValue());
165                    }
166                    writer.append(value.toString());
167                }
168            }
169            if (!call.getVariables().isEmpty()) {
170                writer.append(LF);
171                writer.append("Context Variables");
172                for (String keyVariable : call.getVariables().keySet()) {
173                    writer.append(" | ");
174                    writer.append("Key: ");
175                    writer.append(keyVariable);
176                    writer.append(", Value: ");
177                    Object variable = call.getVariables().get(keyVariable);
178                    if (variable instanceof Calendar) {
179                        writer.append(((Calendar) variable).getTime().toString());
180                    } else {
181                        writer.append(variable == null ? "null" : variable.toString());
182                    }
183                }
184            }
185            if (!call.getNested().isEmpty()) {
186                writer.append(LF);
187                printHeading("start sub chain");
188                for (Trace trace : call.getNested()) {
189                    print(trace);
190                }
191                writer.append(LF);
192                printHeading("end sub chain");
193            }
194        } catch (IOException e) {
195            log.error("Nuxeo TracePrinter cannot write traces output", e);
196        }
197    }
198
199    public void litePrint(Trace trace) throws IOException {
200        printHeading("chain");
201        writer.append(LF);
202        if (trace.getParent() != null) {
203            writer.append("Parent Chain ID: ");
204            writer.append(trace.getParent().getChainId());
205            writer.append(LF);
206        }
207        writer.append("Name: ");
208        writer.append(trace.getChain().getId());
209        if (trace.getChain().getAliases() != null && trace.getChain().getAliases().length > 0) {
210            writer.append(LF);
211            writer.append("Aliases: ");
212            writer.append(Arrays.toString(trace.getChain().getAliases()));
213        }
214        if (trace.error != null) {
215            writer.append(LF);
216            writer.append("Exception: ");
217            writer.append(trace.error.getClass().getSimpleName());
218            writer.append(LF);
219            writer.append("Caught error: ");
220            writer.append(trace.error.getMessage());
221            writer.append(LF);
222            writer.append("Caused by: ");
223            writer.append(trace.error.toString());
224        }
225        writer.append(LF);
226        writer.append("****** Hierarchy calls ******");
227        litePrintCall(trace.calls);
228        writer.flush();
229    }
230
231    public void litePrintCall(List<Call> calls) throws IOException {
232        writer.append(LF);
233        try {
234            printCalls(calls);
235            for (Call call : calls) {
236                if (!call.getNested().isEmpty()) {
237                    writer.append(LF);
238                    printHeading("start sub chain");
239                    for (Trace trace : call.getNested()) {
240                        litePrint(trace);
241                    }
242                    writer.append(LF);
243                    printHeading("end sub chain");
244                }
245            }
246        } catch (IOException e) {
247            log.error("Nuxeo TracePrinter cannot write traces output", e);
248        }
249    }
250
251    public static String print(Trace trace, boolean liteprint) {
252        ByteArrayOutputStream out = new ByteArrayOutputStream();
253        TracePrinter printer = new TracePrinter(out);
254        try {
255            if (liteprint) {
256                printer.litePrint(trace);
257            } else {
258                printer.print(trace);
259            }
260        } catch (IOException cause) {
261            return "Cannot print automation trace of " + trace.chain.getId();
262        }
263        return new String(out.toByteArray(), Charset.forName("UTF-8"));
264    }
265
266}