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

org.objectfabric.NettyRemote Maven / Gradle / Ivy

The newest version!
/**
 * This file is part of ObjectFabric (http://objectfabric.org).
 *
 * ObjectFabric is licensed under the Apache License, Version 2.0, the terms
 * of which may be found at http://www.apache.org/licenses/LICENSE-2.0.html.
 * 
 * Copyright ObjectFabric Inc.
 * 
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

package org.objectfabric;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URISyntaxException;
import java.nio.channels.CancelledKeyException;

import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketVersion;
import org.jboss.netty.util.CharsetUtil;

class NettyRemote extends Remote {

    private final NettyURIHandler _handler;

    protected NettyRemote(Address address, NettyURIHandler handler) {
        super(false, address);

        _handler = handler;
    }

    @Override
    ConnectionAttempt createAttempt() {
        return new Attempt();
    }

    private final class Attempt implements ConnectionAttempt, Runnable {

        private volatile boolean _cancelled;

        private volatile ChannelFuture _connect;

        @Override
        public void start() {
            if (ClientURIHandler.isEnabled())
                ThreadPool.getInstance().execute(this);
        }

        @Override
        public void cancel() {
            _cancelled = true;

            ChannelFuture connect = _connect;

            if (connect != null)
                connect.cancel();
        }

        @Override
        public void run() {
            if (!_cancelled) {
                InetAddress host = null;

                try {
                    host = InetAddress.getByName(address().Host);
                } catch (Exception e) {
                    onError(null, Strings.URI_UNRESOLVED + address() + ", " + e, false);
                    return;
                }

                if (host != null) {
                    try {
                        _connect = connect(host);
                    } catch (Exception e) {
                        onError(null, Platform.get().getStackAsString(e), false);
                        return;
                    }
                }
            }
        }
    }

    protected ChannelFuture connect(InetAddress host) {
        ClientBootstrap bootstrap = new ClientBootstrap(_handler.getChannelFactory());
        int port = address().Port;
        boolean abort = false;
        final boolean tcp = Remote.TCP.equals(address().Scheme);
        final boolean ssl = Remote.SSL.equals(address().Scheme);
        final boolean ws = Remote.WS.equals(address().Scheme);
        final boolean wss = Remote.WSS.equals(address().Scheme);

        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {

            @Override
            public ChannelPipeline getPipeline() throws Exception {
                ChannelPipeline pipeline = _handler.createPipeline(address());

                if (tcp || ssl)
                    pipeline.addLast("tcp-handler", new TCPClientHandler());

                if (ws || wss)
                    pipeline.addLast("ws-handler", new WebSocketClientHandler());

                return pipeline;
            }
        });

        if (ws) {
            if (port == Address.NULL_PORT)
                port = 80;
        } else if (wss) {
            if (port == Address.NULL_PORT)
                port = 443;
        }

        if (port == Address.NULL_PORT) {
            onError(null, Strings.URI_MISSING_PORT + address(), false);
            abort = true;
        }

        ChannelFuture future = null;

        if (!abort)
            future = bootstrap.connect(new InetSocketAddress(host, port));

        return future;
    }

    protected NettyConnection createConnection(Channel channel, boolean webSocket) {
        return new NettyConnection(this, channel, webSocket, null);
    }

    protected class TCPClientHandler extends SimpleChannelUpstreamHandler {

        NettyConnection _connection;

        @Override
        public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
            _connection = createConnection(ctx.getChannel(), false);
            onConnection(_connection);
        }

        @Override
        public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
            _connection.read((ChannelBuffer) e.getMessage());
        }

        @Override
        public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) {
            onError(_connection, Strings.DISCONNECTED, true);
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
            if (e.getCause() instanceof IOException)
                return;

            if (e.getCause() instanceof CancelledKeyException)
                return;

            Log.write(e.getCause());
        }
    }

    protected class WebSocketClientHandler extends TCPClientHandler {

        private final WebSocketClientHandshaker _handshaker;

        WebSocketClientHandler() {
            // HashMap customHeaders = new HashMap();
            // customHeaders.put("MyHeader", "MyValue");
            java.net.URI uri;

            try {
                uri = new java.net.URI(address().toString() + Remote.WS_PATH);
            } catch (URISyntaxException e) {
                throw new RuntimeException(e);
            }

            _handshaker = new WebSocketClientHandshakerFactory().newHandshaker(uri, WebSocketVersion.V13, null, false, null);
        }

        @Override
        public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
            _handshaker.handshake(ctx.getChannel());
        }

        @Override
        public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
            Channel channel = ctx.getChannel();

            if (!_handshaker.isHandshakeComplete())
                _handshaker.finishHandshake(channel, (HttpResponse) e.getMessage());
            else {
                if (_connection == null) {
                    _connection = createConnection(ctx.getChannel(), true);
                    onConnection(_connection);
                }

                if (e.getMessage() instanceof HttpResponse) {
                    HttpResponse response = (HttpResponse) e.getMessage();
                    throw new Exception("Unexpected HttpResponse (status=" + response.getStatus() + ", content=" + response.getContent().toString(CharsetUtil.UTF_8) + ")");
                }

                WebSocketFrame frame = (WebSocketFrame) e.getMessage();

                if (frame instanceof BinaryWebSocketFrame) {
                    BinaryWebSocketFrame data = (BinaryWebSocketFrame) frame;
                    _connection.read(data.getBinaryData());
                } else if (frame instanceof CloseWebSocketFrame)
                    channel.close();
                else
                    Debug.failAlways();
            }
        }
    }

    @Override
    Headers headers() {
        return _handler.getHeaders(address());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy