io.jsync.net.impl.DefaultNetClient.java Source code

Java tutorial

Introduction

Here is the source code for io.jsync.net.impl.DefaultNetClient.java

Source

/*
 * Copyright (c) 2011-2013 The original author or authors
 * ------------------------------------------------------
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution.
 *
 *     The Eclipse Public License is available at
 *     http://www.eclipse.org/legal/epl-v10.html
 *
 *     The Apache License v2.0 is available at
 *     http://www.opensource.org/licenses/apache2.0.php
 *
 * You may elect to redistribute this code under either of these licenses.
 */

package io.jsync.net.impl;

import io.jsync.AsyncResult;
import io.jsync.Handler;
import io.jsync.impl.AsyncInternal;
import io.jsync.impl.Closeable;
import io.jsync.impl.DefaultContext;
import io.jsync.impl.DefaultFutureResult;
import io.jsync.logging.Logger;
import io.jsync.logging.impl.LoggerFactory;
import io.jsync.net.NetClient;
import io.jsync.net.NetSocket;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

import javax.net.ssl.SSLContext;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author <a href="http://tfox.org">Tim Fox</a>
 */
public class DefaultNetClient implements NetClient {

    private static final Logger log = LoggerFactory.getLogger(DefaultNetClient.class);

    private final AsyncInternal async;
    private final DefaultContext actualCtx;
    private final TCPSSLHelper tcpHelper = new TCPSSLHelper();
    private final Map<Channel, DefaultNetSocket> socketMap = new ConcurrentHashMap<>();
    private final Closeable closeHook = new Closeable() {
        @Override
        public void close(Handler<AsyncResult<Void>> doneHandler) {
            DefaultNetClient.this.close();
            doneHandler.handle(new DefaultFutureResult<>((Void) null));
        }
    };
    private Bootstrap bootstrap;
    private int reconnectAttempts;
    private long reconnectInterval = 1000;
    private boolean configurable = true;

    public DefaultNetClient(AsyncInternal async) {
        this.async = async;
        actualCtx = async.getOrCreateContext();
        actualCtx.addCloseHook(closeHook);
    }

    private static void doFailed(Handler<AsyncResult<NetSocket>> connectHandler, Throwable t) {
        connectHandler.handle(new DefaultFutureResult<NetSocket>(t));
    }

    @Override
    public NetClient connect(int port, String host, final Handler<AsyncResult<NetSocket>> connectHandler) {
        connect(port, host, connectHandler, reconnectAttempts);
        return this;
    }

    @Override
    public NetClient connect(int port, final Handler<AsyncResult<NetSocket>> connectCallback) {
        connect(port, "localhost", connectCallback);
        return this;
    }

    @Override
    public void close() {
        for (NetSocket sock : socketMap.values()) {
            sock.close();
        }
        actualCtx.removeCloseHook(closeHook);
    }

    @Override
    public NetClient setReconnectAttempts(int attempts) {
        checkConfigurable();
        if (attempts < -1) {
            throw new IllegalArgumentException("reconnect attempts must be >= -1");
        }
        this.reconnectAttempts = attempts;
        return this;
    }

    @Override
    public int getReconnectAttempts() {
        return reconnectAttempts;
    }

    @Override
    public NetClient setReconnectInterval(long interval) {
        checkConfigurable();
        if (interval < 1) {
            throw new IllegalArgumentException("reconnect interval nust be >= 1");
        }
        this.reconnectInterval = interval;
        return this;
    }

    @Override
    public long getReconnectInterval() {
        return reconnectInterval;
    }

    @Override
    public boolean isTCPNoDelay() {
        return tcpHelper.isTCPNoDelay();
    }

    @Override
    public int getSendBufferSize() {
        return tcpHelper.getSendBufferSize();
    }

    @Override
    public int getReceiveBufferSize() {
        return tcpHelper.getReceiveBufferSize();
    }

    @Override
    public boolean isTCPKeepAlive() {
        return tcpHelper.isTCPKeepAlive();
    }

    @Override
    public boolean isReuseAddress() {
        return tcpHelper.isReuseAddress();
    }

    @Override
    public int getSoLinger() {
        return tcpHelper.getSoLinger();
    }

    @Override
    public int getTrafficClass() {
        return tcpHelper.getTrafficClass();
    }

    @Override
    public int getConnectTimeout() {
        return tcpHelper.getConnectTimeout();
    }

    @Override
    public NetClient setTCPNoDelay(boolean tcpNoDelay) {
        checkConfigurable();
        tcpHelper.setTCPNoDelay(tcpNoDelay);
        return this;
    }

    @Override
    public NetClient setSendBufferSize(int size) {
        checkConfigurable();
        tcpHelper.setSendBufferSize(size);
        return this;
    }

    @Override
    public NetClient setReceiveBufferSize(int size) {
        checkConfigurable();
        tcpHelper.setReceiveBufferSize(size);
        return this;
    }

    @Override
    public NetClient setTCPKeepAlive(boolean keepAlive) {
        checkConfigurable();
        tcpHelper.setTCPKeepAlive(keepAlive);
        return this;
    }

    @Override
    public NetClient setReuseAddress(boolean reuse) {
        checkConfigurable();
        tcpHelper.setReuseAddress(reuse);
        return this;
    }

    @Override
    public NetClient setSoLinger(int linger) {
        checkConfigurable();
        tcpHelper.setSoLinger(linger);
        return this;
    }

    @Override
    public NetClient setTrafficClass(int trafficClass) {
        checkConfigurable();
        tcpHelper.setTrafficClass(trafficClass);
        return this;
    }

    @Override
    public NetClient setConnectTimeout(int timeout) {
        checkConfigurable();
        tcpHelper.setConnectTimeout(timeout);
        return this;
    }

