net.timewalker.ffmq4.transport.tcp.io.TcpPacketTransport.java Source code

Java tutorial

Introduction

Here is the source code for net.timewalker.ffmq4.transport.tcp.io.TcpPacketTransport.java

Source

/*
 * This file is part of FFMQ.
 *
 * FFMQ is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * FFMQ 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with FFMQ; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package net.timewalker.ffmq4.transport.tcp.io;

import java.io.IOException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.URI;

import javax.jms.JMSException;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;

import net.timewalker.ffmq4.FFMQClientSettings;
import net.timewalker.ffmq4.FFMQCoreSettings;
import net.timewalker.ffmq4.FFMQException;
import net.timewalker.ffmq4.transport.PacketTransportException;
import net.timewalker.ffmq4.transport.PacketTransportType;
import net.timewalker.ffmq4.transport.packet.AbstractPacket;
import net.timewalker.ffmq4.transport.tcp.AbstractTcpPacketTransport;
import net.timewalker.ffmq4.transport.tcp.SocketUtils;
import net.timewalker.ffmq4.utils.Settings;
import net.timewalker.ffmq4.utils.ssl.PermissiveTrustManager;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * TcpPacketTransport
 */
public final class TcpPacketTransport extends AbstractTcpPacketTransport {
    private static final Log log = LogFactory.getLog(TcpPacketTransport.class);

    // Attributes
    private Settings settings;
    private int maxPacketSize;

    // Runtime
    private Socket socket;
    private TcpPacketReceiver receiver;
    private TcpPacketSender sender;
    private Thread receiverThread;
    private Thread senderThread;

    /**
     * Constructor
     */
    public TcpPacketTransport(String id, URI transportURI, Settings settings) throws PacketTransportException {
        super(id, true, settings);
        init(settings);
        this.socket = connect(transportURI);
    }

    /**
     * Constructor
     */
    public TcpPacketTransport(String id, Socket socket, Settings settings) throws PacketTransportException {
        super(id, false, settings);
        this.socket = SocketUtils.setupSocket(socket, socketSendBufferSize, socketRecvBufferSize);
        init(settings);
    }

    private void init(Settings settings) {
        this.settings = settings;
        this.maxPacketSize = settings.getIntProperty(FFMQCoreSettings.TRANSPORT_TCP_PACKET_MAX_SIZE,
                1024 * 1024 + 1024);
    }

    /* (non-Javadoc)
     * @see net.timewalker.ffmq4.transport.tcp.AbstractTcpPacketTransport#getRemotePeer()
     */
    @Override
    public SocketAddress getRemotePeer() {
        Socket sock = socket;
        return sock != null ? sock.getRemoteSocketAddress() : null;
    }

    /**
     * Connect the transport to its remote endpoint
     */
    private Socket connect(URI transportURI) throws PacketTransportException {
        String protocol = transportURI.getScheme();
        String host = transportURI.getHost();
        int port = transportURI.getPort();
        int connectTimeout = settings.getIntProperty(FFMQClientSettings.TRANSPORT_TCP_CONNECT_TIMEOUT, 30);

        try {
            Socket socket = SocketUtils.setupSocket(createSocket(protocol), socketSendBufferSize,
                    socketRecvBufferSize);

            log.debug("#" + id + " opening a TCP connection to " + host + ":" + port);
            socket.connect(new InetSocketAddress(host, port), connectTimeout * 1000);

            return socket;
        } catch (ConnectException e) {
            log.error("#" + id + " could not connect to " + host + ":" + port + " (timeout=" + connectTimeout
                    + "s) : " + e.getMessage());
            throw new PacketTransportException("Could not connect to " + host + ":" + port + " : " + e.toString());
        } catch (Exception e) {
            log.error(
                    "#" + id + " could not connect to " + host + ":" + port + " (timeout=" + connectTimeout + "s)",
                    e);
            throw new PacketTransportException("Could not connect to " + host + ":" + port + " : " + e.toString());
        }
    }

    private Socket createSocket(String protocol) throws JMSException {
        if (protocol.equals(PacketTransportType.TCPS)) {
            try {
                return createSSLContext().getSocketFactory().createSocket();
            } catch (IOException e) {
                throw new FFMQException("Cannot create SSL socket", "TRANSPORT_ERROR", e);
            }
        } else
            return new Socket();
    }

