org.restcomm.media.network.netty.channel.AsyncNettyNetworkChannel.java Source code

Java tutorial

Introduction

Here is the source code for org.restcomm.media.network.netty.channel.AsyncNettyNetworkChannel.java

Source

/*
 * TeleStax, Open Source Cloud Communications
 * Copyright 2011-2016, Telestax Inc and individual contributors
 * by the @authors tag. 
 *
 * This 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.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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 this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.restcomm.media.network.netty.channel;

import java.net.SocketAddress;

import org.restcomm.media.network.api.AsynchronousNetworkChannel;
import org.restcomm.media.network.netty.NettyNetworkManager;

import com.google.common.util.concurrent.FutureCallback;

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.DefaultAddressedEnvelope;

/**
 * Asynchronous network channel powered by Netty.
 * 
 * @author Henrique Rosa (henrique.rosa@telestax.com)
 *
 */
public class AsyncNettyNetworkChannel<M> implements AsynchronousNetworkChannel<M> {

    public static final int N_THREADS = 1;

    private final NettyNetworkChannelGlobalContext context;
    private final NettyNetworkChannelFsm fsm;

    public AsyncNettyNetworkChannel(NettyNetworkManager networkManager) {
        this(new NettyNetworkChannelGlobalContext(networkManager));
    }

    public AsyncNettyNetworkChannel(NettyNetworkChannelGlobalContext context) {
        this.context = context;
        this.fsm = NettyNetworkChannelFsmBuilder.INSTANCE.build(this.context);
        this.fsm.start();
    }

    @Override
    public boolean isOpen() {
        final NettyNetworkChannelState state = this.fsm.getCurrentState();
        switch (state) {
        case UNINITIALIZED:
        case OPENING:
        case CLOSING:
        case CLOSED:
            return false;

        default:
            return true;
        }
    }

    @Override
    public boolean isBound() {
        final NettyNetworkChannelState state = this.fsm.getCurrentState();
        switch (state) {
        case BOUND:
        case CONNECTING:
        case CONNECTED:
            return true;

        default:
            return false;
        }
    }

    @Override
    public SocketAddress getLocalAddress() {
        return isBound() ? this.context.getLocalAddress() : null;
    }

    @Override
    public boolean isConnected() {
        return NettyNetworkChannelState.CONNECTED.equals(this.fsm.getCurrentState());
    }

    @Override
    public SocketAddress getRemoteAddress() {
        return isConnected() ? this.context.getRemoteAddress() : null;
    }

    @Override
    public void open(FutureCallback<Void> callback) {
        if (isOpen()) {
            // TODO handle inside FSM Listener
            callback.onFailure(new IllegalStateException("Channel is already open."));
        } else {
            NettyNetworkChannelTransitionContext transitionContext = new NettyNetworkChannelTransitionContext()
                    .setCallback(callback);
            this.fsm.fire(NettyNetworkChannelEvent.OPEN, transitionContext);
        }
    }

    @Override
    public void close(FutureCallback<Void> callback) {
        if (isOpen()) {
            NettyNetworkChannelTransitionContext transitionContext = new NettyNetworkChannelTransitionContext()
                    .setCallback(callback);
            this.fsm.fire(NettyNetworkChannelEvent.CLOSE, transitionContext);
        } else {
            // TODO handle inside FSM Listener
            callback.onFailure(new IllegalStateException("Channel is already closed."));
        }
    }

    @Override
    public void bind(SocketAddress localAddress, FutureCallback<Void> callback) {
        if (isBound()) {
            // TODO handle inside FSM Listener
            callback.onFailure(new IllegalStateException("Channel is already bound."));
        } else {
            this.context.setLocalAddress(localAddress);
            NettyNetworkChannelTransitionContext transitionContext = new NettyNetworkChannelTransitionContext()
                    .setCallback(callback);
            this.fsm.fire(NettyNetworkChannelEvent.BIND, transitionContext);
        }
    }

    @Override
    public void connect(SocketAddress remoteAddress, FutureCallback<Void> callback) {
        if (isConnected()) {
            // TODO handle inside FSM Listener
            callback.onFailure(new IllegalStateException("Channel is already bound."));
        } else {
            this.context.setRemoteAddress(remoteAddress);
            NettyNetworkChannelTransitionContext transitionContext = new NettyNetworkChannelTransitionContext()
                    .setCallback(callback);
            this.fsm.fire(NettyNetworkChannelEvent.CONNECT, transitionContext);
        }
    }

    @Override
    public void disconnect(FutureCallback<Void> callback) {
        if (isConnected()) {
            NettyNetworkChannelTransitionContext transitionContext = new NettyNetworkChannelTransitionContext()
                    .setCallback(callback);
            this.fsm.fire(NettyNetworkChannelEvent.DISCONNECT, transitionContext);
        } else {
            // TODO handle inside FSM Listener
            callback.onFailure(new IllegalStateException("Channel is not connected."));
        }
    }

    @Override
    public void receive() {
        if (isBound()) {
            this.context.getChannel().read();
        }
    }

    @Override
    public void send(M message, FutureCallback<Void> callback) {
        if (isConnected()) {
            final ChannelFuture future = this.context.getChannel().writeAndFlush(message);
            future.addListener(new NettyNetworkChannelVoidCallbackListener(callback));
        } else {
            callback.onFailure(new IllegalStateException("Channel is not connected."));
        }
    }

    @Override
    public void send(M message, SocketAddress remoteAddress, FutureCallback<Void> callback) {
        if (isBound()) {
            if (isConnected()) {
                callback.onFailure(
                        new IllegalStateException("Channel is connected. Cannot send traffic to another peer."));
            } else {
                DefaultAddressedEnvelope<M, SocketAddress> envelope = new DefaultAddressedEnvelope<>(message,
                        remoteAddress);
                final ChannelFuture future = this.context.getChannel().writeAndFlush(envelope);
                future.addListener(new NettyNetworkChannelVoidCallbackListener(callback));
            }
        } else {
            callback.onFailure(new IllegalStateException("Channel is not bound."));
        }
    }

    static final class NettyNetworkChannelVoidCallbackListener implements ChannelFutureListener {

        private final FutureCallback<Void> observer;

        public NettyNetworkChannelVoidCallbackListener(FutureCallback<Void> observer) {
            super();
            this.observer = observer;
        }

        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
            if (future.isSuccess()) {
                this.observer.onSuccess(null);
            } else {
                this.observer.onFailure(future.cause());
            }

        }
    }

}