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

org.mobicents.protocols.sctp.netty.NettySctpManagementImpl Maven / Gradle / Ivy

The newest version!
/*
 * 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 
 *
 */
package org.mobicents.protocols.sctp.netty;

import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.concurrent.DefaultThreadFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;

import javolution.text.TextBuilder;
import javolution.util.FastList;
import javolution.util.FastMap;
import javolution.xml.XMLObjectReader;
import javolution.xml.XMLObjectWriter;
import javolution.xml.stream.XMLStreamException;

import org.apache.log4j.Logger;
import org.mobicents.protocols.api.Association;
import org.mobicents.protocols.api.AssociationType;
import org.mobicents.protocols.api.CongestionListener;
import org.mobicents.protocols.api.IpChannelType;
import org.mobicents.protocols.api.Management;
import org.mobicents.protocols.api.ManagementEventListener;
import org.mobicents.protocols.api.Server;
import org.mobicents.protocols.api.ServerListener;
import org.mobicents.protocols.sctp.AssociationMap;

import com.sun.nio.sctp.SctpStandardSocketOptions;
import com.sun.nio.sctp.SctpStandardSocketOptions.InitMaxStreams;

/**
 * @author Amit Bhayani
 * 
 */
public class NettySctpManagementImpl implements Management {

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

    private static final String SCTP_PERSIST_DIR_KEY = "sctp.persist.dir";
    private static final String USER_DIR_KEY = "user.dir";
    private static final String PERSIST_FILE_NAME = "sctp.xml";

    private static final String SERVERS = "servers";
    private static final String ASSOCIATIONS = "associations";

    private static final String CONNECT_DELAY_PROP = "connectdelay";
    private static final String SINGLE_THREAD_PROP = "singlethread";
    private static final String WORKER_THREADS_PROP = "workerthreads";

    public static final String CONG_CONTROL_DELAY_THRESHOLD_1 = "congControl_DelayThreshold_1";
    public static final String CONG_CONTROL_DELAY_THRESHOLD_2 = "congControl_DelayThreshold_2";
    public static final String CONG_CONTROL_DELAY_THRESHOLD_3 = "congControl_DelayThreshold_3";
    public static final String CONG_CONTROL_BACK_TO_NORMAL_DELAY_THRESHOLD_1 = "congControl_BackToNormalDelayThreshold_1";
    public static final String CONG_CONTROL_BACK_TO_NORMAL_DELAY_THRESHOLD_2 = "congControl_BackToNormalDelayThreshold_2";
    public static final String CONG_CONTROL_BACK_TO_NORMAL_DELAY_THRESHOLD_3 = "congControl_BackToNormalDelayThreshold_3";

    // TODO: make options configurable in future
//    public static final String OPTION_SCTP_DISABLE_FRAGMENTS = "optionSctpDisableFragments";
//    public static final String OPTION_SCTP_FRAGMENT_INTERLEAVE = "optionSctpFragmentInterleave";
//    public static final String OPTION_SCTP_INIT_MAXSTREAMS_IN = "optionSctpInitMaxstreamsIn";
//    public static final String OPTION_SCTP_INIT_MAXSTREAMS_OUT = "optionSctpInitMaxstreamsOut";
//    public static final String OPTION_SCTP_NODELAY = "optionSctpNodelay";
//    public static final String OPTION_SO_SNDBUF = "optionSoSndbuf";
//    public static final String OPTION_SO_RCVBUF = "optionSoRcvbuf";
//    public static final String OPTION_SO_LINGER = "optionSoLinger";

    static final int DEFAULT_IO_THREADS = Runtime.getRuntime().availableProcessors() * 2;

    private final TextBuilder persistFile = TextBuilder.newInstance();

    protected static final NettySctpXMLBinding binding = new NettySctpXMLBinding();
    protected static final String TAB_INDENT = "\t";
    private static final String CLASS_ATTRIBUTE = "type";

    private final String name;

    protected String persistDir = null;
    private int connectDelay = 5000;

    protected double[] congControl_DelayThreshold = new double[] { 2.5, 8, 14 };
    protected double[] congControl_BackToNormalDelayThreshold = new double[] { 1.5, 5.5, 10 };

//    private int workerThreads = DEFAULT_IO_THREADS;
//    private boolean singleThread = true;

    // private NettyClientOpsThread nettyClientOpsThread = null;

    private ServerListener serverListener = null;

    private FastList managementEventListeners = new FastList();
    private FastList congestionListeners = new FastList();
    protected FastList servers = new FastList();
    protected AssociationMap associations = new AssociationMap();
    private volatile boolean started = false;

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private ScheduledExecutorService clientExecutor;

    // SctpStandardSocketOptions

