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

com.dell.doradus.service.rest.RESTService Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2014 Dell, Inc.
 * 
 * Licensed 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 com.dell.doradus.service.rest;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;

import com.dell.doradus.common.ApplicationDefinition;
import com.dell.doradus.common.HttpMethod;
import com.dell.doradus.common.rest.RESTCatalog;
import com.dell.doradus.common.Utils;
import com.dell.doradus.core.ServerConfig;
import com.dell.doradus.service.Service;
import com.dell.doradus.service.StorageService;
import com.dell.doradus.service.rest.annotation.Description;
import com.dell.doradus.service.schema.SchemaService;

/**
 * The REST Service for Doradus. This singleton class creates and runs an instance of the
 * {@link WebServer}. It maintains a set of registered REST commands and matches requests
 * to the appropriate command.
 */
public class RESTService extends Service {
    /**
     * Request callback interface when someone wants to be notified about request actions.
     */
    public static interface RequestCallback {
        void onConnectionOpened();
        void onConnectionClosed();
        void onNewRequest();
        void onRequestSucceeded(long startTimeNanos);
        void onRequestRejected(String reason);
        void onRequestFailed(Throwable e);
    }   // interface RequestCallback
	
    private static final RESTService INSTANCE = new RESTService();
    private WebServer m_webservice;
    private final RESTRegistry m_cmdRegistry = new RESTRegistry();

    /**
     * Get the singleton instance of this service. The service may or may not have been
     * initialized yet.
     * 
     * @return  The singleton instance of this service.
     */
    public static RESTService instance() {
        return INSTANCE;
    }   // instance

    //----- Inherited Service methods
    
    // Initialize WebService so it's ready to run.
    @Override
    public void initService() {
        m_webservice = loadWebServer(); // could be null
    	registerCommands(Arrays.asList(DescribeCmd.class));
    }   // initService

    // Begin servicing REST requests.
    @Override
    public void startService() {
        m_cmdRegistry.freezeCommandSet(true);
        displayCommandSet();
        if (m_webservice != null) {
            try {
                m_webservice.start();
            } catch (Exception e) {
                throw new RuntimeException("Failed to start WebService", e);
            }
        }
    }   // startService

    // Shutdown the REST Service
    @Override
    public void stopService() {
        try {
            if (m_webservice != null) {
                m_webservice.stop();
            }
        } catch (Exception e) {
            m_logger.warn("WebService stop failed", e);
        }
    }   // stopService

    //----- RESTService public methods
    
    /**
     * Return the current set of visible, registered commands as a {@link RESTCatalog}.
     * This object can be serialized, returned via the REST API, and deserialized.
     *  
     * @return  A {@link RESTCatalog} of all visible, registered commands.
     */
    public RESTCatalog describeCommands() {
        return m_cmdRegistry.describeCommands();
    }
    
    /**
     * Register the given activity callback, which will be notified each time client
     * request activity occurs. 
     * 
     * @param callback  {@link RequestCallback} object.
     */
    public void registerRequestCallback(RequestCallback callback) {
       	if (m_webservice != null) {
       		m_webservice.registerRequestCallback(callback);
       	}
    }   // registerRequestCallback
    
    /**
     * Register a set of REST commands as global (system) commands. The commands are
     * defined via a set of {@link RESTCallback} objects, which must use the
     * {@link Description} annotation to provide metadata about the commands.
     * 
     * @param callbackClasses   Iterable collection of {@link RESTCallback} classes
     *                          that define the REST commands to be registered.
     */
    public void registerCommands(Iterable> callbackClasses) {
        m_cmdRegistry.registerCallbacks(null, callbackClasses);
    }
    
    /**
     * Register a set of application REST commands as belonging to the given storage
     * service owner. The commands are defined via a set of {@link RESTCallback} objects,
     * which must use the {@link Description} annotation to provide metadata about the
     * commands.
     * 
     * @param callbackClasses   Iterable collection of {@link RESTCallback} classes
     *                          that define application-specific REST commands to be
     *                          registered.
     * @param service           {@link StorageService} that owns the commands. 
     */
    public void registerCommands(Iterable> cmdClasses,
                                 StorageService service) {
        m_cmdRegistry.registerCallbacks(service, cmdClasses);
    }
    
    /**
     * Register a set of application REST commands as belonging to the given storage
     * service owner, which extends another storage service. The commands are defined
     * via a set of {@link RESTCallback} objects, which must use the {@link Description}
     * annotation to provide metadata about the commands.
     * 
     * @param callbackClasses   Iterable collection of {@link RESTCallback} classes
     *                          that define application-specific REST commands to be
     *                          registered.
     * @param service           {@link StorageService} that owns the commands.
     * @param parentService     {@link StorageService} whose commands are extended and/or
     *                          overridden by the given service's new commands.
     */
    public void registerCommands(Iterable> cmdClasses,
                                 StorageService service,
                                 StorageService parentService) {
        m_cmdRegistry.setParent(service.getClass().getSimpleName(), parentService.getClass().getSimpleName());
        m_cmdRegistry.registerCallbacks(service, cmdClasses);
    }
    
    /**
     * Attempt to match the given REST request to a registered command. If it does, return
     * the corresponding {@link RegisteredCommand} and update the given variable map with
     * decoded URI variables. For example, if the matching command is defined as:
     * 
     *      GET /{application}/{table}/_query?{query}
     * 
* And the actual request passed is: *
     *      GET /Magellan/Stars/_query?q=Tarantula+Nebula%2A
     * 
     * The variable map will be returned with the follow key/value pairs:
     * 
     *      application=Magellan
     *      table=Stars
     *      query=Tarantula+Nebula%2A
     * 
* Note that URI-encoded parameter values remain encoded in the variable map. * * @param appDef {@link ApplicationDefinition} of application that provides * context for command, if any. Null for system commands. * @param method {@link HttpMethod} of the request. * @param uri Request URI (case-sensitive: "/Magellan/Stars/_query") * @param query Optional query parameter (case-sensitive: "q=Tarantula+Nebula%2A"). * @param variableMap Variable parameters defined in the REST command substituted with * the actual values passed, not decoded (see above). * @return The {@link RegisteredCommand} if a match was found, otherwise null. */ public RegisteredCommand findCommand(ApplicationDefinition appDef, HttpMethod method, String uri, String query, Map variableMap) { String cmdOwner = null; if (appDef != null) { StorageService ss = SchemaService.instance().getStorageService(appDef); cmdOwner = ss.getClass().getSimpleName(); } return m_cmdRegistry.findCommand(cmdOwner, method, uri, query, variableMap); } // matchCommand //----- Package-private methods (used by RESTServlet) void onNewrequest() { if (m_webservice != null) { m_webservice.notifyNewRequest(); } } void onRequestSuccess(long startTimeNanos) { if (m_webservice != null) { m_webservice.notifyRequestSuccess(startTimeNanos); } } void onRequestRejected(String reason) { if (m_webservice != null) { m_webservice.notifyRequestRejected(reason); } } void onRequestFailed(Throwable e) { if (m_webservice != null) { m_webservice.notifyRequestFailed(e); } } //----- Private methods // Singleton construction only private RESTService() {} // Attempt to load the WebServer instance defined by webserver_class. private WebServer loadWebServer() { WebServer webServer = null; if (!Utils.isEmpty(ServerConfig.getInstance().webserver_class)) { try { Class serviceClass = Class.forName(ServerConfig.getInstance().webserver_class); Method instanceMethod = serviceClass.getMethod("instance", (Class[])null); webServer = (WebServer)instanceMethod.invoke(null, (Object[])null); webServer.init(RESTServlet.class.getName()); } catch (Exception e) { throw new RuntimeException("Error initializing WebServer: " + ServerConfig.getInstance().webserver_class, e); } } return webServer; } // If DEBUG logging is enabled, log all REST commands in sorted order. private void displayCommandSet() { if (m_logger.isDebugEnabled()) { m_logger.debug("Registered REST Commands:"); Collection commands = m_cmdRegistry.getCommands(); for (String command : commands) { m_logger.debug(command); } } } // displayCommandSet } // class RESTService




© 2015 - 2025 Weber Informatics LLC | Privacy Policy