asterixdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ti...@apache.org
Subject [4/5] asterixdb git commit: Replace Servlets with Netty Based HTTP Servers
Date Thu, 26 Jan 2017 15:29:31 GMT
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/60e7f12b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..b302bab
--- /dev/null
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
@@ -0,0 +1,557 @@
+/*
+ * 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.api.http.server;
+
+import static org.apache.asterix.api.http.servlet.ServletConstants.HYRACKS_CONNECTION_ATTR;
+import static org.apache.asterix.api.http.servlet.ServletConstants.HYRACKS_DATASET_ATTR;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.asterix.app.result.ResultReader;
+import org.apache.asterix.app.result.ResultUtil;
+import org.apache.asterix.app.translator.QueryTranslator;
+import org.apache.asterix.common.api.IClusterManagementWork;
+import org.apache.asterix.common.config.GlobalConfig;
+import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.common.utils.JSONUtil;
+import org.apache.asterix.compiler.provider.ILangCompilationProvider;
+import org.apache.asterix.lang.aql.parser.TokenMgrError;
+import org.apache.asterix.lang.common.base.IParser;
+import org.apache.asterix.lang.common.base.Statement;
+import org.apache.asterix.metadata.MetadataManager;
+import org.apache.asterix.runtime.util.ClusterStateManager;
+import org.apache.asterix.translator.IStatementExecutor;
+import org.apache.asterix.translator.IStatementExecutor.Stats;
+import org.apache.asterix.translator.IStatementExecutorFactory;
+import org.apache.asterix.translator.SessionConfig;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.prettyprint.AlgebricksAppendable;
+import org.apache.hyracks.api.client.IHyracksClientConnection;
+import org.apache.hyracks.api.dataset.IHyracksDataset;
+import org.apache.hyracks.client.dataset.HyracksDataset;
+import org.apache.hyracks.http.server.AbstractServlet;
+import org.apache.hyracks.http.server.IServlet;
+import org.apache.hyracks.http.server.IServletRequest;
+import org.apache.hyracks.http.server.IServletResponse;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpResponseStatus;
+
+public class QueryServiceServlet extends AbstractServlet {
+    private static final Logger LOGGER = Logger.getLogger(QueryServiceServlet.class.getName());
+    private final ILangCompilationProvider compilationProvider;
+    private final IStatementExecutorFactory statementExecutorFactory;
+
+    public QueryServiceServlet(ConcurrentMap<String, Object> ctx, String[] paths,
+            ILangCompilationProvider compilationProvider,
+            IStatementExecutorFactory statementExecutorFactory) {
+        super(ctx, paths);
+        this.compilationProvider = compilationProvider;
+        this.statementExecutorFactory = statementExecutorFactory;
+    }
+
+    @Override
+    public void handle(IServletRequest request, IServletResponse response) {
+        if (request.getHttpRequest().method() != HttpMethod.POST) {
+            response.setStatus(HttpResponseStatus.METHOD_NOT_ALLOWED);
+            return;
+        }
+        try {
+            handleRequest(getRequestParameters(request), response);
+        } catch (IOException e) {
+            // Servlet methods should not throw exceptions
+            // http://cwe.mitre.org/data/definitions/600.html
+            GlobalConfig.ASTERIX_LOGGER.log(Level.SEVERE, e.getMessage(), e);
+        }
+    }
+
+    public enum Parameter {
+        STATEMENT("statement"),
+        FORMAT("format"),
+        CLIENT_ID("client_context_id"),
+        PRETTY("pretty"),
+        MODE("mode");
+
+        private final String str;
+
+        Parameter(String str) {
+            this.str = str;
+        }
+
+        public String str() {
+            return str;
+        }
+    }
+
+    private enum Attribute {
+        HEADER("header"),
+        LOSSLESS("lossless");
+
+        private final String str;
+
+        Attribute(String str) {
+            this.str = str;
+        }
+
+        public String str() {
+            return str;
+        }
+    }
+
+    public enum ResultFields {
+        REQUEST_ID("requestID"),
+        CLIENT_ID("clientContextID"),
+        SIGNATURE("signature"),
+        TYPE("type"),
+        STATUS("status"),
+        RESULTS("results"),
+        HANDLE("handle"),
+        ERRORS("errors"),
+        METRICS("metrics");
+
+        private final String str;
+
+        ResultFields(String str) {
+            this.str = str;
+        }
+
+        public String str() {
+            return str;
+        }
+    }
+
+    public enum ResultStatus {
+        SUCCESS("success"),
+        TIMEOUT("timeout"),
+        ERRORS("errors"),
+        FATAL("fatal");
+
+        private final String str;
+
+        ResultStatus(String str) {
+            this.str = str;
+        }
+
+        public String str() {
+            return str;
+        }
+    }
+
+    private enum ErrorField {
+        CODE("code"),
+        MSG("msg"),
+        STACK("stack");
+
+        private final String str;
+
+        ErrorField(String str) {
+            this.str = str;
+        }
+
+        public String str() {
+            return str;
+        }
+    }
+
+    private enum Metrics {
+        ELAPSED_TIME("elapsedTime"),
+        EXECUTION_TIME("executionTime"),
+        RESULT_COUNT("resultCount"),
+        RESULT_SIZE("resultSize");
+
+        private final String str;
+
+        Metrics(String str) {
+            this.str = str;
+        }
+
+        public String str() {
+            return str;
+        }
+    }
+
+    public enum TimeUnit {
+        SEC("s", 9),
+        MILLI("ms", 6),
+        MICRO("┬Ás", 3),
+        NANO("ns", 0);
+
+        String unit;
+        int nanoDigits;
+
+        TimeUnit(String unit, int nanoDigits) {
+            this.unit = unit;
+            this.nanoDigits = nanoDigits;
+        }
+
+        public static String formatNanos(long nanoTime) {
+            final String strTime = String.valueOf(nanoTime);
+            final int len = strTime.length();
+            for (TimeUnit tu : TimeUnit.values()) {
+                if (len > tu.nanoDigits) {
+                    final String integer = strTime.substring(0, len - tu.nanoDigits);
+                    final String fractional = strTime.substring(len - tu.nanoDigits);
+                    return integer + (fractional.length() > 0 ? "." + fractional : "") + tu.unit;
+                }
+            }
+            return "illegal string value: " + strTime;
+        }
+    }
+
+    static class RequestParameters {
+        String statement;
+        String format;
+        boolean pretty;
+        String clientContextID;
+        String mode;
+
+        @Override
+        public String toString() {
+            return append(new StringBuilder()).toString();
+        }
+
+        public StringBuilder append(final StringBuilder sb) {
+            sb.append("{ ");
+            sb.append("\"statement\": \"");
+            JSONUtil.escape(sb, statement);
+            sb.append("\", ");
+            sb.append("\"format\": \"").append(format).append("\", ");
+            sb.append("\"pretty\": ").append(pretty).append(", ");
+            sb.append("\"mode\": ").append(mode).append(", ");
+            sb.append("\"clientContextID\": \"").append(clientContextID).append("\" ");
+            sb.append('}');
+            return sb;
+        }
+    }
+
+    private static String getParameterValue(String content, String attribute) {
+        if (content == null || attribute == null) {
+            return null;
+        }
+        int sc = content.indexOf(';');
+        if (sc < 0) {
+            return null;
+        }
+        int eq = content.indexOf('=', sc + 1);
+        if (eq < 0) {
+            return null;
+        }
+        if (content.substring(sc + 1, eq).trim().equalsIgnoreCase(attribute)) {
+            return content.substring(eq + 1).trim().toLowerCase();
+        }
+        return null;
+    }
+
+    private static String toLower(String s) {
+        return s != null ? s.toLowerCase() : s;
+    }
+
+    private static SessionConfig.OutputFormat getFormat(String format) {
+        if (format != null) {
+            if (format.startsWith(IServlet.ContentType.CSV)) {
+                return SessionConfig.OutputFormat.CSV;
+            }
+            if (format.equals(IServlet.ContentType.APPLICATION_ADM)) {
+                return SessionConfig.OutputFormat.ADM;
+            }
+            if (format.startsWith(IServlet.ContentType.APPLICATION_JSON)) {
+                return Boolean.parseBoolean(getParameterValue(format, Attribute.LOSSLESS.str()))
+                        ? SessionConfig.OutputFormat.LOSSLESS_JSON : SessionConfig.OutputFormat.CLEAN_JSON;
+            }
+        }
+        return SessionConfig.OutputFormat.CLEAN_JSON;
+    }
+
+    private static SessionConfig createSessionConfig(RequestParameters param, PrintWriter resultWriter) {
+        SessionConfig.ResultDecorator resultPrefix = new SessionConfig.ResultDecorator() {
+            int resultNo = -1;
+
+            @Override
+            public AlgebricksAppendable append(AlgebricksAppendable app) throws AlgebricksException {
+                app.append("\t\"");
+                app.append(ResultFields.RESULTS.str());
+                if (resultNo >= 0) {
+                    app.append('-').append(String.valueOf(resultNo));
+                }
+                ++resultNo;
+                app.append("\": ");
+                return app;
+            }
+        };
+
+        SessionConfig.ResultDecorator resultPostfix = (AlgebricksAppendable app) -> app.append("\t,\n");
+
+        SessionConfig.ResultDecorator handlePrefix = new SessionConfig.ResultDecorator() {
+            @Override
+            public AlgebricksAppendable append(AlgebricksAppendable app) throws AlgebricksException {
+                app.append("\t\"");
+                app.append(ResultFields.HANDLE.str());
+                app.append("\": ");
+                return app;
+            }
+        };
+
+        SessionConfig.ResultDecorator handlePostfix = (AlgebricksAppendable app) -> app.append(",\n");
+
+        SessionConfig.OutputFormat format = getFormat(param.format);
+        SessionConfig sessionConfig = new SessionConfig(resultWriter, format, resultPrefix, resultPostfix,
+                handlePrefix, handlePostfix);
+        sessionConfig.set(SessionConfig.FORMAT_WRAPPER_ARRAY, true);
+        sessionConfig.set(SessionConfig.FORMAT_INDENT_JSON, param.pretty);
+        sessionConfig.set(SessionConfig.FORMAT_QUOTE_RECORD,
+                format != SessionConfig.OutputFormat.CLEAN_JSON && format != SessionConfig.OutputFormat.LOSSLESS_JSON);
+        sessionConfig.set(SessionConfig.FORMAT_CSV_HEADER, format == SessionConfig.OutputFormat.CSV
+                && "present".equals(getParameterValue(param.format, Attribute.HEADER.str())));
+        return sessionConfig;
+    }
+
+    private static void printField(PrintWriter pw, String name, String value) {
+        printField(pw, name, value, true);
+    }
+
+    private static void printField(PrintWriter pw, String name, String value, boolean comma) {
+        pw.print("\t\"");
+        pw.print(name);
+        pw.print("\": \"");
+        pw.print(value);
+        pw.print('"');
+        if (comma) {
+            pw.print(',');
+        }
+        pw.print('\n');
+    }
+
+    private static UUID printRequestId(PrintWriter pw) {
+        UUID requestId = UUID.randomUUID();
+        printField(pw, ResultFields.REQUEST_ID.str(), requestId.toString());
+        return requestId;
+    }
+
+    private static void printClientContextID(PrintWriter pw, RequestParameters params) {
+        if (params.clientContextID != null && !params.clientContextID.isEmpty()) {
+            printField(pw, ResultFields.CLIENT_ID.str(), params.clientContextID);
+        }
+    }
+
+    private static void printSignature(PrintWriter pw) {
+        printField(pw, ResultFields.SIGNATURE.str(), "*");
+    }
+
+    private static void printType(PrintWriter pw, SessionConfig sessionConfig) {
+        switch (sessionConfig.fmt()) {
+            case ADM:
+                printField(pw, ResultFields.TYPE.str(), IServlet.ContentType.APPLICATION_ADM);
+                break;
+            case CSV:
+                String contentType = IServlet.ContentType.CSV + "; header="
+                        + (sessionConfig.is(SessionConfig.FORMAT_CSV_HEADER) ? "present" : "absent");
+                printField(pw, ResultFields.TYPE.str(), contentType);
+                break;
+            default:
+                break;
+        }
+    }
+
+    private static void printStatus(PrintWriter pw, ResultStatus rs) {
+        printField(pw, ResultFields.STATUS.str(), rs.str());
+    }
+
+    private static void printError(PrintWriter pw, Throwable e) throws JsonProcessingException {
+        Throwable rootCause = ResultUtil.getRootCause(e);
+        if (rootCause == null) {
+            rootCause = e;
+        }
+        final boolean addStack = false;
+        pw.print("\t\"");
+        pw.print(ResultFields.ERRORS.str());
+        pw.print("\": [{ \n");
+        printField(pw, ErrorField.CODE.str(), "1");
+        final String msg = rootCause.getMessage();
+        printField(pw, ErrorField.MSG.str(), JSONUtil.escape(msg != null ? msg : rootCause.getClass().getSimpleName()),
+                addStack);
+        pw.print("\t}],\n");
+    }
+
+    private static void printMetrics(PrintWriter pw, long elapsedTime, long executionTime, long resultCount,
+            long resultSize) {
+        pw.print("\t\"");
+        pw.print(ResultFields.METRICS.str());
+        pw.print("\": {\n");
+        pw.print("\t");
+        printField(pw, Metrics.ELAPSED_TIME.str(), TimeUnit.formatNanos(elapsedTime));
+        pw.print("\t");
+        printField(pw, Metrics.EXECUTION_TIME.str(), TimeUnit.formatNanos(executionTime));
+        pw.print("\t");
+        printField(pw, Metrics.RESULT_COUNT.str(), String.valueOf(resultCount));
+        pw.print("\t");
+        printField(pw, Metrics.RESULT_SIZE.str(), String.valueOf(resultSize), false);
+        pw.print("\t}\n");
+    }
+
+    private String getOptText(JsonNode node, String fieldName) {
+        final JsonNode value = node.get(fieldName);
+        return value != null ? value.asText() : null;
+    }
+
+    private boolean getOptBoolean(JsonNode node, String fieldName, boolean defaultValue) {
+        final JsonNode value = node.get(fieldName);
+        return value != null ? value.asBoolean() : defaultValue;
+    }
+
+    private RequestParameters getRequestParameters(IServletRequest request) throws IOException {
+        final String contentTypeParam = request.getHttpRequest().headers().get(HttpHeaderNames.CONTENT_TYPE);
+        int sep = contentTypeParam.indexOf(';');
+        final String contentType = sep < 0 ? contentTypeParam.trim() : contentTypeParam.substring(0, sep).trim();
+        RequestParameters param = new RequestParameters();
+        if (IServlet.ContentType.APPLICATION_JSON.equals(contentType)) {
+            try {
+                JsonNode jsonRequest = new ObjectMapper().readTree(getRequestBody(request));
+                param.statement = jsonRequest.get(Parameter.STATEMENT.str()).asText();
+                param.format = toLower(getOptText(jsonRequest, Parameter.FORMAT.str()));
+                param.pretty = getOptBoolean(jsonRequest, Parameter.PRETTY.str(), false);
+                param.mode = toLower(getOptText(jsonRequest, Parameter.MODE.str()));
+                param.clientContextID = getOptText(jsonRequest, Parameter.CLIENT_ID.str());
+            } catch (JsonParseException | JsonMappingException e) {
+                // if the JSON parsing fails, the statement is empty and we get an empty statement error
+                GlobalConfig.ASTERIX_LOGGER.log(Level.SEVERE, e.getMessage(), e);
+            }
+        } else {
+            param.statement = request.getParameter(Parameter.STATEMENT.str());
+            if (param.statement == null) {
+                param.statement = getRequestBody(request);
+            }
+            param.format = toLower(request.getParameter(Parameter.FORMAT.str()));
+            param.pretty = Boolean.parseBoolean(request.getParameter(Parameter.PRETTY.str()));
+            param.mode = toLower(request.getParameter(Parameter.MODE.str()));
+            param.clientContextID = request.getParameter(Parameter.CLIENT_ID.str());
+        }
+        return param;
+    }
+
+    private static String getRequestBody(IServletRequest request) throws IOException {
+        return request.getHttpRequest().content().toString(StandardCharsets.UTF_8);
+    }
+
+    private static QueryTranslator.ResultDelivery parseResultDelivery(String mode) {
+        if ("async".equals(mode)) {
+            return QueryTranslator.ResultDelivery.ASYNC;
+        } else if ("deferred".equals(mode)) {
+            return QueryTranslator.ResultDelivery.DEFERRED;
+        } else {
+            return QueryTranslator.ResultDelivery.IMMEDIATE;
+        }
+    }
+
+    private void handleRequest(RequestParameters param, IServletResponse response) throws IOException {
+        LOGGER.info(param.toString());
+        long elapsedStart = System.nanoTime();
+        final StringWriter stringWriter = new StringWriter();
+        final PrintWriter resultWriter = new PrintWriter(stringWriter);
+
+        QueryTranslator.ResultDelivery delivery = parseResultDelivery(param.mode);
+
+        SessionConfig sessionConfig = createSessionConfig(param, resultWriter);
+        IServletResponse.setContentType(response, IServlet.ContentType.APPLICATION_JSON, IServlet.Encoding.UTF8);
+
+        HttpResponseStatus status = HttpResponseStatus.OK;
+        Stats stats = new Stats();
+        long execStart = -1;
+        long execEnd = -1;
+
+        resultWriter.print("{\n");
+        printRequestId(resultWriter);
+        printClientContextID(resultWriter, param);
+        printSignature(resultWriter);
+        printType(resultWriter, sessionConfig);
+        try {
+            final IClusterManagementWork.ClusterState clusterState = ClusterStateManager.INSTANCE.getState();
+            if (clusterState != IClusterManagementWork.ClusterState.ACTIVE) {
+                // using a plain IllegalStateException here to get into the right catch clause for a 500
+                throw new IllegalStateException("Cannot execute request, cluster is " + clusterState);
+            }
+            if (param.statement == null || param.statement.isEmpty()) {
+                throw new AsterixException("Empty request, no statement provided");
+            }
+            IHyracksClientConnection hcc = (IHyracksClientConnection) ctx.get(HYRACKS_CONNECTION_ATTR);
+            IHyracksDataset hds = (IHyracksDataset) ctx.get(HYRACKS_DATASET_ATTR);
+            if (hds == null) {
+                synchronized (ctx) {
+                    hds = (IHyracksDataset) ctx.get(HYRACKS_DATASET_ATTR);
+                    if (hds == null) {
+                        hds = new HyracksDataset(hcc, ResultReader.FRAME_SIZE, ResultReader.NUM_READERS);
+                        ctx.put(HYRACKS_DATASET_ATTR, hds);
+                    }
+                }
+            }
+            IParser parser = compilationProvider.getParserFactory().createParser(param.statement);
+            List<Statement> statements = parser.parse();
+            MetadataManager.INSTANCE.init();
+            IStatementExecutor translator = statementExecutorFactory.create(statements, sessionConfig,
+                    compilationProvider);
+            execStart = System.nanoTime();
+            translator.compileAndExecute(hcc, hds, delivery, stats);
+            execEnd = System.nanoTime();
+            printStatus(resultWriter, ResultStatus.SUCCESS);
+        } catch (AsterixException | TokenMgrError | org.apache.asterix.aqlplus.parser.TokenMgrError pe) {
+            GlobalConfig.ASTERIX_LOGGER.log(Level.SEVERE, pe.getMessage(), pe);
+            printError(resultWriter, pe);
+            printStatus(resultWriter, ResultStatus.FATAL);
+            status = HttpResponseStatus.BAD_REQUEST;
+        } catch (Exception e) {
+            GlobalConfig.ASTERIX_LOGGER.log(Level.SEVERE, e.getMessage(), e);
+            printError(resultWriter, e);
+            printStatus(resultWriter, ResultStatus.FATAL);
+            status = HttpResponseStatus.INTERNAL_SERVER_ERROR;
+        } finally {
+            if (execStart == -1) {
+                execEnd = -1;
+            } else if (execEnd == -1) {
+                execEnd = System.nanoTime();
+            }
+        }
+        printMetrics(resultWriter, System.nanoTime() - elapsedStart, execEnd - execStart, stats.getCount(),
+                stats.getSize());
+        resultWriter.print("}\n");
+        resultWriter.flush();
+        String result = stringWriter.toString();
+
+        GlobalConfig.ASTERIX_LOGGER.log(Level.FINE, result);
+
+        response.setStatus(status);
+        response.writer().print(result);
+        if (response.writer().checkError()) {
+            LOGGER.warning("Error flushing output writer");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/60e7f12b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryStatusApiServlet.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..197a39c
--- /dev/null
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryStatusApiServlet.java
@@ -0,0 +1,118 @@
+/*
+ * 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.api.http.server;
+
+import static org.apache.asterix.api.http.servlet.ServletConstants.HYRACKS_CONNECTION_ATTR;
+import static org.apache.asterix.api.http.servlet.ServletConstants.HYRACKS_DATASET_ATTR;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.concurrent.ConcurrentMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.asterix.app.result.ResultReader;
+import org.apache.hyracks.api.client.IHyracksClientConnection;
+import org.apache.hyracks.api.dataset.IHyracksDataset;
+import org.apache.hyracks.api.dataset.ResultSetId;
+import org.apache.hyracks.api.job.JobId;
+import org.apache.hyracks.client.dataset.HyracksDataset;
+import org.apache.hyracks.http.server.AbstractServlet;
+import org.apache.hyracks.http.server.IServlet;
+import org.apache.hyracks.http.server.IServletRequest;
+import org.apache.hyracks.http.server.IServletResponse;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpResponseStatus;
+
+public class QueryStatusApiServlet extends AbstractServlet {
+    private static final Logger LOGGER = Logger.getLogger(QueryStatusApiServlet.class.getName());
+
+    public QueryStatusApiServlet(ConcurrentMap<String, Object> ctx, String[] paths) {
+        super(ctx, paths);
+    }
+
+    @Override
+    public void handle(IServletRequest request, IServletResponse response) {
+        if (request.getHttpRequest().method() != HttpMethod.GET) {
+            response.setStatus(HttpResponseStatus.METHOD_NOT_ALLOWED);
+            return;
+        }
+        response.setStatus(HttpResponseStatus.OK);
+        try {
+            IServletResponse.setContentType(response, IServlet.ContentType.TEXT_HTML, IServlet.Encoding.UTF8);
+        } catch (IOException e) {
+            LOGGER.log(Level.WARNING, "Failure setting content type", e);
+            response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
+            return;
+        }
+        String strHandle = request.getParameter("handle");
+        PrintWriter out = response.writer();
+        try {
+            IHyracksDataset hds = (IHyracksDataset) ctx.get(HYRACKS_DATASET_ATTR);
+            if (hds == null) {
+                synchronized (ctx) {
+                    hds = (IHyracksDataset) ctx.get(HYRACKS_DATASET_ATTR);
+                    if (hds == null) {
+                        hds = new HyracksDataset((IHyracksClientConnection) ctx.get(HYRACKS_CONNECTION_ATTR),
+                                ResultReader.FRAME_SIZE, ResultReader.NUM_READERS);
+                        ctx.put(HYRACKS_DATASET_ATTR, hds);
+                    }
+                }
+            }
+            ObjectMapper om = new ObjectMapper();
+            JsonNode handleObj = om.readTree(strHandle);
+            JsonNode handle = handleObj.get("handle");
+            JobId jobId = new JobId(handle.get(0).asLong());
+            ResultSetId rsId = new ResultSetId(handle.get(1).asLong());
+
+            /* TODO(madhusudancs): We need to find a way to LOSSLESS_JSON serialize default format obtained from
+             * metadataProvider in the AQLTranslator and store it as part of the result handle.
+             */
+            ResultReader resultReader = new ResultReader(hds);
+            resultReader.open(jobId, rsId);
+
+            ObjectNode jsonResponse = om.createObjectNode();
+            String status;
+            switch (resultReader.getStatus()) {
+                case RUNNING:
+                    status = "RUNNING";
+                    break;
+                case SUCCESS:
+                    status = "SUCCESS";
+                    break;
+                default:
+                    status = "ERROR";
+                    break;
+            }
+            jsonResponse.put("status", status);
+            out.write(jsonResponse.toString());
+
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, "Failure handling a request", e);
+            out.println(e.getMessage());
+            e.printStackTrace(out);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/60e7f12b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryWebInterfaceServlet.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryWebInterfaceServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryWebInterfaceServlet.java
new file mode 100644
index 0000000..d924cf1
--- /dev/null
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryWebInterfaceServlet.java
@@ -0,0 +1,133 @@
+/*
+ * 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.api.http.server;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.concurrent.ConcurrentMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.asterix.common.config.ExternalProperties;
+import org.apache.asterix.runtime.util.AppContextInfo;
+import org.apache.commons.io.IOUtils;
+import org.apache.hyracks.http.server.AbstractServlet;
+import org.apache.hyracks.http.server.IServlet;
+import org.apache.hyracks.http.server.IServletRequest;
+import org.apache.hyracks.http.server.IServletResponse;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpResponseStatus;
+
+public class QueryWebInterfaceServlet extends AbstractServlet {
+    private static final Logger LOGGER = Logger.getLogger(QueryWebInterfaceServlet.class.getName());
+
+    public QueryWebInterfaceServlet(ConcurrentMap<String, Object> ctx, String[] paths) {
+        super(ctx, paths);
+    }
+
+    @Override
+    public void handle(IServletRequest request, IServletResponse response) {
+        try {
+            if (request.getHttpRequest().method() == HttpMethod.GET) {
+                doGet(request, response);
+            } else if (request.getHttpRequest().method() == HttpMethod.POST) {
+                doPost(response);
+            } else {
+                response.setStatus(HttpResponseStatus.METHOD_NOT_ALLOWED);
+            }
+        } catch (IOException e) {
+            LOGGER.log(Level.WARNING, "Failure setting content type", e);
+            response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
+            return;
+        }
+    }
+
+    private void doGet(IServletRequest request, IServletResponse response) throws IOException {
+        String resourcePath = null;
+        String requestURI = request.getHttpRequest().uri();
+        response.setStatus(HttpResponseStatus.OK);
+
+        if ("/".equals(requestURI)) {
+            IServletResponse.setContentType(response, IServlet.ContentType.TEXT_HTML);
+            resourcePath = "/queryui/queryui.html";
+        } else {
+            resourcePath = requestURI;
+        }
+
+        try (InputStream is = QueryWebInterfaceServlet.class.getResourceAsStream(resourcePath)) {
+            if (is == null) {
+                response.setStatus(HttpResponseStatus.NOT_FOUND);
+                return;
+            }
+            int i = resourcePath.lastIndexOf('.');
+            if (i >= 0) {
+                String extension = resourcePath.substring(i);
+                String mime = IServlet.ContentType.mime(extension);
+                if (mime != null) {
+                    OutputStream out = response.outputStream();
+                    IServletResponse.setContentType(response, mime);
+                    try {
+                        IOUtils.copy(is, out);
+                    } catch (Exception e) {
+                        LOGGER.log(Level.WARNING, "Failure copying response", e);
+                    } finally {
+                        if (out != null) {
+                            IOUtils.closeQuietly(out);
+                        }
+                        IOUtils.closeQuietly(is);
+                    }
+                    return;
+                }
+            }
+            response.setStatus(HttpResponseStatus.BAD_REQUEST);
+        }
+    }
+
+    private void doPost(IServletResponse response) throws IOException {
+        IServletResponse.setContentType(response, IServlet.ContentType.APPLICATION_JSON, IServlet.Encoding.UTF8);
+        ExternalProperties externalProperties = AppContextInfo.INSTANCE.getExternalProperties();
+        response.setStatus(HttpResponseStatus.OK);
+        ObjectMapper om = new ObjectMapper();
+        ObjectNode obj = om.createObjectNode();
+        try {
+            PrintWriter out = response.writer();
+            obj.put("api_port", String.valueOf(externalProperties.getAPIServerPort()));
+            out.println(obj.toString());
+            return;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, "Failure writing response", e);
+        }
+        try {
+            response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, "Failure setting response status", e);
+        }
+    }
+
+    public static String extension(String path) {
+        int i = path.lastIndexOf('.');
+        return i < 1 ? "" : path.substring(i);
+    }
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/60e7f12b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/RestApiServlet.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/RestApiServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/RestApiServlet.java
new file mode 100644
index 0000000..c2d1d33
--- /dev/null
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/RestApiServlet.java
@@ -0,0 +1,248 @@
+/*
+ * 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.api.http.server;
+
+import static org.apache.asterix.api.http.servlet.ServletConstants.HYRACKS_CONNECTION_ATTR;
+import static org.apache.asterix.api.http.servlet.ServletConstants.HYRACKS_DATASET_ATTR;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.concurrent.ConcurrentMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.asterix.app.result.ResultReader;
+import org.apache.asterix.app.result.ResultUtil;
+import org.apache.asterix.app.translator.QueryTranslator;
+import org.apache.asterix.common.config.GlobalConfig;
+import org.apache.asterix.common.exceptions.AsterixException;
+import org.apache.asterix.compiler.provider.ILangCompilationProvider;
+import org.apache.asterix.lang.aql.parser.TokenMgrError;
+import org.apache.asterix.lang.common.base.IParser;
+import org.apache.asterix.lang.common.base.IParserFactory;
+import org.apache.asterix.lang.common.base.Statement;
+import org.apache.asterix.metadata.MetadataManager;
+import org.apache.asterix.translator.IStatementExecutor;
+import org.apache.asterix.translator.IStatementExecutorFactory;
+import org.apache.asterix.translator.SessionConfig;
+import org.apache.asterix.translator.SessionConfig.OutputFormat;
+import org.apache.hyracks.algebricks.core.algebra.prettyprint.AlgebricksAppendable;
+import org.apache.hyracks.api.client.IHyracksClientConnection;
+import org.apache.hyracks.api.dataset.IHyracksDataset;
+import org.apache.hyracks.client.dataset.HyracksDataset;
+import org.apache.hyracks.http.server.AbstractServlet;
+import org.apache.hyracks.http.server.IServlet;
+import org.apache.hyracks.http.server.IServletRequest;
+import org.apache.hyracks.http.server.IServletResponse;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpResponseStatus;
+
+public abstract class RestApiServlet extends AbstractServlet {
+    private static final Logger LOGGER = Logger.getLogger(RestApiServlet.class.getName());
+    private final ILangCompilationProvider compilationProvider;
+    private final IParserFactory parserFactory;
+    private final IStatementExecutorFactory statementExecutorFactory;
+
+    public RestApiServlet(ConcurrentMap<String, Object> ctx, String[] paths,
+            ILangCompilationProvider compilationProvider, IStatementExecutorFactory statementExecutorFactory) {
+        super(ctx, paths);
+        this.compilationProvider = compilationProvider;
+        this.parserFactory = compilationProvider.getParserFactory();
+        this.statementExecutorFactory = statementExecutorFactory;
+    }
+
+    /**
+     * Initialize the Content-Type of the response, and construct a
+     * SessionConfig with the appropriate output writer and output-format
+     * based on the Accept: header and other servlet parameters.
+     */
+    static SessionConfig initResponse(IServletRequest request, IServletResponse response) throws IOException {
+        IServletResponse.setContentType(response, IServlet.ContentType.TEXT_PLAIN, IServlet.Encoding.UTF8);
+
+        // CLEAN_JSON output is the default; most generally useful for a
+        // programmatic HTTP API
+        OutputFormat format = OutputFormat.CLEAN_JSON;
+        // First check the "output" servlet parameter.
+        String output = request.getParameter("output");
+        String accept = request.getHeader("Accept");
+        if (accept == null) {
+            accept = "";
+        }
+        if (output != null) {
+            if ("CSV".equals(output)) {
+                format = OutputFormat.CSV;
+            } else if ("ADM".equals(output)) {
+                format = OutputFormat.ADM;
+            }
+        } else {
+            // Second check the Accept: HTTP header.
+            if (accept.contains("application/x-adm")) {
+                format = OutputFormat.ADM;
+            } else if (accept.contains("text/csv")) {
+                format = OutputFormat.CSV;
+            }
+        }
+
+        // If it's JSON, check for the "lossless" flag
+
+        if (format == OutputFormat.CLEAN_JSON
+                && ("true".equals(request.getParameter("lossless")) || accept.contains("lossless=true"))) {
+            format = OutputFormat.LOSSLESS_JSON;
+        }
+
+        SessionConfig.ResultDecorator handlePrefix =
+                (AlgebricksAppendable app) -> app.append("{ \"").append("handle").append("\": ");
+        SessionConfig.ResultDecorator handlePostfix = (AlgebricksAppendable app) -> app.append(" }");
+
+        SessionConfig sessionConfig =
+                new SessionConfig(response.writer(), format, null, null, handlePrefix, handlePostfix);
+
+        // If it's JSON or ADM, check for the "wrapper-array" flag. Default is
+        // "true" for JSON and "false" for ADM. (Not applicable for CSV.)
+        boolean wrapperArray;
+        switch (format) {
+            case CLEAN_JSON:
+            case LOSSLESS_JSON:
+                wrapperArray = true;
+                break;
+            default:
+                wrapperArray = false;
+                break;
+        }
+        String wrapperParam = request.getParameter("wrapper-array");
+        if (wrapperParam != null) {
+            wrapperArray = Boolean.valueOf(wrapperParam);
+        } else if (accept.contains("wrap-array=true")) {
+            wrapperArray = true;
+        } else if (accept.contains("wrap-array=false")) {
+            wrapperArray = false;
+        }
+        sessionConfig.set(SessionConfig.FORMAT_WRAPPER_ARRAY, wrapperArray);
+        // Now that format is set, output the content-type
+        switch (format) {
+            case ADM:
+                IServletResponse.setContentType(response, "application/x-adm");
+                break;
+            case CLEAN_JSON:
+                // No need to reflect "clean-ness" in output type; fall through
+            case LOSSLESS_JSON:
+                IServletResponse.setContentType(response, "application/json");
+                break;
+            case CSV:
+                // Check for header parameter or in Accept:.
+                if ("present".equals(request.getParameter("header")) || accept.contains("header=present")) {
+                    IServletResponse.setContentType(response, "text/csv; header=present");
+                    sessionConfig.set(SessionConfig.FORMAT_CSV_HEADER, true);
+                } else {
+                    IServletResponse.setContentType(response, "text/csv; header=absent");
+                }
+                break;
+        }
+        return sessionConfig;
+    }
+
+    @Override
+    public void handle(IServletRequest request, IServletResponse response) {
+        try {
+            String query = query(request);
+            // enable cross-origin resource sharing
+            response.setHeader("Access-Control-Allow-Origin", "*");
+            response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
+
+            SessionConfig sessionConfig = initResponse(request, response);
+            QueryTranslator.ResultDelivery resultDelivery = whichResultDelivery(request);
+            try {
+                response.setStatus(HttpResponseStatus.OK);
+                IHyracksClientConnection hcc = (IHyracksClientConnection) ctx.get(HYRACKS_CONNECTION_ATTR);
+                IHyracksDataset hds = (IHyracksDataset) ctx.get(HYRACKS_DATASET_ATTR);
+                if (hds == null) {
+                    synchronized (ctx) {
+                        hds = (IHyracksDataset) ctx.get(HYRACKS_DATASET_ATTR);
+                        if (hds == null) {
+                            hds = new HyracksDataset(hcc, ResultReader.FRAME_SIZE, ResultReader.NUM_READERS);
+                            ctx.put(HYRACKS_DATASET_ATTR, hds);
+                        }
+                    }
+                }
+                IParser parser = parserFactory.createParser(query);
+                List<Statement> aqlStatements = parser.parse();
+                validate(aqlStatements);
+                MetadataManager.INSTANCE.init();
+                IStatementExecutor translator =
+                        statementExecutorFactory.create(aqlStatements, sessionConfig, compilationProvider);
+                translator.compileAndExecute(hcc, hds, resultDelivery);
+            } catch (AsterixException | TokenMgrError | org.apache.asterix.aqlplus.parser.TokenMgrError pe) {
+                GlobalConfig.ASTERIX_LOGGER.log(Level.SEVERE, pe.getMessage(), pe);
+                String errorMessage = ResultUtil.buildParseExceptionMessage(pe, query);
+                ObjectNode errorResp =
+                        ResultUtil.getErrorResponse(2, errorMessage, "", ResultUtil.extractFullStackTrace(pe));
+                sessionConfig.out().write(new ObjectMapper().writeValueAsString(errorResp));
+                response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
+            } catch (Exception e) {
+                GlobalConfig.ASTERIX_LOGGER.log(Level.SEVERE, e.getMessage(), e);
+                ResultUtil.apiErrorHandler(sessionConfig.out(), e);
+                response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
+            }
+        } catch (Exception e) {
+            LOGGER.log(Level.WARNING, "Failure handling request", e);
+            response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
+            return;
+        }
+    }
+
+    //TODO: Both Get and Post of this API must use the same parameter names
+    private String query(IServletRequest request) {
+        if (request.getHttpRequest().method() == HttpMethod.POST) {
+            return request.getHttpRequest().content().toString(StandardCharsets.UTF_8);
+        } else {
+            return getQueryParameter(request);
+        }
+    }
+
+    private void validate(List<Statement> aqlStatements) throws AsterixException {
+        for (Statement st : aqlStatements) {
+            if ((st.getCategory() & getAllowedCategories()) == 0) {
+                throw new AsterixException(String.format(getErrorMessage(), st.getKind()));
+            }
+        }
+    }
+
+    protected QueryTranslator.ResultDelivery whichResultDelivery(IServletRequest request) {
+        String mode = request.getParameter("mode");
+        if (mode != null) {
+            if ("asynchronous".equals(mode) || "async".equals(mode)) {
+                return QueryTranslator.ResultDelivery.ASYNC;
+            } else if ("asynchronous-deferred".equals(mode) || "deferred".equals(mode)) {
+                return QueryTranslator.ResultDelivery.DEFERRED;
+            }
+        }
+        return QueryTranslator.ResultDelivery.IMMEDIATE;
+    }
+
+    protected abstract String getQueryParameter(IServletRequest request);
+
+    protected abstract byte getAllowedCategories();
+
+    protected abstract String getErrorMessage();
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/60e7f12b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ShutdownApiServlet.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ShutdownApiServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ShutdownApiServlet.java
new file mode 100644
index 0000000..25be651
--- /dev/null
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ShutdownApiServlet.java
@@ -0,0 +1,107 @@
+/*
+ * 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.api.http.server;
+
+import static org.apache.asterix.api.http.servlet.ServletConstants.HYRACKS_CONNECTION_ATTR;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Date;
+import java.util.concurrent.ConcurrentMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.asterix.common.config.GlobalConfig;
+import org.apache.asterix.runtime.util.ClusterStateManager;
+import org.apache.hyracks.api.client.IHyracksClientConnection;
+import org.apache.hyracks.http.server.AbstractServlet;
+import org.apache.hyracks.http.server.IServlet;
+import org.apache.hyracks.http.server.IServletRequest;
+import org.apache.hyracks.http.server.IServletResponse;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpResponseStatus;
+
+public class ShutdownApiServlet extends AbstractServlet {
+    private static final Logger LOGGER = Logger.getLogger(ShutdownApiServlet.class.getName());
+    public static final String NODE_ID_KEY = "node_id";
+    public static final String NCSERVICE_PID = "ncservice_pid";
+    public static final String INI = "ini";
+    public static final String PID = "pid";
+
+    public ShutdownApiServlet(ConcurrentMap<String, Object> ctx, String[] paths) {
+        super(ctx, paths);
+    }
+
+    @Override
+    public void handle(IServletRequest request, IServletResponse response) {
+        if (request.getHttpRequest().method() != HttpMethod.POST) {
+            response.setStatus(HttpResponseStatus.METHOD_NOT_ALLOWED);
+            return;
+        }
+        IHyracksClientConnection hcc = (IHyracksClientConnection) ctx.get(HYRACKS_CONNECTION_ATTR);
+        boolean terminateNCServices = "true".equalsIgnoreCase(request.getParameter("all"));
+        Thread t = new Thread(() -> {
+            try {
+                hcc.stopCluster(terminateNCServices);
+            } catch (Exception e) {
+                GlobalConfig.ASTERIX_LOGGER.log(Level.SEVERE, "Exception stopping cluster", e);
+            }
+        }, "Shutdown Servlet Worker");
+
+        try {
+            IServletResponse.setContentType(response, IServlet.ContentType.APPLICATION_JSON,
+                    IServlet.Encoding.UTF8);
+        } catch (IOException e) {
+            LOGGER.log(Level.WARNING, "Failure handling request", e);
+            response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
+            return;
+        }
+        response.setStatus(HttpResponseStatus.ACCEPTED);
+        ObjectMapper om = new ObjectMapper();
+        ObjectNode jsonObject = om.createObjectNode();
+        try {
+            jsonObject.put("status", "SHUTTING_DOWN");
+            jsonObject.putPOJO("date", new Date());
+            ObjectNode clusterState = ClusterStateManager.INSTANCE.getClusterStateDescription();
+            ArrayNode ncs = (ArrayNode) clusterState.get("ncs");
+            for (int i = 0; i < ncs.size(); i++) {
+                ObjectNode nc = (ObjectNode) ncs.get(i);
+                String node = nc.get(NODE_ID_KEY).asText();
+                ObjectNode details = (ObjectNode) om.readTree(hcc.getNodeDetailsJSON(node, false, true));
+                nc.set(PID, details.get(PID));
+                if (details.has(INI) && details.get(INI).has(NCSERVICE_PID)) {
+                    nc.put(NCSERVICE_PID, details.get(INI).get(NCSERVICE_PID).asInt());
+                }
+            }
+            jsonObject.set("cluster", clusterState);
+            final PrintWriter writer = response.writer();
+            writer.print(om.writeValueAsString(jsonObject));
+            writer.close();
+        } catch (Exception e) {
+            GlobalConfig.ASTERIX_LOGGER.log(Level.INFO, "Exception writing response", e);
+        }
+        t.start();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/60e7f12b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/UpdateApiServlet.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/UpdateApiServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/UpdateApiServlet.java
new file mode 100644
index 0000000..1c173d2
--- /dev/null
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/UpdateApiServlet.java
@@ -0,0 +1,50 @@
+/*
+ * 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.api.http.server;
+
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.asterix.compiler.provider.ILangCompilationProvider;
+import org.apache.asterix.lang.common.base.Statement;
+import org.apache.asterix.translator.IStatementExecutorFactory;
+import org.apache.hyracks.http.server.IServletRequest;
+
+public class UpdateApiServlet extends RestApiServlet {
+    private static final byte ALLOWED_CATEGORIES = Statement.Category.QUERY | Statement.Category.UPDATE;
+
+    public UpdateApiServlet(ConcurrentMap<String, Object> ctx, String[] paths,
+            ILangCompilationProvider compilationProvider, IStatementExecutorFactory statementExecutorFactory) {
+        super(ctx, paths, compilationProvider, statementExecutorFactory);
+    }
+
+    @Override
+    protected String getQueryParameter(IServletRequest request) {
+        return request.getParameter("statements");
+    }
+
+    @Override
+    protected byte getAllowedCategories() {
+        return ALLOWED_CATEGORIES;
+    }
+
+    @Override
+    protected String getErrorMessage() {
+        return "Invalid statement: Non-Update statement %s to the Update API.";
+    }
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/60e7f12b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/VersionApiServlet.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/VersionApiServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/VersionApiServlet.java
new file mode 100644
index 0000000..e5ceccc
--- /dev/null
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/VersionApiServlet.java
@@ -0,0 +1,75 @@
+/*
+ * 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.api.http.server;
+
+import static org.apache.asterix.api.http.servlet.ServletConstants.ASTERIX_BUILD_PROP_ATTR;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.asterix.runtime.util.AppContextInfo;
+import org.apache.hyracks.http.server.AbstractServlet;
+import org.apache.hyracks.http.server.IServlet;
+import org.apache.hyracks.http.server.IServletRequest;
+import org.apache.hyracks.http.server.IServletResponse;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpResponseStatus;
+
+public class VersionApiServlet extends AbstractServlet {
+    private static final Logger LOGGER = Logger.getLogger(VersionApiServlet.class.getName());
+
+    public VersionApiServlet(ConcurrentMap<String, Object> ctx, String[] paths) {
+        super(ctx, paths);
+    }
+
+    @Override
+    public void handle(IServletRequest request, IServletResponse response) {
+        if (request.getHttpRequest().method() != HttpMethod.GET) {
+            response.setStatus(HttpResponseStatus.METHOD_NOT_ALLOWED);
+            return;
+        }
+        response.setStatus(HttpResponseStatus.OK);
+        AppContextInfo props = (AppContextInfo) ctx.get(ASTERIX_BUILD_PROP_ATTR);
+        Map<String, String> buildProperties = props.getBuildProperties().getAllProps();
+        ObjectMapper om = new ObjectMapper();
+        ObjectNode responseObject = om.createObjectNode();
+        for (Map.Entry<String, String> e : buildProperties.entrySet()) {
+            responseObject.put(e.getKey(), e.getValue());
+        }
+        try {
+            IServletResponse.setContentType(response, IServlet.ContentType.TEXT_PLAIN, IServlet.Encoding.UTF8);
+        } catch (IOException e) {
+            LOGGER.log(Level.WARNING, "Failure handling request", e);
+            response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
+            return;
+        }
+        PrintWriter responseWriter = response.writer();
+        responseWriter.write(responseObject.toString());
+        responseWriter.flush();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/60e7f12b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/APIServlet.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/APIServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/APIServlet.java
deleted file mode 100644
index b693407..0000000
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/APIServlet.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * 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.api.http.servlet;
-
-import static org.apache.asterix.api.http.servlet.ServletConstants.HYRACKS_CONNECTION_ATTR;
-import static org.apache.asterix.api.http.servlet.ServletConstants.HYRACKS_DATASET_ATTR;
-
-import java.awt.image.BufferedImage;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.imageio.ImageIO;
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.asterix.app.result.ResultReader;
-import org.apache.asterix.app.result.ResultUtil;
-import org.apache.asterix.common.config.GlobalConfig;
-import org.apache.asterix.common.exceptions.AsterixException;
-import org.apache.asterix.compiler.provider.ILangCompilationProvider;
-import org.apache.asterix.lang.aql.parser.TokenMgrError;
-import org.apache.asterix.lang.common.base.IParser;
-import org.apache.asterix.lang.common.base.IParserFactory;
-import org.apache.asterix.lang.common.base.Statement;
-import org.apache.asterix.metadata.MetadataManager;
-import org.apache.asterix.translator.IStatementExecutor;
-import org.apache.asterix.translator.IStatementExecutorFactory;
-import org.apache.asterix.translator.SessionConfig;
-import org.apache.asterix.translator.SessionConfig.OutputFormat;
-import org.apache.hyracks.api.client.IHyracksClientConnection;
-import org.apache.hyracks.api.dataset.IHyracksDataset;
-import org.apache.hyracks.client.dataset.HyracksDataset;
-
-public class APIServlet extends HttpServlet {
-    private static final long serialVersionUID = 1L;
-    private static final Logger LOGGER = Logger.getLogger(APIServlet.class.getName());
-    public static final String HTML_STATEMENT_SEPARATOR = "<!-- BEGIN -->";
-
-    private final ILangCompilationProvider aqlCompilationProvider;
-    private final ILangCompilationProvider sqlppCompilationProvider;
-    private final transient IStatementExecutorFactory statementExectorFactory;
-
-    public APIServlet(ILangCompilationProvider aqlCompilationProvider,
-            ILangCompilationProvider sqlppCompilationProvider, IStatementExecutorFactory statementExecutorFactory) {
-        this.aqlCompilationProvider = aqlCompilationProvider;
-        this.sqlppCompilationProvider = sqlppCompilationProvider;
-        this.statementExectorFactory = statementExecutorFactory;
-    }
-
-    @Override
-    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
-        // Query language
-        ILangCompilationProvider compilationProvider = "AQL".equals(request.getParameter("query-language"))
-                ? aqlCompilationProvider : sqlppCompilationProvider;
-        IParserFactory parserFactory = compilationProvider.getParserFactory();
-
-        // Output format.
-        OutputFormat format;
-        boolean csv_and_header = false;
-        String output = request.getParameter("output-format");
-        try {
-            format = OutputFormat.valueOf(output);
-        } catch (IllegalArgumentException e) {
-            LOGGER.info(output + ": unsupported output-format, using " + OutputFormat.CLEAN_JSON + " instead");
-            // Default output format
-            format = OutputFormat.CLEAN_JSON;
-        }
-
-        String query = request.getParameter("query");
-        String wrapperArray = request.getParameter("wrapper-array");
-        String printExprParam = request.getParameter("print-expr-tree");
-        String printRewrittenExprParam = request.getParameter("print-rewritten-expr-tree");
-        String printLogicalPlanParam = request.getParameter("print-logical-plan");
-        String printOptimizedLogicalPlanParam = request.getParameter("print-optimized-logical-plan");
-        String printJob = request.getParameter("print-job");
-        String executeQuery = request.getParameter("execute-query");
-        response.setCharacterEncoding("utf-8");
-        response.setContentType("text/html");
-        PrintWriter out = response.getWriter();
-        ServletContext context = getServletContext();
-        IHyracksClientConnection hcc;
-        IHyracksDataset hds;
-
-        try {
-            synchronized (context) {
-                hcc = (IHyracksClientConnection) context.getAttribute(HYRACKS_CONNECTION_ATTR);
-
-                hds = (IHyracksDataset) context.getAttribute(HYRACKS_DATASET_ATTR);
-                if (hds == null) {
-                    hds = new HyracksDataset(hcc, ResultReader.FRAME_SIZE, ResultReader.NUM_READERS);
-                    context.setAttribute(HYRACKS_DATASET_ATTR, hds);
-                }
-            }
-            IParser parser = parserFactory.createParser(query);
-            List<Statement> aqlStatements = parser.parse();
-            SessionConfig sessionConfig = new SessionConfig(out, format, true, isSet(executeQuery), true);
-            sessionConfig.set(SessionConfig.FORMAT_HTML, true);
-            sessionConfig.set(SessionConfig.FORMAT_CSV_HEADER, csv_and_header);
-            sessionConfig.set(SessionConfig.FORMAT_WRAPPER_ARRAY, isSet(wrapperArray));
-            sessionConfig.setOOBData(isSet(printExprParam), isSet(printRewrittenExprParam),
-                    isSet(printLogicalPlanParam), isSet(printOptimizedLogicalPlanParam), isSet(printJob));
-            MetadataManager.INSTANCE.init();
-            IStatementExecutor translator =
-                    statementExectorFactory.create(aqlStatements, sessionConfig, compilationProvider);
-            double duration = 0;
-            long startTime = System.currentTimeMillis();
-            translator.compileAndExecute(hcc, hds, IStatementExecutor.ResultDelivery.IMMEDIATE);
-            long endTime = System.currentTimeMillis();
-            duration = (endTime - startTime) / 1000.00;
-            out.println(HTML_STATEMENT_SEPARATOR);
-            out.println("<PRE>Duration of all jobs: " + duration + " sec</PRE>");
-        } catch (AsterixException | TokenMgrError | org.apache.asterix.aqlplus.parser.TokenMgrError pe) {
-            GlobalConfig.ASTERIX_LOGGER.log(Level.INFO, pe.toString(), pe);
-            ResultUtil.webUIParseExceptionHandler(out, pe, query);
-        } catch (Exception e) {
-            GlobalConfig.ASTERIX_LOGGER.log(Level.SEVERE, e.getMessage(), e);
-            ResultUtil.webUIErrorHandler(out, e);
-        }
-    }
-
-    @Override
-    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
-        String resourcePath = null;
-        String requestURI = request.getRequestURI();
-
-        if (requestURI.equals("/")) {
-            response.setContentType("text/html");
-            resourcePath = "/webui/querytemplate.html";
-        } else {
-            resourcePath = requestURI;
-        }
-
-        try (InputStream is = APIServlet.class.getResourceAsStream(resourcePath)) {
-            if (is == null) {
-                response.sendError(HttpServletResponse.SC_NOT_FOUND);
-                return;
-            }
-
-            // Special handler for font files and .png resources
-            if (resourcePath.endsWith(".png")) {
-
-                BufferedImage img = ImageIO.read(is);
-                OutputStream outputStream = response.getOutputStream();
-                String formatName = "png";
-                response.setContentType("image/png");
-                ImageIO.write(img, formatName, outputStream);
-                outputStream.close();
-                return;
-            }
-
-            response.setCharacterEncoding("utf-8");
-            InputStreamReader isr = new InputStreamReader(is);
-            StringBuilder sb = new StringBuilder();
-            BufferedReader br = new BufferedReader(isr);
-            String line;
-            try {
-                line = br.readLine();
-            } catch (NullPointerException e) {
-                LOGGER.log(Level.WARNING, "NPE reading resource " + resourcePath
-                        + ", assuming JDK-8080094; returning 404", e);
-                // workaround lame JDK bug where a broken InputStream is returned in case the resourcePath is a
-                // directory; see https://bugs.openjdk.java.net/browse/JDK-8080094
-                response.sendError(HttpServletResponse.SC_NOT_FOUND);
-                return;
-            }
-
-            while (line != null) {
-                sb.append(line);
-                line = br.readLine();
-            }
-
-            PrintWriter out = response.getWriter();
-            out.println(sb.toString());
-        }
-    }
-
-    private static boolean isSet(String requestParameter) {
-        return (requestParameter != null && requestParameter.equals("true"));
-    }
-}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/60e7f12b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ClusterAPIServlet.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ClusterAPIServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ClusterAPIServlet.java
deleted file mode 100644
index 988b5bb..0000000
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ClusterAPIServlet.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * 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.api.http.servlet;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Pattern;
-
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializationFeature;
-import org.apache.asterix.common.config.AsterixProperties;
-import org.apache.asterix.common.config.ExternalProperties;
-import org.apache.asterix.common.utils.JSONUtil;
-import org.apache.asterix.common.config.AbstractProperties;
-import org.apache.asterix.common.config.ReplicationProperties;
-import org.apache.asterix.runtime.util.ClusterStateManager;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-
-public class ClusterAPIServlet extends HttpServlet {
-    private static final long serialVersionUID = 1L;
-    private static final Logger LOGGER = Logger.getLogger(ClusterAPIServlet.class.getName());
-
-    protected static final String NODE_ID_KEY = "node_id";
-    protected static final String CONFIG_URI_KEY = "configUri";
-    protected static final String STATS_URI_KEY = "statsUri";
-    protected static final String THREAD_DUMP_URI_KEY = "threadDumpUri";
-    protected static final String SHUTDOWN_URI_KEY = "shutdownUri";
-    protected static final String FULL_SHUTDOWN_URI_KEY = "fullShutdownUri";
-    protected static final String VERSION_URI_KEY = "versionUri";
-    protected static final String DIAGNOSTICS_URI_KEY = "diagnosticsUri";
-    protected static final String REPLICATION_URI_KEY = "replicationUri";
-    private static final Pattern PARENT_DIR = Pattern.compile("/[^./]+/\\.\\./");
-    private static final Pattern REPLICATION_PROPERTY = Pattern.compile("^replication\\.");
-    private final ObjectMapper om = new ObjectMapper();
-
-    @Override
-    public final void doGet(HttpServletRequest request, HttpServletResponse response) {
-        try {
-            getUnsafe(request, response);
-        } catch (IOException e) {
-            LOGGER.log(Level.WARNING, "Unhandled IOException thrown from " + getClass().getName() + " get impl", e);
-        }
-    }
-
-    protected void getUnsafe(HttpServletRequest request, HttpServletResponse response) throws IOException {
-        response.setContentType("application/json");
-        response.setCharacterEncoding("utf-8");
-        PrintWriter responseWriter = response.getWriter();
-        try {
-            ObjectNode json;
-            switch (request.getPathInfo() == null ? "" : request.getPathInfo()) {
-                case "":
-                    json = getClusterStateJSON(request, "");
-                    break;
-                case "/replication":
-                    json = getReplicationJSON();
-                    break;
-                case "/summary":
-                    json = getClusterStateSummaryJSON();
-                    break;
-                default:
-                    throw new IllegalArgumentException();
-            }
-            response.setStatus(HttpServletResponse.SC_OK);
-            responseWriter.write(JSONUtil.convertNode(json));
-        } catch (IllegalArgumentException e) { // NOSONAR - exception not logged or rethrown
-            response.sendError(HttpServletResponse.SC_NOT_FOUND);
-        } catch (Exception e) {
-            LOGGER.log(Level.INFO, "exception thrown for " + request, e);
-            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.toString());
-        }
-        responseWriter.flush();
-    }
-
-    protected ObjectNode getClusterStateSummaryJSON() {
-        return ClusterStateManager.INSTANCE.getClusterStateSummary();
-    }
-
-    protected ObjectNode getReplicationJSON() {
-        for (AbstractProperties props : getPropertiesInstances()) {
-            if (props instanceof ReplicationProperties) {
-                ObjectNode json = om.createObjectNode();
-                json.putPOJO("config", props.getProperties(key -> REPLICATION_PROPERTY.matcher(key).replaceFirst("")));
-                return json;
-            }
-        }
-        throw new IllegalStateException("ERROR: replication properties not found");
-    }
-
-    protected Map<String, Object> getAllClusterProperties() {
-        Map<String, Object> allProperties = new HashMap<>();
-        for (AbstractProperties properties : getPropertiesInstances()) {
-            if (!(properties instanceof ReplicationProperties)) {
-                allProperties.putAll(properties.getProperties());
-            }
-        }
-        return allProperties;
-    }
-
-    protected List<AbstractProperties> getPropertiesInstances() {
-        return AbstractProperties.getImplementations();
-    }
-
-    protected ObjectNode getClusterStateJSON(HttpServletRequest request, String pathToNode) {
-        ObjectNode json = ClusterStateManager.INSTANCE.getClusterStateDescription();
-        Map<String, Object> allProperties = getAllClusterProperties();
-        json.putPOJO("config", allProperties);
-
-        ArrayNode ncs = (ArrayNode) json.get("ncs");
-        final StringBuilder requestURL = new StringBuilder(request.getRequestURL());
-        if (requestURL.charAt(requestURL.length() - 1) != '/') {
-            requestURL.append('/');
-        }
-        requestURL.append(pathToNode);
-        String clusterURL = canonicalize(requestURL);
-        String adminURL = canonicalize(clusterURL + "../");
-        String nodeURL = clusterURL + "node/";
-        for (int i = 0; i < ncs.size(); i++) {
-            ObjectNode nc = (ObjectNode) ncs.get(i);
-            nc.put(CONFIG_URI_KEY, nodeURL + nc.get(NODE_ID_KEY).asText() + "/config");
-            nc.put(STATS_URI_KEY, nodeURL + nc.get(NODE_ID_KEY).asText() + "/stats");
-            nc.put(THREAD_DUMP_URI_KEY, nodeURL + nc.get(NODE_ID_KEY).asText() + "/threaddump");
-        }
-        ObjectNode cc;
-        if (json.has("cc")) {
-            cc = (ObjectNode) json.get("cc");
-        } else {
-            cc = om.createObjectNode();
-            json.set("cc", cc);
-        }
-        cc.put(CONFIG_URI_KEY, clusterURL + "cc/config");
-        cc.put(STATS_URI_KEY, clusterURL + "cc/stats");
-        cc.put(THREAD_DUMP_URI_KEY, clusterURL + "cc/threaddump");
-        json.put(REPLICATION_URI_KEY, clusterURL + "replication");
-        json.put(SHUTDOWN_URI_KEY, adminURL + "shutdown");
-        json.put(FULL_SHUTDOWN_URI_KEY, adminURL + "shutdown?all=true");
-        json.put(VERSION_URI_KEY, adminURL + "version");
-        json.put(DIAGNOSTICS_URI_KEY, adminURL + "diagnostics");
-        return json;
-    }
-
-    private String canonicalize(CharSequence requestURL) {
-        String clusterURL = "";
-        String newClusterURL = requestURL.toString();
-        while (!clusterURL.equals(newClusterURL)) {
-            clusterURL = newClusterURL;
-            newClusterURL = PARENT_DIR.matcher(clusterURL).replaceAll("/");
-        }
-        return clusterURL;
-    }
-}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/60e7f12b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ClusterCCDetailsAPIServlet.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ClusterCCDetailsAPIServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ClusterCCDetailsAPIServlet.java
deleted file mode 100644
index 569d5a0..0000000
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/ClusterCCDetailsAPIServlet.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.api.http.servlet;
-
-import static org.apache.asterix.api.http.servlet.ServletConstants.HYRACKS_CONNECTION_ATTR;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.hyracks.api.client.IHyracksClientConnection;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-
-public class ClusterCCDetailsAPIServlet extends ClusterAPIServlet {
-    private static final long serialVersionUID = 1L;
-    private static final Logger LOGGER = Logger.getLogger(ClusterCCDetailsAPIServlet.class.getName());
-    private final ObjectMapper om = new ObjectMapper();
-
-    @Override
-    protected void getUnsafe(HttpServletRequest request, HttpServletResponse response) throws IOException {
-        PrintWriter responseWriter = response.getWriter();
-        ServletContext context = getServletContext();
-        IHyracksClientConnection hcc = (IHyracksClientConnection) context.getAttribute(HYRACKS_CONNECTION_ATTR);
-        try {
-            ObjectNode json;
-            if (request.getPathInfo() == null) {
-                json = (ObjectNode) getClusterStateJSON(request, "../").get("cc");
-            } else {
-                json = processNode(request, hcc);
-            }
-            response.setStatus(HttpServletResponse.SC_OK);
-            response.setContentType("application/json");
-            response.setCharacterEncoding("utf-8");
-            responseWriter.write(new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(json));
-        } catch (IllegalArgumentException e) { // NOSONAR - exception not logged or rethrown
-            response.sendError(HttpServletResponse.SC_NOT_FOUND);
-        } catch (Exception e) {
-            LOGGER.log(Level.INFO, "exception thrown for " + request, e);
-            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.toString());
-        }
-        responseWriter.flush();
-    }
-
-    private ObjectNode processNode(HttpServletRequest request, IHyracksClientConnection hcc) throws Exception {
-        String pathInfo = request.getPathInfo();
-        if (pathInfo.endsWith("/")) {
-            throw new IllegalArgumentException();
-        }
-        String[] parts = pathInfo.substring(1).split("/");
-
-        if (request.getPathInfo() == null) {
-            return (ObjectNode) getClusterStateJSON(request, "../../").get("cc");
-        } else if (parts.length == 1) {
-            switch (parts[0]) {
-                case "config":
-                    return om.readValue(hcc.getNodeDetailsJSON(null, false, true), ObjectNode.class);
-                case "stats":
-                    return om.readValue(hcc.getNodeDetailsJSON(null, true, false), ObjectNode.class);
-                case "threaddump":
-                    return processCCThreadDump(hcc);
-
-                default:
-                    throw new IllegalArgumentException();
-            }
-
-        } else {
-            throw new IllegalArgumentException();
-        }
-    }
-
-    private ObjectNode processCCThreadDump(IHyracksClientConnection hcc) throws Exception {
-        String dump = hcc.getThreadDump(null);
-        if (dump == null) {
-            throw new IllegalArgumentException();
-        }
-        return (ObjectNode) om.readTree(dump);
-    }
-
-}


Mime
View raw message