    // SCTP option: Enables or disables message fragmentation.
    // If enabled no SCTP message fragmentation will be performed.
    // Instead if a message being sent exceeds the current PMTU size,
    // the message will NOT be sent and an error will be indicated to the user.
    private Boolean optionSctpDisableFragments = null;
    // SCTP option: Fragmented interleave controls how the presentation of messages occur for the message receiver.
    // There are three levels of fragment interleave defined
    // level 0 - Prevents the interleaving of any messages
    // level 1 - Allows interleaving of messages that are from different associations
    // level 2 - Allows complete interleaving of messages.
    private Integer optionSctpFragmentInterleave = null;
    // SCTP option: The maximum number of streams requested by the local endpoint during association initialization
    // For an SctpServerChannel this option determines the maximum number of inbound/outbound streams
    // accepted sockets will negotiate with their connecting peer.
    private Integer optionSctpInitMaxstreams_MaxOutStreams = null;
    private Integer optionSctpInitMaxstreams_MaxInStreams = null;
    // SCTP option: Enables or disables a Nagle-like algorithm.
    // The value of this socket option is a Boolean that represents whether the option is enabled or disabled.
    // SCTP uses an algorithm like The Nagle Algorithm to coalesce short segments and improve network efficiency.
    private Boolean optionSctpNodelay = true;
    // SCTP option: The size of the socket send buffer.
    private Integer optionSoSndbuf = null;
    // SCTP option: The size of the socket receive buffer.
    private Integer optionSoRcvbuf = null;
    // SCTP option: Linger on close if data is present.
    // The value of this socket option is an Integer that controls the action taken when unsent data is queued on the socket
    // and a method to close the socket is invoked.
    // If the value of the socket option is zero or greater, then it represents a timeout value, in seconds, known as the linger interval.
    // The linger interval is the timeout for the close method to block while the operating system attempts to transmit the unsent data
    // or it decides that it is unable to transmit the data.
    // If the value of the socket option is less than zero then the option is disabled.
    // In that case the close method does not wait until unsent data is transmitted;
    // if possible the operating system will transmit any unsent data before the connection is closed. 
    private Integer optionSoLinger = null;

    /**
	 * 
	 */
    public NettySctpManagementImpl(String name) throws IOException {
        this.name = name;
        binding.setClassAttribute(CLASS_ATTRIBUTE);
        binding.setAlias(NettyServerImpl.class, "server");
        binding.setAlias(NettyAssociationImpl.class, "association");
        binding.setAlias(String.class, "string");
    }

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

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#getPersistDir()
     */
    @Override
    public String getPersistDir() {
        return this.persistDir;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#setPersistDir(java.lang.String)
     */
    @Override
    public void setPersistDir(String persistDir) {
        this.persistDir = persistDir;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#getServerListener()
     */
    @Override
    public ServerListener getServerListener() {
        return this.serverListener;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#setServerListener(org.mobicents .protocols.api.ServerListener)
     */
    @Override
    public void setServerListener(ServerListener serverListener) {
        this.serverListener = serverListener;
    }

    protected EventLoopGroup getBossGroup() {
        return bossGroup;
    }

    protected EventLoopGroup getWorkerGroup() {
        return workerGroup;
    }

    protected ScheduledExecutorService getClientExecutor() {
        return clientExecutor;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#addManagementEventListener(org
     * .mobicents.protocols.api.ManagementEventListener)
     */
    @Override
    public void addManagementEventListener(ManagementEventListener listener) {
        synchronized (this) {
            if (this.managementEventListeners.contains(listener))
                return;

            FastList newManagementEventListeners = new FastList();
            newManagementEventListeners.addAll(this.managementEventListeners);
            newManagementEventListeners.add(listener);
            this.managementEventListeners = newManagementEventListeners;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#removeManagementEventListener(
     * org.mobicents.protocols.api.ManagementEventListener)
     */
    @Override
    public void removeManagementEventListener(ManagementEventListener listener) {
        synchronized (this) {
            if (!this.managementEventListeners.contains(listener))
                return;

            FastList newManagementEventListeners = new FastList();
            newManagementEventListeners.addAll(this.managementEventListeners);
            newManagementEventListeners.remove(listener);
            this.managementEventListeners = newManagementEventListeners;
        }
    }

    @Override
    public void addCongestionListener(CongestionListener listener) {
        synchronized (this) {
            if (this.congestionListeners.contains(listener))
                return;

            FastList newCongestionListeners = new FastList();
            newCongestionListeners.addAll(this.congestionListeners);
            newCongestionListeners.add(listener);
            this.congestionListeners = newCongestionListeners;
        }
    }

    @Override
    public void removeCongestionListener(CongestionListener listener) {
        synchronized (this) {
            if (!this.congestionListeners.contains(listener))
                return;

            FastList newCongestionListeners = new FastList();
            newCongestionListeners.addAll(this.congestionListeners);
            newCongestionListeners.remove(listener);
            this.congestionListeners = newCongestionListeners;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#start()
     */
    @Override
    public void start() throws Exception {
        if (this.started) {
            logger.warn(String.format("management=%s is already started", this.name));
            return;
        }

        synchronized (this) {
            this.persistFile.clear();

            if (persistDir != null) {
                this.persistFile.append(persistDir).append(File.separator).append(this.name).append("_")
                        .append(PERSIST_FILE_NAME);
            } else {
                persistFile.append(System.getProperty(SCTP_PERSIST_DIR_KEY, System.getProperty(USER_DIR_KEY)))
                        .append(File.separator).append(this.name).append("_").append(PERSIST_FILE_NAME);
            }

            logger.info(String.format("SCTP configuration file path %s", persistFile.toString()));

            this.bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("Sctp-BossGroup-" + this.name));
            // TODO: make a thread count for WorkerGroup configurable
            this.workerGroup = new NioEventLoopGroup(0, new DefaultThreadFactory("Sctp-WorkerGroup-" + this.name));
            this.clientExecutor = new ScheduledThreadPoolExecutor(1, new DefaultThreadFactory("Sctp-ClientExecutorGroup-"
                    + this.name));

            // this.nettyClientOpsThread = new NettyClientOpsThread(this);
            // (new Thread(this.nettyClientOpsThread )).start();

            try {
                this.load();
            } catch (FileNotFoundException e) {
                logger.warn(String.format("Failed to load the SCTP configuration file. \n%s", e.getMessage()));
            }

            this.started = true;

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

            for (ManagementEventListener lstr : managementEventListeners) {
                try {
                    lstr.onServiceStarted();
                } catch (Throwable ee) {
                    logger.error("Exception while invoking onServiceStarted", ee);
                }
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#stop()
     */
    @Override
    public void stop() throws Exception {
        if (!this.started) {
            logger.warn(String.format("management=%s is already stopped", this.name));
            return;
        }

        // this.nettyClientOpsThread.setStarted(false);

        for (ManagementEventListener lstr : managementEventListeners) {
            try {
                lstr.onServiceStopped();
            } catch (Throwable ee) {
                logger.error("Exception while invoking onServiceStopped", ee);
            }
        }

        // We store the original state first
        this.store();

        // Stop all associations
        FastMap associationsTemp = this.associations;
        for (FastMap.Entry n = associationsTemp.head(), end = associationsTemp.tail(); (n = n.getNext()) != end;) {
            Association associationTemp = n.getValue();
            if (associationTemp.isStarted()) {
                ((NettyAssociationImpl) associationTemp).stop();
            }
        }

        FastList tempServers = servers;
        for (FastList.Node n = tempServers.head(), end = tempServers.tail(); (n = n.getNext()) != end;) {
            Server serverTemp = n.getValue();
            if (serverTemp.isStarted()) {
                try {
                    ((NettyServerImpl) serverTemp).stop();
                } catch (Exception e) {
                    logger.error(String.format("Exception while stopping the Server=%s", serverTemp.getName()), e);
                }
            }
        }

        // waiting till stopping associations
        for (int i1 = 0; i1 < 20; i1++) {
            boolean assConnected = false;
            for (FastMap.Entry n = this.associations.head(), end = this.associations.tail(); (n = n
                    .getNext()) != end;) {
                Association associationTemp = n.getValue();
                if (associationTemp.isConnected()) {
                    assConnected = true;
                    break;
                }
            }
            if (!assConnected)
                break;
            Thread.sleep(100);
        }

        // TODO - make a general shutdown and waiting for it instead of "waiting till stopping associations" 
        this.bossGroup.shutdownGracefully();
        this.workerGroup.shutdownGracefully();
        this.clientExecutor.shutdown();
       

        // TODO Should servers be also checked for shutdown?

        this.started = false;

    }

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

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#removeAllResourses()
     */
    @Override
    public void removeAllResourses() throws Exception {
        synchronized (this) {
            if (!this.started) {
                throw new Exception(String.format("Management=%s not started", this.name));
            }

            if (this.associations.size() == 0 && this.servers.size() == 0)
                // no resources allocated - nothing to do
                return;

            if (logger.isInfoEnabled()) {
                logger.info(String.format("Removing allocated resources: Servers=%d, Associations=%d", this.servers.size(),
                        this.associations.size()));
            }

            // Remove all associations
            ArrayList lst = new ArrayList();
            for (FastMap.Entry n = this.associations.head(), end = this.associations.tail(); (n = n
                    .getNext()) != end;) {
                lst.add(n.getKey());
            }
            for (String n : lst) {
                this.stopAssociation(n);
                this.removeAssociation(n);
            }

            // Remove all servers
            lst.clear();
            for (FastList.Node n = this.servers.head(), end = this.servers.tail(); (n = n.getNext()) != end;) {
                lst.add(n.getValue().getName());
            }
            for (String n : lst) {
                this.stopServer(n);
                this.removeServer(n);
            }

            // We store the cleared state
            this.store();

            for (ManagementEventListener lstr : managementEventListeners) {
                try {
                    lstr.onRemoveAllResources();
                } catch (Throwable ee) {
                    logger.error("Exception while invoking onRemoveAllResources", ee);
                }
            }
        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#addServer(java.lang.String, java.lang.String, int,
     * org.mobicents.protocols.api.IpChannelType, boolean, int, java.lang.String[])
     */
    @Override
    public Server addServer(String serverName, String hostAddress, int port, IpChannelType ipChannelType,
            boolean acceptAnonymousConnections, int maxConcurrentConnectionsCount, String[] extraHostAddresses)
            throws Exception {
        if (!this.started) {
            throw new Exception(String.format("Management=%s not started", this.name));
        }

        if (serverName == null) {
            throw new Exception("Server name cannot be null");
        }

        if (hostAddress == null) {
            throw new Exception("Server host address cannot be null");
        }

        if (port < 1) {
            throw new Exception("Server host port cannot be less than 1");
        }

        synchronized (this) {
            for (FastList.Node n = this.servers.head(), end = this.servers.tail(); (n = n.getNext()) != end;) {
                Server serverTemp = n.getValue();
                if (serverName.equals(serverTemp.getName())) {
                    throw new Exception(String.format("Server name=%s already exist", serverName));
                }

                if (hostAddress.equals(serverTemp.getHostAddress()) && port == serverTemp.getHostport()) {
                    throw new Exception(String.format("Server name=%s is already bound to %s:%d", serverTemp.getName(),
                            serverTemp.getHostAddress(), serverTemp.getHostport()));
                }
            }

            NettyServerImpl server = new NettyServerImpl(serverName, hostAddress, port, ipChannelType,
                    acceptAnonymousConnections, maxConcurrentConnectionsCount, extraHostAddresses);
            server.setManagement(this);

            FastList newServers = new FastList();
            newServers.addAll(this.servers);
            newServers.add(server);
            this.servers = newServers;
            // this.servers.add(server);

            this.store();

            for (ManagementEventListener lstr : managementEventListeners) {
                try {
                    lstr.onServerAdded(server);
                } catch (Throwable ee) {
                    logger.error("Exception while invoking onServerAdded", ee);
                }
            }

            if (logger.isInfoEnabled()) {
                logger.info(String.format("Created Server=%s", server.getName()));
            }

            return server;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#addServer(java.lang.String, java.lang.String, int,
     * org.mobicents.protocols.api.IpChannelType, java.lang.String[])
     */
    @Override
    public Server addServer(String serverName, String hostAddress, int port, IpChannelType ipChannelType,
            String[] extraHostAddresses) throws Exception {
        return addServer(serverName, hostAddress, port, ipChannelType, false, 0, extraHostAddresses);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#addServer(java.lang.String, java.lang.String, int)
     */
    @Override
    public Server addServer(String serverName, String hostAddress, int port) throws Exception {
        return addServer(serverName, hostAddress, port, IpChannelType.SCTP, false, 0, null);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#removeServer(java.lang.String)
     */
    @Override
    public void removeServer(String serverName) throws Exception {
        if (!this.started) {
            throw new Exception(String.format("Management=%s not started", this.name));
        }

        if (serverName == null) {
            throw new Exception("Server name cannot be null");
        }

        synchronized (this) {
            Server removeServer = null;
            for (FastList.Node n = this.servers.head(), end = this.servers.tail(); (n = n.getNext()) != end;) {
                NettyServerImpl serverTemp = (NettyServerImpl) n.getValue();

                if (serverName.equals(serverTemp.getName())) {
                    if (serverTemp.isStarted()) {
                        throw new Exception(String.format("Server=%s is started. Stop the server before removing", serverName));
                    }

                    if (serverTemp.anonymAssociations.size() != 0 || serverTemp.associations.size() != 0) {
                        throw new Exception(String.format(
                                "Server=%s has Associations. Remove all those Associations before removing Server", serverName));
                    }
                    removeServer = serverTemp;
                    break;
                }
            }

            if (removeServer == null) {
                throw new Exception(String.format("No Server found with name=%s", serverName));
            }

            FastList newServers = new FastList();
            newServers.addAll(this.servers);
            newServers.remove(removeServer);
            this.servers = newServers;
            // this.servers.remove(removeServer);

            this.store();

            for (ManagementEventListener lstr : managementEventListeners) {
                try {
                    lstr.onServerRemoved(removeServer);
                } catch (Throwable ee) {
                    logger.error("Exception while invoking onServerRemoved", ee);
                }
            }
        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#startServer(java.lang.String)
     */
    @Override
    public void startServer(String serverName) throws Exception {
        if (!this.started) {
            throw new Exception(String.format("Management=%s not started", this.name));
        }

        if (name == null) {
            throw new Exception("Server name cannot be null");
        }

        FastList tempServers = servers;
        for (FastList.Node n = tempServers.head(), end = tempServers.tail(); (n = n.getNext()) != end;) {
            Server serverTemp = n.getValue();

            if (serverName.equals(serverTemp.getName())) {
                if (serverTemp.isStarted()) {
                    throw new Exception(String.format("Server=%s is already started", serverName));
                }
                ((NettyServerImpl) serverTemp).start();
                this.store();
                return;
            }
        }

        throw new Exception(String.format("No Server foubd with name=%s", serverName));

    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#stopServer(java.lang.String)
     */
    @Override
    public void stopServer(String serverName) throws Exception {
        if (!this.started) {
            throw new Exception(String.format("Management=%s not started", this.name));
        }

        if (serverName == null) {
            throw new Exception("Server name cannot be null");
        }

        FastList tempServers = servers;
        for (FastList.Node n = tempServers.head(), end = tempServers.tail(); (n = n.getNext()) != end;) {
            Server serverTemp = n.getValue();

            if (serverName.equals(serverTemp.getName())) {
                ((NettyServerImpl) serverTemp).stop();
                this.store();
                return;
            }
        }

        throw new Exception(String.format("No Server found with name=%s", serverName));

    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#getServers()
     */
    @Override
    public List getServers() {
        return servers.unmodifiable();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#addServerAssociation(java.lang .String, int, java.lang.String,
     * java.lang.String)
     */
    @Override
    public Association addServerAssociation(String peerAddress, int peerPort, String serverName, String assocName)
            throws Exception {
        return addServerAssociation(peerAddress, peerPort, serverName, assocName, IpChannelType.SCTP);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#addServerAssociation(java.lang .String, int, java.lang.String,
     * java.lang.String, org.mobicents.protocols.api.IpChannelType)
     */
    @Override
    public Association addServerAssociation(String peerAddress, int peerPort, String serverName, String assocName,
            IpChannelType ipChannelType) throws Exception {
        if (!this.started) {
            throw new Exception(String.format("Management=%s not started", this.name));
        }

        if (peerAddress == null) {
            throw new Exception("Peer address cannot be null");
        }

        if (peerPort < 1) {
            throw new Exception("Peer port cannot be less than 1");
        }

        if (serverName == null) {
            throw new Exception("Server name cannot be null");
        }

        if (assocName == null) {
            throw new Exception("Association name cannot be null");
        }

        synchronized (this) {
            if (this.associations.get(assocName) != null) {
                throw new Exception(String.format("Already has association=%s", assocName));
            }

            Server server = null;

            for (FastList.Node n = this.servers.head(), end = this.servers.tail(); (n = n.getNext()) != end;) {
                Server serverTemp = n.getValue();
                if (serverTemp.getName().equals(serverName)) {
                    server = serverTemp;
                }
            }

            if (server == null) {
                throw new Exception(String.format("No Server found for name=%s", serverName));
            }

            for (FastMap.Entry n = this.associations.head(), end = this.associations.tail(); (n = n
                    .getNext()) != end;) {
                Association associationTemp = n.getValue();

                if (peerAddress.equals(associationTemp.getPeerAddress()) && associationTemp.getPeerPort() == peerPort) {
                    throw new Exception(String.format("Already has association=%s with same peer address=%s and port=%d",
                            associationTemp.getName(), peerAddress, peerPort));
                }
            }

            if (server.getIpChannelType() != ipChannelType)
                throw new Exception(String.format("Server and Accociation has different IP channel type"));

            NettyAssociationImpl association = new NettyAssociationImpl(peerAddress, peerPort, serverName, assocName,
                    ipChannelType);
            association.setManagement(this);

            AssociationMap newAssociations = new AssociationMap();
            newAssociations.putAll(this.associations);
            newAssociations.put(assocName, association);
            this.associations = newAssociations;
            // this.associations.put(assocName, association);

            FastList newAssociations2 = new FastList();
            newAssociations2.addAll(((NettyServerImpl) server).associations);
            newAssociations2.add(assocName);
            ((NettyServerImpl) server).associations = newAssociations2;
            // ((ServerImpl) server).associations.add(assocName);

            this.store();

            for (ManagementEventListener lstr : managementEventListeners) {
                try {
                    lstr.onAssociationAdded(association);
                } catch (Throwable ee) {
                    logger.error("Exception while invoking onAssociationAdded", ee);
                }
            }

            if (logger.isInfoEnabled()) {
                logger.info(String.format("Added Associoation=%s of type=%s", association.getName(),
                        association.getAssociationType()));
            }

            return association;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#addAssociation(java.lang.String, int, java.lang.String, int,
     * java.lang.String)
     */
    @Override
    public Association addAssociation(String hostAddress, int hostPort, String peerAddress, int peerPort, String assocName)
            throws Exception {
        return addAssociation(hostAddress, hostPort, peerAddress, peerPort, assocName, IpChannelType.SCTP, null);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#addAssociation(java.lang.String, int, java.lang.String, int,
     * java.lang.String, org.mobicents.protocols.api.IpChannelType, java.lang.String[])
     */
    @Override
    public Association addAssociation(String hostAddress, int hostPort, String peerAddress, int peerPort, String assocName,
            IpChannelType ipChannelType, String[] extraHostAddresses) throws Exception {

        if (!this.started) {
            throw new Exception(String.format("Management=%s not started", this.name));
        }

        if (hostAddress == null) {
            throw new Exception("Host address cannot be null");
        }

        if (hostPort < 0) {
            throw new Exception("Host port cannot be less than 0");
        }

        if (peerAddress == null) {
            throw new Exception("Peer address cannot be null");
        }

        if (peerPort < 1) {
            throw new Exception("Peer port cannot be less than 1");
        }

        if (assocName == null) {
            throw new Exception("Association name cannot be null");
        }

        synchronized (this) {
            for (FastMap.Entry n = this.associations.head(), end = this.associations.tail(); (n = n
                    .getNext()) != end;) {
                Association associationTemp = n.getValue();

                if (assocName.equals(associationTemp.getName())) {
                    throw new Exception(String.format("Already has association=%s", associationTemp.getName()));
                }

                if (peerAddress.equals(associationTemp.getPeerAddress()) && associationTemp.getPeerPort() == peerPort) {
                    throw new Exception(String.format("Already has association=%s with same peer address=%s and port=%d",
                            associationTemp.getName(), peerAddress, peerPort));
                }

                if (hostAddress.equals(associationTemp.getHostAddress()) && associationTemp.getHostPort() == hostPort) {
                    throw new Exception(String.format("Already has association=%s with same host address=%s and port=%d",
                            associationTemp.getName(), hostAddress, hostPort));
                }

            }

            NettyAssociationImpl association = new NettyAssociationImpl(hostAddress, hostPort, peerAddress, peerPort,
                    assocName, ipChannelType, extraHostAddresses);
            association.setManagement(this);

            AssociationMap newAssociations = new AssociationMap();
            newAssociations.putAll(this.associations);
            newAssociations.put(assocName, association);
            this.associations = newAssociations;
            // associations.put(assocName, association);

            this.store();

            for (ManagementEventListener lstr : managementEventListeners) {
                try {
                    lstr.onAssociationAdded(association);
                } catch (Throwable ee) {
                    logger.error("Exception while invoking onAssociationAdded", ee);
                }
            }

            if (logger.isInfoEnabled()) {
                logger.info(String.format("Added Associoation=%s of type=%s", association.getName(),
                        association.getAssociationType()));
            }

            return association;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#removeAssociation(java.lang.String )
     */
    @Override
    public void removeAssociation(String assocName) throws Exception {
        if (!this.started) {
            throw new Exception(String.format("Management=%s not started", this.name));
        }

        if (assocName == null) {
            throw new Exception("Association name cannot be null");
        }

        synchronized (this) {
            Association association = this.associations.get(assocName);

            if (association == null) {
                throw new Exception(String.format("No Association found for name=%s", assocName));
            }

            if (association.isStarted()) {
                throw new Exception(String.format("Association name=%s is started. Stop before removing", assocName));
            }

            AssociationMap newAssociations = new AssociationMap();
            newAssociations.putAll(this.associations);
            newAssociations.remove(assocName);
            this.associations = newAssociations;
            // this.associations.remove(assocName);

            if (((NettyAssociationImpl) association).getAssociationType() == AssociationType.SERVER) {
                for (FastList.Node n = this.servers.head(), end = this.servers.tail(); (n = n.getNext()) != end;) {
                    Server serverTemp = n.getValue();
                    if (serverTemp.getName().equals(association.getServerName())) {
                        FastList newAssociations2 = new FastList();
                        newAssociations2.addAll(((NettyServerImpl) serverTemp).associations);
                        newAssociations2.remove(assocName);
                        ((NettyServerImpl) serverTemp).associations = newAssociations2;
                        break;
                    }
                }
            }

            this.store();

            for (ManagementEventListener lstr : managementEventListeners) {
                try {
                    lstr.onAssociationRemoved(association);
                } catch (Throwable ee) {
                    logger.error("Exception while invoking onAssociationRemoved", ee);
                }
            }
        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#getAssociation(java.lang.String)
     */
    @Override
    public Association getAssociation(String assocName) throws Exception {
        if (assocName == null) {
            throw new Exception("Association name cannot be null");
        }
        Association associationTemp = this.associations.get(assocName);

        if (associationTemp == null) {
            throw new Exception(String.format("No Association found for name=%s", assocName));
        }
        return associationTemp;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#getAssociations()
     */
    @Override
    public Map getAssociations() {
        Map routeTmp = new HashMap();
        routeTmp.putAll(this.associations);
        return routeTmp;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#startAssociation(java.lang.String)
     */
    @Override
    public void startAssociation(String assocName) throws Exception {
        if (!this.started) {
            throw new Exception(String.format("Management=%s not started", this.name));
        }

        if (assocName == null) {
            throw new Exception("Association name cannot be null");
        }

        Association associationTemp = this.associations.get(assocName);

        if (associationTemp == null) {
            throw new Exception(String.format("No Association found for name=%s", assocName));
        }

        if (associationTemp.isStarted()) {
            throw new Exception(String.format("Association=%s is already started", assocName));
        }

        ((NettyAssociationImpl) associationTemp).start();
        this.store();

    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#stopAssociation(java.lang.String)
     */
    @Override
    public void stopAssociation(String assocName) throws Exception {
        if (!this.started) {
            throw new Exception(String.format("Management=%s not started", this.name));
        }

        if (assocName == null) {
            throw new Exception("Association name cannot be null");
        }

        Association association = this.associations.get(assocName);

        if (association == null) {
            throw new Exception(String.format("No Association found for name=%s", assocName));
        }

        ((NettyAssociationImpl) association).stop();
        this.store();

    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#getConnectDelay()
     */
    @Override
    public int getConnectDelay() {
        return this.connectDelay;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#setConnectDelay(int)
     */
    @Override
    public void setConnectDelay(int connectDelay) throws Exception {
        if (!this.started)
            throw new Exception("ConnectDelay parameter can be updated only when SCTP stack is running");

        this.connectDelay = connectDelay;

        this.store();
    }

    @Override
    public double getCongControl_DelayThreshold_1() {
        return congControl_DelayThreshold[0];
    }

    @Override
    public double getCongControl_DelayThreshold_2() {
        return congControl_DelayThreshold[1];
    }

    @Override
    public double getCongControl_DelayThreshold_3() {
        return congControl_DelayThreshold[2];
    }

    @Override
    public void setCongControl_DelayThreshold_1(double val) throws Exception {
        if (!this.started)
            throw new Exception("CongControl_DelayThreshold parameter can be updated only when SCTP stack is running");

        congControl_DelayThreshold[0] = val;

        this.store();
    }

    @Override
    public void setCongControl_DelayThreshold_2(double val) throws Exception {
        if (!this.started)
            throw new Exception("CongControl_DelayThreshold parameter can be updated only when SCTP stack is running");

        congControl_DelayThreshold[1] = val;

        this.store();
    }

    @Override
    public void setCongControl_DelayThreshold_3(double val) throws Exception {
        if (!this.started)
            throw new Exception("CongControl_DelayThreshold parameter can be updated only when SCTP stack is running");

        congControl_DelayThreshold[2] = val;

        this.store();
    }

    @Override
    public double getCongControl_BackToNormalDelayThreshold_1() {
        return congControl_BackToNormalDelayThreshold[0];
    }

    @Override
    public double getCongControl_BackToNormalDelayThreshold_2() {
        return congControl_BackToNormalDelayThreshold[1];
    }

    @Override
    public double getCongControl_BackToNormalDelayThreshold_3() {
        return congControl_BackToNormalDelayThreshold[2];
    }

    @Override
    public void setCongControl_BackToNormalDelayThreshold_1(double val) throws Exception {
        if (!this.started)
            throw new Exception(
                    "CongControl_BackToNormalDelayThreshold parameter can be updated only when SCTP stack is running");

        congControl_BackToNormalDelayThreshold[0] = val;

        this.store();
    }

    @Override
    public void setCongControl_BackToNormalDelayThreshold_2(double val) throws Exception {
        if (!this.started)
            throw new Exception(
                    "CongControl_BackToNormalDelayThreshold parameter can be updated only when SCTP stack is running");

        congControl_BackToNormalDelayThreshold[1] = val;

        this.store();
    }

    @Override
    public void setCongControl_BackToNormalDelayThreshold_3(double val) throws Exception {
        if (!this.started)
            throw new Exception(
                    "CongControl_BackToNormalDelayThreshold parameter can be updated only when SCTP stack is running");

        congControl_BackToNormalDelayThreshold[2] = val;

        this.store();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#getWorkerThreads()
     */
    @Override
    public int getWorkerThreads() {
        return 1;
//        return this.workerThreads;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#setWorkerThreads(int)
     */
    @Override
    public void setWorkerThreads(int workerThreads) throws Exception {
//        if (this.started)
//            throw new Exception("WorkerThreads parameter can be updated only when SCTP stack is NOT running");
//
//        if (workerThreads < 1) {
//            workerThreads = DEFAULT_IO_THREADS;
//        }
//        this.workerThreads = workerThreads;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#isSingleThread()
     */
    @Override
    public boolean isSingleThread() {
        return true;
//        return this.singleThread;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mobicents.protocols.api.Management#setSingleThread(boolean)
     */
    @Override
    public void setSingleThread(boolean singleThread) throws Exception {
//        if (this.started)
//            throw new Exception("SingleThread parameter can be updated only when SCTP stack is NOT running");
//
//        this.singleThread = singleThread;

    }

    @Override
    public Boolean getOptionSctpDisableFragments() {
        return optionSctpDisableFragments;
    }

    @Override
    public void setOptionSctpDisableFragments(Boolean optionSctpDisableFragments) {
        this.optionSctpDisableFragments = optionSctpDisableFragments;
    }

    @Override
    public Integer getOptionSctpFragmentInterleave() {
        return optionSctpFragmentInterleave;
    }

    @Override
    public void setOptionSctpFragmentInterleave(Integer optionSctpFragmentInterleave) {
        this.optionSctpFragmentInterleave = optionSctpFragmentInterleave;
    }

    public InitMaxStreams getOptionSctpInitMaxstreams() {
        if (optionSctpInitMaxstreams_MaxInStreams != null && optionSctpInitMaxstreams_MaxOutStreams != null) {
            return SctpStandardSocketOptions.InitMaxStreams.create(optionSctpInitMaxstreams_MaxInStreams,
                    optionSctpInitMaxstreams_MaxOutStreams);
        } else {
            return null;
        }
    }

    @Override
    public Integer getOptionSctpInitMaxstreams_MaxOutStreams() {
        return optionSctpInitMaxstreams_MaxOutStreams;
    }

    @Override
    public Integer getOptionSctpInitMaxstreams_MaxInStreams() {
        return optionSctpInitMaxstreams_MaxInStreams;
    }

    @Override
    public void setOptionSctpInitMaxstreams_MaxOutStreams(Integer val) {
        this.optionSctpInitMaxstreams_MaxOutStreams = val;
    }

    @Override
    public void setOptionSctpInitMaxstreams_MaxInStreams(Integer val) {
        this.optionSctpInitMaxstreams_MaxInStreams = val;
    }

    @Override
    public Boolean getOptionSctpNodelay() {
        return optionSctpNodelay;
    }

    @Override
    public void setOptionSctpNodelay(Boolean optionSctpNodelay) {
        this.optionSctpNodelay = optionSctpNodelay;
    }

    @Override
    public Integer getOptionSoSndbuf() {
        return optionSoSndbuf;
    }

    @Override
    public void setOptionSoSndbuf(Integer optionSoSndbuf) {
        this.optionSoSndbuf = optionSoSndbuf;
    }

    @Override
    public Integer getOptionSoRcvbuf() {
        return optionSoRcvbuf;
    }

    @Override
    public void setOptionSoRcvbuf(Integer optionSoRcvbuf) {
        this.optionSoRcvbuf = optionSoRcvbuf;
    }

    @Override
    public Integer getOptionSoLinger() {
        return optionSoLinger;
    }

    @Override
    public void setOptionSoLinger(Integer optionSoLinger) {
        this.optionSoLinger = optionSoLinger;
    }

    protected FastList getManagementEventListeners() {
        return managementEventListeners;
    }

    protected FastList getCongestionListeners() {
        return congestionListeners;
    }

    @SuppressWarnings("unchecked")
    private void load() throws FileNotFoundException {
        XMLObjectReader reader = null;
        try {
            reader = XMLObjectReader.newInstance(new FileInputStream(persistFile.toString()));
            reader.setBinding(binding);

            try {
                Integer vali = reader.read(CONNECT_DELAY_PROP, Integer.class);
                if (vali != null)
                    this.connectDelay = vali;
                // this.workerThreads = reader.read(WORKER_THREADS_PROP, Integer.class);
                // this.singleThread = reader.read(SINGLE_THREAD_PROP, Boolean.class);
                vali = reader.read(WORKER_THREADS_PROP, Integer.class);
                Boolean valb = reader.read(SINGLE_THREAD_PROP, Boolean.class);
            } catch (java.lang.NullPointerException npe) {
                // ignore.
                // For backward compatibility we can ignore if these values are not defined
            }

            Double valTH1 = reader.read(CONG_CONTROL_DELAY_THRESHOLD_1, Double.class);
            Double valTH2 = reader.read(CONG_CONTROL_DELAY_THRESHOLD_2, Double.class);
            Double valTH3 = reader.read(CONG_CONTROL_DELAY_THRESHOLD_3, Double.class);
            Double valTB1 = reader.read(CONG_CONTROL_BACK_TO_NORMAL_DELAY_THRESHOLD_1, Double.class);
            Double valTB2 = reader.read(CONG_CONTROL_BACK_TO_NORMAL_DELAY_THRESHOLD_2, Double.class);
            Double valTB3 = reader.read(CONG_CONTROL_BACK_TO_NORMAL_DELAY_THRESHOLD_3, Double.class);
            if (valTH1 != null && valTH2 != null && valTH3 != null && valTB1 != null && valTB2 != null && valTB3 != null) {
                this.congControl_DelayThreshold = new double[3];
                this.congControl_DelayThreshold[0] = valTH1;
                this.congControl_DelayThreshold[1] = valTH2;
                this.congControl_DelayThreshold[2] = valTH3;
                this.congControl_BackToNormalDelayThreshold = new double[3];
                this.congControl_BackToNormalDelayThreshold[0] = valTB1;
                this.congControl_BackToNormalDelayThreshold[1] = valTB2;
                this.congControl_BackToNormalDelayThreshold[2] = valTB3;
            }

            // TODO: add storing of parameters
//            Boolean valB = reader.read(OPTION_SCTP_DISABLE_FRAGMENTS, Boolean.class);
//            if (valB != null)
//                this.optionSctpDisableFragments = valB;
//            Integer valI = reader.read(OPTION_SCTP_FRAGMENT_INTERLEAVE, Integer.class);
//            if (valI != null)
//                this.optionSctpFragmentInterleave = valI;
//            Integer valI_In = reader.read(OPTION_SCTP_INIT_MAXSTREAMS_IN, Integer.class);
//            Integer valI_Out = reader.read(OPTION_SCTP_INIT_MAXSTREAMS_OUT, Integer.class);
//            if (valI_In != null && valI_Out != null) {
//                this.optionSctpInitMaxstreams = SctpStandardSocketOptions.InitMaxStreams.create(valI_In, valI_Out);
//            }
//            valB = reader.read(OPTION_SCTP_NODELAY, Boolean.class);
//            if (valB != null)
//                this.optionSctpNodelay = valB;
//            valI = reader.read(OPTION_SO_SNDBUF, Integer.class);
//            if (valI != null)
//                this.optionSoSndbuf = valI;
//            valI = reader.read(OPTION_SO_RCVBUF, Integer.class);
//            if (valI != null)
//                this.optionSoRcvbuf = valI;
//            valI = reader.read(OPTION_SO_LINGER, Integer.class);
//            if (valI != null)
//                this.optionSoLinger = valI;

            this.servers = reader.read(SERVERS, FastList.class);

            for (FastList.Node n = this.servers.head(), end = this.servers.tail(); (n = n.getNext()) != end;) {
                Server serverTemp = n.getValue();
                ((NettyServerImpl) serverTemp).setManagement(this);
                if (serverTemp.isStarted()) {
                    try {
                        ((NettyServerImpl) serverTemp).start();
                    } catch (Exception e) {
                        logger.error(String.format("Error while initiating Server=%s", serverTemp.getName()), e);
                    }
                }
            }

            this.associations = reader.read(ASSOCIATIONS, AssociationMap.class);
            for (FastMap.Entry n = this.associations.head(), end = this.associations.tail(); (n = n
                    .getNext()) != end;) {
                NettyAssociationImpl associationTemp = (NettyAssociationImpl) n.getValue();
                associationTemp.setManagement(this);
            }

        } catch (XMLStreamException ex) {
            // this.logger.info(
            // "Error while re-creating Linksets from persisted file", ex);
        }
    }

    private void store() {
        try {
            XMLObjectWriter writer = XMLObjectWriter.newInstance(new FileOutputStream(persistFile.toString()));
            writer.setBinding(binding);
            // Enables cross-references.
            // writer.setReferenceResolver(new XMLReferenceResolver());
            writer.setIndentation(TAB_INDENT);

            writer.write(this.connectDelay, CONNECT_DELAY_PROP, Integer.class);
            // writer.write(this.workerThreads, WORKER_THREADS_PROP, Integer.class);
            // writer.write(this.singleThread, SINGLE_THREAD_PROP, Boolean.class);

            if (this.congControl_DelayThreshold != null && this.congControl_DelayThreshold.length == 3) {
                writer.write(this.congControl_DelayThreshold[0], CONG_CONTROL_DELAY_THRESHOLD_1, Double.class);
                writer.write(this.congControl_DelayThreshold[1], CONG_CONTROL_DELAY_THRESHOLD_2, Double.class);
                writer.write(this.congControl_DelayThreshold[2], CONG_CONTROL_DELAY_THRESHOLD_3, Double.class);
            }
            if (this.congControl_BackToNormalDelayThreshold != null && this.congControl_BackToNormalDelayThreshold.length == 3) {
                writer.write(this.congControl_BackToNormalDelayThreshold[0], CONG_CONTROL_BACK_TO_NORMAL_DELAY_THRESHOLD_1, Double.class);
                writer.write(this.congControl_BackToNormalDelayThreshold[1], CONG_CONTROL_BACK_TO_NORMAL_DELAY_THRESHOLD_2, Double.class);
                writer.write(this.congControl_BackToNormalDelayThreshold[2], CONG_CONTROL_BACK_TO_NORMAL_DELAY_THRESHOLD_3, Double.class);
            }

            // TODO: add storing of parameters
//            if (this.optionSctpDisableFragments != null) {
//                writer.write(this.optionSctpDisableFragments, OPTION_SCTP_DISABLE_FRAGMENTS, Boolean.class);
//            }
//            if (this.optionSctpFragmentInterleave != null) {
//                writer.write(this.optionSctpFragmentInterleave, OPTION_SCTP_FRAGMENT_INTERLEAVE, Integer.class);
//            }
//            if (this.optionSctpInitMaxstreams != null) {
//                writer.write(this.optionSctpInitMaxstreams.maxInStreams(), OPTION_SCTP_INIT_MAXSTREAMS_IN, Integer.class);
//                writer.write(this.optionSctpInitMaxstreams.maxOutStreams(), OPTION_SCTP_INIT_MAXSTREAMS_OUT, Integer.class);
//            }
//            if (this.optionSctpNodelay != null) {
//                writer.write(this.optionSctpNodelay, OPTION_SCTP_NODELAY, Boolean.class);
//            }
//            if (this.optionSoSndbuf != null) {
//                writer.write(this.optionSoSndbuf, OPTION_SO_SNDBUF, Integer.class);
//            }
//            if (this.optionSoRcvbuf != null) {
//                writer.write(this.optionSoRcvbuf, OPTION_SO_RCVBUF, Integer.class);
//            }
//            if (this.optionSoLinger != null) {
//                writer.write(this.optionSoLinger, OPTION_SO_LINGER, Integer.class);
//            }

            writer.write(this.servers, SERVERS, FastList.class);
            writer.write(this.associations, ASSOCIATIONS, AssociationMap.class);

            writer.close();
        } catch (Exception e) {
            logger.error("Error while persisting the Rule state in file", e);
        }
    }

    @Override
    public int getBufferSize() {
        // this parameter is only needed for non-netty version
        return 0;
    }

    @Override
    public void setBufferSize(int bufferSize) throws Exception {
        // this parameter is only needed for non-netty version
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy