org.mobicents.protocols.sctp.netty.NettyServerImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.mobicents.protocols.sctp.netty.NettyServerImpl.java

Source

/*
 * TeleStax, Open Source Cloud Communications
 * Copyright 2011-2014, 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.mobicents.protocols.sctp.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.ServerChannel;
import io.netty.channel.sctp.SctpChannelOption;
import io.netty.channel.sctp.SctpServerChannel;
import io.netty.channel.sctp.nio.NioSctpServerChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.List;

import javolution.util.FastList;
import javolution.xml.XMLFormat;
import javolution.xml.stream.XMLStreamException;

import org.apache.log4j.Logger;
import org.mobicents.protocols.api.Association;
import org.mobicents.protocols.api.IpChannelType;
import org.mobicents.protocols.api.Server;

/**
 * @author <a href="mailto:amit.bhayani@telestax.com">Amit Bhayani</a>
 * 
 */
public class NettyServerImpl implements Server {

    private static final Logger logger = Logger.getLogger(NettyServerImpl.class.getName());

    private static final String COMMA = ", ";
    private static final String NAME = "name";
    private static final String HOST_ADDRESS = "hostAddress";
    private static final String HOST_PORT = "hostPort";
    private static final String IPCHANNEL_TYPE = "ipChannelType";

    private static final String ASSOCIATIONS = "associations";
    private static final String EXTRA_HOST_ADDRESS = "extraHostAddress";
    private static final String ACCEPT_ANONYMOUS_CONNECTIONS = "acceptAnonymousConnections";
    private static final String MAX_CONCURRENT_CONNECTIONS_COUNT = "maxConcurrentConnectionsCount";

    private static final String STARTED = "started";

    private static final String EXTRA_HOST_ADDRESS_SIZE = "extraHostAddresseSize";

    private String name;
    private String hostAddress;
    private int hostport;
    private volatile boolean started = false;
    private IpChannelType ipChannelType;
    private boolean acceptAnonymousConnections;
    private int maxConcurrentConnectionsCount;
    private String[] extraHostAddresses;

    private NettySctpManagementImpl management = null;

    protected FastList<String> associations = new FastList<String>();
    protected FastList<Association> anonymAssociations = new FastList<Association>();

    // Netty declarations
    // The channel on which we'll accept connections
    private SctpServerChannel serverChannelSctp;
    private NioServerSocketChannel serverChannelTcp;

    /**
     * 
     */
    public NettyServerImpl() {
        super();
    }

