package com.dianping.pigeon.remoting.netty.channel;
import com.dianping.pigeon.log.Logger;
import com.dianping.pigeon.log.LoggerLoader;
import com.dianping.pigeon.remoting.common.exception.NetworkException;
import com.dianping.pigeon.util.NetUtils;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author qi.yin
* 2016/09/23 上午10:31.
*/
public class DefaultNettyChannel implements NettyChannel {
private static final Logger logger = LoggerLoader.getLogger(NettyChannel.class);
private ReentrantLock connectLock = new ReentrantLock();
private int timeout;
private volatile Channel channel;
private ClientBootstrap bootstrap;
private InetSocketAddress localAddress;
private InetSocketAddress remoteAddress;
private String remoteAddressString;
public DefaultNettyChannel(ClientBootstrap bootstrap, String remoteHost, int remotePort, int timeout) {
this.bootstrap = bootstrap;
this.remoteAddress = new InetSocketAddress(remoteHost, remotePort);
this.remoteAddressString = NetUtils.toAddress(remoteHost, remotePort);
this.timeout = timeout;
}
@Override
public void connect() throws NetworkException {
connectLock.lock();
try {
if (isAvaliable()) {
logger.info("[connect] is connected to remote " + remoteAddress + ".");
return;
}
ChannelFuture future = bootstrap.connect(remoteAddress);
try {
if (future.awaitUninterruptibly(timeout, TimeUnit.MILLISECONDS)) {
if (future.isSuccess()) {
disConnect();
this.channel = future.getChannel();
localAddress = (InetSocketAddress) this.channel.getLocalAddress();
} else {
throw new NetworkException("connected to remote " + remoteAddress + " failed.");
}
} else {
throw new NetworkException("timeout connecting to remote " + remoteAddress + ".");
}
} catch (Throwable e) {
throw new NetworkException("error connecting to remote " + remoteAddress + ".", e);
} finally {
if (!isConnected()) {
future.cancel();
}
}
} finally {
connectLock.unlock();
}
}
@Override
public void disConnect() {
connectLock.lock();
try {
if (this.channel != null) {
this.channel.close();
}
} catch (Throwable e) {
logger.error("[disConnect] error disConnecting channel. ", e);
} finally {
connectLock.unlock();
}
}
@Override
public ChannelFuture write0(Object message) throws NetworkException {
if (!isAvaliable()) {
throw new NetworkException("[write0] channel is null or channel is close.");
}
return channel.write(message);
}
@Override
public void write(Object message) throws NetworkException {
write0(message);
}
private boolean isConnected() {
if (this.channel != null) {
return this.channel.isConnected();
}
return false;
}
@Override
public boolean isAvaliable() {
return channel != null && channel.isConnected();
}
public boolean isWritable() {
return channel.isWritable();
}
@Override
public InetSocketAddress getLocalAddress() {
return localAddress;
}
@Override
public InetSocketAddress getRemoteAddress() {
return remoteAddress;
}
@Override
public String getRemoteAddressString() {
return this.remoteAddressString;
}
public int getTimeout() {
return timeout;
}
public String toString() {
return "NettyChannel[avaliable = " + isAvaliable() + "localAddress=" + localAddress.toString() + "remoteAddress= " + remoteAddress.toString() + "]";
}
}