rapture.server.rest.RestServer.java Source code

Java tutorial

Introduction

Here is the source code for rapture.server.rest.RestServer.java

Source

/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2011-2016 Incapture Technologies LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package rapture.server.rest;

import static rapture.server.rest.JsonUtil.json;
import static spark.Spark.before;
import static spark.Spark.delete;
import static spark.Spark.exception;
import static spark.Spark.get;
import static spark.Spark.halt;
import static spark.Spark.post;
import static spark.Spark.put;
import static spark.Spark.secure;
import static spark.Spark.webSocket;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import com.google.common.collect.ImmutableMap;

import rapture.app.RaptureAppService;
import rapture.common.CallingContext;
import rapture.common.RaptureURI;
import rapture.common.Scheme;
import rapture.common.exception.RaptureException;
import rapture.common.impl.jackson.JacksonUtil;
import rapture.config.ConfigLoader;
import rapture.kernel.Kernel;
import spark.Request;

public class RestServer {

    private static final Logger log = Logger.getLogger(RestServer.class);
    private Map<String, CallingContext> ctxs = new HashMap<>();
    private static final String APP_KEY = "restserver";

    public static void main(String[] args) {
        try {
            Kernel.initBootstrap(ImmutableMap.of("STD", ConfigLoader.getConf().StandardTemplate), RestServer.class,
                    false);
            RaptureAppService.setupApp("RestServer");
        } catch (RaptureException e) {
            log.error("Failed to start RestServer", e);
            return;
        }
        new RestServer().run();
    }

    private void run() {
        setupHttps();
        setupWebSocket();
        setupRoutes();
        setupExceptionHandling();
    }

    private void setupHttps() {
        String keyStoreLocation = "keystore.jks";
        secure(keyStoreLocation, keyStoreLocation, null, null);

    }

    private void setupWebSocket() {
        webSocket("/websocket", GenericWebSocket.class);
    }