    @Override
    public boolean isSSL() {
        return tcpHelper.isSSL();
    }

    @Override
    public String getKeyStorePath() {
        return tcpHelper.getKeyStorePath();
    }

    @Override
    public String getKeyStorePassword() {
        return tcpHelper.getKeyStorePassword();
    }

    @Override
    public String getTrustStorePath() {
        return tcpHelper.getTrustStorePath();
    }

    @Override
    public String getTrustStorePassword() {
        return tcpHelper.getTrustStorePassword();
    }

    @Override
    public boolean isTrustAll() {
        return tcpHelper.isTrustAll();
    }

    @Override
    public NetClient setSSL(boolean ssl) {
        checkConfigurable();
        tcpHelper.setSSL(ssl);
        return this;
    }

    @Override
    public NetClient setSSLContext(SSLContext sslContext) {
        checkConfigurable();
        tcpHelper.setExternalSSLContext(sslContext);
        return this;
    }

    @Override
    public NetClient setKeyStorePath(String path) {
        checkConfigurable();
        tcpHelper.setKeyStorePath(path);
        return this;
    }

    @Override
    public NetClient setKeyStorePassword(String pwd) {
        checkConfigurable();
        tcpHelper.setKeyStorePassword(pwd);
        return this;
    }

    @Override
    public NetClient setTrustStorePath(String path) {
        checkConfigurable();
        tcpHelper.setTrustStorePath(path);
        return this;
    }

    @Override
    public NetClient setTrustStorePassword(String pwd) {
        checkConfigurable();
        tcpHelper.setTrustStorePassword(pwd);
        return this;
    }

    @Override
    public NetClient setTrustAll(boolean trustAll) {
        checkConfigurable();
        tcpHelper.setTrustAll(trustAll);
        return this;
    }

    @Override
    public NetClient setUsePooledBuffers(boolean pooledBuffers) {
        checkConfigurable();
        tcpHelper.setUsePooledBuffers(pooledBuffers);
        return this;
    }

    @Override
    public boolean isUsePooledBuffers() {
        return tcpHelper.isUsePooledBuffers();
    }

    private void checkConfigurable() {
        if (!configurable) {
            throw new IllegalStateException("Can't set property after connect has been called");
        }
    }

    private void connect(final int port, final String host, final Handler<AsyncResult<NetSocket>> connectHandler,
            final int remainingAttempts) {
        if (bootstrap == null) {
            tcpHelper.checkSSL(async);

            bootstrap = new Bootstrap();
            bootstrap.group(actualCtx.getEventLoop());
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.handler(new ChannelInitializer<Channel>() {
                @Override
                protected void initChannel(Channel ch) throws Exception {
                    ChannelPipeline pipeline = ch.pipeline();
                    if (tcpHelper.isSSL()) {
                        SslHandler sslHandler = tcpHelper.createSslHandler(async, true);
                        pipeline.addLast("ssl", sslHandler);
                    }
                    if (tcpHelper.isSSL()) {
                        // only add ChunkedWriteHandler when SSL is enabled otherwise it is not needed as FileRegion is used.
                        pipeline.addLast("chunkedWriter", new ChunkedWriteHandler()); // For large file / sendfile support
                    }
                    pipeline.addLast("handler", new AsyncNetHandler(async, socketMap));
                }
            });
            configurable = false;
        }
        tcpHelper.applyConnectionOptions(bootstrap);
        ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
        future.addListener(new ChannelFutureListener() {
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                final Channel ch = channelFuture.channel();

                if (channelFuture.isSuccess()) {

                    if (tcpHelper.isSSL()) {
                        // TCP connected, so now we must do the SSL handshake

                        SslHandler sslHandler = ch.pipeline().get(SslHandler.class);

                        Future<Channel> fut = sslHandler.handshakeFuture();
                        fut.addListener(new GenericFutureListener<Future<Channel>>() {
                            @Override
                            public void operationComplete(Future<Channel> future) throws Exception {
                                if (future.isSuccess()) {
                                    connected(ch, connectHandler);
                                } else {
                                    failed(ch, future.cause(), connectHandler);
                                }
                            }
                        });
                    } else {
                        connected(ch, connectHandler);
                    }
                } else {
                    if (remainingAttempts > 0 || remainingAttempts == -1) {
                        actualCtx.execute(ch.eventLoop(), new Runnable() {
                            public void run() {
                                log.debug("Failed to create connection. Will retry in " + reconnectInterval
                                        + " milliseconds");
                                //Set a timer to retry connection
                                async.setTimer(reconnectInterval, new Handler<Long>() {
                                    public void handle(Long timerID) {
                                        connect(port, host, connectHandler,
                                                remainingAttempts == -1 ? remainingAttempts
                                                        : remainingAttempts - 1);
                                    }
                                });
                            }
                        });
                    } else {
                        failed(ch, channelFuture.cause(), connectHandler);
                    }
                }
            }
        });
    }

    private void connected(final Channel ch, final Handler<AsyncResult<NetSocket>> connectHandler) {
        actualCtx.execute(ch.eventLoop(), new Runnable() {
            public void run() {
                doConnected(ch, connectHandler);
            }
        });
    }

    private void doConnected(Channel ch, final Handler<AsyncResult<NetSocket>> connectHandler) {
        DefaultNetSocket sock = new DefaultNetSocket(async, ch, actualCtx, tcpHelper, true);
        socketMap.put(ch, sock);
        connectHandler.handle(new DefaultFutureResult<NetSocket>(sock));
    }

    private void failed(Channel ch, final Throwable t, final Handler<AsyncResult<NetSocket>> connectHandler) {
        ch.close();
        actualCtx.execute(ch.eventLoop(), new Runnable() {
            public void run() {
                doFailed(connectHandler, t);
            }
        });
    }
}