package com.dianping.pigeon.remoting.invoker.concurrent;
import java.io.Serializable;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import com.dianping.pigeon.extension.ExtensionLoader;
import com.dianping.pigeon.log.Logger;
import com.dianping.pigeon.log.LoggerLoader;
import com.dianping.pigeon.monitor.Monitor;
import com.dianping.pigeon.monitor.MonitorLoader;
import com.dianping.pigeon.monitor.MonitorTransaction;
import com.dianping.pigeon.remoting.common.domain.InvocationRequest;
import com.dianping.pigeon.remoting.common.domain.InvocationResponse;
import com.dianping.pigeon.remoting.common.domain.generic.UnifiedResponse;
import com.dianping.pigeon.remoting.common.util.Constants;
import com.dianping.pigeon.remoting.common.util.ContextUtils;
import com.dianping.pigeon.remoting.common.util.InvocationUtils;
import com.dianping.pigeon.remoting.invoker.Client;
import com.dianping.pigeon.remoting.invoker.process.ExceptionManager;
import com.dianping.pigeon.remoting.invoker.process.InvokerContextProcessor;
import com.dianping.pigeon.remoting.invoker.route.statistics.ServiceStatisticsHolder;
/**
* Created by chenchongze on 16/9/9.
*/
public class CallbackFuture implements Callback, CallFuture {
private static final Logger logger = LoggerLoader.getLogger(CallbackFuture.class);
protected static final Monitor monitor = MonitorLoader.getMonitor();
private static final InvokerContextProcessor contextProcessor = ExtensionLoader
.getExtension(InvokerContextProcessor.class);
protected InvocationResponse response;
private boolean done = false;
private boolean cancelled = false;
protected InvocationRequest request;
protected Client client;
protected MonitorTransaction transaction;
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
public CallbackFuture() {
transaction = monitor.getCurrentCallTransaction();
}
@Override
public void callback(InvocationResponse response) {
this.response = response;
}
@Override
public void run() {
lock.lock();
try {
this.done = true;
if (condition != null) {
condition.signal();
}
} finally {
lock.unlock();
}
}
@Override
public boolean isDone() {
return this.done;
}
protected InvocationResponse waitResponse(long timeoutMillis) throws InterruptedException {
if (response != null && response.getMessageType() == Constants.MESSAGE_TYPE_SERVICE) {
return response;
}
if (request == null && response != null) {
return response;
}
lock.lock();
try {
long start = request.getCreateMillisTime();
long timeoutLeft = timeoutMillis;
while (!isDone()) {
condition.await(timeoutLeft, TimeUnit.MILLISECONDS);
long timeoutPassed = System.currentTimeMillis() - start;
if (isDone() || timeoutPassed >= timeoutMillis) {
break;
} else {
timeoutLeft = timeoutMillis - timeoutPassed;
}
}
} finally {
lock.unlock();
}
if (!isDone()) {
if (client != null) {
ServiceStatisticsHolder.flowOut(request, client.getAddress());
}
throw InvocationUtils.newTimeoutException(
"request timeout, current time:" + System.currentTimeMillis() + "\r\nrequest:" + request);
}
return this.response;
}
@Override
public InvocationResponse getResponse() throws InterruptedException {
return getResponse(Long.MAX_VALUE);
}
@Override
public InvocationResponse getResponse(long timeout, TimeUnit unit) throws InterruptedException {
return getResponse(unit.toMillis(timeout));
}
@Override
public InvocationResponse getResponse(long timeoutMillis) throws InterruptedException {
waitResponse(timeoutMillis);
processContext();
if (response.getMessageType() == Constants.MESSAGE_TYPE_EXCEPTION) {
String addr = client != null ? client.getAddress() : "";
ExceptionManager.INSTANCE.logRemoteCallException(addr, request.getServiceName(),
request.getMethodName(), "remote call error", request, response, transaction);
} else if (response.getMessageType() == Constants.MESSAGE_TYPE_SERVICE_EXCEPTION) {
ExceptionManager.INSTANCE.logRemoteServiceException("remote service biz error", request, response);
}
return this.response;
}
@Override
public void setRequest(InvocationRequest request) {
this.request = request;
}
@Override
public void setClient(Client client) {
this.client = client;
}
@Override
public Client getClient() {
return this.client;
}
@Override
public void dispose() {
}
@Override
public boolean cancel() {
return this.cancelled;
}
@Override
public boolean isCancelled() {
return this.cancelled;
}
protected void processContext() {
if (response == null) {
return;
}
if (response instanceof UnifiedResponse) {
processContext0((UnifiedResponse) response);
} else {
processContext0();
}
}
protected void processContext0() {
Map<String, Serializable> responseValues = response.getResponseValues();
if (responseValues != null) {
ContextUtils.setResponseContext(responseValues);
}
if (contextProcessor != null) {
contextProcessor.postInvoke(response);
}
}
protected void processContext0(UnifiedResponse response) {
if (response != null) {
UnifiedResponse _response = (UnifiedResponse) response;
Map<String, String> responseValues = _response.getLocalContext();
if (responseValues != null) {
ContextUtils.setResponseContext((Map) responseValues);
}
}
}
protected void setResponseContext(InvocationResponse response) {
if (response == null) {
return;
}
if (response instanceof UnifiedResponse) {
UnifiedResponse _response = (UnifiedResponse) response;
Map<String, String> responseValues = _response.getLocalContext();
if (responseValues != null) {
ContextUtils.setResponseContext((Map) responseValues);
}
} else {
Map<String, Serializable> responseValues = response.getResponseValues();
if (responseValues != null) {
ContextUtils.setResponseContext(responseValues);
}
}
}
}