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

org.kaazing.robot.driver.RobotServer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2014 "Kaazing Corporation," (www.kaazing.com)
 *
 * This file is part of Robot.
 *
 * Robot is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see .
 */

package org.kaazing.robot.driver;

import static java.util.concurrent.Executors.newCachedThreadPool;
import static org.jboss.netty.channel.Channels.pipeline;

import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeoutException;

import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandler;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChildChannelStateEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.jboss.netty.channel.socket.nio.NioClientBossPool;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerBossPool;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.channel.socket.nio.NioWorker;
import org.jboss.netty.channel.socket.nio.NioWorkerPool;
import org.jboss.netty.channel.socket.nio.ShareableWorkerPool;
import org.jboss.netty.handler.logging.LoggingHandler;
import org.jboss.netty.logging.InternalLogger;
import org.jboss.netty.logging.InternalLoggerFactory;
import org.kaazing.robot.driver.control.handler.ControlDecoder;
import org.kaazing.robot.driver.control.handler.ControlEncoder;
import org.kaazing.robot.driver.control.handler.ControlServerHandler;
import org.kaazing.robot.driver.netty.bootstrap.BootstrapFactory;
import org.kaazing.robot.driver.netty.bootstrap.ServerBootstrap;
import org.kaazing.robot.driver.netty.channel.ChannelAddress;
import org.kaazing.robot.driver.netty.channel.ChannelAddressFactory;

public class RobotServer {

    private final ChannelGroup channelGroup;
    private final List controlHandlers;
    private static final InternalLogger LOGGER = InternalLoggerFactory.getInstance(RobotServer.class);

    private BootstrapFactory bootstrapFactory;
    private final URI controlURI;
    private Channel serverChannel;
    private final boolean verbose;
    private final ClassLoader scriptLoader;

    private ShareableWorkerPool sharedWorkerPool;
    private NioClientSocketChannelFactory clientChannelFactory;
    private NioServerSocketChannelFactory serverChannelFactory;

    public RobotServer(URI controlURI, boolean verbose, ClassLoader scriptLoader) {
        this.controlURI = controlURI;
        this.verbose = verbose;
        this.scriptLoader = scriptLoader;
        this.channelGroup = new DefaultChannelGroup("robot-server");
        this.controlHandlers = new CopyOnWriteArrayList<>();
    }

    public void start() throws Exception {
        if (controlURI == null) {
            throw new NullPointerException("controlURI");
        }

        Map options = new HashMap<>();
        // TODO: options.put("tcp.transport", "socks://...");

        final ChannelAddressFactory addressFactory = ChannelAddressFactory.newChannelAddressFactory();
        ChannelAddress localAddress = addressFactory.newChannelAddress(controlURI, options);

        NioClientBossPool clientBossPool = new NioClientBossPool(newCachedThreadPool(), 1);
        NioServerBossPool serverBossPool = new NioServerBossPool(newCachedThreadPool(), 1);
        NioWorkerPool workerPool = new NioWorkerPool(newCachedThreadPool(), 1);
        sharedWorkerPool = new ShareableWorkerPool(workerPool);
        clientChannelFactory = new NioClientSocketChannelFactory(clientBossPool, sharedWorkerPool);
        serverChannelFactory = new NioServerSocketChannelFactory(serverBossPool, sharedWorkerPool);

        Map, Object> injectables = new HashMap, Object>();
        injectables.put(ChannelAddressFactory.class, addressFactory);
        injectables.put(NioClientSocketChannelFactory.class, clientChannelFactory);
        injectables.put(NioServerSocketChannelFactory.class, serverChannelFactory);

        bootstrapFactory = BootstrapFactory.newBootstrapFactory(injectables);

        String transportName = controlURI.getScheme();
        ServerBootstrap server = bootstrapFactory.newServerBootstrap(transportName);

        server.setPipelineFactory(new ChannelPipelineFactory() {

            @Override
            public ChannelPipeline getPipeline() throws Exception {

                ChannelPipeline pipeline = pipeline();

                ChannelHandler decoder = new ControlDecoder();
                pipeline.addLast("control.decoder", decoder);

                ChannelHandler encoder = new ControlEncoder();
                pipeline.addLast("control.encoder", encoder);

                if (verbose) {
                    ChannelHandler logging = new LoggingHandler("robot.server", false);
                    pipeline.addLast("control.logging", logging);
                }

                ControlServerHandler controller = new ControlServerHandler();
                controller.setAddressFactory(addressFactory);
                controller.setBootstrapFactory(bootstrapFactory);
                controller.setScriptLoader(scriptLoader);
                pipeline.addLast("control.handler", controller);

                return pipeline;
            }
        });

        /* Keep track of all open channels */
        server.setParentHandler(new SimpleChannelHandler() {

            @Override
            public void childChannelOpen(ChannelHandlerContext ctx, ChildChannelStateEvent e) throws Exception {
                LOGGER.debug("Control Channel Opened");
                Channel childChannel = e.getChildChannel();
                channelGroup.add(childChannel);
                final ControlServerHandler controller =
                        (ControlServerHandler) childChannel.getPipeline().getContext("control.handler").getHandler();
                // Add the controller to our list
                controlHandlers.add(controller);


                 // And remove it when the channel is closed.
                controller.getChannelClosedFuture().addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        controlHandlers.remove(controller);
                    }
                });
            }

        });

        serverChannel = server.bind(localAddress);
    }

    public void stop() throws TimeoutException {
        boolean isDebugEnabled = LOGGER.isDebugEnabled();
        if (serverChannel != null) {

            serverChannel.close().awaitUninterruptibly(2000);

            if (isDebugEnabled) {
                LOGGER.debug("Server control channel closed.");
            }
        }

        channelGroup.close().awaitUninterruptibly(2000);

        if (isDebugEnabled) {
            LOGGER.debug("Control channels closed.");
        }

        // Note it is important that we wait for the control handler to process the channelClosed
        // event, otherwise there will be a race between it and releasing resources.
        for (ControlServerHandler controller : controlHandlers) {
            controller.getChannelClosedFuture().awaitUninterruptibly(2000);
            // controller.completeShutDown(2000);
        }

        if (clientChannelFactory != null) {
            LOGGER.debug("Releasing tcp client channel factory");
            clientChannelFactory.shutdown();
            clientChannelFactory.releaseExternalResources();
            LOGGER.debug("Released tcp client channel factory");
        }

        if (serverChannelFactory != null) {
            LOGGER.debug("Releasing tcp server channel factory");
            serverChannelFactory.shutdown();
            serverChannelFactory.releaseExternalResources();
            LOGGER.debug("Released tcp server channel factory");
        }

        if (sharedWorkerPool != null) {
            LOGGER.debug("Destroying shared worker pool");
            sharedWorkerPool.destroy();
            LOGGER.debug("Destroyed shared worker pool.");
        }

        if (bootstrapFactory != null) {
            LOGGER.debug("Releasing external resources");
            bootstrapFactory.releaseExternalResources();
            LOGGER.debug("External resources released.");
        }
    }

    public void join() throws InterruptedException {
        if (serverChannel != null) {
            serverChannel.getCloseFuture().await();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy