/*
* Copyright 2009-2016 Weibo, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.weibo.api.motan.transport.netty;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
import org.jboss.netty.channel.ChannelFuture;
import com.weibo.api.motan.common.ChannelState;
import com.weibo.api.motan.common.URLParamType;
import com.weibo.api.motan.exception.MotanErrorMsgConstant;
import com.weibo.api.motan.exception.MotanFrameworkException;
import com.weibo.api.motan.exception.MotanServiceException;
import com.weibo.api.motan.rpc.Future;
import com.weibo.api.motan.rpc.FutureListener;
import com.weibo.api.motan.rpc.Request;
import com.weibo.api.motan.rpc.Response;
import com.weibo.api.motan.rpc.URL;
import com.weibo.api.motan.transport.TransportException;
import com.weibo.api.motan.util.ExceptionUtil;
import com.weibo.api.motan.util.LoggerUtil;
import com.weibo.api.motan.util.MotanFrameworkUtil;
/**
* @author maijunsheng
* @version 创建时间:2013-5-31
*
*/
public class NettyChannel implements com.weibo.api.motan.transport.Channel {
private volatile ChannelState state = ChannelState.UNINIT;
private NettyClient nettyClient;
private org.jboss.netty.channel.Channel channel = null;
private InetSocketAddress remoteAddress = null;
private InetSocketAddress localAddress = null;
public NettyChannel(NettyClient nettyClient) {
this.nettyClient = nettyClient;
this.remoteAddress = new InetSocketAddress(nettyClient.getUrl().getHost(), nettyClient.getUrl().getPort());
}
@Override
public Response request(Request request) throws TransportException {
int timeout = nettyClient.getUrl().getMethodParameter(request.getMethodName(), request.getParamtersDesc(),
URLParamType.requestTimeout.getName(), URLParamType.requestTimeout.getIntValue());
if (timeout <= 0) {
throw new MotanFrameworkException("NettyClient init Error: timeout(" + timeout + ") <= 0 is forbid.",
MotanErrorMsgConstant.FRAMEWORK_INIT_ERROR);
}
NettyResponseFuture response = new NettyResponseFuture(request, timeout, this.nettyClient);
this.nettyClient.registerCallback(request.getRequestId(), response);
ChannelFuture writeFuture = this.channel.write(request);
boolean result = writeFuture.awaitUninterruptibly(timeout, TimeUnit.MILLISECONDS);
if (result && writeFuture.isSuccess()) {
response.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (future.isSuccess() || (future.isDone() && ExceptionUtil.isBizException(future.getException()))) {
// 成功的调用
nettyClient.resetErrorCount();
} else {
// 失败的调用
nettyClient.incrErrorCount();
}
}
});
return response;
}
writeFuture.cancel();
response = this.nettyClient.removeCallback(request.getRequestId());
if (response != null) {
response.cancel();
}
// 失败的调用
nettyClient.incrErrorCount();
if (writeFuture.getCause() != null) {
throw new MotanServiceException("NettyChannel send request to server Error: url="
+ nettyClient.getUrl().getUri() + " local=" + localAddress + " "
+ MotanFrameworkUtil.toString(request), writeFuture.getCause());
} else {
throw new MotanServiceException("NettyChannel send request to server Timeout: url="
+ nettyClient.getUrl().getUri() + " local=" + localAddress + " "
+ MotanFrameworkUtil.toString(request));
}
}
@Override
public synchronized boolean open() {
if (isAvailable()) {
LoggerUtil.warn("the channel already open, local: " + localAddress + " remote: " + remoteAddress + " url: "
+ nettyClient.getUrl().getUri());
return true;
}
try {
ChannelFuture channleFuture = nettyClient.getBootstrap().connect(
new InetSocketAddress(nettyClient.getUrl().getHost(), nettyClient.getUrl().getPort()));
long start = System.currentTimeMillis();
int timeout = nettyClient.getUrl().getIntParameter(URLParamType.connectTimeout.getName(), URLParamType.connectTimeout.getIntValue());
if (timeout <= 0) {
throw new MotanFrameworkException("NettyClient init Error: timeout(" + timeout + ") <= 0 is forbid.",
MotanErrorMsgConstant.FRAMEWORK_INIT_ERROR);
}
// 不去依赖于connectTimeout
boolean result = channleFuture.awaitUninterruptibly(timeout, TimeUnit.MILLISECONDS);
boolean success = channleFuture.isSuccess();
if (result && success) {
channel = channleFuture.getChannel();
if (channel.getLocalAddress() != null && channel.getLocalAddress() instanceof InetSocketAddress) {
localAddress = (InetSocketAddress) channel.getLocalAddress();
}
state = ChannelState.ALIVE;
return true;
}
boolean connected = false;
if(channleFuture.getChannel() != null){
connected = channleFuture.getChannel().isConnected();
}
if (channleFuture.getCause() != null) {
channleFuture.cancel();
throw new MotanServiceException("NettyChannel failed to connect to server, url: "
+ nettyClient.getUrl().getUri()+ ", result: " + result + ", success: " + success + ", connected: " + connected, channleFuture.getCause());
} else {
channleFuture.cancel();
throw new MotanServiceException("NettyChannel connect to server timeout url: "
+ nettyClient.getUrl().getUri() + ", cost: " + (System.currentTimeMillis() - start) + ", result: " + result + ", success: " + success + ", connected: " + connected);
}
} catch (MotanServiceException e) {
throw e;
} catch (Exception e) {
throw new MotanServiceException("NettyChannel failed to connect to server, url: "
+ nettyClient.getUrl().getUri(), e);
} finally {
if (!state.isAliveState()) {
nettyClient.incrErrorCount();
}
}
}
@Override
public synchronized void close() {
close(0);
}
@Override
public synchronized void close(int timeout) {
try {
state = ChannelState.CLOSE;
if (channel != null) {
channel.close();
}
} catch (Exception e) {
LoggerUtil
.error("NettyChannel close Error: " + nettyClient.getUrl().getUri() + " local=" + localAddress, e);
}
}
@Override
public InetSocketAddress getLocalAddress() {
return localAddress;
}
@Override
public InetSocketAddress getRemoteAddress() {
return remoteAddress;
}
@Override
public boolean isClosed() {
return state.isCloseState();
}
@Override
public boolean isAvailable() {
return state.isAliveState();
}
@Override
public URL getUrl() {
return nettyClient.getUrl();
}
}