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