    private SSLContext createSSLContext() throws JMSException {
        try {
            String sslProtocol = settings.getStringProperty(FFMQClientSettings.TRANSPORT_TCP_SSL_PROTOCOL, "SSLv3");
            boolean ignoreCertificates = settings
                    .getBooleanProperty(FFMQClientSettings.TRANSPORT_TCP_SSL_IGNORE_CERTS, false);

            SSLContext sslContext = SSLContext.getInstance(sslProtocol);
            log.debug("#" + id + " created an SSL context : protocol=[" + sslContext.getProtocol() + "] provider=["
                    + sslContext.getProvider() + "]");

            // Load available keys
            KeyManager[] keyManagers = null;
            TrustManager[] trustManagers = null;

            if (ignoreCertificates)
                trustManagers = new TrustManager[] { new PermissiveTrustManager() };

            sslContext.init(keyManagers, trustManagers, null);

            return sslContext;
        } catch (Exception e) {
            throw new FFMQException("Cannot create SSL context", "TRANSPORT_ERROR", e);
        }
    }

    /*
     * (non-Javadoc)
     * @see net.timewalker.ffmq4.remote.transport.PacketTransport#start()
     */
    @Override
    public void start() throws PacketTransportException {
        try {
            NetworkInputChannel inputChannel = new NetworkInputChannel(initialPacketBufferSize,
                    new TcpBufferedInputStream(socket.getInputStream(), streamRecvBufferSize));
            NetworkOutputChannel outputChannel = new NetworkOutputChannel(initialPacketBufferSize,
                    new TcpBufferedOutputStream(socket.getOutputStream(), streamSendBufferSize));

            sender = new TcpPacketSender(this, outputChannel, listener, client ? pingInterval : -1,
                    sendQueueMaxSize);

            receiver = new TcpPacketReceiver(this, inputChannel, listener, pingInterval,
                    client ? -1 : maxPacketSize);

            senderThread = new Thread(sender, "TcpPacketSender[" + (client ? "client" : "server") + "]");
            senderThread.start();

            receiverThread = new Thread(receiver, "TcpPacketReceiver[" + (client ? "client" : "server") + "]");
            receiverThread.start();
        } catch (Exception e) {
            log.error("#" + id + " cannot start TCP packet I/O handlers", e);
            throw new PacketTransportException("Cannot start TCP packet I/O handlers : " + e.toString());
        }
    }

    /*
     * (non-Javadoc)
     * @see net.timewalker.ffmq4.remote.transport.PacketTransport#send(net.timewalker.ffmq4.remote.transport.packet.AbstractPacket)
     */
    @Override
    public void send(AbstractPacket packet) throws PacketTransportException {
        if (closed)
            throw new PacketTransportException("Transport is closed");

        sender.send(packet);
    }

    @Override
    public boolean needsThrottling() {
        return sender.needsThrottling();
    }

    protected void closeSocket() {
        try {
            if (socket != null)
                socket.close();
        } catch (Exception e) {
            log.error("#" + id + " cannot close socket", e);
        } finally {
            socket = null;
        }
    }

    protected void closeTransport(boolean linkFailed) {
        synchronized (closeLock) {
            if (closed)
                return; // Already closed
            closed = true;
        }

        if (sender != null)
            sender.pleaseStop();

        if (receiver != null)
            receiver.pleaseStop();

        // Close the socket
        closeSocket();

        // Notify listener
        if (listener != null)
            listener.transportClosed(linkFailed, true);
    }

    /*
     * (non-Javadoc)
     * @see net.timewalker.ffmq4.remote.transport.PacketTransport#close()
     */
    @Override
    public void close() {
        closeTransport(false);

        // Wait for threads to complete
        try {
            if (receiverThread != null && Thread.currentThread() != receiverThread)
                receiverThread.join();
        } catch (InterruptedException e) {
            log.error("#" + id + " wait for receiver thread termination was interrupted.");
        }
        try {
            if (senderThread != null && Thread.currentThread() != senderThread)
                senderThread.join();
        } catch (InterruptedException e) {
            log.error("#" + id + " wait for sender thread termination was interrupted.");
        }
    }
}