/*
* 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.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.weibo.api.motan.common.FutureState;
import com.weibo.api.motan.exception.MotanErrorMsgConstant;
import com.weibo.api.motan.exception.MotanServiceException;
import com.weibo.api.motan.protocol.rpc.RpcProtocolVersion;
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.ResponseFuture;
import com.weibo.api.motan.transport.Channel;
import com.weibo.api.motan.util.LoggerUtil;
import com.weibo.api.motan.util.MotanFrameworkUtil;
/**
* netty response
*
* <pre>
* 1) getValue() :
*
* if (request is timeout or request is cancel or get exception)
* throw exception;
* else
* return value;
*
* 2) getException() :
*
* if (task is doing) :
* return null
* if (task is done and get exception):
* return exception
*
* </pre>
*
* @author maijunsheng
* @version 创建时间:2013-5-31
*
*/
public class NettyResponseFuture implements ResponseFuture {
private volatile FutureState state = FutureState.DOING;
private Object lock = new Object();
private Object result = null;
private Exception exception = null;
private long createTime = System.currentTimeMillis();
private int timeout = 0;
private long processTime = 0;
private Request request;
private List<FutureListener> listeners;
private Channel channel;
public NettyResponseFuture(Request requestObj, int timeout, Channel channel) {
this.request = requestObj;
this.timeout = timeout;
this.channel = channel;
}
public void onSuccess(Response response) {
this.result = response.getValue();
this.processTime = response.getProcessTime();
done();
}
public void onFailure(Response response) {
this.exception = response.getException();
this.processTime = response.getProcessTime();
done();
}
@Override
public Object getValue() {
synchronized (lock) {
if (!isDoing()) {
return getValueOrThrowable();
}
if (timeout <= 0) {
try {
lock.wait();
} catch (Exception e) {
cancel(new MotanServiceException("NettyResponseFuture getValue InterruptedException : "
+ MotanFrameworkUtil.toString(request) + " cost="
+ (System.currentTimeMillis() - createTime), e));
}
// don't need to notifylisteners, because onSuccess or
// onFailure or cancel method already call notifylisteners
return getValueOrThrowable();
} else {
long waitTime = timeout - (System.currentTimeMillis() - createTime);
if (waitTime > 0) {
for (;;) {
try {
lock.wait(waitTime);
} catch (InterruptedException e) {
}
if (!isDoing()) {
break;
} else {
waitTime = timeout - (System.currentTimeMillis() - createTime);
if (waitTime <= 0) {
break;
}
}
}
}
if (isDoing()) {
timeoutSoCancel();
}
}
return getValueOrThrowable();
}
}
@Override
public Exception getException() {
return exception;
}
@Override
public boolean cancel() {
Exception e = new MotanServiceException("NettyResponseFuture task cancel: serverPort="
+ channel.getUrl().getServerPortStr() + " " + MotanFrameworkUtil.toString(request) + " cost="
+ (System.currentTimeMillis() - createTime));
return cancel(e);
}
private boolean cancel(Exception e) {
synchronized (lock) {
if (!isDoing()) {
return false;
}
state = FutureState.CANCELLED;
exception = e;
lock.notifyAll();
}
notifyListeners();
return true;
}
@Override
public boolean isCancelled() {
return state.isCancelledState();
}
@Override
public boolean isDone() {
return state.isDoneState();
}
@Override
public boolean isSuccess() {
return isDone() && (exception == null);
}
@Override
public void addListener(FutureListener listener) {
if (listener == null) {
throw new NullPointerException("FutureListener is null");
}
boolean notifyNow = false;
synchronized (lock) {
if (!isDoing()) {
// is success, failure, timeout or cancel, don't add into
// listeners, just notify
notifyNow = true;
} else {
if (listeners == null) {
listeners = new ArrayList<FutureListener>(1);
}
listeners.add(listener);
}
}
if (notifyNow) {
notifyListener(listener);
}
}
public long getCreateTime() {
return createTime;
}
public Object getRequestObj() {
return request;
}
public FutureState getState() {
return state;
}
private void timeoutSoCancel() {
this.processTime = System.currentTimeMillis() - createTime;
synchronized (lock) {
if (!isDoing()) {
return;
}
state = FutureState.CANCELLED;
exception = new MotanServiceException("NettyResponseFuture request timeout: serverPort="
+ channel.getUrl().getServerPortStr() + " " + MotanFrameworkUtil.toString(request) + " cost="
+ (System.currentTimeMillis() - createTime), MotanErrorMsgConstant.SERVICE_TIMEOUT);
lock.notifyAll();
}
notifyListeners();
}
private void notifyListeners() {
if (listeners != null) {
for (FutureListener listener : listeners) {
notifyListener(listener);
}
}
}
private void notifyListener(FutureListener listener) {
try {
listener.operationComplete(this);
} catch (Throwable t) {
LoggerUtil.error("NettyResponseFuture notifyListener Error: " + listener.getClass().getSimpleName(), t);
}
}
private boolean isDoing() {
return state.isDoingState();
}
private boolean done() {
synchronized (lock) {
if (!isDoing()) {
return false;
}
state = FutureState.DONE;
lock.notifyAll();
}
notifyListeners();
return true;
}
public long getRequestId() {
return this.request.getRequestId();
}
private Object getValueOrThrowable() {
if (exception != null) {
throw (exception instanceof RuntimeException) ? (RuntimeException) exception : new MotanServiceException(
exception.getMessage(), exception);
}
return result;
}
@Override
public long getProcessTime() {
return processTime;
}
@Override
public void setProcessTime(long time) {
this.processTime = time;
}
public int getTimeout() {
return timeout;
}
@Override
public Map<String, String> getAttachments() {
// 不需要使用
return Collections.EMPTY_MAP;
}
@Override
public void setAttachment(String key, String value) {}
@Override
public void setRpcProtocolVersion(byte rpcProtocolVersion) {}
@Override
public byte getRpcProtocolVersion() {
return RpcProtocolVersion.VERSION_1.getVersion();
}
}