org.apache.hadoop.gateway.websockets.GatewayWebsocketHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.gateway.websockets.GatewayWebsocketHandler.java

Source

/**
 * 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.hadoop.gateway.websockets;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.gateway.config.GatewayConfig;
import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
import org.apache.hadoop.gateway.service.definition.ServiceDefinition;
import org.apache.hadoop.gateway.services.GatewayServices;
import org.apache.hadoop.gateway.services.registry.ServiceDefEntry;
import org.apache.hadoop.gateway.services.registry.ServiceDefinitionRegistry;
import org.apache.hadoop.gateway.services.registry.ServiceRegistry;
import org.apache.hadoop.gateway.util.ServiceDefinitionsLoader;
import org.eclipse.jetty.websocket.server.WebSocketHandler;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;

/**
 * Websocket handler that will handle websocket connection request. This class
 * is responsible for creating a proxy socket for inbound and outbound
 * connections. This is also where the http to websocket handoff happens.
 * 
 * @since 0.10
 */
public class GatewayWebsocketHandler extends WebSocketHandler implements WebSocketCreator {

    private static final WebsocketLogMessages LOG = MessagesFactory.get(WebsocketLogMessages.class);

    public static final String WEBSOCKET_PROTOCOL_STRING = "ws://";

    static final String REGEX_SPLIT_CLUSTER_NAME = "^((?:[^/]*/){1}[^/]*)";

    static final String REGEX_SPLIT_CONTEXT = "^((?:[^/]*/){2}[^/]*)";

    private static final int POOL_SIZE = 10;

    /**
     * Manage the threads that are spawned
     * @since 0.13
     */
    private final ExecutorService pool;

    final GatewayConfig config;
    final GatewayServices services;

    /**
     * Create an instance
     * 
     * @param config
     * @param services
     */
    public GatewayWebsocketHandler(final GatewayConfig config, final GatewayServices services) {
        super();

        this.config = config;
        this.services = services;
        pool = Executors.newFixedThreadPool(POOL_SIZE);

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.eclipse.jetty.websocket.server.WebSocketHandler#configure(org.eclipse.
     * jetty.websocket.servlet.WebSocketServletFactory)
     */
    @Override
    public void configure(final WebSocketServletFactory factory) {
        factory.setCreator(this);
        factory.getPolicy().setMaxTextMessageSize(config.getWebsocketMaxTextMessageSize());
        factory.getPolicy().setMaxBinaryMessageSize(config.getWebsocketMaxBinaryMessageSize());

        factory.getPolicy().setMaxBinaryMessageBufferSize(config.getWebsocketMaxBinaryMessageBufferSize());
        factory.getPolicy().setMaxTextMessageBufferSize(config.getWebsocketMaxTextMessageBufferSize());

        factory.getPolicy().setInputBufferSize(config.getWebsocketInputBufferSize());

        factory.getPolicy().setAsyncWriteTimeout(config.getWebsocketAsyncWriteTimeout());
        factory.getPolicy().setIdleTimeout(config.getWebsocketIdleTimeout());

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.eclipse.jetty.websocket.servlet.WebSocketCreator#createWebSocket(org.
     * eclipse.jetty.websocket.servlet.ServletUpgradeRequest,
     * org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse)
     */
    @Override
    public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp) {

        try {
            final URI requestURI = req.getRequestURI();
            final String path = requestURI.getPath();

            /* URL used to connect to websocket backend */
            final String backendURL = getMatchedBackendURL(path);

            /* Upgrade happens here */
            return new ProxyWebSocketAdapter(URI.create(backendURL), pool);
        } catch (final Exception e) {
            LOG.failedCreatingWebSocket(e);
            throw e;
        }
    }

    /**
     * This method looks at the context path and returns the backend websocket
     * url. If websocket url is found it is used as is, or we default to
     * ws://{host}:{port} which might or might not be right.
     * 
     * @param  The context path
     * @return Websocket backend url
     */
    private synchronized String getMatchedBackendURL(final String path) {

        final ServiceRegistry serviceRegistryService = services
                .getService(GatewayServices.SERVICE_REGISTRY_SERVICE);

        final ServiceDefinitionRegistry serviceDefinitionService = services
                .getService(GatewayServices.SERVICE_DEFINITION_REGISTRY);

        /* Filter out the /cluster/topology to get the context we want */
        String[] pathInfo = path.split(REGEX_SPLIT_CONTEXT);

        final ServiceDefEntry entry = serviceDefinitionService.getMatchingService(pathInfo[1]);

        if (entry == null) {
            throw new RuntimeException(String.format("Cannot find service for the given path: %s", path));
        }

        final File servicesDir = new File(config.getGatewayServicesDir());

        final Set<ServiceDefinition> serviceDefs = ServiceDefinitionsLoader.getServiceDefinitions(servicesDir);

        /* URL used to connect to websocket backend */
        String backendURL = urlFromServiceDefinition(serviceDefs, serviceRegistryService, entry, path);

        try {

            /* if we do not find websocket URL we default to HTTP */
            if (!StringUtils.contains(backendURL, WEBSOCKET_PROTOCOL_STRING)) {
                URL serviceUrl = new URL(backendURL);

                final StringBuffer backend = new StringBuffer();
                /* Use http host:port if ws url not configured */
                final String protocol = (serviceUrl.getProtocol() == "ws" || serviceUrl.getProtocol() == "wss")
                        ? serviceUrl.getProtocol()
                        : "ws";
                backend.append(protocol).append("://");
                backend.append(serviceUrl.getHost()).append(":");
                backend.append(serviceUrl.getPort()).append("/");
                ;
                backend.append(serviceUrl.getPath());

                backendURL = backend.toString();
            }

        } catch (MalformedURLException e) {
            LOG.badUrlError(e);
            throw new RuntimeException(e.toString());
        }

        return backendURL;
    }

    private static String urlFromServiceDefinition(final Set<ServiceDefinition> serviceDefs,
            final ServiceRegistry serviceRegistry, final ServiceDefEntry entry, final String path) {

        final String[] contexts = path.split("/");

        final String serviceURL = serviceRegistry.lookupServiceURL(contexts[2], entry.getName().toUpperCase());

        /*
         * we have a match, if ws:// is present it is returend else http:// is
         * returned
         */
        return serviceURL;

    }

}