    @SuppressWarnings("unchecked")
    private void setupRoutes() {

        before((req, res) -> {
            log.info("Path is: " + req.pathInfo());
            CallingContext ctx = ctxs.get(req.session().id());
            if (ctx == null) {
                // check x-api-key header
                String apiKey = req.headers("x-api-key");
                if (StringUtils.isNotBlank(apiKey)) {
                    ctx = Kernel.INSTANCE.loadContext(APP_KEY, apiKey);
                    if (ctx == null) {
                        String msg = String.format("Invalid apiKey [%s] for app [%s]", apiKey, APP_KEY);
                        log.warn(msg);
                        halt(401, msg);
                    }
                    String id = req.session(true).id();
                    ctxs.put(id, ctx);
                } else {
                    log.warn("x-api-key header not found, rejecting...");
                    halt(401, "Please provide 'x-api-key' header to access this API");
                }
            }
        });

        post("/doc/:authority", (req, res) -> {
            log.info(req.body());
            Map<String, Object> data = JacksonUtil.getMapFromJson(req.body());
            String authority = req.params(":authority");
            String config = (String) data.get("config");
            CallingContext ctx = getContext(req);
            if (Kernel.getDoc().docRepoExists(ctx, authority)) {
                halt(409, String.format("Repo [%s] already exists", authority));
            }
            Kernel.getDoc().createDocRepo(ctx, authority, config);
            return new RaptureURI(authority, Scheme.DOCUMENT).toString();
        });

        get("/doc/*", (req, res) -> {
            String meta = req.queryParams("meta");
            if (StringUtils.isNotBlank(meta)) {
                return Kernel.getDoc().getDocAndMeta(getContext(req), getDocUriParam(req));
            }
            return Kernel.getDoc().getDoc(getContext(req), getDocUriParam(req));
        });

        put("/doc/*", (req, res) -> {
            return Kernel.getDoc().putDoc(getContext(req), getDocUriParam(req), req.body());
        });

        delete("/doc/:authority", (req, res) -> {
            Kernel.getDoc().deleteDocRepo(getContext(req), req.params(":authority"));
            return true;
        });

        delete("/doc/*", (req, res) -> {
            return Kernel.getDoc().deleteDoc(getContext(req), getDocUriParam(req));
        });

        post("/blob/:authority", (req, res) -> {
            log.info(req.body());
            Map<String, Object> data = JacksonUtil.getMapFromJson(req.body());
            String authority = req.params(":authority");
            String config = (String) data.get("config");
            String metaConfig = (String) data.get("metaConfig");
            CallingContext ctx = getContext(req);
            if (Kernel.getBlob().blobRepoExists(ctx, authority)) {
                halt(409, String.format("Repo [%s] already exists", authority));
            }
            Kernel.getBlob().createBlobRepo(ctx, authority, config, metaConfig);
            return new RaptureURI(authority, Scheme.BLOB).toString();
        });

        get("/blob/*", (req, res) -> {
            return Kernel.getBlob().getBlob(getContext(req), getBlobUriParam(req));
        }, json());

        put("/blob/*", (req, res) -> {
            String uri = getBlobUriParam(req);
            Kernel.getBlob().putBlob(getContext(req), uri, req.bodyAsBytes(), req.contentType());
            return uri;
        });

        delete("/blob/:authority", (req, res) -> {
            Kernel.getBlob().deleteBlobRepo(getContext(req), req.params(":authority"));
            return true;
        });

        delete("/blob/*", (req, res) -> {
            Kernel.getBlob().deleteBlob(getContext(req), getBlobUriParam(req));
            return true;
        });

        post("/series/:authority", (req, res) -> {
            log.info(req.body());
            Map<String, Object> data = JacksonUtil.getMapFromJson(req.body());
            String authority = req.params(":authority");
            String config = (String) data.get("config");
            CallingContext ctx = getContext(req);
            if (Kernel.getSeries().seriesRepoExists(ctx, authority)) {
                halt(409, String.format("Repo [%s] already exists", authority));
            }
            Kernel.getSeries().createSeriesRepo(ctx, authority, config);
            return new RaptureURI(authority, Scheme.SERIES).toString();
        });

        get("/series/*", (req, res) -> {
            return Kernel.getSeries().getPoints(getContext(req), getSeriesUriParam(req));
        }, json());

        put("/series/*", (req, res) -> {
            String uri = getSeriesUriParam(req);
            Map<String, Object> data = JacksonUtil.getMapFromJson(req.body());
            List<String> keys = (List<String>) data.get("keys");
            List<Object> values = (List<Object>) data.get("values");
            if (!values.isEmpty()) {
                Object obj = values.get(0);
                if (obj instanceof Long) {
                    Kernel.getSeries().addLongsToSeries(getContext(req), uri, keys, (List<Long>) (List<?>) values);
                } else if (obj instanceof String) {
                    Kernel.getSeries().addStringsToSeries(getContext(req), uri, keys,
                            (List<String>) (List<?>) values);
                } else if (obj instanceof Double) {
                    Kernel.getSeries().addDoublesToSeries(getContext(req), uri, keys,
                            (List<Double>) (List<?>) values);
                } else {
                    halt(400, "Unknown type in values parameter");
                }
            }
            return uri;
        });

        delete("/series/:authority", (req, res) -> {
            Kernel.getSeries().deleteSeriesRepo(getContext(req), req.params(":authority"));
            return true;
        });

        delete("/series/*", (req, res) -> {
            Kernel.getSeries().deleteSeries(getContext(req), getSeriesUriParam(req));
            return true;
        });

        post("/workorder/*", (req, res) -> {
            Map<String, String> params = new HashMap<>();
            String body = req.body();
            if (StringUtils.isNotBlank(body)) {
                Map<String, Object> data = JacksonUtil.getMapFromJson(body);
                params = (Map<String, String>) data.get("params");
            }
            return Kernel.getDecision().createWorkOrder(getContext(req), getWorkorderUriParam(req), params);
        });

        post("/sstore/:authority", (req, res) -> {
            log.info(req.body());
            Map<String, Object> data = JacksonUtil.getMapFromJson(req.body());
            String authority = req.params(":authority");
            String config = (String) data.get("config");
            CallingContext ctx = getContext(req);
            if (Kernel.getStructured().structuredRepoExists(ctx, authority)) {
                halt(409, String.format("Repo [%s] already exists", authority));
            }
            Kernel.getStructured().createStructuredRepo(ctx, authority, config);
            return new RaptureURI(authority, Scheme.STRUCTURED).toString();
        });

        post("/sstore/:authority/:table", (req, res) -> {
            log.info(req.body());
            Map<String, Object> data = JacksonUtil.getMapFromJson(req.body());
            String authority = req.params(":authority");
            String table = req.params(":table");
            String tableUri = RaptureURI.builder(Scheme.STRUCTURED, authority).docPath(table).build().toString();
            CallingContext ctx = getContext(req);
            if (!Kernel.getStructured().structuredRepoExists(ctx, authority)) {
                halt(404, String.format("Repo [%s] doesn't exist", authority));
            } else if (Kernel.getStructured().tableExists(ctx, tableUri)) {
                halt(409, String.format("Table [%s] already exists", tableUri));
            }
            Kernel.getStructured().createTable(ctx, tableUri, (Map) data);
            return tableUri;
        });

        put("/sstore/*", (req, res) -> {
            log.info(req.body());
            Map<String, Object> data = JacksonUtil.getMapFromJson(req.body());
            String tableUri = getStructuredUriParam(req);
            CallingContext ctx = getContext(req);
            if (!Kernel.getStructured().tableExists(ctx, tableUri)) {
                halt(404, String.format("Table [%s] doesn't exist", tableUri));
            }
            Kernel.getStructured().insertRow(ctx, tableUri, (Map) data);
            return tableUri;
        });

        get("/sstore/*", (req, res) -> {
            List<String> columns = new ArrayList<String>();
            int limit = 10;
            String where = "";
            List<String> order = null;
            Boolean ascending = false;
            List<Map<String, Object>> selectRows = new ArrayList<Map<String, Object>>();

            log.info("Params are: " + req.queryString());
            String tableUri = getStructuredUriParam(req);
            CallingContext ctx = getContext(req);

            if (req.queryParams("sql") != null) {
                String schema = new RaptureURI(tableUri, Scheme.STRUCTURED).getAuthority();
                String updatedRawSql = req.queryParams("sql");
                log.info("Running raw sql: " + updatedRawSql);
                selectRows = Kernel.getStructured().selectUsingSql(ctx, schema, updatedRawSql);
            } else {
                if (req.queryParams("columns") != null) {
                    columns.add(req.queryParams("columns"));
                } else {
                    columns.add("*"); // return all columns
                }
                if (req.queryParams("where") != null) {
                    where = req.queryParams("where");
                }
                if (req.queryParams("order") != null) {
                    order = new ArrayList<String>();
                    order.add(req.queryParams("order"));
                }
                if (req.queryParams("ascending") != null) {
                    ascending = Boolean.valueOf(req.queryParams("ascending"));
                }
                if (req.queryParams("limit") != null) {
                    limit = Integer.valueOf(req.queryParams("limit")).intValue();
                }
                selectRows = Kernel.getStructured().selectRows(ctx, tableUri, columns, where, order, ascending,
                        limit);
            }

            return selectRows;
        });

        delete("/sstore/:authority", (req, res) -> {
            String repoUri = getStructuredUriParam(req);
            CallingContext ctx = getContext(req);
            log.info("deleting repo: " + repoUri);
            Kernel.getStructured().deleteStructuredRepo(ctx, repoUri);
            return true;
        });

        delete("/sstore/:authority/:table/:pkid", (req, res) -> {
            String pkId = req.params(":pkid");
            String tableUri = getStructuredUriParam(req, pkId);
            CallingContext ctx = getContext(req);
            String pkWhere = pkId + "=" + req.queryParams("pkvalue");
            log.info("Delete from " + tableUri + " using pk " + pkWhere);
            Kernel.getStructured().deleteRows(ctx, tableUri, pkWhere);
            return true;
        });

        delete("/sstore/:authority/:table", (req, res) -> {
            String tableUri = getStructuredUriParam(req);
            CallingContext ctx = getContext(req);

            if (req.body().isEmpty()) { // drop the table if request body is
                                        // empty.
                log.info("Dropping table: " + tableUri);
                Kernel.getStructured().dropTable(ctx, tableUri);
            } else { // delete rows from table
                Map<String, Object> data = JacksonUtil.getMapFromJson(req.body());
                String where = (String) data.get("where");
                log.info("Delete where clause: " + where + " from " + tableUri);
                Kernel.getStructured().deleteRows(ctx, tableUri, where);
            }

            return true;
        });

    }

