aboutsummaryrefslogtreecommitdiffstats
path: root/container-core/src/main/java/com/yahoo/processing/rendering/ProcessingRenderer.java
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
committerJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
commit72231250ed81e10d66bfe70701e64fa5fe50f712 (patch)
tree2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /container-core/src/main/java/com/yahoo/processing/rendering/ProcessingRenderer.java
Publish
Diffstat (limited to 'container-core/src/main/java/com/yahoo/processing/rendering/ProcessingRenderer.java')
-rw-r--r--container-core/src/main/java/com/yahoo/processing/rendering/ProcessingRenderer.java229
1 files changed, 229 insertions, 0 deletions
diff --git a/container-core/src/main/java/com/yahoo/processing/rendering/ProcessingRenderer.java b/container-core/src/main/java/com/yahoo/processing/rendering/ProcessingRenderer.java
new file mode 100644
index 00000000000..ba2d34eabd7
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/processing/rendering/ProcessingRenderer.java
@@ -0,0 +1,229 @@
+// Copyright 2016 Yahoo Inc. 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<Response> {
+
+ private Map<Request,Request> 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();
+ }
+ }
+}