org.jdiameter.client.impl.transport.tcp.netty.TCPTransportClient.java Source code

Java tutorial

Introduction

Here is the source code for org.jdiameter.client.impl.transport.tcp.netty.TCPTransportClient.java

Source

/*
 * TeleStax, Open Source Cloud Communications
 * Copyright 2011-2016, TeleStax Inc. and individual contributors
 * by the @authors tag.
 *
 * This program is free software: you can redistribute it and/or modify
 * 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 <http://www.gnu.org/licenses/>
 */

package org.jdiameter.client.impl.transport.tcp.netty;

import java.io.IOException;
import java.net.InetSocketAddress;

import org.jdiameter.client.api.IMessage;
import org.jdiameter.client.api.parser.IMessageParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;

/**
 *
 * @author <a href="mailto:jqayyum@gmail.com"> Jehanzeb Qayyum </a>
 */
public class TCPTransportClient {
    protected EventLoopGroup workerGroup;
    protected EventExecutorGroup eventExecutorGroup = new DefaultEventExecutorGroup(1);
    protected Channel channel;
    protected TCPClientConnection parentConnection;
    protected InetSocketAddress destAddress;
    protected InetSocketAddress sourceAddress; // TODO: what?
    protected String socketDescription;
    protected static final Logger logger = LoggerFactory.getLogger(TCPClientConnection.class);
    protected IMessageParser parser;

    protected static final int CONNECT_TIMEOUT = 500; // mills

    protected TCPTransportClient(TCPClientConnection parentConnection, IMessageParser parser) {
        if (parentConnection == null) {
            throw new IllegalArgumentException("Parent connection cannot be null");
        }
        this.parentConnection = parentConnection;

        if (parser == null) {
            throw new IllegalArgumentException("Parser cannot be null");
        }
        this.parser = parser;
    }

    public TCPTransportClient(TCPClientConnection parentConnection, IMessageParser parser,
            InetSocketAddress destAddress, InetSocketAddress sourceAddress) {
        this(parentConnection, parser);

        logger.debug("Client only connection");

        if (destAddress == null && sourceAddress == null) {
            throw new IllegalArgumentException("Either Destination or Source address is required");
        }

        if (sourceAddress != null) {
            this.sourceAddress = sourceAddress;
        }

        if (destAddress != null) {
            this.destAddress = destAddress;
            this.socketDescription = destAddress.toString();
        }
    }

    public TCPTransportClient(TCPClientConnection parentConnection, IMessageParser parser, Channel channel) {
        this(parentConnection, parser);
        logger.debug("Server only connection");

        if (channel == null) {
            throw new IllegalArgumentException("Channel cannot be null");
        }
        this.channel = channel;
        ChannelPipeline pipeline = this.channel.pipeline();
        pipeline.addLast("decoder", new DiameterMessageDecoder(parentConnection, parser));
        pipeline.addLast("encoder", new DiameterMessageEncoder(parser));
        pipeline.addLast(eventExecutorGroup, "msgHandler", new DiameterMessageHandler(parentConnection));

        this.destAddress = (InetSocketAddress) this.channel.remoteAddress();
    }

    public void start() throws InterruptedException {
        logger.debug("Starting TCP Transport on [{}]", socketDescription);
        if (isConnected()) {
            logger.debug("TCP Transport already started, [{}]", socketDescription);
            return;
        }

        this.workerGroup = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap().group(workerGroup).channel(NioSocketChannel.class)
                .option(ChannelOption.SO_KEEPALIVE, true)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONNECT_TIMEOUT)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast("decoder", new DiameterMessageDecoder(parentConnection, parser));
                        pipeline.addLast("encoder", new DiameterMessageEncoder(parser));
                        pipeline.addLast(eventExecutorGroup, "msgHandler",
                                new DiameterMessageHandler(parentConnection));
                    }
                });

        this.channel = bootstrap.remoteAddress(destAddress).connect().sync().channel();
        logger.debug("TCP Transport connected successfully, [{}]", socketDescription);

        parentConnection.onConnected();
    }

    public void stop() {
        logger.debug("Stopping TCP Transport, [{}]", socketDescription);
        if (!isConnected()) {
            logger.debug("Already stoppped TCP Transport, [{}]", socketDescription);
            return;
        }
        closeChannel();
        closeWorkerGroup();
        closeEventExecutorGroup();
        logger.debug("Transport is stopped [{}]", socketDescription);
    }

    private void closeEventExecutorGroup() {
        if (eventExecutorGroup != null) {
            try {
                eventExecutorGroup.shutdownGracefully().sync();
            } catch (InterruptedException e) {
                logger.error("Error stopping socket " + socketDescription, e);
            }
            eventExecutorGroup = null;
        }
    }

    private void closeWorkerGroup() {
        if (workerGroup != null) {
            try {
                workerGroup.shutdownGracefully().sync();
            } catch (InterruptedException e) {
                logger.error("Error stopping socket " + socketDescription, e);
            }
            workerGroup = null;
        }
    }

    private void closeChannel() {
        if (channel != null) {
            try {
                channel.closeFuture().sync();
            } catch (InterruptedException e) {
                logger.error("Error stopping socket " + socketDescription, e);
            }
            channel = null;
        }
    }

    public void release() throws InterruptedException, IOException {
        logger.debug("Releasing TCP Transport, [{}]", socketDescription);
        stop();
        destAddress = null;
        sourceAddress = null;
    }

    public void sendMessage(IMessage message) {
        if (!isConnected()) {
            throw new IllegalStateException("TCP transport is stopped on socket " + socketDescription);
        }
        channel.writeAndFlush(message);
    }

    public String toString() {
        StringBuffer buffer = new StringBuffer();
        buffer.append("Transport to ");
        if (this.destAddress != null) {
            buffer.append(this.destAddress.getHostName());
            buffer.append(":");
            buffer.append(this.destAddress.getPort());
        } else {
            buffer.append("null");
        }
        buffer.append("@");
        buffer.append(super.toString());
        return buffer.toString();
    }

    public TCPClientConnection getParent() {
        return parentConnection;
    }

    public InetSocketAddress getDestAddress() {
        return this.destAddress;
    }

    boolean isConnected() {
        return channel != null && channel.isActive();
    }
}