This is an automated email from the ASF dual-hosted git repository.

mhubail pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/asterixdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 4deb30b  [NO ISSUE][OTH] Introduce ResponsePrinter
4deb30b is described below

commit 4deb30b6abdc2b82d0b62d85059b7a04f7d2ed50
Author: Murtadha Hubail <[email protected]>
AuthorDate: Mon May 27 06:35:21 2019 +0300

    [NO ISSUE][OTH] Introduce ResponsePrinter
    
    - user model changes: no
    - storage format changes: no
    - interface changes: yes
    
    Details:
    - Introduce ResponsePrinter that is responsible for printing
      the returned response from query service.
    - Make ResponsePrinter responsible for adding separators between
      fields in the returned response.
    - Introduce IResponseFieldPrinter and encapsulate each response
      field in its own printer.
    - Introduce ICodedMessage and use it for any object that
      has code<->message (e.g errors and warnings).
    
    Change-Id: I797e6615a72c886391fed26281fc648b38fa748f
    Reviewed-on: https://asterix-gerrit.ics.uci.edu/3410
    Contrib: Jenkins <[email protected]>
    Tested-by: Jenkins <[email protected]>
    Integration-Tests: Jenkins <[email protected]>
    Reviewed-by: Till Westmann <[email protected]>
---
 .../asterix/translator/IStatementExecutor.java     |   8 +
 .../translator/IStatementExecutorFactory.java      |   5 +-
 .../apache/asterix/translator/SessionOutput.java   |   4 +
 .../apache/asterix/api/common/APIFramework.java    |  19 +-
 .../api/http/server/AbstractQueryApiServlet.java   |  50 -----
 .../apache/asterix/api/http/server/ApiServlet.java |   3 +-
 .../api/http/server/NCQueryServiceServlet.java     |  31 ++-
 .../api/http/server/QueryResultApiServlet.java     |  20 +-
 .../api/http/server/QueryServiceServlet.java       | 225 +++++++--------------
 .../api/http/server/QueryStatusApiServlet.java     |  32 +--
 .../apache/asterix/api/http/server/ResultUtil.java |  90 +--------
 .../apache/asterix/api/java/AsterixJavaClient.java |   3 +-
 .../message/ExecuteStatementRequestMessage.java    |   3 +-
 .../result/ExecutionError.java}                    |  25 ++-
 .../server => app/result}/ExecutionWarning.java    |   8 +-
 .../apache/asterix/app/result/ResponseMertics.java |  74 +++++++
 .../apache/asterix/app/result/ResponsePrinter.java | 109 ++++++++++
 .../apache/asterix/app/result/ResultHandle.java    |   2 +-
 .../result/fields/AbstractCodedMessagePrinter.java |  71 +++++++
 .../result/fields/ClientContextIdPrinter.java}     |  28 ++-
 .../result/fields/ErrorsPrinter.java}              |  23 +--
 .../result/fields/ExplainOnlyResultsPrinter.java   |  54 +++++
 .../asterix/app/result/fields/MetricsPrinter.java  | 100 +++++++++
 .../asterix/app/result/fields/NcResultPrinter.java |  80 ++++++++
 .../result/fields/ParseOnlyResultPrinter.java}     |  30 ++-
 .../asterix/app/result/fields/PlansPrinter.java    |  58 ++++++
 .../result/fields/RequestIdPrinter.java}           |  29 ++-
 .../app/result/fields/ResultHandlePrinter.java     |  65 ++++++
 .../asterix/app/result/fields/ResultsPrinter.java  |  59 ++++++
 .../result/fields/SignaturePrinter.java}           |  31 +--
 .../result/fields/StatusPrinter.java}              |  28 ++-
 .../asterix/app/result/fields/TypePrinter.java     |  59 ++++++
 .../result/fields/WarningsPrinter.java}            |  23 +--
 .../DefaultStatementExecutorFactory.java           |   6 +-
 .../asterix/app/translator/QueryTranslator.java    |  51 +++--
 .../org/apache/asterix/utils/FeedOperations.java   |   3 +-
 .../asterix/app/result/ResultPrinterTest.java      |   9 +-
 .../asterix/test/active/ActiveStatsTest.java       |   7 +-
 .../apache/asterix/common/api/ICodedMessage.java}  |  30 ++-
 .../asterix/common/api/IResponseFieldPrinter.java} |  31 +--
 .../asterix/common/api/IResponsePrinter.java       |  76 +++++++
 41 files changed, 1185 insertions(+), 477 deletions(-)

diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IStatementExecutor.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IStatementExecutor.java
index e00c15a..c27a30f 100644
--- 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IStatementExecutor.java
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IStatementExecutor.java
@@ -24,6 +24,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.asterix.common.api.IResponsePrinter;
 import org.apache.asterix.common.exceptions.ACIDException;
 import org.apache.asterix.common.exceptions.AsterixException;
 import org.apache.asterix.lang.common.base.IStatementRewriter;
@@ -155,4 +156,11 @@ public interface IStatementExecutor {
      * @return the executions plans
      */
     ExecutionPlans getExecutionPlans();
+
+    /**
+     * Gets the response printer
+     *
+     * @return the responer printer
+     */
+    IResponsePrinter getResponsePrinter();
 }
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IStatementExecutorFactory.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IStatementExecutorFactory.java
index b244c0c..26ebbd5 100644
--- 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IStatementExecutorFactory.java
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IStatementExecutorFactory.java
@@ -20,6 +20,7 @@ package org.apache.asterix.translator;
 
 import java.util.List;
 
+import org.apache.asterix.common.api.IResponsePrinter;
 import org.apache.asterix.common.context.IStorageComponentProvider;
 import org.apache.asterix.common.dataflow.ICcApplicationContext;
 import org.apache.asterix.compiler.provider.ILangCompilationProvider;