    /**
     * @param name
     * @param ip
     * @param port
     * @throws IOException
     */
    public NettyServerImpl(String name, String hostAddress, int hostport, IpChannelType ipChannelType,
            boolean acceptAnonymousConnections, int maxConcurrentConnectionsCount, String[] extraHostAddresses)
            throws IOException {
        super();
        this.name = name;
        this.hostAddress = hostAddress;
        this.hostport = hostport;
        this.ipChannelType = ipChannelType;
        this.acceptAnonymousConnections = acceptAnonymousConnections;
        this.maxConcurrentConnectionsCount = maxConcurrentConnectionsCount;
        this.extraHostAddresses = extraHostAddresses;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Server#getIpChannelType()
     */
    @Override
    public IpChannelType getIpChannelType() {
        return this.ipChannelType;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Server#isAcceptAnonymousConnections()
     */
    @Override
    public boolean isAcceptAnonymousConnections() {
        return acceptAnonymousConnections;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Server#getMaxConcurrentConnectionsCount()
     */
    @Override
    public int getMaxConcurrentConnectionsCount() {
        return this.maxConcurrentConnectionsCount;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Server#setMaxConcurrentConnectionsCount(int)
     */
    @Override
    public void setMaxConcurrentConnectionsCount(int val) {
        this.maxConcurrentConnectionsCount = val;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Server#getName()
     */
    @Override
    public String getName() {
        return this.name;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Server#getHostAddress()
     */
    @Override
    public String getHostAddress() {
        return this.hostAddress;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Server#getHostport()
     */
    @Override
    public int getHostport() {
        return this.hostport;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Server#getExtraHostAddresses()
     */
    @Override
    public String[] getExtraHostAddresses() {
        return this.extraHostAddresses;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Server#isStarted()
     */
    @Override
    public boolean isStarted() {
        return this.started;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Server#getAssociations()
     */
    @Override
    public List<String> getAssociations() {
        return this.associations.unmodifiable();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Server#getAnonymAssociations()
     */
    @Override
    public List<Association> getAnonymAssociations() {
        return this.anonymAssociations.unmodifiable();
    }

    protected ServerChannel getIpChannel() {
        if (this.ipChannelType == IpChannelType.SCTP)
            return this.serverChannelSctp;
        else
            return this.serverChannelTcp;
    }

    /**
     * @param management the management to set
     */
    protected void setManagement(NettySctpManagementImpl management) {
        this.management = management;
    }

    protected void start() throws Exception {
        this.initSocket();
        this.started = true;

        if (logger.isInfoEnabled()) {
            logger.info(String.format("Started Server=%s", this.name));
        }
    }

    protected void stop() throws Exception {
        FastList<String> tempAssociations = associations;
        for (FastList.Node<String> n = tempAssociations.head(), end = tempAssociations
                .tail(); (n = n.getNext()) != end;) {
            String assocName = n.getValue();
            Association associationTemp = this.management.getAssociation(assocName);
            if (associationTemp.isStarted()) {
                throw new Exception(
                        String.format("Stop all the associations first. Association=%s is still started",
                                associationTemp.getName()));
            }
        }

        synchronized (this.anonymAssociations) {
            // stopping all anonymous associations
            for (Association ass : this.anonymAssociations) {
                ass.stopAnonymousAssociation();
            }
            this.anonymAssociations.clear();
        }

        this.started = false;

        if (logger.isInfoEnabled()) {
            logger.info(String.format("Stoped Server=%s", this.name));
        }

        // Stop underlying channel and wait till its done
        if (this.getIpChannel() != null) {
            try {
                this.getIpChannel().close().sync();
            } catch (Exception e) {
                logger.warn(String.format("Error while stopping the Server=%s", this.name), e);
            }
        }
    }

    private void initSocket() throws Exception {
        ServerBootstrap b = new ServerBootstrap();
        b.group(this.management.getBossGroup(), this.management.getWorkerGroup());
        if (this.ipChannelType == IpChannelType.SCTP) {
            b.channel(NioSctpServerChannel.class);
            b.option(ChannelOption.SO_BACKLOG, 100);
            b.childHandler(new NettySctpServerChannelInitializer(this, this.management));
            this.applySctpOptions(b);
        } else {
            b.channel(NioServerSocketChannel.class);
            b.option(ChannelOption.SO_BACKLOG, 100);
            b.childHandler(new NettyTcpServerChannelInitializer(this, this.management));
        }
        b.handler(new LoggingHandler(LogLevel.INFO));

        InetSocketAddress localAddress = new InetSocketAddress(this.hostAddress, this.hostport);

        // Bind the server to primary address.
        ChannelFuture channelFuture = b.bind(localAddress).sync();

        // Get the underlying sctp channel
        if (this.ipChannelType == IpChannelType.SCTP) {
            this.serverChannelSctp = (SctpServerChannel) channelFuture.channel();

            // Bind the secondary address.
            // Please note that, bindAddress in the client channel should be done before connecting if you have not
            // enable Dynamic Address Configuration. See net.sctp.addip_enable kernel param
            if (this.extraHostAddresses != null) {
                for (int count = 0; count < this.extraHostAddresses.length; count++) {
                    String localSecondaryAddress = this.extraHostAddresses[count];
                    InetAddress localSecondaryInetAddress = InetAddress.getByName(localSecondaryAddress);

                    channelFuture = this.serverChannelSctp.bindAddress(localSecondaryInetAddress).sync();
                }
            }

            if (logger.isInfoEnabled()) {
                logger.info(String.format("SctpServerChannel bound to=%s ",
                        this.serverChannelSctp.allLocalAddresses()));
            }
        } else {
            this.serverChannelTcp = (NioServerSocketChannel) channelFuture.channel();

            if (logger.isInfoEnabled()) {
                logger.info(
                        String.format("ServerSocketChannel bound to=%s ", this.serverChannelTcp.localAddress()));
            }
        }
    }

    private void applySctpOptions(ServerBootstrap b) {
        b.childOption(SctpChannelOption.SCTP_NODELAY, this.management.getOptionSctpNodelay());
        b.childOption(SctpChannelOption.SCTP_DISABLE_FRAGMENTS, this.management.getOptionSctpDisableFragments());
        b.childOption(SctpChannelOption.SCTP_FRAGMENT_INTERLEAVE,
                this.management.getOptionSctpFragmentInterleave());
        b.childOption(SctpChannelOption.SCTP_INIT_MAXSTREAMS, this.management.getOptionSctpInitMaxstreams());
        b.childOption(SctpChannelOption.SO_SNDBUF, this.management.getOptionSoSndbuf());
        b.childOption(SctpChannelOption.SO_RCVBUF, this.management.getOptionSoRcvbuf());
        b.childOption(SctpChannelOption.SO_LINGER, this.management.getOptionSoLinger());
    }

    @Override
    public String toString() {

        StringBuilder sb = new StringBuilder();

        sb.append("Server [name=").append(this.name).append(", started=").append(this.started)
                .append(", hostAddress=").append(this.hostAddress).append(", hostPort=").append(hostport)
                .append(", ipChannelType=").append(ipChannelType).append(", acceptAnonymousConnections=")
                .append(this.acceptAnonymousConnections).append(", maxConcurrentConnectionsCount=")
                .append(this.maxConcurrentConnectionsCount).append(", associations(anonymous does not included)=[");

        for (FastList.Node<String> n = this.associations.head(), end = this.associations
                .tail(); (n = n.getNext()) != end;) {
            sb.append(n.getValue());
            sb.append(", ");
        }

        sb.append("], extraHostAddress=[");

        if (this.extraHostAddresses != null) {
            for (int i = 0; i < this.extraHostAddresses.length; i++) {
                String extraHostAddress = this.extraHostAddresses[i];
                sb.append(extraHostAddress);
                sb.append(", ");
            }
        }

        sb.append("]]");

        return sb.toString();
    }

    /**
     * XML Serialization/Deserialization
     */
    protected static final XMLFormat<NettyServerImpl> SERVER_XML = new XMLFormat<NettyServerImpl>(
            NettyServerImpl.class) {

        @SuppressWarnings("unchecked")
        @Override
        public void read(javolution.xml.XMLFormat.InputElement xml, NettyServerImpl server)
                throws XMLStreamException {
            server.name = xml.getAttribute(NAME, "");
            server.started = xml.getAttribute(STARTED, false);
            server.hostAddress = xml.getAttribute(HOST_ADDRESS, "");
            server.hostport = xml.getAttribute(HOST_PORT, 0);
            server.ipChannelType = IpChannelType
                    .getInstance(xml.getAttribute(IPCHANNEL_TYPE, IpChannelType.SCTP.getCode()));
            if (server.ipChannelType == null)
                throw new XMLStreamException("Bad value for server.ipChannelType");

            server.acceptAnonymousConnections = xml.getAttribute(ACCEPT_ANONYMOUS_CONNECTIONS, false);
            server.maxConcurrentConnectionsCount = xml.getAttribute(MAX_CONCURRENT_CONNECTIONS_COUNT, 0);

            int extraHostAddressesSize = xml.getAttribute(EXTRA_HOST_ADDRESS_SIZE, 0);
            server.extraHostAddresses = new String[extraHostAddressesSize];

            for (int i = 0; i < extraHostAddressesSize; i++) {
                server.extraHostAddresses[i] = xml.get(EXTRA_HOST_ADDRESS, String.class);
            }

            server.associations = xml.get(ASSOCIATIONS, FastList.class);
        }

        @Override
        public void write(NettyServerImpl server, javolution.xml.XMLFormat.OutputElement xml)
                throws XMLStreamException {
            xml.setAttribute(NAME, server.name);
            xml.setAttribute(STARTED, server.started);
            xml.setAttribute(HOST_ADDRESS, server.hostAddress);
            xml.setAttribute(HOST_PORT, server.hostport);
            xml.setAttribute(IPCHANNEL_TYPE, server.ipChannelType.getCode());
            xml.setAttribute(ACCEPT_ANONYMOUS_CONNECTIONS, server.acceptAnonymousConnections);
            xml.setAttribute(MAX_CONCURRENT_CONNECTIONS_COUNT, server.maxConcurrentConnectionsCount);

            xml.setAttribute(EXTRA_HOST_ADDRESS_SIZE,
                    server.extraHostAddresses != null ? server.extraHostAddresses.length : 0);
            if (server.extraHostAddresses != null) {
                for (String s : server.extraHostAddresses) {
                    xml.add(s, EXTRA_HOST_ADDRESS, String.class);
                }
            }
            xml.add(server.associations, ASSOCIATIONS, FastList.class);
        }
    };
}