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.ByteArrayOutputStream;
026import java.io.IOException;
027import java.io.OutputStreamWriter;
028import java.nio.charset.Charset;
029import java.util.ArrayList;
030import java.util.Arrays;
031import java.util.List;
032
033import org.apache.commons.logging.Log;
034import org.apache.commons.logging.LogFactory;
035import org.nuxeo.ecm.automation.OperationException;
036import org.nuxeo.ecm.automation.OperationType;
037
038/**
039 * @since 5.7.3
040 */
041public class Trace {
042
043    private static final Log log = LogFactory.getLog(TracerFactory.class);
044
045    protected final Call parent;
046
047    protected final OperationType chain;
048
049    protected final List<Call> calls;
050
051    protected final Object input;
052
053    protected final Object output;
054
055    protected final OperationException error;
056
057    protected Trace(Call parent, OperationType chain, List<Call> calls, Object input, Object output,
058            OperationException error) {
059        this.parent = parent;
060        this.chain = chain;
061        this.calls = new ArrayList<>(calls);
062        this.input = input;
063        this.output = output;
064        this.error = error;
065    }
066
067    public Call getParent() {
068        return parent;
069    }
070
071    public OperationType getChain() {
072        return chain;
073    }
074
075    public OperationException getError() {
076        return error;
077    }
078
079    public Object getInput() {
080        return input;
081    }
082
083    public Object getOutput() {
084        return output;
085    }
086
087    public List<Call> getCalls() {
088        return calls;
089    }
090
091    @Override
092    public String toString() {
093        return print(true);
094    }
095
096    protected void printHeading(String heading, BufferedWriter writer) throws IOException {
097        writer.append(LF).append(LF).append("****** ").append(heading).append(" ******");
098    }
099
100    protected void litePrint(BufferedWriter writer) throws IOException {
101        printHeading("chain", writer);
102        writer.append(LF);
103        if (getParent() != null) {
104            writer.append("Parent Chain ID: ");
105            writer.append(getParent().getChainId());
106            writer.append(LF);
107        }
108        writer.append("Name: ");
109        writer.append(getChain().getId());
110        if (getChain().getAliases() != null && getChain().getAliases().length > 0) {
111            writer.append(LF);
112            writer.append("Aliases: ");
113            writer.append(Arrays.toString(getChain().getAliases()));
114        }
115        if (error != null) {
116            writer.append(LF);
117            writer.append("Exception: ");
118            writer.append(error.getClass().getSimpleName());
119            writer.append(LF);
120            writer.append("Caught error: ");
121            writer.append(error.getMessage());
122            writer.append(LF);
123            writer.append("Caused by: ");
124            writer.append(error.toString());
125        }
126        writer.append(LF);
127        writer.append("****** Hierarchy calls ******");
128        litePrintCall(calls, writer);
129        writer.flush();
130    }
131
132    protected void litePrintCall(List<Call> calls, BufferedWriter writer) throws IOException {
133        writer.append(LF);
134        try {
135            printCalls(calls, writer);
136            for (Call call : calls) {
137                if (!call.getNested().isEmpty()) {
138                    writer.append(LF);
139                    printHeading("start sub chain", writer);
140                    for (Trace trace : call.getNested()) {
141                        trace.litePrint(writer);
142                    }
143                    writer.append(LF);
144                    printHeading("end sub chain", writer);
145                }
146            }
147        } catch (IOException e) {
148            log.error("Nuxeo TracePrinter cannot write traces output", e);
149        }
150    }
151
152    protected void print(BufferedWriter writer) throws IOException {
153        printHeading("chain", writer);
154        if (error != null) {
155            writer.append(LF);
156            if (getParent() != null) {
157                writer.append("Parent Chain ID: ");
158                writer.append(getParent().getChainId());
159                writer.append(LF);
160            }
161            writer.append("Name: ");
162            writer.append(getChain().getId());
163            if (getChain().getAliases() != null && getChain().getAliases().length > 0) {
164                writer.append(LF);
165                writer.append("Aliases: ");
166                writer.append(Arrays.toString(getChain().getAliases()));
167            }
168            writer.append(LF);
169            writer.append("Exception: ");
170            writer.append(error.getClass().getSimpleName());
171            writer.append(LF);
172            writer.append("Caught error: ");
173            writer.append(error.getMessage());
174            writer.append(LF);
175            writer.append("Caused by: ");
176            writer.append(error.toString());
177        } else {
178            writer.append(LF);
179            if (getParent() != null) {
180                writer.append("Parent Chain ID: ");
181                writer.append(getParent().getChainId());
182                writer.append(LF);
183            }
184            writer.append("Name: ");
185            writer.append(getChain().getId());
186            writer.append(LF);
187            writer.append("Produced output type: ");
188            writer.append(output == null ? "Void" : output.getClass().getSimpleName());
189        }
190        writer.append(LF);
191        writer.append("****** Hierarchy calls ******");
192        writer.append(LF);
193        printCalls(calls, writer);
194        writer.flush();
195    }
196
197    protected void printCalls(List<Call> calls, BufferedWriter writer) throws IOException {
198        String tabs = "\t";
199        for (Call call : calls) {
200            writer.append(tabs);
201            call.print(writer);
202            tabs += "\t";
203        }
204    }
205
206    public String print(boolean litePrint) {
207        ByteArrayOutputStream out = new ByteArrayOutputStream();
208        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
209        try {
210            if (litePrint) {
211                litePrint(writer);
212            } else {
213                print(writer);
214            }
215        } catch (IOException cause) {
216            return "Cannot print automation trace of " + chain.getId();
217        }
218        return new String(out.toByteArray(), Charset.forName("UTF-8"));
219    }
220}