@@ -43,8 +44,10 @@ public interface IStatementExecutorFactory {
      *            provides query language related components
      * @param storageComponentProvider
      *            provides storage related components
+     * @param responsePrinter
      * @return an implementation of {@code IStatementExecutor} thaxt is used 
to execute the passed list of statements
      */
     IStatementExecutor create(ICcApplicationContext appCtx, List<Statement> 
statements, SessionOutput output,
-            ILangCompilationProvider compilationProvider, 
IStorageComponentProvider storageComponentProvider);
+            ILangCompilationProvider compilationProvider, 
IStorageComponentProvider storageComponentProvider,
+            IResponsePrinter responsePrinter);
 }
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SessionOutput.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SessionOutput.java
index 97125f0..ed59cf4 100644
--- 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SessionOutput.java
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SessionOutput.java
@@ -38,6 +38,10 @@ public class SessionOutput {
     private SessionOutput.ResultAppender handleAppender;
     private final SessionOutput.ResultAppender statusAppender;
 
+    public SessionOutput(PrintWriter out) {
+        this(null, out);
+    }
+
     public SessionOutput(SessionConfig config, PrintWriter out) {
         this(config, out, null, null, null, null);
     }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
index d558e0d..d971c1d 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
@@ -33,8 +33,9 @@ import java.util.Set;
 
 import org.apache.asterix.algebra.base.ILangExpressionToPlanTranslator;
 import org.apache.asterix.algebra.base.ILangExpressionToPlanTranslatorFactory;
-import org.apache.asterix.api.http.server.ResultUtil;
+import org.apache.asterix.app.result.fields.ExplainOnlyResultsPrinter;
 import org.apache.asterix.common.api.INodeJobTracker;
+import org.apache.asterix.common.api.IResponsePrinter;
 import org.apache.asterix.common.config.CompilerProperties;
 import org.apache.asterix.common.config.OptimizationConfUtil;
 import org.apache.asterix.common.exceptions.ACIDException;
@@ -74,7 +75,6 @@ import 
org.apache.asterix.optimizer.rules.am.AbstractIntroduceAccessMethodRule;
 import org.apache.asterix.runtime.job.listener.JobEventListenerFactory;
 import org.apache.asterix.translator.CompiledStatements.ICompiledDmlStatement;
 import org.apache.asterix.translator.ExecutionPlans;
-import org.apache.asterix.translator.IStatementExecutor.Stats;
 import org.apache.asterix.translator.SessionConfig;
 import org.apache.asterix.translator.SessionOutput;
 import org.apache.asterix.translator.SqlppExpressionToPlanTranslator;
@@ -192,7 +192,7 @@ public class APIFramework {
 
     public JobSpecification compileQuery(IClusterInfoCollector 
clusterInfoCollector, MetadataProvider metadataProvider,
             Query query, int varCounter, String outputDatasetName, 
SessionOutput output,
-            ICompiledDmlStatement statement, Map<VarIdentifier, IAObject> 
externalVars)
+            ICompiledDmlStatement statement, Map<VarIdentifier, IAObject> 
externalVars, IResponsePrinter printer)
             throws AlgebricksException, ACIDException {
 
         // establish facts
@@ -261,7 +261,7 @@ public class APIFramework {
             }
         }
         if (isExplainOnly) {
-            printPlanAsResult(metadataProvider, output);
+            printPlanAsResult(metadataProvider, output, printer);
             if (!conf.is(SessionConfig.OOB_OPTIMIZED_LOGICAL_PLAN)) {
                 executionPlans.setOptimizedLogicalPlan(null);
             }
@@ -307,13 +307,12 @@ public class APIFramework {
         return spec;
     }
 
-    private void printPlanAsResult(MetadataProvider metadataProvider, 
SessionOutput output) throws AlgebricksException {
-        final SessionConfig conf = output.config();
-        boolean quoteResult = output.config().getPlanFormat() == 
SessionConfig.PlanFormat.STRING;
-        conf.set(SessionConfig.FORMAT_QUOTE_RECORD, quoteResult);
+    private void printPlanAsResult(MetadataProvider metadataProvider, 
SessionOutput output, IResponsePrinter printer)
+            throws AlgebricksException {
         try {
-            ResultUtil.printResults(metadataProvider.getApplicationContext(), 
executionPlans.getOptimizedLogicalPlan(),
-                    output, new Stats(), null);
+            printer.addResultPrinter(new 
ExplainOnlyResultsPrinter(metadataProvider.getApplicationContext(),
+                    executionPlans.getOptimizedLogicalPlan(), output));
+            printer.printResults();
         } catch (HyracksDataException e) {
             throw new AlgebricksException(e);
         }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/AbstractQueryApiServlet.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/AbstractQueryApiServlet.java
index 6f27eb5..4dadf55 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/AbstractQueryApiServlet.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/AbstractQueryApiServlet.java
@@ -20,7 +20,6 @@ package org.apache.asterix.api.http.server;
 
 import static 
org.apache.asterix.api.http.server.ServletConstants.HYRACKS_CONNECTION_ATTR;
 
-import java.io.PrintWriter;
 import java.util.concurrent.ConcurrentMap;
 
 import org.apache.asterix.common.api.IApplicationContext;
@@ -38,30 +37,6 @@ public class AbstractQueryApiServlet extends AbstractServlet 
{
     private static final Logger LOGGER = LogManager.getLogger();
     protected final IApplicationContext appCtx;
 
-    public enum ResultFields {
-        REQUEST_ID("requestID"),
-        CLIENT_ID("clientContextID"),
-        SIGNATURE("signature"),
-        TYPE("type"),
-        STATUS("status"),
-        RESULTS("results"),
-        HANDLE("handle"),
-        ERRORS("errors"),
-        METRICS("metrics"),
-        PLANS("plans"),
-        WARNINGS("warnings");
-
-        private final String str;
-
-        ResultFields(String str) {
-            this.str = str;
-        }
-
-        public String str() {
-            return str;
-        }
-    }
-
     public enum ResultStatus {
         RUNNING("running"),
         SUCCESS("success"),
@@ -80,22 +55,6 @@ public class AbstractQueryApiServlet extends AbstractServlet 
{
         }
     }
 
-    public enum ErrorField {
-        CODE("code"),
-        MSG("msg"),
-        STACK("stack");
-
-        private final String str;
-
-        ErrorField(String str) {
-            this.str = str;
-        }
-
-        public String str() {
-            return str;
-        }
-    }
-
     AbstractQueryApiServlet(IApplicationContext appCtx, ConcurrentMap<String, 
Object> ctx, String[] paths) {
         super(ctx, paths);
         this.appCtx = appCtx;
@@ -118,13 +77,4 @@ public class AbstractQueryApiServlet extends 
AbstractServlet {
         }
         return hcc;
     }
-
-    protected static void printRequestId(PrintWriter pw, String requestId) {
-        ResultUtil.printField(pw, ResultFields.REQUEST_ID.str(), requestId);
-    }
-
-    protected static void printHandle(PrintWriter pw, String handle, boolean 
comma) {
-        ResultUtil.printField(pw, ResultFields.HANDLE.str(), handle, comma);
-    }
-
 }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ApiServlet.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ApiServlet.java
index ea6e616..f395931 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ApiServlet.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ApiServlet.java
@@ -32,6 +32,7 @@ import java.util.concurrent.ConcurrentMap;
 
 import javax.imageio.ImageIO;
 
+import org.apache.asterix.app.result.ResponsePrinter;
 import org.apache.asterix.app.translator.RequestParameters;
 import org.apache.asterix.common.api.IRequestReference;
 import org.apache.asterix.common.config.GlobalConfig;
@@ -147,7 +148,7 @@ public class ApiServlet extends AbstractServlet {
             SessionOutput sessionOutput = new SessionOutput(sessionConfig, 
out);
             MetadataManager.INSTANCE.init();
             IStatementExecutor translator = 
statementExectorFactory.create(appCtx, statements, sessionOutput,
-                    compilationProvider, componentProvider);
+                    compilationProvider, componentProvider, new 
ResponsePrinter(sessionOutput));
             double duration;
             long startTime = System.currentTimeMillis();
             final IRequestParameters requestParameters = new 
RequestParameters(requestReference, query, resultSet,
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java
index 8923ae8..7d7bad2 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java
@@ -29,7 +29,8 @@ import org.apache.asterix.algebra.base.ILangExtension;
 import org.apache.asterix.app.message.CancelQueryRequest;
 import org.apache.asterix.app.message.ExecuteStatementRequestMessage;
 import org.apache.asterix.app.message.ExecuteStatementResponseMessage;
-import org.apache.asterix.app.result.ResultReader;
+import org.apache.asterix.app.result.ResponsePrinter;
+import org.apache.asterix.app.result.fields.NcResultPrinter;
 import org.apache.asterix.common.api.Duration;
 import org.apache.asterix.common.api.IApplicationContext;
 import org.apache.asterix.common.api.IRequestReference;
@@ -40,14 +41,10 @@ import 
org.apache.asterix.common.exceptions.RuntimeDataException;
 import org.apache.asterix.common.messaging.api.INCMessageBroker;
 import org.apache.asterix.common.messaging.api.MessageFuture;
 import org.apache.asterix.compiler.provider.ILangCompilationProvider;
-import org.apache.asterix.om.types.ARecordType;
 import org.apache.asterix.translator.IStatementExecutor;
 import org.apache.asterix.translator.ResultProperties;
 import org.apache.asterix.translator.SessionOutput;
-import org.apache.commons.lang3.tuple.Triple;
 import org.apache.hyracks.api.application.INCServiceContext;
-import org.apache.hyracks.api.job.JobId;
-import org.apache.hyracks.api.result.ResultSetId;
 import org.apache.hyracks.http.api.IChannelClosedHandler;
 import org.apache.hyracks.http.api.IServletRequest;
 import org.apache.hyracks.http.server.HttpServer;
@@ -73,7 +70,8 @@ public class NCQueryServiceServlet extends 
QueryServiceServlet {
     protected void executeStatement(IRequestReference requestReference, String 
statementsText,
             SessionOutput sessionOutput, ResultProperties resultProperties, 
IStatementExecutor.Stats stats,
             QueryServiceRequestParameters param, RequestExecutionState 
execution,
-            Map<String, String> optionalParameters, Map<String, byte[]> 
statementParameters) throws Exception {
+            Map<String, String> optionalParameters, Map<String, byte[]> 
statementParameters,
+            ResponsePrinter responsePrinter) throws Exception {
         // Running on NC -> send 'execute' message to CC
         INCServiceContext ncCtx = (INCServiceContext) serviceCtx;
         INCMessageBroker ncMb = (INCMessageBroker) ncCtx.getMessageBroker();
@@ -119,20 +117,11 @@ public class NCQueryServiceServlet extends 
QueryServiceServlet {
                 throw new Exception(err.toString(), err);
             }
         }
-        // no errors - stop buffering and allow for streaming result delivery
-        sessionOutput.release();
-
-        IStatementExecutor.ResultMetadata resultMetadata = 
responseMsg.getMetadata();
-        if (delivery == IStatementExecutor.ResultDelivery.IMMEDIATE && 
!resultMetadata.getResultSets().isEmpty()) {
-            
stats.setProcessedObjects(responseMsg.getStats().getProcessedObjects());
-            for (Triple<JobId, ResultSetId, ARecordType> rsmd : 
resultMetadata.getResultSets()) {
-                ResultReader resultReader = new ResultReader(getResultSet(), 
rsmd.getLeft(), rsmd.getMiddle());
-                ResultUtil.printResults(appCtx, resultReader, sessionOutput, 
stats, rsmd.getRight());
-            }
-        } else {
-            sessionOutput.out().append(responseMsg.getResult());
+        if (hasResult(responseMsg)) {
+            responsePrinter.addResultPrinter(
+                    new NcResultPrinter(appCtx, responseMsg, getResultSet(), 
delivery, sessionOutput));
         }
-        printExecutionPlans(sessionOutput, responseMsg.getExecutionPlans());
+        buildResponseResults(responsePrinter, sessionOutput, 
responseMsg.getExecutionPlans());
     }
 
     private void cancelQuery(INCMessageBroker messageBroker, String nodeId, 
String uuid, String clientContextID,
@@ -174,4 +163,8 @@ public class NCQueryServiceServlet extends 
QueryServiceServlet {
     public IChannelClosedHandler getChannelClosedHandler(HttpServer server) {
         return InterruptOnCloseHandler.INSTANCE;
     }
+
+    private static boolean hasResult(ExecuteStatementResponseMessage 
responseMsg) {
+        return !responseMsg.getMetadata().getResultSets().isEmpty() || 
!responseMsg.getResult().isEmpty();
+    }
 }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryResultApiServlet.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryResultApiServlet.java
index cda4d34..2b06dbb 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryResultApiServlet.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryResultApiServlet.java
@@ -22,8 +22,12 @@ import java.io.IOException;
 import java.util.concurrent.ConcurrentMap;
 
 import org.apache.asterix.api.common.ResultMetadata;
+import org.apache.asterix.app.result.ResponseMertics;
+import org.apache.asterix.app.result.ResponsePrinter;
 import org.apache.asterix.app.result.ResultHandle;
 import org.apache.asterix.app.result.ResultReader;
+import org.apache.asterix.app.result.fields.MetricsPrinter;
+import org.apache.asterix.app.result.fields.ResultsPrinter;
 import org.apache.asterix.common.api.IApplicationContext;
 import org.apache.asterix.translator.IStatementExecutor.Stats;
 import org.apache.asterix.translator.SessionConfig;
@@ -87,15 +91,19 @@ public class QueryResultApiServlet extends 
AbstractQueryApiServlet {
             }
             ResultMetadata metadata = (ResultMetadata) 
resultReader.getMetadata();
             SessionOutput sessionOutput = initResponse(request, response, 
metadata.getFormat());
+            ResponsePrinter printer = new ResponsePrinter(sessionOutput);
             if (metadata.getFormat() == SessionConfig.OutputFormat.CLEAN_JSON
                     || metadata.getFormat() == 
SessionConfig.OutputFormat.LOSSLESS_JSON) {
                 final Stats stats = new Stats();
-                sessionOutput.out().print("{\n");
-                ResultUtil.printResults(appCtx, resultReader, sessionOutput, 
stats, null);
-                QueryServiceServlet.printMetrics(sessionOutput.out(), 
System.nanoTime() - elapsedStart,
-                        metadata.getJobDuration(), stats.getCount(), 
stats.getSize(), metadata.getProcessedObjects(), 0,
-                        0, HttpUtil.getPreferredCharset(request));
-                sessionOutput.out().print("}\n");
+                printer.begin();
+                printer.addResultPrinter(new ResultsPrinter(appCtx, 
resultReader, null, stats, sessionOutput));
+                printer.printResults();
+                ResponseMertics mertics =
+                        ResponseMertics.of(System.nanoTime() - elapsedStart, 
metadata.getJobDuration(),
+                                stats.getCount(), stats.getSize(), 
metadata.getProcessedObjects(), 0, 0);
+                printer.addFooterPrinter(new MetricsPrinter(mertics, 
HttpUtil.getPreferredCharset(request)));
+                printer.printFooters();
+                printer.end();
             } else {
                 ResultUtil.printResults(appCtx, resultReader, sessionOutput, 
new Stats(), null);
             }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
index 06b75e3..4eb3524 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
@@ -31,7 +31,6 @@ import java.io.PrintWriter;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -43,10 +42,23 @@ import java.util.function.BiFunction;
 import java.util.function.Function;
 
 import org.apache.asterix.algebra.base.ILangExtension;
+import org.apache.asterix.app.result.ExecutionError;
+import org.apache.asterix.app.result.ResponseMertics;
+import org.apache.asterix.app.result.ResponsePrinter;
+import org.apache.asterix.app.result.fields.ClientContextIdPrinter;
+import org.apache.asterix.app.result.fields.ErrorsPrinter;
+import org.apache.asterix.app.result.fields.MetricsPrinter;
+import org.apache.asterix.app.result.fields.ParseOnlyResultPrinter;
+import org.apache.asterix.app.result.fields.PlansPrinter;
+import org.apache.asterix.app.result.fields.RequestIdPrinter;
+import org.apache.asterix.app.result.fields.SignaturePrinter;
+import org.apache.asterix.app.result.fields.StatusPrinter;
+import org.apache.asterix.app.result.fields.TypePrinter;
+import org.apache.asterix.app.result.fields.WarningsPrinter;
 import org.apache.asterix.app.translator.QueryTranslator;
-import org.apache.asterix.common.api.Duration;
 import org.apache.asterix.common.api.IApplicationContext;
 import org.apache.asterix.common.api.IClusterManagementWork;
+import org.apache.asterix.common.api.ICodedMessage;
 import org.apache.asterix.common.api.IReceptionist;
 import org.apache.asterix.common.api.IRequestReference;
 import org.apache.asterix.common.config.GlobalConfig;
@@ -65,7 +77,6 @@ import org.apache.asterix.lang.common.statement.Query;
 import org.apache.asterix.metadata.MetadataManager;
 import org.apache.asterix.om.base.IAObject;
 import org.apache.asterix.translator.ExecutionPlans;
-import org.apache.asterix.translator.ExecutionPlansJsonPrintUtil;
 import org.apache.asterix.translator.IRequestParameters;
 import org.apache.asterix.translator.IStatementExecutor;
 import org.apache.asterix.translator.IStatementExecutor.ResultDelivery;
@@ -184,26 +195,6 @@ public class QueryServiceServlet extends 
AbstractQueryApiServlet {
         }
     }
 
-    private enum Metrics {
-        ELAPSED_TIME("elapsedTime"),
-        EXECUTION_TIME("executionTime"),
-        RESULT_COUNT("resultCount"),
-        RESULT_SIZE("resultSize"),
-        ERROR_COUNT("errorCount"),
-        PROCESSED_OBJECTS_COUNT("processedObjects"),
-        WARNING_COUNT("warningCount");
-
-        private final String str;
-
-        Metrics(String str) {
-            this.str = str;
-        }
-
-        public String str() {
-            return str;
-        }
-    }
-
     protected static final class RequestExecutionState {
         private long execStart = -1;
         private long execEnd = -1;
@@ -290,68 +281,6 @@ public class QueryServiceServlet extends 
AbstractQueryApiServlet {
         return new SessionOutput(sessionConfig, resultWriter, resultPrefix, 
resultPostfix, null, appendStatus);
     }
 
-    private static void printClientContextID(PrintWriter pw, 
QueryServiceRequestParameters params) {
-        if (params.getClientContextID() != null && 
!params.getClientContextID().isEmpty()) {
-            ResultUtil.printField(pw, ResultFields.CLIENT_ID.str(), 
params.getClientContextID());
-        }
-    }
-
-    private static void printSignature(PrintWriter pw, 
QueryServiceRequestParameters param) {
-        if (param.isSignature()) {
-            pw.print("\t\"");
-            pw.print(ResultFields.SIGNATURE.str());
-            pw.print("\": {\n");
-            pw.print("\t");
-            ResultUtil.printField(pw, "*", "*", false);
-            pw.print("\t},\n");
-        }
-    }
-
-    private static void printType(PrintWriter pw, SessionConfig sessionConfig) 
{
-        switch (sessionConfig.fmt()) {
-            case ADM:
-                ResultUtil.printField(pw, ResultFields.TYPE.str(), 
HttpUtil.ContentType.APPLICATION_ADM);
-                break;
-            case CSV:
-                String contentType = HttpUtil.ContentType.CSV + "; header="
-                        + (sessionConfig.is(SessionConfig.FORMAT_CSV_HEADER) ? 
"present" : "absent");
-                ResultUtil.printField(pw, ResultFields.TYPE.str(), 
contentType);
-                break;
-            default:
-                break;
-        }
-    }
-
-    public static void printMetrics(PrintWriter pw, long elapsedTime, long 
executionTime, long resultCount,
-            long resultSize, long processedObjects, long errorCount, long 
warnCount, Charset resultCharset) {
-        boolean hasErrors = errorCount != 0;
-        boolean hasWarnings = warnCount != 0;
-        boolean useAscii = !StandardCharsets.UTF_8.equals(resultCharset)
-                && 
!"μ".contentEquals(resultCharset.decode(resultCharset.encode("μ")));
-        pw.print("\t\"");
-        pw.print(ResultFields.METRICS.str());
-        pw.print("\": {\n");
-        pw.print("\t");
-        ResultUtil.printField(pw, Metrics.ELAPSED_TIME.str(), 
Duration.formatNanos(elapsedTime, useAscii));
-        pw.print("\t");
-        ResultUtil.printField(pw, Metrics.EXECUTION_TIME.str(), 
Duration.formatNanos(executionTime, useAscii));
-        pw.print("\t");
-        ResultUtil.printField(pw, Metrics.RESULT_COUNT.str(), resultCount, 
true);
-        pw.print("\t");
-        ResultUtil.printField(pw, Metrics.RESULT_SIZE.str(), resultSize, true);
-        pw.print("\t");
-        ResultUtil.printField(pw, Metrics.PROCESSED_OBJECTS_COUNT.str(), 
processedObjects, hasWarnings || hasErrors);
-        if (hasWarnings) {
-            pw.print("\t");
-            ResultUtil.printField(pw, Metrics.WARNING_COUNT.str(), warnCount, 
hasErrors);
-        }
-        if (hasErrors) {
-            pw.print("\t");
-            ResultUtil.printField(pw, Metrics.ERROR_COUNT.str(), errorCount, 
false);
-        }
-        pw.print("\t}\n");
-    }
-
     protected String getOptText(JsonNode node, Parameter parameter) {
         return getOptText(node, parameter.str());
     }
@@ -539,74 +468,96 @@ public class QueryServiceServlet extends 
AbstractQueryApiServlet {
         long errorCount = 1;
         Stats stats = new Stats();
         RequestExecutionState execution = new RequestExecutionState();
-        List<ExecutionWarning> warnings = Collections.emptyList();
+        List<ICodedMessage> warnings = Collections.emptyList();
         Charset resultCharset = HttpUtil.setContentType(response, 
HttpUtil.ContentType.APPLICATION_JSON, request);
         PrintWriter httpWriter = response.writer();
         SessionOutput sessionOutput = createSessionOutput(httpWriter);
         QueryServiceRequestParameters param = newRequestParameters();
+        ResponsePrinter responsePrinter = new ResponsePrinter(sessionOutput);
+        ResultDelivery delivery = ResultDelivery.IMMEDIATE;
         try {
             // buffer the output until we are ready to set the status of the 
response message correctly
-            sessionOutput.hold();
-            sessionOutput.out().print("{\n");
+            responsePrinter.begin();
             Map<String, String> optionalParams = null;
             if (optionalParamProvider != null) {
                 optionalParams = optionalParamProvider.apply(request);
             }
             setRequestParam(request, param, optionalParams);
             LOGGER.info(() -> "handleRequest: " + 
LogRedactionUtil.userData(param.toString()));
-            ResultDelivery delivery = parseResultDelivery(param.getMode());
+            delivery = parseResultDelivery(param.getMode());
             setSessionConfig(sessionOutput, param, delivery);
             final ResultProperties resultProperties = 
param.getMaxResultReads() == null ? new ResultProperties(delivery)
                     : new ResultProperties(delivery, 
Long.parseLong(param.getMaxResultReads()));
-            printAdditionalResultFields(sessionOutput.out());
-            printRequestId(sessionOutput.out(), requestRef.getUuid());
-            printClientContextID(sessionOutput.out(), param);
-            if (!param.isParseOnly()) {
-                printSignature(sessionOutput.out(), param);
-            }
-            printType(sessionOutput.out(), sessionOutput.config());
+            buildResponseHeaders(requestRef, sessionOutput, param, 
responsePrinter, delivery);
+            responsePrinter.printHeaders();
             validateStatement(param.getStatement());
             String statementsText = param.getStatement() + ";";
             if (param.isParseOnly()) {
                 ResultUtil.ParseOnlyResult parseOnlyResult = 
parseStatement(statementsText);
                 setAccessControlHeaders(request, response);
-                response.setStatus(HttpResponseStatus.OK);
-                printParseOnlyValueResult(sessionOutput, parseOnlyResult);
-                ResultUtil.printStatus(sessionOutput, 
execution.getResultStatus());
+                response.setStatus(execution.getHttpStatus());
+                responsePrinter.addResultPrinter(new 
ParseOnlyResultPrinter(parseOnlyResult));
             } else {
                 Map<String, byte[]> statementParams = 
org.apache.asterix.app.translator.RequestParameters
                         .serializeParameterValues(param.getStatementParams());
                 setAccessControlHeaders(request, response);
                 response.setStatus(execution.getHttpStatus());
                 executeStatement(requestRef, statementsText, sessionOutput, 
resultProperties, stats, param, execution,
-                        optionalParams, statementParams);
-                if (ResultDelivery.IMMEDIATE == delivery || 
ResultDelivery.DEFERRED == delivery) {
-                    ResultUtil.printStatus(sessionOutput, 
execution.getResultStatus());
-                }
-            }
-            if (!warnings.isEmpty()) {
-                printWarnings(sessionOutput.out(), warnings);
+                        optionalParams, statementParams, responsePrinter);
             }
             errorCount = 0;
         } catch (Exception | TokenMgrError | 
org.apache.asterix.aqlplus.parser.TokenMgrError e) {
             handleExecuteStatementException(e, execution, param);
             response.setStatus(execution.getHttpStatus());
-            printError(sessionOutput.out(), e);
-            ResultUtil.printStatus(sessionOutput, execution.getResultStatus());
+            requestFailed(e, responsePrinter);
         } finally {
-            // make sure that we stop buffering and return the result to the 
http response
-            sessionOutput.release();
             execution.finish();
         }
-        printMetrics(sessionOutput.out(), System.nanoTime() - elapsedStart, 
execution.duration(), stats.getCount(),
-                stats.getSize(), stats.getProcessedObjects(), errorCount, 
warnings.size(), resultCharset);
-        sessionOutput.out().print("}\n");
-        sessionOutput.out().flush();
+        responsePrinter.printResults();
+        buildResponseFooters(elapsedStart, errorCount, stats, execution, 
warnings, resultCharset, responsePrinter,
+                delivery);
+        responsePrinter.printFooters();
+        responsePrinter.end();
         if (sessionOutput.out().checkError()) {
             LOGGER.warn("Error flushing output writer");
         }
     }
 
+    protected void buildResponseHeaders(IRequestReference requestRef, 
SessionOutput sessionOutput,
+            QueryServiceRequestParameters param, ResponsePrinter 
responsePrinter, ResultDelivery delivery) {
+        responsePrinter.addHeaderPrinter(new 
RequestIdPrinter(requestRef.getUuid()));
+        if (param.getClientContextID() != null && 
!param.getClientContextID().isEmpty()) {
+            responsePrinter.addHeaderPrinter(new 
ClientContextIdPrinter(param.getClientContextID()));
+        }
+        if (param.isSignature() && delivery != ResultDelivery.ASYNC && 
!param.isParseOnly()) {
+            responsePrinter.addHeaderPrinter(SignaturePrinter.INSTANCE);
+        }
+        if (sessionOutput.config().fmt() == SessionConfig.OutputFormat.ADM
+                || sessionOutput.config().fmt() == 
SessionConfig.OutputFormat.CSV) {
+            responsePrinter.addHeaderPrinter(new 
TypePrinter(sessionOutput.config()));
+        }
+    }
+
+    protected void buildResponseResults(ResponsePrinter responsePrinter, 
SessionOutput sessionOutput,
+            ExecutionPlans plans) {
+        responsePrinter.addResultPrinter(new PlansPrinter(plans, 
sessionOutput.config().getPlanFormat()));
+    }
+
+    protected void buildResponseFooters(long elapsedStart, long errorCount, 
Stats stats,
+            RequestExecutionState execution, List<ICodedMessage> warnings, 
Charset resultCharset,
+            ResponsePrinter responsePrinter, ResultDelivery delivery) {
+        if (ResultDelivery.ASYNC != delivery) {
+            // in case of ASYNC delivery, the status is printed by query 
translator
+            responsePrinter.addFooterPrinter(new 
StatusPrinter(execution.getResultStatus()));
+        }
+        if (!warnings.isEmpty()) {
+            responsePrinter.addFooterPrinter(new WarningsPrinter(warnings));
+        }
+        final ResponseMertics mertics = ResponseMertics.of(System.nanoTime() - 
elapsedStart, execution.duration(),
+                stats.getCount(), stats.getSize(), 
stats.getProcessedObjects(), errorCount, warnings.size());
+        responsePrinter.addFooterPrinter(new MetricsPrinter(mertics, 
resultCharset));
+    }
+
     protected void validateStatement(String statement) throws 
RuntimeDataException {
         if (statement == null || statement.isEmpty()) {
             throw new RuntimeDataException(ErrorCode.NO_STATEMENT_PROVIDED);
@@ -628,7 +579,8 @@ public class QueryServiceServlet extends 
AbstractQueryApiServlet {
     protected void executeStatement(IRequestReference requestReference, String 
statementsText,
             SessionOutput sessionOutput, ResultProperties resultProperties, 
Stats stats,
             QueryServiceRequestParameters param, RequestExecutionState 
execution,
-            Map<String, String> optionalParameters, Map<String, byte[]> 
statementParameters) throws Exception {
+            Map<String, String> optionalParameters, Map<String, byte[]> 
statementParameters,
+            ResponsePrinter responsePrinter) throws Exception {
         IClusterManagementWork.ClusterState clusterState =
                 ((ICcApplicationContext) 
appCtx).getClusterStateManager().getState();
         if (clusterState != IClusterManagementWork.ClusterState.ACTIVE) {
@@ -639,7 +591,7 @@ public class QueryServiceServlet extends 
AbstractQueryApiServlet {
         List<Statement> statements = parser.parse();
         MetadataManager.INSTANCE.init();
         IStatementExecutor translator = 
statementExecutorFactory.create((ICcApplicationContext) appCtx, statements,
-                sessionOutput, compilationProvider, componentProvider);
+                sessionOutput, compilationProvider, componentProvider, 
responsePrinter);
         execution.start();
         Map<String, IAObject> stmtParams =
                 
org.apache.asterix.app.translator.RequestParameters.deserializeParameterValues(statementParameters);
@@ -648,7 +600,7 @@ public class QueryServiceServlet extends 
AbstractQueryApiServlet {
                 optionalParameters, stmtParams, param.isMultiStatement());
         translator.compileAndExecute(getHyracksClientConnection(), 
requestParameters);
         execution.end();
-        printExecutionPlans(sessionOutput, translator.getExecutionPlans());
+        buildResponseResults(responsePrinter, sessionOutput, 
translator.getExecutionPlans());
     }
 
     protected void handleExecuteStatementException(Throwable t, 
RequestExecutionState state,
@@ -716,42 +668,9 @@ public class QueryServiceServlet extends 
AbstractQueryApiServlet {
                 && "present".equals(getParameterValue(param.getFormat(), 
Attribute.HEADER.str())));
     }
 
-    protected void printError(PrintWriter sessionOut, Throwable throwable) {
-        ResultUtil.printError(sessionOut, throwable);
-    }
-
-    protected void printAdditionalResultFields(PrintWriter sessionOut) {
-        // do nothing
-    }
-
-    protected void printWarnings(PrintWriter pw, List<ExecutionWarning> 
warnings) {
-        ResultUtil.printWarnings(pw, warnings);
-    }
-
-    protected void printParseOnlyValueResult(SessionOutput output, 
ResultUtil.ParseOnlyResult parseOnlyResult) {
-        final PrintWriter pw = output.out();
-        pw.print("\t\"");
-        pw.print(ResultFields.RESULTS.str()); //TODO: use ResultUtil, 
ResultPrinter
-        pw.print("\":");
-        pw.print(parseOnlyResult.asJson());
-        pw.print(",\n");
-    }
-
-    protected void printExecutionPlans(SessionOutput output, ExecutionPlans 
executionPlans) {
-        final PrintWriter pw = output.out();
-        pw.print("\t\"");
-        pw.print(ResultFields.PLANS.str());
-        pw.print("\":");
-        final SessionConfig.PlanFormat planFormat = 
output.config().getPlanFormat();
-        switch (planFormat) {
-            case JSON:
-            case STRING:
-                pw.print(ExecutionPlansJsonPrintUtil.asJson(executionPlans, 
planFormat));
-                break;
-            default:
-                throw new IllegalStateException("Unrecognized plan format: " + 
planFormat);
-        }
-        pw.print(",\n");
+    protected void requestFailed(Throwable throwable, ResponsePrinter 
responsePrinter) {
+        final ExecutionError executionError = ExecutionError.of(throwable);
+        responsePrinter.addResultPrinter(new 
ErrorsPrinter(Collections.singletonList(executionError)));
     }
 
     protected QueryServiceRequestParameters newRequestParameters() {
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryStatusApiServlet.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryStatusApiServlet.java
index df09aee..c91f0e3 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryStatusApiServlet.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryStatusApiServlet.java
@@ -21,12 +21,19 @@ package org.apache.asterix.api.http.server;
 import static 
org.apache.asterix.api.http.server.AbstractQueryApiServlet.ResultStatus.FAILED;
 
 import java.io.PrintWriter;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.ConcurrentMap;
 
+import org.apache.asterix.app.result.ExecutionError;
+import org.apache.asterix.app.result.ResponsePrinter;
 import org.apache.asterix.app.result.ResultHandle;
 import org.apache.asterix.app.result.ResultReader;
+import org.apache.asterix.app.result.fields.ErrorsPrinter;
+import org.apache.asterix.app.result.fields.ResultHandlePrinter;
+import org.apache.asterix.app.result.fields.StatusPrinter;
 import org.apache.asterix.common.api.IApplicationContext;
+import org.apache.asterix.translator.SessionOutput;
 import org.apache.hyracks.api.result.ResultJobRecord;
 import org.apache.hyracks.http.api.IServletRequest;
 import org.apache.hyracks.http.api.IServletResponse;
@@ -52,37 +59,32 @@ public class QueryStatusApiServlet extends 
AbstractQueryApiServlet {
             response.setStatus(HttpResponseStatus.BAD_REQUEST);
             return;
         }
-
         ResultReader resultReader = new ResultReader(getResultSet(), 
handle.getJobId(), handle.getResultSetId());
-
         final ResultJobRecord.Status resultReaderStatus = 
resultReader.getStatus();
         if (resultReaderStatus == null) {
             LOGGER.log(Level.INFO, "No results for: \"" + strHandle + "\"");
             response.setStatus(HttpResponseStatus.NOT_FOUND);
             return;
         }
-
         ResultStatus resultStatus = resultStatus(resultReaderStatus);
         Exception ex = extractException(resultReaderStatus);
-
         HttpUtil.setContentType(response, 
HttpUtil.ContentType.APPLICATION_JSON, request);
         final PrintWriter resultWriter = response.writer();
-
-        HttpResponseStatus httpStatus = HttpResponseStatus.OK;
-
-        resultWriter.print("{\n");
-        ResultUtil.printStatus(resultWriter, resultStatus, (ex != null) || 
ResultStatus.SUCCESS == resultStatus);
-
+        response.setStatus(HttpResponseStatus.OK);
+        SessionOutput sessionOutput = new SessionOutput(resultWriter);
+        ResponsePrinter printer = new ResponsePrinter(sessionOutput);
+        printer.begin();
+        printer.addHeaderPrinter(new StatusPrinter(resultStatus));
+        printer.printHeaders();
         if (ResultStatus.SUCCESS == resultStatus) {
             String servletPath = servletPath(request).replace("status", 
"result");
             String resHandle = "http://"; + host(request) + servletPath + 
strHandle;
-            printHandle(resultWriter, resHandle, false);
+            printer.addResultPrinter(new ResultHandlePrinter(resHandle));
         } else if (ex != null) {
-            ResultUtil.printError(resultWriter, ex, false);
+            printer.addResultPrinter(new 
ErrorsPrinter(Collections.singletonList(ExecutionError.of(ex))));
         }
-
-        resultWriter.print("}\n");
-        response.setStatus(httpStatus);
+        printer.printResults();
+        printer.end();
         if (response.writer().checkError()) {
             LOGGER.warn("Error flushing output writer");
         }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ResultUtil.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ResultUtil.java
index fa3c03d..63da9ce 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ResultUtil.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ResultUtil.java
@@ -32,11 +32,12 @@ import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.apache.asterix.app.result.ResultHandle;
 import org.apache.asterix.app.result.ResultPrinter;
 import org.apache.asterix.app.result.ResultReader;
+import org.apache.asterix.app.result.fields.ResultHandlePrinter;
+import org.apache.asterix.app.result.fields.ResultsPrinter;
+import org.apache.asterix.app.result.fields.StatusPrinter;
 import org.apache.asterix.common.api.IApplicationContext;
-import org.apache.asterix.lang.aql.parser.TokenMgrError;
 import org.apache.asterix.lang.common.expression.VariableExpr;
 import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil;
 import org.apache.asterix.om.types.ARecordType;
@@ -47,8 +48,6 @@ import org.apache.http.ParseException;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import 
org.apache.hyracks.algebricks.core.algebra.prettyprint.AlgebricksAppendable;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.HyracksException;
-import org.apache.hyracks.util.JSONUtil;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -91,76 +90,6 @@ public class ResultUtil {
         new ResultPrinter(appCtx, output, stats, recordType).print(record);
     }
 
-    public static void printResultHandle(SessionOutput output, ResultHandle 
handle) throws HyracksDataException {
-        try {
-            final AlgebricksAppendable app = new 
AlgebricksAppendable(output.out());
-            output.appendHandle(app, handle.toString());
-        } catch (AlgebricksException e) {
-            LOGGER.warn("error printing handle", e);
-        }
-    }
-
-    public static void printStatus(SessionOutput output, 
AbstractQueryApiServlet.ResultStatus rs) {
-        try {
-            final AlgebricksAppendable app = new 
AlgebricksAppendable(output.out());
-            output.appendStatus(app, rs.str());
-        } catch (AlgebricksException e) {
-            LOGGER.warn("error printing status", e);
-        }
-    }
-
-    public static void printStatus(PrintWriter pw, 
AbstractQueryApiServlet.ResultStatus rs, boolean comma) {
-        printField(pw, AbstractQueryApiServlet.ResultFields.STATUS.str(), 
rs.str(), comma);
-    }
-
-    public static void printError(PrintWriter pw, Throwable e) {
-        printError(pw, e, true);
-    }
-
-    public static void printError(PrintWriter pw, Throwable e, boolean comma) {
-        printError(pw, e, 1, comma);
-    }
-
-    public static void printError(PrintWriter pw, Throwable e, int code, 
boolean comma) {
-        Throwable rootCause = getRootCause(e);
-        String msg = rootCause.getMessage();
-        if (!(rootCause instanceof AlgebricksException || rootCause instanceof 
HyracksException
-                || rootCause instanceof TokenMgrError
-                || rootCause instanceof 
org.apache.asterix.aqlplus.parser.TokenMgrError)) {
-            msg = rootCause.getClass().getSimpleName() + (msg == null ? "" : 
": " + msg);
-        }
-        printError(pw, msg, code, comma);
-    }
-
-    public static void printError(PrintWriter pw, String msg, int code, 
boolean comma) {
-        pw.print("\t\"");
-        pw.print(AbstractQueryApiServlet.ResultFields.ERRORS.str());
-        pw.print("\": [{ \n\t");
-        printField(pw, QueryServiceServlet.ErrorField.CODE.str(), code);
-        pw.print("\t");
-        printField(pw, QueryServiceServlet.ErrorField.MSG.str(), 
JSONUtil.escape(msg), false);
-        pw.print(comma ? "\t}],\n" : "\t}]\n");
-    }
-
-    public static void printWarnings(PrintWriter pw, List<ExecutionWarning> 
warnings) {
-        pw.print("\t\"");
-        pw.print(AbstractQueryApiServlet.ResultFields.WARNINGS.str());
-        pw.print("\": [");
-        for (int i = 0; i < warnings.size(); i++) {
-            final ExecutionWarning warning = warnings.get(i);
-            pw.print("{ \n\t");
-            printField(pw, QueryServiceServlet.ErrorField.CODE.str(), 
warning.getCode());
-            pw.print("\t");
-            printField(pw, QueryServiceServlet.ErrorField.MSG.str(), 
JSONUtil.escape(warning.getMessage()), false);
-            pw.print("\t} \n\t");
-            boolean lastWarning = i == warnings.size() - 1;
-            if (!lastWarning) {
-                pw.print(",");
-            }
-        }
-        pw.print("],\n");
-    }
-
     public static void printField(PrintWriter pw, String name, String value) {
         printField(pw, name, value, true);
     }
@@ -185,7 +114,6 @@ public class ResultUtil {
         if (comma) {
             pw.print(',');
         }
-        pw.print('\n');
     }
 
     public static ObjectNode getErrorResponse(int errorCode, String 
errorMessage, String errorSummary,
@@ -359,7 +287,7 @@ public class ResultUtil {
             @Override
             public AlgebricksAppendable append(AlgebricksAppendable app) 
throws AlgebricksException {
                 app.append("\t\"");
-                app.append(AbstractQueryApiServlet.ResultFields.RESULTS.str());
+                app.append(ResultsPrinter.FIELD_NAME);
                 if (resultNo >= 0) {
                     app.append('-').append(String.valueOf(resultNo));
                 }
@@ -371,17 +299,17 @@ public class ResultUtil {
     }
 
     public static SessionOutput.ResultDecorator createPostResultDecorator() {
-        return app -> app.append("\t,\n");
+        return app -> app.append("\t");
     }
 
     public static SessionOutput.ResultAppender 
createResultHandleAppender(String handleUrl) {
-        return (app, handle) -> 
app.append("\t\"").append(AbstractQueryApiServlet.ResultFields.HANDLE.str())
-                .append("\": 
\"").append(handleUrl).append(handle).append("\",\n");
+        return (app, handle) -> 
app.append("\t\"").append(ResultHandlePrinter.FIELD_NAME).append("\": \"")
+                .append(handleUrl).append(handle).append("\"");
     }
 
     public static SessionOutput.ResultAppender createResultStatusAppender() {
-        return (app, status) -> 
app.append("\t\"").append(AbstractQueryApiServlet.ResultFields.STATUS.str())
-                .append("\": \"").append(status).append("\",\n");
+        return (app, status) -> 
app.append("\t\"").append(StatusPrinter.FIELD_NAME).append("\": 
\"").append(status)
+                .append("\"");
     }
 
     public static class ParseOnlyResult {
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/java/AsterixJavaClient.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/java/AsterixJavaClient.java
index a5c8645..1f8e44c 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/java/AsterixJavaClient.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/java/AsterixJavaClient.java
@@ -25,6 +25,7 @@ import java.util.Map;
 import java.util.UUID;
 
 import org.apache.asterix.api.common.APIFramework;
+import org.apache.asterix.app.result.ResponsePrinter;
 import org.apache.asterix.app.translator.RequestParameters;
 import org.apache.asterix.common.api.RequestReference;
 import org.apache.asterix.common.context.IStorageComponentProvider;
@@ -128,7 +129,7 @@ public class AsterixJavaClient {
         SessionOutput output = new SessionOutput(conf, writer);
 
         IStatementExecutor translator = 
statementExecutorFactory.create(appCtx, statements, output, compilationProvider,
-                storageComponentProvider);
+                storageComponentProvider, new ResponsePrinter(output));
         final RequestReference requestReference =
                 RequestReference.of(UUID.randomUUID().toString(), "CC", 
System.currentTimeMillis());
         final IRequestParameters requestParameters = new 
RequestParameters(requestReference, statement, null,
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/message/ExecuteStatementRequestMessage.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/message/ExecuteStatementRequestMessage.java
index a89728c..b0b94c5 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/message/ExecuteStatementRequestMessage.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/message/ExecuteStatementRequestMessage.java
@@ -28,6 +28,7 @@ import java.util.concurrent.TimeUnit;
 import org.apache.asterix.algebra.base.ILangExtension;
 import org.apache.asterix.api.http.server.ResultUtil;
 import org.apache.asterix.app.cc.CCExtensionManager;
+import org.apache.asterix.app.result.ResponsePrinter;
 import org.apache.asterix.app.translator.RequestParameters;
 import org.apache.asterix.common.api.IClusterManagementWork;
 import org.apache.asterix.common.api.IRequestReference;
@@ -130,7 +131,7 @@ public final class ExecuteStatementRequestMessage 
implements ICcAddressedMessage
             IStatementExecutor.ResultMetadata outMetadata = new 
IStatementExecutor.ResultMetadata();
             MetadataManager.INSTANCE.init();
             IStatementExecutor translator = 
statementExecutorFactory.create(ccAppCtx, statements, sessionOutput,
-                    compilationProvider, storageComponentProvider);
+                    compilationProvider, storageComponentProvider, new 
ResponsePrinter(sessionOutput));
             final IStatementExecutor.Stats stats = new 
IStatementExecutor.Stats();
             Map<String, IAObject> stmtParams = 
RequestParameters.deserializeParameterValues(statementParameters);
             final IRequestParameters requestParameters =
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ExecutionError.java
similarity index 50%
copy from 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
copy to 
asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ExecutionError.java
index baaa5bd..c57e61d 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ExecutionError.java
@@ -16,22 +16,41 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.asterix.api.http.server;
+package org.apache.asterix.app.result;
 
-public class ExecutionWarning {
+import org.apache.asterix.api.http.server.ResultUtil;
+import org.apache.asterix.common.api.ICodedMessage;
+import org.apache.asterix.lang.aql.parser.TokenMgrError;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.api.exceptions.HyracksException;
+
+public class ExecutionError implements ICodedMessage {
 
     private final int code;
     private final String message;
 
-    public ExecutionWarning(int code, String message) {
+    private ExecutionError(int code, String message) {
         this.code = code;
         this.message = message;
     }
 
+    public static ExecutionError of(Throwable t) {
+        Throwable rootCause = ResultUtil.getRootCause(t);
+        String msg = rootCause.getMessage();
+        if (!(rootCause instanceof AlgebricksException || rootCause instanceof 
HyracksException
+                || rootCause instanceof TokenMgrError
+                || rootCause instanceof 
org.apache.asterix.aqlplus.parser.TokenMgrError)) {
+            msg = rootCause.getClass().getSimpleName() + (msg == null ? "" : 
": " + msg);
+        }
+        return new ExecutionError(1, msg);
+    }
+
+    @Override
     public int getCode() {
         return code;
     }
 
+    @Override
     public String getMessage() {
         return message;
     }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ExecutionWarning.java
similarity index 86%
copy from 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
copy to 
asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ExecutionWarning.java
index baaa5bd..29eb098 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ExecutionWarning.java
@@ -16,9 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.asterix.api.http.server;
+package org.apache.asterix.app.result;
 
-public class ExecutionWarning {
+import org.apache.asterix.common.api.ICodedMessage;
+
+public class ExecutionWarning implements ICodedMessage {
 
     private final int code;
     private final String message;
@@ -28,10 +30,12 @@ public class ExecutionWarning {
         this.message = message;
     }
 
+    @Override
     public int getCode() {
         return code;
     }
 
+    @Override
     public String getMessage() {
         return message;
     }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ResponseMertics.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ResponseMertics.java
new file mode 100644
index 0000000..666b759
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ResponseMertics.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.asterix.app.result;
+
+public class ResponseMertics {
+
+    private long elapsedTime;
+    private long executionTime;
+    private long resultCount;
+    private long resultSize;
+    private long processedObjects;
+    private long errorCount;
+    private long warnCount;
+
+    private ResponseMertics() {
+    }
+
+    public static ResponseMertics of(long elapsedTime, long executionTime, 
long resultCount, long resultSize,
+            long processedObjects, long errorCount, long warnCount) {
+        ResponseMertics mertics = new ResponseMertics();
+        mertics.elapsedTime = elapsedTime;
+        mertics.executionTime = executionTime;
+        mertics.resultCount = resultCount;
+        mertics.resultSize = resultSize;
+        mertics.processedObjects = processedObjects;
+        mertics.errorCount = errorCount;
+        mertics.warnCount = warnCount;
+        return mertics;
+    }
+
+    public long getElapsedTime() {
+        return elapsedTime;
+    }
+
+    public long getExecutionTime() {
+        return executionTime;
+    }
+
+    public long getResultCount() {
+        return resultCount;
+    }
+
+    public long getResultSize() {
+        return resultSize;
+    }
+
+    public long getProcessedObjects() {
+        return processedObjects;
+    }
+
+    public long getErrorCount() {
+        return errorCount;
+    }
+
+    public long getWarnCount() {
+        return warnCount;
+    }
+}
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ResponsePrinter.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ResponsePrinter.java
new file mode 100644
index 0000000..43e128d
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ResponsePrinter.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.asterix.app.result;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.asterix.common.api.IResponseFieldPrinter;
+import org.apache.asterix.common.api.IResponsePrinter;
+import org.apache.asterix.translator.SessionOutput;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+
+public class ResponsePrinter implements IResponsePrinter {
+
+    private final SessionOutput sessionOutput;
+    private final List<IResponseFieldPrinter> headers = new ArrayList<>();
+    private final List<IResponseFieldPrinter> results = new ArrayList<>();
+    private final List<IResponseFieldPrinter> footers = new ArrayList<>();
+    private boolean headersPrinted = false;
+    private boolean resultsPrinted = false;
+
+    public ResponsePrinter(SessionOutput sessionOutput) {
+        this.sessionOutput = sessionOutput;
+    }
+
+    @Override
+    public void begin() {
+        sessionOutput.hold();
+        sessionOutput.out().print("{\n");
+    }
+
+    @Override
+    public void addHeaderPrinter(IResponseFieldPrinter printer) {
+        headers.add(printer);
+    }
+
+    @Override
+    public void addResultPrinter(IResponseFieldPrinter printer) {
+        results.add(printer);
+    }
+
+    @Override
+    public void addFooterPrinter(IResponseFieldPrinter printer) {
+        footers.add(printer);
+    }
+
+    @Override
+    public void printHeaders() throws HyracksDataException {
+        print(headers);
+        headersPrinted = !headers.isEmpty();
+    }
+
+    @Override
+    public void printResults() throws HyracksDataException {
+        sessionOutput.release();
+        print(results);
+        if (!resultsPrinted) {
+            resultsPrinted = !results.isEmpty();
+        }
+        results.clear();
+    }
+
+    @Override
+    public void printFooters() throws HyracksDataException {
+        print(footers);
+    }
+
+    @Override
+    public void end() {
+        sessionOutput.out().print("\n}\n");
+        sessionOutput.release();
+        sessionOutput.out().flush();
+    }
+
+    private void print(List<IResponseFieldPrinter> printers) throws 
HyracksDataException {
+        final int fieldsCount = printers.size();
+        if ((headersPrinted || resultsPrinted) && fieldsCount > 0) {
+            printFieldSeparator(sessionOutput.out());
+        }
+        for (int i = 0; i < printers.size(); i++) {
+            IResponseFieldPrinter printer = printers.get(i);
+            printer.print(sessionOutput.out());
+            if (i + 1 != fieldsCount) {
+                printFieldSeparator(sessionOutput.out());
+            }
+        }
+    }
+
+    public static void printFieldSeparator(PrintWriter pw) {
+        pw.print(",\n");
+    }
+}
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ResultHandle.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ResultHandle.java
index 6f9fc47..330cf4b 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ResultHandle.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ResultHandle.java
@@ -62,6 +62,6 @@ public class ResultHandle {
 
     @Override
     public String toString() {
-        return Long.toString(jobId.getId()) + "-" + 
Long.toString(resultSetId.getId());
+        return jobId.getId() + "-" + resultSetId.getId();
     }
 }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/AbstractCodedMessagePrinter.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/AbstractCodedMessagePrinter.java
new file mode 100644
index 0000000..6270c4c
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/AbstractCodedMessagePrinter.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.asterix.app.result.fields;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+import org.apache.asterix.api.http.server.ResultUtil;
+import org.apache.asterix.common.api.ICodedMessage;
+import org.apache.asterix.common.api.IResponseFieldPrinter;
+import org.apache.hyracks.util.JSONUtil;
+
+public abstract class AbstractCodedMessagePrinter implements 
IResponseFieldPrinter {
+
+    private enum CodedMessageField {
+        CODE("code"),
+        MSG("msg");
+
+        private final String str;
+
+        CodedMessageField(String str) {
+            this.str = str;
+        }
+
+        public String str() {
+            return str;
+        }
+    }
+
+    private final List<ICodedMessage> messages;
+
+    public AbstractCodedMessagePrinter(List<ICodedMessage> messages) {
+        this.messages = messages;
+    }
+
+    @Override
+    public void print(PrintWriter pw) {
+        pw.print("\t\"");
+        pw.print(getName());
+        pw.print("\": [");
+        for (int i = 0; i < messages.size(); i++) {
+            final ICodedMessage codedMessage = messages.get(i);
+            pw.print("{ \n\t");
+            ResultUtil.printField(pw, CodedMessageField.CODE.str(), 
codedMessage.getCode());
+            pw.print("\t");
+            ResultUtil.printField(pw, CodedMessageField.MSG.str(), 
JSONUtil.escape(codedMessage.getMessage()), false);
+            pw.print("\t} \n\t");
+            boolean lastMsg = i == messages.size() - 1;
+            if (!lastMsg) {
+                pw.print(",");
+            }
+        }
+        pw.print("]");
+    }
+}
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ClientContextIdPrinter.java
similarity index 55%
copy from 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
copy to 
asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ClientContextIdPrinter.java
index baaa5bd..c83210d 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ClientContextIdPrinter.java
@@ -16,23 +16,29 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.asterix.api.http.server;
+package org.apache.asterix.app.result.fields;
 
-public class ExecutionWarning {
+import java.io.PrintWriter;
 
-    private final int code;
-    private final String message;
+import org.apache.asterix.api.http.server.ResultUtil;
+import org.apache.asterix.common.api.IResponseFieldPrinter;
 
-    public ExecutionWarning(int code, String message) {
-        this.code = code;
-        this.message = message;
+public class ClientContextIdPrinter implements IResponseFieldPrinter {
+
+    private static final String FIELD_NAME = "clientContextID";
+    private final String clientContextId;
+
+    public ClientContextIdPrinter(String clientContextId) {
+        this.clientContextId = clientContextId;
     }
 
-    public int getCode() {
-        return code;
+    @Override
+    public void print(PrintWriter pw) {
+        ResultUtil.printField(pw, FIELD_NAME, clientContextId, false);
     }
 
-    public String getMessage() {
-        return message;
+    @Override
+    public String getName() {
+        return FIELD_NAME;
     }
 }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ErrorsPrinter.java
similarity index 67%
copy from 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
copy to 
asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ErrorsPrinter.java
index baaa5bd..cecc8e5 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ErrorsPrinter.java
@@ -16,23 +16,22 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.asterix.api.http.server;
+package org.apache.asterix.app.result.fields;
 
-public class ExecutionWarning {
+import java.util.List;
 
-    private final int code;
-    private final String message;
+import org.apache.asterix.common.api.ICodedMessage;
 
-    public ExecutionWarning(int code, String message) {
-        this.code = code;
-        this.message = message;
-    }
+public class ErrorsPrinter extends AbstractCodedMessagePrinter {
+
+    private static final String FIELD_NAME = "errors";
 
-    public int getCode() {
-        return code;
+    public ErrorsPrinter(List<ICodedMessage> errors) {
+        super(errors);
     }
 
-    public String getMessage() {
-        return message;
+    @Override
+    public String getName() {
+        return FIELD_NAME;
     }
 }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ExplainOnlyResultsPrinter.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ExplainOnlyResultsPrinter.java
new file mode 100644
index 0000000..1949e54
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ExplainOnlyResultsPrinter.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.asterix.app.result.fields;
+
+import java.io.PrintWriter;
+
+import org.apache.asterix.api.http.server.ResultUtil;
+import org.apache.asterix.common.api.IApplicationContext;
+import org.apache.asterix.common.api.IResponseFieldPrinter;
+import org.apache.asterix.translator.IStatementExecutor;
+import org.apache.asterix.translator.SessionConfig;
+import org.apache.asterix.translator.SessionOutput;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+
+public class ExplainOnlyResultsPrinter implements IResponseFieldPrinter {
+
+    private final IApplicationContext appCtx;
+    private final String plan;
+    private final SessionOutput sessionOutput;
+
+    public ExplainOnlyResultsPrinter(IApplicationContext appCtx, String plan, 
SessionOutput sessionOutput) {
+        this.appCtx = appCtx;
+        this.plan = plan;
+        this.sessionOutput = sessionOutput;
+    }
+
+    @Override
+    public void print(PrintWriter pw) throws HyracksDataException {
+        boolean quoteResult = sessionOutput.config().getPlanFormat() == 
SessionConfig.PlanFormat.STRING;
+        sessionOutput.config().set(SessionConfig.FORMAT_QUOTE_RECORD, 
quoteResult);
+        ResultUtil.printResults(appCtx, plan, sessionOutput, new 
IStatementExecutor.Stats(), null);
+    }
+
+    @Override
+    public String getName() {
+        return ResultsPrinter.FIELD_NAME;
+    }
+}
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/MetricsPrinter.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/MetricsPrinter.java
new file mode 100644
index 0000000..35b5f43
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/MetricsPrinter.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.asterix.app.result.fields;
+
+import java.io.PrintWriter;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.asterix.api.http.server.ResultUtil;
+import org.apache.asterix.app.result.ResponseMertics;
+import org.apache.asterix.common.api.Duration;
+import org.apache.asterix.common.api.IResponseFieldPrinter;
+
+public class MetricsPrinter implements IResponseFieldPrinter {
+
+    public enum Metrics {
+        ELAPSED_TIME("elapsedTime"),
+        EXECUTION_TIME("executionTime"),
+        RESULT_COUNT("resultCount"),
+        RESULT_SIZE("resultSize"),
+        ERROR_COUNT("errorCount"),
+        PROCESSED_OBJECTS_COUNT("processedObjects"),
+        WARNING_COUNT("warningCount");
+
+        private final String str;
+
+        Metrics(String str) {
+            this.str = str;
+        }
+
+        public String str() {
+            return str;
+        }
+    }
+
+    public static final String FIELD_NAME = "metrics";
+    private final ResponseMertics mertics;
+    private final Charset resultCharset;
+
+    public MetricsPrinter(ResponseMertics mertics, Charset resultCharset) {
+        this.mertics = mertics;
+        this.resultCharset = resultCharset;
+    }
+
+    @Override
+    public void print(PrintWriter pw) {
+        boolean useAscii = !StandardCharsets.UTF_8.equals(resultCharset)
+                && 
!"μ".contentEquals(resultCharset.decode(resultCharset.encode("μ")));
+        pw.print("\t\"");
+        pw.print(FIELD_NAME);
+        pw.print("\": {\n");
+        pw.print("\t");
+        ResultUtil.printField(pw, Metrics.ELAPSED_TIME.str(), 
Duration.formatNanos(mertics.getElapsedTime(), useAscii));
+        pw.print("\n\t");
+        ResultUtil.printField(pw, Metrics.EXECUTION_TIME.str(),
+                Duration.formatNanos(mertics.getExecutionTime(), useAscii));
+        pw.print("\n\t");
+        ResultUtil.printField(pw, Metrics.RESULT_COUNT.str(), 
mertics.getResultCount(), true);
+        pw.print("\n\t");
+        ResultUtil.printField(pw, Metrics.RESULT_SIZE.str(), 
mertics.getResultSize(), true);
+        pw.print("\n\t");
+        final boolean hasErrors = mertics.getErrorCount() > 0;
+        final boolean hasWarnings = mertics.getWarnCount() > 0;
+        ResultUtil.printField(pw, Metrics.PROCESSED_OBJECTS_COUNT.str(), 
mertics.getProcessedObjects(),
+                hasWarnings || hasErrors);
+        pw.print("\n");
+        if (hasWarnings) {
+            pw.print("\t");
+            ResultUtil.printField(pw, Metrics.WARNING_COUNT.str(), 
mertics.getWarnCount(), hasErrors);
+            pw.print("\n");
+        }
+        if (hasErrors) {
+            pw.print("\t");
+            ResultUtil.printField(pw, Metrics.ERROR_COUNT.str(), 
mertics.getErrorCount(), false);
+            pw.print("\n");
+        }
+        pw.print("\t}");
+    }
+
+    @Override
+    public String getName() {
+        return FIELD_NAME;
+    }
+}
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/NcResultPrinter.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/NcResultPrinter.java
new file mode 100644
index 0000000..2989d2c
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/NcResultPrinter.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.asterix.app.result.fields;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+import org.apache.asterix.api.http.server.ResultUtil;
+import org.apache.asterix.app.message.ExecuteStatementResponseMessage;
+import org.apache.asterix.app.result.ResponsePrinter;
+import org.apache.asterix.app.result.ResultReader;
+import org.apache.asterix.common.api.IApplicationContext;
+import org.apache.asterix.common.api.IResponseFieldPrinter;
+import org.apache.asterix.om.types.ARecordType;
+import org.apache.asterix.translator.IStatementExecutor;
+import org.apache.asterix.translator.SessionOutput;
+import org.apache.commons.lang3.tuple.Triple;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.api.job.JobId;
+import org.apache.hyracks.api.result.IResultSet;
+import org.apache.hyracks.api.result.ResultSetId;
+
+public class NcResultPrinter implements IResponseFieldPrinter {
+
+    private final IStatementExecutor.ResultDelivery delivery;
+    private final ExecuteStatementResponseMessage responseMsg;
+    private final IApplicationContext appCtx;
+    private final IResultSet resultSet;
+    private final SessionOutput sessionOutput;
+
+    public NcResultPrinter(IApplicationContext appCtx, 
ExecuteStatementResponseMessage responseMsg,
+            IResultSet resultSet, IStatementExecutor.ResultDelivery delivery, 
SessionOutput sessionOutput) {
+        this.appCtx = appCtx;
+        this.responseMsg = responseMsg;
+        this.delivery = delivery;
+        this.resultSet = resultSet;
+        this.sessionOutput = sessionOutput;
+    }
+
+    @Override
+    public void print(PrintWriter pw) throws HyracksDataException {
+        IStatementExecutor.ResultMetadata resultMetadata = 
responseMsg.getMetadata();
+        List<Triple<JobId, ResultSetId, ARecordType>> resultSets = 
resultMetadata.getResultSets();
+        if (delivery == IStatementExecutor.ResultDelivery.IMMEDIATE && 
!resultSets.isEmpty()) {
+            final IStatementExecutor.Stats stats = responseMsg.getStats();
+            
stats.setProcessedObjects(responseMsg.getStats().getProcessedObjects());
+            for (int i = 0; i < resultSets.size(); i++) {
+                Triple<JobId, ResultSetId, ARecordType> rsmd = 
resultSets.get(i);
+                ResultReader resultReader = new ResultReader(resultSet, 
rsmd.getLeft(), rsmd.getMiddle());
+                ResultUtil.printResults(appCtx, resultReader, sessionOutput, 
stats, rsmd.getRight());
+                if (i + 1 != resultSets.size()) {
+                    ResponsePrinter.printFieldSeparator(pw);
+                }
+            }
+        } else {
+            pw.append(responseMsg.getResult());
+        }
+    }
+
+    @Override
+    public String getName() {
+        return ResultsPrinter.FIELD_NAME;
+    }
+}
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ParseOnlyResultPrinter.java
similarity index 53%
copy from 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
copy to 
asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ParseOnlyResultPrinter.java
index baaa5bd..d7ee269 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ParseOnlyResultPrinter.java
@@ -16,23 +16,31 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.asterix.api.http.server;
+package org.apache.asterix.app.result.fields;
 
-public class ExecutionWarning {
+import java.io.PrintWriter;
 
-    private final int code;
-    private final String message;
+import org.apache.asterix.api.http.server.ResultUtil;
+import org.apache.asterix.common.api.IResponseFieldPrinter;
 
-    public ExecutionWarning(int code, String message) {
-        this.code = code;
-        this.message = message;
+public class ParseOnlyResultPrinter implements IResponseFieldPrinter {
+
+    private final ResultUtil.ParseOnlyResult parseOnlyResult;
+
+    public ParseOnlyResultPrinter(ResultUtil.ParseOnlyResult parseOnlyResult) {
+        this.parseOnlyResult = parseOnlyResult;
     }
 
-    public int getCode() {
-        return code;
+    @Override
+    public void print(PrintWriter pw) {
+        pw.print("\t\"");
+        pw.print(getName());
+        pw.print("\":");
+        pw.print(parseOnlyResult.asJson());
     }
 
-    public String getMessage() {
-        return message;
+    @Override
+    public String getName() {
+        return ResultsPrinter.FIELD_NAME;
     }
 }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/PlansPrinter.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/PlansPrinter.java
new file mode 100644
index 0000000..4021956
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/PlansPrinter.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.asterix.app.result.fields;
+
+import java.io.PrintWriter;
+
+import org.apache.asterix.common.api.IResponseFieldPrinter;
+import org.apache.asterix.translator.ExecutionPlans;
+import org.apache.asterix.translator.ExecutionPlansJsonPrintUtil;
+import org.apache.asterix.translator.SessionConfig;
+
+public class PlansPrinter implements IResponseFieldPrinter {
+
+    private static final String FIELD_NAME = "plans";
+    private final ExecutionPlans executionPlans;
+    private final SessionConfig.PlanFormat planFormat;
+
+    public PlansPrinter(ExecutionPlans executionPlans, 
SessionConfig.PlanFormat planFormat) {
+        this.executionPlans = executionPlans;
+        this.planFormat = planFormat;
+    }
+
+    @Override
+    public void print(PrintWriter pw) {
+        pw.print("\t\"");
+        pw.print(FIELD_NAME);
+        pw.print("\":");
+        switch (planFormat) {
+            case JSON:
+            case STRING:
+                pw.print(ExecutionPlansJsonPrintUtil.asJson(executionPlans, 
planFormat));
+                break;
+            default:
+                throw new IllegalStateException("Unrecognized plan format: " + 
planFormat);
+        }
+    }
+
+    @Override
+    public String getName() {
+        return FIELD_NAME;
+    }
+}
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/RequestIdPrinter.java
similarity index 57%
copy from 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
copy to 
asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/RequestIdPrinter.java
index baaa5bd..b66cbfb 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/RequestIdPrinter.java
@@ -16,23 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.asterix.api.http.server;
+package org.apache.asterix.app.result.fields;
 
-public class ExecutionWarning {
+import java.io.PrintWriter;
 
-    private final int code;
-    private final String message;
+import org.apache.asterix.api.http.server.ResultUtil;
+import org.apache.asterix.common.api.IResponseFieldPrinter;
 
-    public ExecutionWarning(int code, String message) {
-        this.code = code;
-        this.message = message;
+public class RequestIdPrinter implements IResponseFieldPrinter {
+
+    private static final String FIELD_NAME = "requestID";
+
+    private final String requestId;
+
+    public RequestIdPrinter(String requestId) {
+        this.requestId = requestId;
     }
 
-    public int getCode() {
-        return code;
+    @Override
+    public void print(PrintWriter pw) {
+        ResultUtil.printField(pw, FIELD_NAME, requestId, false);
     }
 
-    public String getMessage() {
-        return message;
+    @Override
+    public String getName() {
+        return FIELD_NAME;
     }
 }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ResultHandlePrinter.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ResultHandlePrinter.java
new file mode 100644
index 0000000..830c751
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ResultHandlePrinter.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.asterix.app.result.fields;
+
+import java.io.PrintWriter;
+
+import org.apache.asterix.api.http.server.ResultUtil;
+import org.apache.asterix.app.result.ResultHandle;
+import org.apache.asterix.common.api.IResponseFieldPrinter;
+import org.apache.asterix.translator.SessionOutput;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import 
org.apache.hyracks.algebricks.core.algebra.prettyprint.AlgebricksAppendable;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+
+public class ResultHandlePrinter implements IResponseFieldPrinter {
+
+    public static final String FIELD_NAME = "handle";
+    private final SessionOutput sessionOutput;
+    private final String handle;
+
+    public ResultHandlePrinter(SessionOutput sessionOutput, ResultHandle 
handle) {
+        this.sessionOutput = sessionOutput;
+        this.handle = handle.toString();
+    }
+
+    public ResultHandlePrinter(String handle) {
+        this.handle = handle;
+        sessionOutput = null;
+    }
+
+    @Override
+    public void print(PrintWriter pw) throws HyracksDataException {
+        if (sessionOutput != null) {
+            final AlgebricksAppendable app = new AlgebricksAppendable(pw);
+            try {
+                sessionOutput.appendHandle(app, handle);
+            } catch (AlgebricksException e) {
+                throw HyracksDataException.create(e);
+            }
+        } else {
+            ResultUtil.printField(pw, FIELD_NAME, handle, false);
+        }
+    }
+
+    @Override
+    public String getName() {
+        return FIELD_NAME;
+    }
+}
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ResultsPrinter.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ResultsPrinter.java
new file mode 100644
index 0000000..52198de
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ResultsPrinter.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.asterix.app.result.fields;
+
+import java.io.PrintWriter;
+
+import org.apache.asterix.api.http.server.ResultUtil;
+import org.apache.asterix.app.result.ResultReader;
+import org.apache.asterix.common.api.IApplicationContext;
+import org.apache.asterix.common.api.IResponseFieldPrinter;
+import org.apache.asterix.om.types.ARecordType;
+import org.apache.asterix.translator.IStatementExecutor;
+import org.apache.asterix.translator.SessionOutput;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+
+public class ResultsPrinter implements IResponseFieldPrinter {
+
+    public static final String FIELD_NAME = "results";
+    private final IApplicationContext appCtx;
+    private final ARecordType recordType;
+    private final ResultReader resultReader;
+    private final IStatementExecutor.Stats stats;
+    private final SessionOutput sessionOutput;
+
+    public ResultsPrinter(IApplicationContext appCtx, ResultReader 
resultReader, ARecordType recordType,
+            IStatementExecutor.Stats stats, SessionOutput sessionOutput) {
+        this.appCtx = appCtx;
+        this.recordType = recordType;
+        this.resultReader = resultReader;
+        this.stats = stats;
+        this.sessionOutput = sessionOutput;
+    }
+
+    @Override
+    public void print(PrintWriter pw) throws HyracksDataException {
+        ResultUtil.printResults(appCtx, resultReader, sessionOutput, stats, 
recordType);
+    }
+
+    @Override
+    public String getName() {
+        return FIELD_NAME;
+    }
+}
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/SignaturePrinter.java
similarity index 54%
copy from 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
copy to 
asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/SignaturePrinter.java
index baaa5bd..fe9d2be 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/SignaturePrinter.java
@@ -16,23 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.asterix.api.http.server;
+package org.apache.asterix.app.result.fields;
 
-public class ExecutionWarning {
+import java.io.PrintWriter;
 
-    private final int code;
-    private final String message;
+import org.apache.asterix.api.http.server.ResultUtil;
+import org.apache.asterix.common.api.IResponseFieldPrinter;
 
-    public ExecutionWarning(int code, String message) {
-        this.code = code;
-        this.message = message;
-    }
+public class SignaturePrinter implements IResponseFieldPrinter {
+
+    public static final SignaturePrinter INSTANCE = new SignaturePrinter();
+    private static final String FIELD_NAME = "signature";
 
-    public int getCode() {
-        return code;
+    @Override
+    public void print(PrintWriter pw) {
+        pw.print("\t\"");
+        pw.print(FIELD_NAME);
+        pw.print("\": {\n");
+        pw.print("\t");
+        ResultUtil.printField(pw, "*", "*", false);
+        pw.print("\n\t}");
     }
 
-    public String getMessage() {
-        return message;
+    @Override
+    public String getName() {
+        return FIELD_NAME;
     }
 }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/StatusPrinter.java
similarity index 54%
copy from 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
copy to 
asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/StatusPrinter.java
index baaa5bd..372ea45 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/StatusPrinter.java
@@ -16,23 +16,29 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.asterix.api.http.server;
+package org.apache.asterix.app.result.fields;
 
-public class ExecutionWarning {
+import java.io.PrintWriter;
 
-    private final int code;
-    private final String message;
+import org.apache.asterix.api.http.server.AbstractQueryApiServlet;
+import org.apache.asterix.common.api.IResponseFieldPrinter;
 
-    public ExecutionWarning(int code, String message) {
-        this.code = code;
-        this.message = message;
+public class StatusPrinter implements IResponseFieldPrinter {
+
+    public static final String FIELD_NAME = "status";
+    private final AbstractQueryApiServlet.ResultStatus status;
+
+    public StatusPrinter(AbstractQueryApiServlet.ResultStatus status) {
+        this.status = status;
     }
 
-    public int getCode() {
-        return code;
+    @Override
+    public void print(PrintWriter pw) {
+        pw.append("\t\"").append(FIELD_NAME).append("\": 
\"").append(status.str()).append("\"");
     }
 
-    public String getMessage() {
-        return message;
+    @Override
+    public String getName() {
+        return FIELD_NAME;
     }
 }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/TypePrinter.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/TypePrinter.java
new file mode 100644
index 0000000..788fbcf
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/TypePrinter.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.asterix.app.result.fields;
+
+import static org.apache.hyracks.http.server.utils.HttpUtil.ContentType.CSV;
+
+import java.io.PrintWriter;
+
+import org.apache.asterix.api.http.server.ResultUtil;
+import org.apache.asterix.common.api.IResponseFieldPrinter;
+import org.apache.asterix.translator.SessionConfig;
+import org.apache.hyracks.http.server.utils.HttpUtil;
+
+public class TypePrinter implements IResponseFieldPrinter {
+
+    private static final String FIELD_NAME = "type";
+    private final SessionConfig sessionConfig;
+
+    public TypePrinter(SessionConfig sessionConfig) {
+        this.sessionConfig = sessionConfig;
+    }
+
+    @Override
+    public void print(PrintWriter pw) {
+        switch (sessionConfig.fmt()) {
+            case ADM:
+                ResultUtil.printField(pw, FIELD_NAME, 
HttpUtil.ContentType.APPLICATION_ADM, false);
+                break;
+            case CSV:
+                String contentType =
+                        CSV + "; header=" + 
(sessionConfig.is(SessionConfig.FORMAT_CSV_HEADER) ? "present" : "absent");
+                ResultUtil.printField(pw, FIELD_NAME, contentType, false);
+                break;
+            default:
+                break;
+        }
+    }
+
+    @Override
+    public String getName() {
+        return FIELD_NAME;
+    }
+}
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/WarningsPrinter.java
similarity index 67%
copy from 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
copy to 
asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/WarningsPrinter.java
index baaa5bd..2ebe5f4 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/WarningsPrinter.java
@@ -16,23 +16,22 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.asterix.api.http.server;
+package org.apache.asterix.app.result.fields;
 
-public class ExecutionWarning {
+import java.util.List;
 
-    private final int code;
-    private final String message;
+import org.apache.asterix.common.api.ICodedMessage;
 
-    public ExecutionWarning(int code, String message) {
-        this.code = code;
-        this.message = message;
-    }
+public class WarningsPrinter extends AbstractCodedMessagePrinter {
+
+    private static final String FIELD_NAME = "warnings";
 
-    public int getCode() {
-        return code;
+    public WarningsPrinter(List<ICodedMessage> warnings) {
+        super(warnings);
     }
 
-    public String getMessage() {
-        return message;
+    @Override
+    public String getName() {
+        return FIELD_NAME;
     }
 }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/DefaultStatementExecutorFactory.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/DefaultStatementExecutorFactory.java
index f6bd708..3e47362 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/DefaultStatementExecutorFactory.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/DefaultStatementExecutorFactory.java
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
+import org.apache.asterix.common.api.IResponsePrinter;
 import org.apache.asterix.common.context.IStorageComponentProvider;
 import org.apache.asterix.common.dataflow.ICcApplicationContext;
 import org.apache.asterix.compiler.provider.ILangCompilationProvider;
@@ -49,7 +50,8 @@ public class DefaultStatementExecutorFactory implements 
IStatementExecutorFactor
 
     @Override
     public IStatementExecutor create(ICcApplicationContext appCtx, 
List<Statement> statements, SessionOutput output,
-            ILangCompilationProvider compilationProvider, 
IStorageComponentProvider storageComponentProvider) {
-        return new QueryTranslator(appCtx, statements, output, 
compilationProvider, executorService);
+            ILangCompilationProvider compilationProvider, 
IStorageComponentProvider storageComponentProvider,
+            IResponsePrinter responsePrinter) {
+        return new QueryTranslator(appCtx, statements, output, 
compilationProvider, executorService, responsePrinter);
     }
 }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
index 8c0cc98..60e98f9 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
@@ -45,15 +45,20 @@ import 
org.apache.asterix.algebra.extension.ExtensionStatement;
 import org.apache.asterix.api.common.APIFramework;
 import org.apache.asterix.api.http.server.AbstractQueryApiServlet;
 import org.apache.asterix.api.http.server.ApiServlet;
-import org.apache.asterix.api.http.server.ResultUtil;
 import org.apache.asterix.app.active.ActiveEntityEventsListener;
 import org.apache.asterix.app.active.ActiveNotificationHandler;
 import org.apache.asterix.app.active.FeedEventsListener;
+import org.apache.asterix.app.result.ExecutionError;
 import org.apache.asterix.app.result.ResultHandle;
 import org.apache.asterix.app.result.ResultReader;
+import org.apache.asterix.app.result.fields.ErrorsPrinter;
+import org.apache.asterix.app.result.fields.ResultHandlePrinter;
+import org.apache.asterix.app.result.fields.ResultsPrinter;
+import org.apache.asterix.app.result.fields.StatusPrinter;
 import org.apache.asterix.common.api.IClientRequest;
 import org.apache.asterix.common.api.IMetadataLockManager;
 import org.apache.asterix.common.api.IRequestTracker;
+import org.apache.asterix.common.api.IResponsePrinter;
 import org.apache.asterix.common.cluster.IClusterStateManager;
 import org.apache.asterix.common.config.DatasetConfig.DatasetType;
 import org.apache.asterix.common.config.DatasetConfig.ExternalFilePendingOp;
@@ -224,9 +229,11 @@ public class QueryTranslator extends 
AbstractLangTranslator implements IStatemen
     protected final ExecutorService executorService;
     protected final EnumSet<JobFlag> jobFlags = EnumSet.noneOf(JobFlag.class);
     protected final IMetadataLockManager lockManager;
+    protected final IResponsePrinter responsePrinter;
 
     public QueryTranslator(ICcApplicationContext appCtx, List<Statement> 
statements, SessionOutput output,
-            ILangCompilationProvider compilationProvider, ExecutorService 
executorService) {
+            ILangCompilationProvider compilationProvider, ExecutorService 
executorService,
+            IResponsePrinter responsePrinter) {
         this.appCtx = appCtx;
         this.lockManager = appCtx.getMetadataLockManager();
         this.statements = statements;
@@ -238,6 +245,7 @@ public class QueryTranslator extends AbstractLangTranslator 
implements IStatemen
         rewriterFactory = compilationProvider.getRewriterFactory();
         activeDataverse = MetadataBuiltinEntities.DEFAULT_DATAVERSE;
         this.executorService = executorService;
+        this.responsePrinter = responsePrinter;
         if 
(appCtx.getServiceContext().getAppConfig().getBoolean(CCConfig.Option.ENFORCE_FRAME_WRITER_PROTOCOL))
 {
             this.jobFlags.add(JobFlag.ENFORCE_CONTRACT);
         }
@@ -1831,8 +1839,8 @@ public class QueryTranslator extends 
AbstractLangTranslator implements IStatemen
                     new CompiledLoadFromFileStatement(dataverseName, 
loadStmt.getDatasetName().getValue(),
                             loadStmt.getAdapter(), loadStmt.getProperties(), 
loadStmt.dataIsAlreadySorted());
             cls.setSourceLocation(stmt.getSourceLocation());
-            JobSpecification spec =
-                    apiFramework.compileQuery(hcc, metadataProvider, null, 0, 
null, sessionOutput, cls, null);
+            JobSpecification spec = apiFramework.compileQuery(hcc, 
metadataProvider, null, 0, null, sessionOutput, cls,
+                    null, responsePrinter);
             afterCompile();
             MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
             bActiveTxn = false;
@@ -1963,7 +1971,8 @@ public class QueryTranslator extends 
AbstractLangTranslator implements IStatemen
 
         // Query Compilation (happens under the same ongoing metadata 
transaction)
         return apiFramework.compileQuery(clusterInfoCollector, 
metadataProvider, (Query) rewrittenResult.first,
-                rewrittenResult.second, stmt == null ? null : 
stmt.getDatasetName(), sessionOutput, stmt, externalVars);
+                rewrittenResult.second, stmt == null ? null : 
stmt.getDatasetName(), sessionOutput, stmt, externalVars,
+                responsePrinter);
     }
 
     private JobSpecification rewriteCompileInsertUpsert(IClusterInfoCollector 
clusterInfoCollector,
@@ -2001,7 +2010,7 @@ public class QueryTranslator extends 
AbstractLangTranslator implements IStatemen
         // Insert/upsert statement compilation (happens under the same ongoing 
metadata
         // transaction)
         return apiFramework.compileQuery(clusterInfoCollector, 
metadataProvider, rewrittenInsertUpsert.getQuery(),
-                rewrittenResult.second, datasetName, sessionOutput, clfrqs, 
externalVars);
+                rewrittenResult.second, datasetName, sessionOutput, clfrqs, 
externalVars, responsePrinter);
     }
 
     protected void handleCreateFeedStatement(MetadataProvider 
metadataProvider, Statement stmt) throws Exception {
@@ -2506,16 +2515,17 @@ public class QueryTranslator extends 
AbstractLangTranslator implements IStatemen
                 createAndRunJob(hcc, jobFlags, null, compiler, locker, 
resultDelivery, id -> {
                     final ResultReader resultReader = new 
ResultReader(resultSet, id, resultSetId);
                     updateJobStats(id, stats, 
metadataProvider.getResultSetId());
-                    // stop buffering and allow for streaming result delivery
-                    sessionOutput.release();
-                    ResultUtil.printResults(appCtx, resultReader, 
sessionOutput, stats,
-                            metadataProvider.findOutputRecordType());
+                    responsePrinter.addResultPrinter(new 
ResultsPrinter(appCtx, resultReader,
+                            metadataProvider.findOutputRecordType(), stats, 
sessionOutput));
+                    responsePrinter.printResults();
                 }, requestParameters, cancellable, appCtx, metadataProvider);
                 break;
             case DEFERRED:
                 createAndRunJob(hcc, jobFlags, null, compiler, locker, 
resultDelivery, id -> {
                     updateJobStats(id, stats, 
metadataProvider.getResultSetId());
-                    ResultUtil.printResultHandle(sessionOutput, new 
ResultHandle(id, resultSetId));
+                    responsePrinter.addResultPrinter(
+                            new ResultHandlePrinter(sessionOutput, new 
ResultHandle(id, resultSetId)));
+                    responsePrinter.printResults();
                     if (outMetadata != null) {
                         outMetadata.getResultSets()
                                 .add(Triple.of(id, resultSetId, 
metadataProvider.findOutputRecordType()));
@@ -2543,8 +2553,9 @@ public class QueryTranslator extends 
AbstractLangTranslator implements IStatemen
         try {
             createAndRunJob(hcc, jobFlags, jobId, compiler, locker, 
resultDelivery, id -> {
                 final ResultHandle handle = new ResultHandle(id, resultSetId);
-                ResultUtil.printStatus(sessionOutput, 
AbstractQueryApiServlet.ResultStatus.RUNNING);
-                ResultUtil.printResultHandle(sessionOutput, handle);
+                responsePrinter.addResultPrinter(new 
StatusPrinter(AbstractQueryApiServlet.ResultStatus.RUNNING));
+                responsePrinter.addResultPrinter(new 
ResultHandlePrinter(sessionOutput, handle));
+                responsePrinter.printResults();
                 synchronized (printed) {
                     printed.setTrue();
                     printed.notify();
@@ -2553,8 +2564,13 @@ public class QueryTranslator extends 
AbstractLangTranslator implements IStatemen
         } catch (Exception e) {
             if (Objects.equals(JobId.INVALID, jobId.getValue())) {
                 // compilation failed
-                ResultUtil.printStatus(sessionOutput, 
AbstractQueryApiServlet.ResultStatus.FAILED);
-                ResultUtil.printError(sessionOutput.out(), e);
+                responsePrinter.addResultPrinter(new 
StatusPrinter(AbstractQueryApiServlet.ResultStatus.FAILED));
+                responsePrinter.addResultPrinter(new 
ErrorsPrinter(Collections.singletonList(ExecutionError.of(e))));
+                try {
+                    responsePrinter.printResults();
+                } catch (HyracksDataException ex) {
+                    LOGGER.error("failed to print result", ex);
+                }
             } else {
                 GlobalConfig.ASTERIX_LOGGER.log(Level.ERROR,
                         resultDelivery.name() + " job with id " + 
jobId.getValue() + " " + "failed", e);
@@ -2892,6 +2908,11 @@ public class QueryTranslator extends 
AbstractLangTranslator implements IStatemen
         return apiFramework.getExecutionPlans();
     }
 
+    @Override
+    public IResponsePrinter getResponsePrinter() {
+        return responsePrinter;
+    }
+
     public String getActiveDataverse(Identifier dataverse) {
         return getActiveDataverseName(dataverse != null ? dataverse.getValue() 
: null);
     }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/utils/FeedOperations.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/utils/FeedOperations.java
index debcd19..b74f4c6 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/utils/FeedOperations.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/utils/FeedOperations.java
@@ -29,6 +29,7 @@ import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TreeSet;
 
+import org.apache.asterix.app.result.ResponsePrinter;
 import org.apache.asterix.app.translator.DefaultStatementExecutorFactory;
 import org.apache.asterix.app.translator.QueryTranslator;
 import org.apache.asterix.common.cluster.IClusterStateManager;
@@ -438,7 +439,7 @@ public class FeedOperations {
         List<Statement> stmts = new ArrayList<>();
         DefaultStatementExecutorFactory qtFactory = new 
DefaultStatementExecutorFactory();
         IStatementExecutor translator = 
qtFactory.create(metadataProvider.getApplicationContext(), stmts, sessionOutput,
-                new SqlppCompilationProvider(), new 
StorageComponentProvider());
+                new SqlppCompilationProvider(), new 
StorageComponentProvider(), new ResponsePrinter(sessionOutput));
         return translator;
     }
 
diff --git 
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/result/ResultPrinterTest.java
 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/result/ResultPrinterTest.java
index 02bbcf5..fe64dd1 100644
--- 
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/result/ResultPrinterTest.java
+++ 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/result/ResultPrinterTest.java
@@ -23,9 +23,11 @@ import static 
org.apache.hyracks.util.StorageUtil.StorageUnit.KILOBYTE;
 import java.io.ByteArrayOutputStream;
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
+import java.util.Collections;
 
-import org.apache.asterix.api.http.server.AbstractQueryApiServlet;
 import org.apache.asterix.api.http.server.ResultUtil;
+import org.apache.asterix.app.result.fields.ErrorsPrinter;
+import org.apache.asterix.app.result.fields.MetricsPrinter;
 import org.apache.asterix.common.api.IApplicationContext;
 import org.apache.asterix.common.config.CompilerProperties;
 import org.apache.asterix.test.common.ResultExtractor;
@@ -65,7 +67,8 @@ public class ResultPrinterTest {
         try {
             rs.print(resultReader);
         } catch (RuntimeException e) {
-            ResultUtil.printError(out, e, true);
+            final ExecutionError error = ExecutionError.of(e);
+            new ErrorsPrinter(Collections.singletonList(error)).print(out);
             printMetrics(out, 1);
         }
         out.print("}");
@@ -98,7 +101,7 @@ public class ResultPrinterTest {
 
     private static void printMetrics(PrintWriter pw, long errorCount) {
         pw.print("\t\"");
-        pw.print(AbstractQueryApiServlet.ResultFields.METRICS.str());
+        pw.print(MetricsPrinter.FIELD_NAME);
         pw.print("\": {\n");
         ResultUtil.printField(pw, "errorCount", errorCount, false);
         pw.print("\t}\n");
diff --git 
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/active/ActiveStatsTest.java
 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/active/ActiveStatsTest.java
index dfb92b7..80dde8a 100644
--- 
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/active/ActiveStatsTest.java
+++ 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/active/ActiveStatsTest.java
@@ -36,6 +36,7 @@ import 
org.apache.asterix.app.active.ActiveEntityEventsListener;
 import org.apache.asterix.app.active.ActiveNotificationHandler;
 import org.apache.asterix.app.cc.CCExtensionManager;
 import org.apache.asterix.app.nc.NCAppRuntimeContext;
+import org.apache.asterix.app.result.ResponsePrinter;
 import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.external.feed.watch.WaitForStateSubscriber;
 import org.apache.asterix.external.operators.FeedIntakeOperatorNodePushable;
@@ -94,10 +95,12 @@ public class ActiveStatsTest {
 
         // Mock MetadataProvider
         CCExtensionManager extensionManager = (CCExtensionManager) 
appCtx.getExtensionManager();
+        SessionOutput sessionOutput = Mockito.mock(SessionOutput.class);
         IStatementExecutor statementExecutor = extensionManager
                 
.getStatementExecutorFactory(appCtx.getServiceContext().getControllerService().getExecutor())
-                .create(appCtx, Collections.emptyList(), 
Mockito.mock(SessionOutput.class),
-                        
extensionManager.getCompilationProvider(Language.SQLPP), 
appCtx.getStorageComponentProvider());
+                .create(appCtx, Collections.emptyList(), sessionOutput,
+                        
extensionManager.getCompilationProvider(Language.SQLPP), 
appCtx.getStorageComponentProvider(),
+                        new ResponsePrinter(sessionOutput));
         MetadataProvider mdProvider = new MetadataProvider(appCtx, null);
         // Add event listener
         ActiveEntityEventsListener eventsListener = new 
DummyFeedEventsListener(statementExecutor, appCtx, null,
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
 
b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/ICodedMessage.java
similarity index 68%
copy from 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
copy to 
asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/ICodedMessage.java
index baaa5bd..06202c4 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
+++ 
b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/ICodedMessage.java
@@ -16,23 +16,21 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.asterix.api.http.server;
+package org.apache.asterix.common.api;
 
-public class ExecutionWarning {
+public interface ICodedMessage {
 
-    private final int code;
-    private final String message;
+    /**
+     * Gets the code of the message
+     *
+     * @return the code
+     */
+    int getCode();
 
-    public ExecutionWarning(int code, String message) {
-        this.code = code;
-        this.message = message;
-    }
-
-    public int getCode() {
-        return code;
-    }
-
-    public String getMessage() {
-        return message;
-    }
+    /**
+     * Gets the message
+     *
+     * @return the message
+     */
+    String getMessage();
 }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
 
b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/IResponseFieldPrinter.java
similarity index 63%
rename from 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
rename to 
asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/IResponseFieldPrinter.java
index baaa5bd..d2006cb 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ExecutionWarning.java
+++ 
b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/IResponseFieldPrinter.java
@@ -16,23 +16,26 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.asterix.api.http.server;
+package org.apache.asterix.common.api;
 
-public class ExecutionWarning {
+import java.io.PrintWriter;
 
-    private final int code;
-    private final String message;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
 
-    public ExecutionWarning(int code, String message) {
-        this.code = code;
-        this.message = message;
-    }
+public interface IResponseFieldPrinter {
 
-    public int getCode() {
-        return code;
-    }
+    /**
+     * Prints this field using {@code pw}
+     *
+     * @param pw
+     * @throws HyracksDataException
+     */
+    void print(PrintWriter pw) throws HyracksDataException;
 
-    public String getMessage() {
-        return message;
-    }
+    /**
+     * Gets the name of this field
+     *
+     * @return the name of the field
+     */
+    String getName();
 }
diff --git 
a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/IResponsePrinter.java
 
b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/IResponsePrinter.java
new file mode 100644
index 0000000..a4dc7b5
--- /dev/null
+++ 
b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/IResponsePrinter.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.asterix.common.api;
+
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+
+public interface IResponsePrinter {
+
+    /**
+     * Performs any operations required before printing the response fields.
+     */
+    void begin();
+
+    /**
+     * Adds a response header printer.
+     *
+     * @param printer
+     */
+    void addHeaderPrinter(IResponseFieldPrinter printer);
+
+    /**
+     * Adds a response result printer.
+     *
+     * @param printer
+     */
+    void addResultPrinter(IResponseFieldPrinter printer);
+
+    /**
+     * Adds a response footer printer.
+     *
+     * @param printer
+     */
+    void addFooterPrinter(IResponseFieldPrinter printer);
+
+    /**
+     * Prints the added response headers.
+     *
+     * @throws HyracksDataException
+     */
+    void printHeaders() throws HyracksDataException;
+
+    /**
+     * Prints the added response results.
+     *
+     * @throws HyracksDataException
+     */
+    void printResults() throws HyracksDataException;
+
+    /**
+     * Prints the added response footers.
+     *
+     * @throws HyracksDataException
+     */
+    void printFooters() throws HyracksDataException;
+
+    /**
+     * Performs any operations required after printing the response fields.
+     */
+    void end();
+}

Reply via email to