    private String getStructuredUriParam(Request req) {
        return getUriParam(req, "/sstore/");
    }

    private String getStructuredUriParam(Request req, String postfix) {
        return getUriSection(req, "/sstore/", postfix);
    }

    private String getDocUriParam(Request req) {
        return getUriParam(req, "/doc/");
    }

    private String getBlobUriParam(Request req) {
        return getUriParam(req, "/blob/");
    }

    private String getSeriesUriParam(Request req) {
        return getUriParam(req, "/series/");
    }

    private String getWorkorderUriParam(Request req) {
        return getUriParam(req, "/workorder/");
    }

    private String getUriSection(Request req, String prefix, String postfix) {
        String ret = (String) req.pathInfo().subSequence(prefix.length(), req.pathInfo().indexOf(postfix));
        return ret;
    }

    private String getUriParam(Request req, String prefix) {
        String ret = req.pathInfo().substring(prefix.length());
        int index = ret.indexOf("?");
        if (index != -1) {
            ret = ret.substring(0, index);
        }
        return ret;
    }

    private void setupExceptionHandling() {
        exception(Exception.class, (e, req, res) -> {
            log.error(String.format("Exception for request: %s [%s]%n[%s]", req.requestMethod(), req.pathInfo(),
                    req.body()), e);
        });
    }

    private CallingContext getContext(Request req) {
        return ctxs.get(req.session().id());
    }

}