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

oracle.kv.impl.admin.web.AdminWebService Maven / Gradle / Ivy

Go to download

NoSQL Database Server - supplies build and runtime support for the server (store) side of the Oracle NoSQL Database.

The newest version!
/*-
 * Copyright (C) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle NoSQL
 * Database made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/nosqldb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle NoSQL Database for a copy of the license and
 * additional information.
 */

package oracle.kv.impl.admin.web;

import static oracle.kv.util.http.Constants.NOSQL_ADMIN_PATH;
import static oracle.kv.util.http.Constants.NULL_KEY;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import oracle.kv.impl.admin.CommandService;
import oracle.kv.impl.admin.LoginService;
import oracle.kv.impl.admin.client.CommandShell;
import oracle.kv.impl.admin.web.service.AdminSubService;
import oracle.kv.impl.admin.web.service.CommandWebService;
import oracle.kv.impl.admin.web.service.LoginWebService;
import oracle.kv.impl.admin.web.service.LogoutWebService;
import oracle.kv.impl.util.contextlogger.LogContext;
import oracle.kv.impl.util.server.LoggerUtils;
import oracle.kv.impl.util.sklogger.SkLogger;
import oracle.kv.util.ErrorMessage;
import oracle.kv.util.http.Service;
import oracle.kv.util.shell.ShellCommand;
import oracle.kv.util.shell.ShellCommandResult;

import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpMethod;

/**
 * AdminWebService handles the incoming Http request. It will first lookup the
 * web service from registry map according to the combination of request's
 * path, JSON field command in Http payload, Http method. If a admin sub web
 * service is found, then request handler will execute the web service for the
 * request. Http response will be handled in ResponseHandler. After request is
 * executed on web service, request handler will call ResponseHandler to
 * generate the result. If any exception happened during request processing,
 * a JSON payload of ShellCommandResult will be saved in the Http response to
 * indicate the problem.
 */
public class AdminWebService implements Service {

    /* Logger for request handler */
    private SkLogger logger;

    /* Used to create CommandWebService */
    private CommandService commandService;

    /* Used to create LoginWebService */
    private LoginService loginService;

    /* Store the mapping of request to web service */
    private final Map> registryMap =
        new HashMap>();

    public AdminWebService(CommandService commandService,
                                   LoginService loginService,
                                   SkLogger logger) {
        this.logger = logger;
        this.commandService = commandService;
        this.loginService = loginService;
    }

    /*
     * Return web service from registry map according to related information
     * in Http request. The first component of request path will be used to
     * map to the first key of registry, then the command field in request JSON
     * payload will indicate the second key in registry map. If any of these
     * keys is not present, then a NULL key will be used to look for the web
     * service. If not mapping service is found, then IAE will be thrown.
     * Eventually a response with JSON payload of 5100 in the returnCode field
     * will be sent to the client in the IAE case.
     */
    AdminSubService getServiceFromRegistry(WrappedHttpRequest wrapRequest) {

        /* Retrieve information from wrapped request */
        final List paths = wrapRequest.getPaths();
        final CommandInputs inputs = wrapRequest.getInputs();
        final HttpMethod method = wrapRequest.getRequest().method();

        /* NULL key for empty path */
        if (paths.isEmpty()) {
            paths.add(NULL_KEY);
        }

        String command = null;
        /* NULL key for empty command */
        if (inputs == null || inputs.getCommand() == null) {
            command = NULL_KEY;
        } else {
            command = inputs.getCommand();
        }

        final Map map = registryMap.get(paths.get(0));
        if (map == null) {
            throw new IllegalArgumentException(
                "Cannot find mapping service for sub path: " +
                paths.get(0));
        }

        final AdminSubService ws = map.get(command);
        if (ws == null) {
            throw new IllegalArgumentException(
                "Cannot find mapping service for command key: " +
                command);
        }

        /*
         * For command web service, we need to check the request method
         * mapping the annotation on the command.
         */
        if (ws instanceof CommandWebService) {
            final CommandWebService commandWs = (CommandWebService) ws;
            if (!commandWs.checkHttpMethod(method)) {
                throw new IllegalArgumentException(
                    "Cannot find mapping service for http method: " +
                    method.name());
            }
        }
        return ws;
    }

    /*
     * This must be called before RequestHandler is in use. The initialization
     * create web services instances for all shell commands and register them
     * into the registry map.
     * Login service will also be created to handle login request.
     */
    public AdminWebService initService() {

        boolean isSecure = false;
        if (loginService != null) {
            isSecure = true;
        }

        registryMap.clear();

        for (ShellCommand sc : CommandShell.getAllCommands()) {
            final AdminSubService ws =
                new CommandWebService(sc, commandService, isSecure, logger);
            ws.registerService(registryMap);
        }

        if (isSecure) {
            /* Register security login and logout services */
            final AdminSubService login =
                new LoginWebService(loginService, logger);
            login.registerService(registryMap);
            final AdminSubService logout =
                new LogoutWebService(loginService, logger);
            logout.registerService(registryMap);
        }

        return this;
    }

    @Override
    public FullHttpResponse handleRequest(FullHttpRequest request,
                                          ChannelHandlerContext ctx,
                                          LogContext lc) {

        final ResponseHandler handler = new ResponseHandler();

        try {

            /* First phase to parse Http request and stored the result */
            final WrappedHttpRequest wrappedRequest =
                new WrappedHttpRequest(request, ctx);

            /* Retrieve web service */
            final AdminSubService ws = getServiceFromRegistry(wrappedRequest);

            /* Execute web service */
            ws.executeService(wrappedRequest, handler);

            return handler.createResponse();

        } catch (Exception e) {

            /* Clear up any pre-exist info created in execution */
            handler.setDefaults();

            /* Use Json result */
            handler.setAppJsonHeader();

            /* Generate POJO instance for command result */
            final ShellCommandResult scr =
                ShellCommandResult.getDefault("Http request failed");

            /* Create the message to indicate the problem */
            scr.setDescription(e.toString());

            /*
             * In general, the return code of exception is already handled by
             * shell command execution, so the WebService.executeService() is
             * not likely to throw any exception up to here. If the exception
             * can show up to this level but it is not
             * IllegalArgumentException, then this is probably
             * internal problem/bug caused.Therefore, we set 5500 by default
             * here. For IAE, it should be 5100 code.
             * Return a proper code back to the client according to above
             * logic.
             */
            scr.setReturnCode(ErrorMessage.NOSQL_5500.getValue());
            if (e instanceof IllegalArgumentException) {
                scr.setReturnCode(ErrorMessage.NOSQL_5100.getValue());
            } else {
                /*
                 * Log the internal problem
                 */
                logger.severe(LoggerUtils.getStackTrace(e));
            }

            /* print JSON result in Http payload */
            try {
                handler.println(scr.convertToJson());
            } catch (IOException ioe) {
                handler.println(scr.getConversionErrorJsonResult(ioe));
            }

            return handler.createResponse();
        } finally {

            /*
             * Clear the references in handler to avoid any protential
             * problem.
             */
            handler.clear();
        }
    }

    @Override
    public boolean lookupService(String uri) {
        return uri.startsWith(NOSQL_ADMIN_PATH);
    }

    public void shutdown() {
        for (Map map: registryMap.values()) {
            for (AdminSubService service : map.values()) {
                service.shutdown();
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy