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

org.mobicents.protocols.sctp.netty.NettySctpChannelInboundHandlerAdapter 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.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.sctp.SctpMessage;

import org.apache.log4j.Logger;
import org.apache.log4j.Priority;
import org.mobicents.protocols.api.IpChannelType;
import org.mobicents.protocols.api.PayloadData;

import com.sun.nio.sctp.AssociationChangeNotification;
import com.sun.nio.sctp.PeerAddressChangeNotification;
import com.sun.nio.sctp.SendFailedNotification;
import com.sun.nio.sctp.ShutdownNotification;

/**
 * @author Amit Bhayani
 * 
 */
public class NettySctpChannelInboundHandlerAdapter extends ChannelInboundHandlerAdapter {

    Logger logger = Logger.getLogger(NettySctpChannelInboundHandlerAdapter.class);

    // Default value is 1 for TCP
    private volatile int maxInboundStreams = 1;
    private volatile int maxOutboundStreams = 1;

    protected NettyAssociationImpl association = null;

    protected Channel channel = null;
    protected ChannelHandlerContext ctx = null;

    protected long lastCongestionMonitorSecondPart;

    /**
     * 
     */
    public NettySctpChannelInboundHandlerAdapter() {
        // TODO Auto-generated constructor stub
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        if (logger.isDebugEnabled()) {
            logger.debug(String.format("channelInactive event: association=%s", this.association));
        }

        if (this.association != null)
            this.association.markAssociationDown();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (logger.isDebugEnabled()) {
            logger.debug(String.format("userEventTriggered event: association=%s \nevent=%s", this.association, evt));
        }

        if (evt instanceof AssociationChangeNotification) {
            // SctpAssocChange not = (SctpAssocChange) evt;
            AssociationChangeNotification not = (AssociationChangeNotification) evt;

            switch (not.event()) {
                case COMM_UP:
                    if (not.association() != null) {
                        this.maxOutboundStreams = not.association().maxOutboundStreams();
                        this.maxInboundStreams = not.association().maxInboundStreams();
                    }

                    if (logger.isInfoEnabled()) {
                        logger.info(String.format(
                                "New association setup for Association=%s with %d outbound streams, and %d inbound streams.\n",
                                association.getName(), this.maxOutboundStreams, this.maxInboundStreams));
                    }

                    this.association.markAssociationUp(this.maxInboundStreams, this.maxOutboundStreams);
                    break;
                case CANT_START:
                    logger.error(String.format("Can't start for Association=%s", association.getName()));
                    break;
                case COMM_LOST:
                    logger.warn(String.format("Communication lost for Association=%s", association.getName()));

                    // Close the Socket
                    association.getAssociationListener().onCommunicationLost(association);
                    ctx.close();
//                    if (association.getAssociationType() == AssociationType.CLIENT) {
//                        association.scheduleConnect();
//                    }
                    break;
                case RESTART:
                    logger.warn(String.format("Restart for Association=%s", association.getName()));
                    try {
                        association.getAssociationListener().onCommunicationRestart(association);
                    } catch (Exception e) {
                        logger.error(String.format(
                                "Exception while calling onCommunicationRestart on AssociationListener for Association=%s",
                                association.getName()), e);
                    }
                    break;
                case SHUTDOWN:
                    if (logger.isInfoEnabled()) {
                        logger.info(String.format("Shutdown for Association=%s", association.getName()));
                    }
//                    try {
//                        association.markAssociationDown();
//                    } catch (Exception e) {
//                        logger.error(String.format(
//                                "Exception while calling onCommunicationShutdown on AssociationListener for Association=%s",
//                                association.getName()), e);
//                    }
                    break;
                default:
                    logger.warn(String.format("Received unkown Event=%s for Association=%s", not.event(), association.getName()));
                    break;
            }
        }

        if (evt instanceof PeerAddressChangeNotification) {
            PeerAddressChangeNotification notification = (PeerAddressChangeNotification) evt;

            if (logger.isEnabledFor(Priority.WARN)) {
                logger.warn(String.format("Peer Address changed to=%s for Association=%s", notification.address(),
                        association.getName()));
            }

        } else if (evt instanceof SendFailedNotification) {
            SendFailedNotification notification = (SendFailedNotification) evt;
            logger.error(String.format("Association=" + association.getName() + " SendFailedNotification, errorCode="
                    + notification.errorCode()));

        } else if (evt instanceof ShutdownNotification) {
            ShutdownNotification notification = (ShutdownNotification) evt;

            if (logger.isInfoEnabled()) {
                logger.info(String.format("Association=%s SHUTDOWN", association.getName()));
            }

            // TODO assign Thread's ?

//            try {
//                association.markAssociationDown();
//                association.getAssociationListener().onCommunicationShutdown(association);
//            } catch (Exception e) {
//                logger.error(String.format(
//                        "Exception while calling onCommunicationShutdown on AssociationListener for Association=%s",
//                        association.getName()), e);
//            }
        }// if (evt instanceof AssociationChangeNotification)

    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // try {
        PayloadData payload;
        if (this.association.getIpChannelType() == IpChannelType.SCTP) {
            SctpMessage sctpMessage = (SctpMessage) msg;
            ByteBuf byteBuf = sctpMessage.content();
            payload = new PayloadData(byteBuf.readableBytes(), byteBuf, sctpMessage.isComplete(), sctpMessage.isUnordered(),
                    sctpMessage.protocolIdentifier(), sctpMessage.streamIdentifier());
        } else {
            ByteBuf byteBuf = (ByteBuf) msg;
            payload = new PayloadData(byteBuf.readableBytes(), byteBuf, true, false, 0, 0);
        }

        if (logger.isDebugEnabled()) {
            logger.debug(String.format("Rx : Ass=%s %s", this.association.getName(), payload));
        }

        this.association.read(payload);
        // } finally {
        // ReferenceCountUtil.release(msg);
        // }
    }

    protected void writeAndFlush(Object message) {
        Channel ch = this.channel;
        if (ch != null) {
            ChannelFuture future = ch.writeAndFlush(message);

            long curMillisec = System.currentTimeMillis();
            long secPart = curMillisec / 500;
            if (lastCongestionMonitorSecondPart < secPart) {
                lastCongestionMonitorSecondPart = secPart;
                CongestionMonitor congestionMonitor = new CongestionMonitor();
                future.addListener(congestionMonitor);
            }
        }
     }

    private void onCongestionMonitor(double delaySec) {
        int newAlarmLevel = this.association.getCongestionLevel();
        for (int i1 = this.association.getCongestionLevel() - 1; i1 >= 0; i1--) {
            if (delaySec <= this.association.getManagement().congControl_BackToNormalDelayThreshold[i1]) {
                newAlarmLevel = i1;
            }
        }
        for (int i1 = this.association.getCongestionLevel(); i1 < 3; i1++) {
            if (delaySec >= this.association.getManagement().congControl_DelayThreshold[i1]) {
                newAlarmLevel = i1 + 1;
            }
        }
        this.association.setCongestionLevel(newAlarmLevel);
    }

    private class CongestionMonitor implements ChannelFutureListener {
        long startTime = System.currentTimeMillis();

        @Override
        public void operationComplete(ChannelFuture arg0) throws Exception {
            long delay = System.currentTimeMillis() - startTime;
            double delaySec = (double) delay / 1000;
            onCongestionMonitor(delaySec);
        }

    }

    protected void closeChannel() {
        Channel ch = this.channel;
        if (ch != null) {
            try {
                ch.close().sync();
            } catch (InterruptedException e) {
                logger.error(String.format("Error while trying to close Channel for Associtaion %s",
                        this.association.getName(), e));
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy