All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yahoo.processing.rendering.ProcessingRenderer Maven / Gradle / Ivy

There is a newer version: 8.458.13
Show newest version
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.processing.rendering;

import com.yahoo.processing.Request;
import com.yahoo.processing.Response;
import com.yahoo.processing.handler.ResponseHeaders;
import com.yahoo.processing.handler.ResponseStatus;
import com.yahoo.processing.request.ErrorMessage;
import com.yahoo.processing.response.Data;
import com.yahoo.processing.response.DataList;
import com.yahoo.text.JSONWriter;
import com.yahoo.yolean.trace.TraceNode;
import com.yahoo.yolean.trace.TraceVisitor;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.IdentityHashMap;
import java.util.Map;

/**
 * The default renderer for processing responses. Renders a response in JSON.
 * This can be overridden to specify particular rendering of leaf Data elements.
 * This default implementation renders the toString of each element.
 *
 * @author bratseth
 */
public class ProcessingRenderer extends AsynchronousSectionedRenderer {

    private Map renderedRequests = new IdentityHashMap<>();

    private JSONWriter jsonWriter;

    /** The current nesting level */
    private int level;

    @Override
    public void init() {
        super.init();
        level = 0;
    }

    @Override
    public final void beginResponse(OutputStream stream) throws IOException {
        jsonWriter = new JSONWriter(stream);
    }

    @Override
    public final void endResponse() throws IOException {
    }

    @Override
    public final void beginList(DataList list) throws IOException {
        if (level>0)
            jsonWriter.beginArrayValue();

        jsonWriter.beginObject();

        if (level==0)
            renderTrace();

        if ( ! list.request().errors().isEmpty() && ! rendered(list.request())) {
            jsonWriter.beginField("errors");
            jsonWriter.beginArray();
            for (ErrorMessage error : list.request().errors()) {
                if (renderedRequests == null)
                    renderedRequests = new IdentityHashMap<>();
                renderedRequests.put(list.request(),list.request());
                jsonWriter.beginArrayValue();
                if (error.getCause() != null) { // render object
                    jsonWriter.beginObject();
                    jsonWriter.beginField("error").value(error.toString()).endField();
                    jsonWriter.beginField("stacktrace").value(stackTraceAsString(error.getCause())).endField();
                    jsonWriter.endObject();
                }
                else { // render string
                    jsonWriter.value(error.toString());
                }
                jsonWriter.endArrayValue();
            }
            jsonWriter.endArray();
            jsonWriter.endField();
        }

        jsonWriter.beginField("datalist");
        jsonWriter.beginArray();
        level++;
    }

    private String stackTraceAsString(Throwable e) {
        StringWriter writer = new StringWriter();
        e.printStackTrace(new PrintWriter(writer));
        return writer.toString();
    }

    private boolean rendered(Request request) {
        return renderedRequests != null && renderedRequests.containsKey(request);
    }

    @Override
    public final void endList(DataList list) throws IOException {
        jsonWriter.endArray();
        jsonWriter.endField();
        jsonWriter.endObject();
        if (level>0)
            jsonWriter.endArrayValue();
        level--;
    }

    @Override
    public final void data(Data data) throws IOException {
        if (! shouldRender(data)) return;
        jsonWriter.beginArrayValue();
        jsonWriter.beginObject();
        jsonWriter.beginField("data");
        renderValue(data,jsonWriter);
        jsonWriter.endField();
        jsonWriter.endObject();
        jsonWriter.endArrayValue();
    }

    /**
     * Renders the value of a data element.
     * This default implementation does writer.fieldValue(data.toString())
     * Override this to render data in application specific ways.
     */
    protected void renderValue(Data data,JSONWriter writer) throws IOException {
        writer.value(data.toString());
    }

    /**
     * Returns whether this data element should be rendered.
     * This can be overridden to add new kinds of data which should not be rendered.
     * This default implementation returns true unless the data is instanceof ResponseHeaders.
     *
     * @return true to render it, false to skip completely
     */
    protected boolean shouldRender(Data data) {
        if (data instanceof ResponseHeaders) return false;
        if (data instanceof ResponseStatus) return false;
        return true;
    }

    @Override
    public final String getEncoding() {
        return null;
    }

    @Override
    public final String getMimeType() {
        return "application/json";
    }

    private boolean renderTrace() throws IOException {
        if (getExecution().trace().getTraceLevel() == 0) return false;

        jsonWriter.beginField("trace");
        try {
            getExecution().trace().traceNode().accept(new TraceRenderingVisitor(jsonWriter));
        } catch (WrappedIOException e) {
            throw e.getCause();
        }
        jsonWriter.endField();
        return true;
    }

    private static class TraceRenderingVisitor extends TraceVisitor {

        private JSONWriter jsonWriter;

        public TraceRenderingVisitor(JSONWriter jsonWriter) {
            this.jsonWriter = jsonWriter;
        }

        @Override
        public void entering(TraceNode node) {
            try {
                jsonWriter.beginArray();
            }
            catch (IOException e) {
                throw new WrappedIOException(e);
            }
        }

        @Override
        public void leaving(TraceNode node) {
            try {
                jsonWriter.endArray();
            }
            catch (IOException e) {
                throw new WrappedIOException(e);
            }
        }

        @Override
        public void visit(TraceNode node) {
            if ( ! (node.payload() instanceof String)) return; // skip other info than trace messages
            try {
                jsonWriter.beginArrayValue();
                if (node.timestamp() != 0) { // render object
                    jsonWriter.beginObject();
                    jsonWriter.beginField("timestamp").value(node.timestamp()).endField();
                    jsonWriter.beginField("message").value(node.payload().toString()).endField();
                    jsonWriter.endObject();
                }
                else { // render string
                    jsonWriter.value(node.payload().toString());
                }
                jsonWriter.endArrayValue();
            }
            catch (IOException e) {
                throw new WrappedIOException(e);
            }
        }

    }

    private static class WrappedIOException extends RuntimeException {
        private WrappedIOException(IOException cause) {
            super(cause);
        }

        @Override
        public IOException getCause() {
            return (IOException) super.getCause();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy