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

com.zipwhip.api.signals.sockets.netty.NettySignalConnection Maven / Gradle / Ivy

package com.zipwhip.api.signals.sockets.netty;

import com.zipwhip.api.signals.reconnect.DefaultReconnectStrategy;
import com.zipwhip.api.signals.reconnect.ReconnectStrategy;
import com.zipwhip.api.signals.sockets.ConnectionHandle;
import com.zipwhip.api.signals.sockets.ConnectionState;
import com.zipwhip.concurrent.ObservableFuture;
import com.zipwhip.events.ObservableHelper;
import com.zipwhip.executors.CommonExecutorFactory;
import com.zipwhip.executors.CommonExecutorTypes;
import com.zipwhip.executors.NamedThreadFactory;
import com.zipwhip.lifecycle.Destroyable;
import com.zipwhip.lifecycle.DestroyableBase;
import com.zipwhip.util.Asserts;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.socket.oio.OioClientSocketChannelFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;

/**
 * Connects to the SignalServer via Netty over a raw socket
 */
public class NettySignalConnection extends SignalConnectionBase {

    private static final Logger LOGGER = LoggerFactory.getLogger(NettySignalConnection.class);

    private final ChannelWrapperFactory channelWrapperFactory;
    private final ChannelFactory channelFactory;

    /**
     * Create a new {@code NettySignalConnection} with a default {@code ReconnectStrategy} and {@code ChannelPipelineFactory}.
     */
    public NettySignalConnection() {
        this(null, null);
    }

    /**
     * Create a new {@code NettySignalConnection}
     *
     * @param channelPipelineFactory The Factory to create a Netty pipeline.
     */
    public NettySignalConnection(ChannelPipelineFactory channelPipelineFactory) {
        this(null, channelPipelineFactory);
    }

    /**
     * Create a new {@code NettySignalConnection} with a default {@code ChannelPipelineFactory}.
     *
     * @param reconnectStrategy The reconnect strategy to use in the case of socket disconnects.
     */
    public NettySignalConnection(ReconnectStrategy reconnectStrategy) {
        this(reconnectStrategy, null);
    }

    /**
     * Create a new {@code NettySignalConnection}
     *
     * @param reconnectStrategy      The reconnect strategy to use in the case of socket disconnects.
     * @param channelPipelineFactory The Factory to create a Netty pipeline.
     */
    public NettySignalConnection(ReconnectStrategy reconnectStrategy, ChannelPipelineFactory channelPipelineFactory) {
        this(null, reconnectStrategy, channelPipelineFactory);
    }

    /**
     * Create a new {@code NettySignalConnection}
     *
     * @param reconnectStrategy      The reconnect strategy to use in the case of socket disconnects.
     * @param channelPipelineFactory The Factory to create a Netty pipeline.
     */
    public NettySignalConnection(CommonExecutorFactory executorFactory,
                                 ReconnectStrategy reconnectStrategy,
                                 ChannelPipelineFactory channelPipelineFactory) {

        super(executorFactory != null ? executorFactory.create(CommonExecutorTypes.EVENTS, "NettySignalConnection") : null);

        if (executorFactory != null){
            // We created the executor that our parent is using. We need to destroy it.
            this.link(new DestroyableBase() {
                @Override
                protected void onDestroy() {
                    if (executor instanceof ExecutorService) {
                        ((ExecutorService) executor).shutdownNow();
                    }
                }
            });
        }

        if (channelPipelineFactory == null) {
            channelPipelineFactory = new RawSocketIoChannelPipelineFactory();
            this.link((Destroyable) channelPipelineFactory);
        }

        if (reconnectStrategy == null) {
            setReconnectStrategy(new DefaultReconnectStrategy());
            this.link(getReconnectStrategy());
        } else {
            setReconnectStrategy(reconnectStrategy);
        }

        if (executorFactory == null){

            executorFactory = new CommonExecutorFactory() {
                private Map factories = new HashMap();
                private NamedThreadFactory getOrCreate(String name) {
                    if (factories.containsKey(name)) {
                        return factories.get(name);
                    }
                    factories.put(name, new NamedThreadFactory(name));
                    return factories.get(name);
                }

                @Override
                public ExecutorService create(CommonExecutorTypes type, String name) {
                    switch (type) {
                        case BOSS:
                            return java.util.concurrent.Executors.newFixedThreadPool(1, getOrCreate(name + "-boss"));
                        case WORKER:
                            return java.util.concurrent.Executors.newFixedThreadPool(10, getOrCreate(name + "-worker"));
                        case EVENTS:
                            return java.util.concurrent.Executors.newFixedThreadPool(1, getOrCreate(name + "-events"));
                    }

                    throw new IllegalStateException("Not sure! " + type);
                }

                @Override
                public ExecutorService create() {
                    return create(CommonExecutorTypes.BOSS, NettySignalConnection.this.toString());
                }
            };
        }

//        channelFactory = new NioClientSocketChannelFactory(
//                executorFactory.create(CommonExecutorTypes.BOSS, "nio"),
//                executorFactory.create(CommonExecutorTypes.WORKER, "nio"), 1, 1);
        channelFactory = new OioClientSocketChannelFactory(executorFactory.create(CommonExecutorTypes.BOSS, "Netty OIO"));

        this.channelWrapperFactory = new ChannelWrapperFactory(channelPipelineFactory, channelFactory, this, executorFactory);
        this.link(channelWrapperFactory);
    }

    @Override
    protected ConnectionHandle createConnectionHandle() {
        ChannelWrapper channelWrapper = channelWrapperFactory.create();

        // this can never crash.
        ConnectionHandle connectionHandle = channelWrapper.getConnection();

        return connectionHandle;
    }

    @Override
    protected void executeConnect(ConnectionHandle connectionHandle, SocketAddress address) throws Throwable {
        ChannelWrapper channelWrapper = ((ChannelWrapperConnectionHandle)connectionHandle).channelWrapper;
        try {
            // this can crash, so be sure we clean up if it does then rethrow
            channelWrapper.connect(address);
        } catch (Throwable throwable) {
            LOGGER.error("Got exception trying to connect!", throwable);

            executeDisconnectDestroyConnection(connectionHandle, true);

            throw throwable;
        }
    }

    protected void executeDisconnectDestroyConnection(ConnectionHandle connectionHandle, boolean causedByNetwork) {
        ChannelWrapperConnectionHandle con = (ChannelWrapperConnectionHandle) connectionHandle;
        Asserts.assertTrue(!con.isDestroyed(), "Already destroyed?");
        ChannelWrapper w = con.channelWrapper;

        con.causedByNetwork = causedByNetwork;
        if (con.channelWrapper.stateManager.get() != ConnectionState.DISCONNECTED){
            con.channelWrapper.disconnect();
        }
        // dont manually trigger the disconnectFuture. The caller will do that later.


        con.destroy();

        Asserts.assertTrue(con.isDestroyed(), "Connection must be destroyed");
        Asserts.assertTrue(w.isDestroyed(), "ChannelWrapper must be destroyed");
    }

    protected ObservableFuture executeSend(ConnectionHandle connectionHandle, final Object command) {
        synchronized (CONNECTION_BEING_TOUCHED_LOCK) {
            validateConnected();

            return ((ChannelWrapperConnectionHandle) connectionHandle).write(command);
        }
    }

    @Override
    protected Executor getExecutorForConnection(ConnectionHandle connectionHandle) {
        return ((ChannelWrapperConnectionHandle)connectionHandle).channelWrapper.executor;
    }

    @Override
    protected void onDestroy() {
        ConnectionState state = getConnectionState();
        if (state == ConnectionState.CONNECTED || state == ConnectionState.CONNECTING) {
            try {
                executeDisconnectDestroyConnection(connectionHandle, false);
            } catch (Exception e) {
                LOGGER.error("Failed to disconnect!", e);
            }
        }

        if (channelFactory instanceof Destroyable) {
            ((Destroyable) channelFactory).destroy();
        } else {
            channelFactory.releaseExternalResources();
        }
    }

    @Override
    protected  void notifyEvent(final ConnectionHandle connectionHandle, final ObservableHelper event, final T data) {
        ((ChannelWrapperConnectionHandle) connectionHandle).channelWrapper.executor.execute(new Runnable() {
            @Override
            public void run() {
                synchronized (NettySignalConnection.this) {
                    synchronized (CONNECTION_BEING_TOUCHED_LOCK) {
                        synchronized (connectionHandle) {
                            event.notifyObservers(connectionHandle, data);
                        }
                    }
                }
            }
        });
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy