/*
* TeleStax, Open Source Cloud Communications
* Copyright 2011-2017, 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 org.apache.log4j.Logger;
import org.restcomm.media.network.deprecated.netty.NetworkManager;
import com.google.common.util.concurrent.FutureCallback;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
/**
* Implementation if {@link NettyNetworkChannelFsm} that uses Asynchronous API of {@link NetworkManager}.
*
* @author Henrique Rosa (henrique.rosa@telestax.com)
*
*/
public class AsyncNettyNetworkChannelFsm extends AbstractNettyNetworkChannelFsm {
private static final Logger log = Logger.getLogger(AsyncNettyNetworkChannelFsm.class);
private final NettyNetworkChannelGlobalContext globalContext;
public AsyncNettyNetworkChannelFsm(NettyNetworkChannelGlobalContext globalContext) {
super();
this.globalContext = globalContext;
}
@Override
public void enterOpening(NettyNetworkChannelState from, NettyNetworkChannelState to, NettyNetworkChannelEvent event, NettyNetworkChannelTransitionContext context) {
// Try opening the channel
// A listener will get the response asynchronously
this.globalContext.getNetworkManager().openChannel(new OpenCallback(context));
}
@Override
public void enterBinding(NettyNetworkChannelState from, NettyNetworkChannelState to, NettyNetworkChannelEvent event, NettyNetworkChannelTransitionContext context) {
// Bind channel to local address
// A listener will get the response asynchronously
final ChannelFuture future = this.globalContext.getChannel().bind(this.globalContext.getLocalAddress());
future.addListener(new BindCallback(context));
}
@Override
public void enterConnecting(NettyNetworkChannelState from, NettyNetworkChannelState to, NettyNetworkChannelEvent event, NettyNetworkChannelTransitionContext context) {
final ChannelFuture future = this.globalContext.getChannel().connect(this.globalContext.getRemoteAddress());
future.addListener(new ConnectCallback(context));
}
@Override
public void enterDisconnecting(NettyNetworkChannelState from, NettyNetworkChannelState to, NettyNetworkChannelEvent event, NettyNetworkChannelTransitionContext context) {
// Disconnect channel from remote peer
// A listener will get the response asynchronously
final ChannelFuture future = this.globalContext.getChannel().disconnect();
future.addListener(new DisconnectCallback(context));
}
@Override
public void exitDisconnecting(NettyNetworkChannelState from, NettyNetworkChannelState to, NettyNetworkChannelEvent event, NettyNetworkChannelTransitionContext context) {
// Clean reference to remote address
this.globalContext.setRemoteAddress(null);
}
@Override
public void enterClosing(NettyNetworkChannelState from, NettyNetworkChannelState to, NettyNetworkChannelEvent event, NettyNetworkChannelTransitionContext context) {
final ChannelFuture future = this.globalContext.getChannel().close();
future.addListener(new CloseCallback(context));
}
@Override
public void enterClosed(NettyNetworkChannelState from, NettyNetworkChannelState to, NettyNetworkChannelEvent event, NettyNetworkChannelTransitionContext context) {
this.globalContext.clean();
}
private final class OpenCallback implements FutureCallback<Channel> {
private final NettyNetworkChannelTransitionContext context;
public OpenCallback(NettyNetworkChannelTransitionContext context) {
this.context = context;
}
@Override
public void onSuccess(Channel result) {
if(log.isTraceEnabled()) {
log.trace("Channel opened successfully");
}
globalContext.setChannel(result);
fire(NettyNetworkChannelEvent.OPENED, this.context);
this.context.getCallback().onSuccess(null);
}
@Override
public void onFailure(Throwable t) {
log.warn("Channel could not be opened, so will be terminated prematurely.", t);
fire(NettyNetworkChannelEvent.CLOSE, this.context);
this.context.getCallback().onFailure(t);
}
}
private final class BindCallback implements ChannelFutureListener {
private final NettyNetworkChannelTransitionContext context;
public BindCallback(NettyNetworkChannelTransitionContext context) {
this.context = context;
}
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
if(log.isDebugEnabled()) {
log.debug("Channel is bound to " + future.channel().localAddress().toString());
}
fire(NettyNetworkChannelEvent.BOUND, this.context);
this.context.getCallback().onSuccess(null);
} else {
log.warn("Could not bind channel to " + globalContext.getLocalAddress() + ". Channel will be closed.", future.cause());
fire(NettyNetworkChannelEvent.CLOSE, this.context);
this.context.getCallback().onFailure(future.cause());
}
}
}
private final class ConnectCallback implements ChannelFutureListener {
private final NettyNetworkChannelTransitionContext context;
public ConnectCallback(NettyNetworkChannelTransitionContext context) {
this.context = context;
}
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
if(log.isDebugEnabled()) {
log.debug("Channel is connected to " + future.channel().remoteAddress().toString());
}
fire(NettyNetworkChannelEvent.CONNECTED, this.context);
this.context.getCallback().onSuccess(null);
} else {
log.warn("Could not connect channel to " + globalContext.getRemoteAddress() + ". Channel will be closed.", future.cause());
fire(NettyNetworkChannelEvent.CLOSE, this.context);
this.context.getCallback().onFailure(future.cause());
}
}
}
private final class DisconnectCallback implements ChannelFutureListener {
private final NettyNetworkChannelTransitionContext context;
public DisconnectCallback(NettyNetworkChannelTransitionContext context) {
this.context = context;
}
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
if(log.isDebugEnabled()) {
log.debug("Channel disconnected from " + future.channel().remoteAddress().toString());
}
fire(NettyNetworkChannelEvent.DISCONNECTED, this.context);
this.context.getCallback().onSuccess(null);
} else {
log.warn("Could not disconnect channel from " + globalContext.getRemoteAddress() + ". Channel will be closed.", future.cause());
fire(NettyNetworkChannelEvent.CLOSE, this.context);
this.context.getCallback().onFailure(future.cause());
}
}
}
private final class CloseCallback implements ChannelFutureListener {
private final NettyNetworkChannelTransitionContext context;
public CloseCallback(NettyNetworkChannelTransitionContext context) {
this.context = context;
}
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
if(log.isDebugEnabled()) {
log.debug("Channel closed in elegant manner");
}
this.context.getCallback().onSuccess(null);
} else {
log.warn("Channel could not be closed properly", future.cause());
this.context.getCallback().onFailure(future.cause());
}
fire(NettyNetworkChannelEvent.CLOSED, this.context);
}
}
}