/**
* Dianping.com Inc.
* Copyright (c) 2003-2013 All Rights Reserved.
*/
package com.dianping.pigeon.remoting.invoker.process.filter;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import com.dianping.pigeon.util.AppUtils;
import org.apache.commons.lang.StringUtils;
import com.dianping.pigeon.config.ConfigChangeListener;
import com.dianping.pigeon.config.ConfigManager;
import com.dianping.pigeon.config.ConfigManagerLoader;
import com.dianping.pigeon.extension.ExtensionLoader;
import com.dianping.pigeon.log.Logger;
import com.dianping.pigeon.log.LoggerLoader;
import com.dianping.pigeon.monitor.MonitorLoader;
import com.dianping.pigeon.monitor.MonitorTransaction;
import com.dianping.pigeon.registry.RegistryManager;
import com.dianping.pigeon.registry.exception.RegistryException;
import com.dianping.pigeon.remoting.common.codec.SerializerType;
import com.dianping.pigeon.remoting.common.domain.CallMethod;
import com.dianping.pigeon.remoting.common.domain.CallType;
import com.dianping.pigeon.remoting.common.domain.CompactRequest;
import com.dianping.pigeon.remoting.common.domain.InvocationContext.TimePhase;
import com.dianping.pigeon.remoting.common.domain.InvocationContext.TimePoint;
import com.dianping.pigeon.remoting.common.domain.InvocationRequest;
import com.dianping.pigeon.remoting.common.domain.InvocationResponse;
import com.dianping.pigeon.remoting.common.domain.generic.UnifiedRequest;
import com.dianping.pigeon.remoting.common.process.ServiceInvocationHandler;
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.config.InvokerConfig;
import com.dianping.pigeon.remoting.invoker.domain.InvokerContext;
import com.dianping.pigeon.remoting.invoker.process.InvokerContextProcessor;
import com.dianping.pigeon.util.VersionUtils;
public class ContextPrepareInvokeFilter extends InvocationInvokeFilter {
private static final Logger logger = LoggerLoader.getLogger(ContextPrepareInvokeFilter.class);
private ConcurrentHashMap<String, Boolean> protoVersionMap = new ConcurrentHashMap<String, Boolean>();
private ConcurrentHashMap<String, Boolean> compactVersionMap = new ConcurrentHashMap<String, Boolean>();
private static AtomicLong requestSequenceMaker = new AtomicLong();
private static final String KEY_COMPACT = "pigeon.invoker.request.compact";
private static final String KEY_TIMEOUT_RESET = "pigeon.timeout.reset";
private static final String KEY_CONTEXT_ENABLE = "pigeon.invoker.context.enable";
private static final InvokerContextProcessor contextProcessor = ExtensionLoader
.getExtension(InvokerContextProcessor.class);
private static final ConfigManager configManager = ConfigManagerLoader.getConfigManager();
private static volatile boolean isCompact = configManager.getBooleanValue(KEY_COMPACT, true);
private static volatile boolean isTimeoutReset = configManager.getBooleanValue(KEY_TIMEOUT_RESET, true);
private static volatile boolean contextEnable = configManager.getBooleanValue(KEY_CONTEXT_ENABLE, true);
public ContextPrepareInvokeFilter() {
ConfigManagerLoader.getConfigManager().registerConfigChangeListener(new InnerConfigChangeListener());
}
private static class InnerConfigChangeListener implements ConfigChangeListener {
@Override
public void onKeyUpdated(String key, String value) {
if (key.endsWith(KEY_COMPACT)) {
try {
isCompact = Boolean.valueOf(value);
} catch (RuntimeException e) {
logger.warn("invalid value for key " + key, e);
}
} else if (key.endsWith(KEY_TIMEOUT_RESET)) {
try {
isTimeoutReset = Boolean.valueOf(value);
} catch (RuntimeException e) {
logger.warn("invalid value for key " + key, e);
}
} else if (key.endsWith(KEY_CONTEXT_ENABLE)) {
try {
contextEnable = Boolean.valueOf(value);
} catch (RuntimeException e) {
logger.warn("invalid value for key " + key, e);
}
}
}
@Override
public void onKeyAdded(String key, String value) {
// TODO Auto-generated method stub
}
@Override
public void onKeyRemoved(String key) {
// TODO Auto-generated method stub
}
}
@Override
public InvocationResponse invoke(ServiceInvocationHandler handler, InvokerContext invocationContext)
throws Throwable {
invocationContext.getTimeline().add(new TimePoint(TimePhase.C));
readMonitorContext(invocationContext);
initRequest(invocationContext);
if (contextEnable) {
transferContextValueToRequest(invocationContext, invocationContext.getRequest());
}
try {
return handler.handle(invocationContext);
} finally {
ContextUtils.clearRequestContext();
}
}
private void readMonitorContext(InvokerContext invocationContext) {
MonitorTransaction transaction = MonitorLoader.getMonitor().getCurrentCallTransaction();
if (transaction != null) {
Client client = invocationContext.getClient();
String targetApp = RegistryManager.getInstance().getReferencedAppFromCache(client.getAddress());
if (StringUtils.isEmpty(targetApp)) {
InvokerConfig invokerConfig = invocationContext.getInvokerConfig();
String vip = invokerConfig.getVip();
if (vip != null && vip.startsWith("console:")) {
targetApp = AppUtils.getAppName();
} else {
targetApp = AppUtils.UNKNOWN;
}
}
transaction.readMonitorContext(targetApp);
}
}
// 初始化Request的createTime和timeout,以便统一这两个值
private void initRequest(InvokerContext invokerContext) {
InvocationRequest request = invokerContext.getRequest();
if (!(request instanceof UnifiedRequest)) {
compactRequest(invokerContext);
} else {
UnifiedRequest _request = (UnifiedRequest) request;
_request.setServiceInterface(invokerContext.getInvokerConfig().getServiceInterface());
_request.setParameterTypes(invokerContext.getParameterTypes());
}
checkSerialize(invokerContext);
request = invokerContext.getRequest();
request.setSequence(requestSequenceMaker.incrementAndGet() * -1);
request.setCreateMillisTime(System.currentTimeMillis());
request.setMessageType(Constants.MESSAGE_TYPE_SERVICE);
InvokerConfig<?> invokerConfig = invokerContext.getInvokerConfig();
if (invokerConfig != null) {
request.setTimeout(invokerConfig.getTimeout(invokerContext.getMethodName()));
if (isTimeoutReset) {
Object timeout = ContextUtils.getLocalContext(Constants.REQUEST_TIMEOUT);
if (timeout != null) {
int timeout_ = Integer.parseInt(String.valueOf(timeout));
if (timeout_ > 0 && timeout_ < request.getTimeout()) {
request.setTimeout(timeout_);
}
}
}
if (CallMethod.isOneway(invokerConfig.getCallType())) {
request.setCallType(CallType.NOREPLY.getCode());
} else {
request.setCallType(CallType.REPLY.getCode());
}
}
}
private void checkSerialize(InvokerContext invokerContext) {
InvocationRequest request = invokerContext.getRequest();
if (SerializerType.isProto(request.getSerialize()) || SerializerType.isFst(request.getSerialize())) {
checkVersion(invokerContext);
} else if (SerializerType.isThrift(request.getSerialize())) {
checkProtocol(invokerContext);
}
}
// 缺服务是否支持判断
private void checkVersion(InvokerContext invokerContext) {
Client client = invokerContext.getClient();
InvocationRequest request = invokerContext.getRequest();
String version = RegistryManager.getInstance().getReferencedVersionFromCache(client.getAddress());
boolean supported = true;
if (StringUtils.isBlank(version)) {
supported = false;
} else if (protoVersionMap.containsKey(version)) {
supported = protoVersionMap.get(version);
} else {
supported = VersionUtils.isProtoFstSupported(version);
protoVersionMap.putIfAbsent(version, supported);
}
if (!supported) {
request.setSerialize(SerializerType.HESSIAN.getCode());
invokerContext.getInvokerConfig().setSerialize(SerializerType.HESSIAN.getName());
}
}
private void checkProtocol(InvokerContext invokerContext) {
Client client = invokerContext.getClient();
InvocationRequest request = invokerContext.getRequest();
boolean supported = false;
try {
supported = RegistryManager.getInstance().isSupportNewProtocol(client.getAddress(),
request.getServiceName());
} catch (RegistryException e) {
supported = false;
}
if (!supported) {
InvocationRequest _request = InvocationUtils.newRequest(invokerContext);
_request.setSerialize(SerializerType.HESSIAN.getCode());
invokerContext.setRequest(_request);
}
}
private void compactRequest(InvokerContext invokerContext) {
boolean isCompactReq = false;
if (isCompact && InvokerConfig.PROTOCOL_DEFAULT.equals(invokerContext.getInvokerConfig().getProtocol())) {
Client client = invokerContext.getClient();
String version = RegistryManager.getInstance().getReferencedVersionFromCache(client.getAddress());
if (StringUtils.isBlank(version)) {
isCompactReq = false;
} else if (compactVersionMap.containsKey(version)) {
isCompactReq = compactVersionMap.get(version);
} else {
isCompactReq = VersionUtils.isCompactSupported(version);
compactVersionMap.putIfAbsent(version, isCompactReq);
}
}
if (isCompactReq) {
invokerContext.setRequest(new CompactRequest(invokerContext));
}
}
private void transferContextValueToRequest(final InvokerContext invocationContext,
final InvocationRequest request) {
if (request instanceof UnifiedRequest) {
UnifiedRequest _request = (UnifiedRequest) request;
_request.setParameterTypes(invocationContext.getParameterTypes());
transferContextValueToRequest0(_request);
} else {
transferContextValueToRequest0(invocationContext, request);
}
}
private void transferContextValueToRequest0(final InvokerContext invocationContext,
final InvocationRequest request) {
if (contextProcessor != null) {
contextProcessor.preInvoke(invocationContext);
}
if (ContextUtils.getGlobalContext(Constants.CONTEXT_KEY_SOURCE_APP) == null) {
ContextUtils.putGlobalContext(Constants.CONTEXT_KEY_SOURCE_APP,
ConfigManagerLoader.getConfigManager().getAppName());
ContextUtils.putGlobalContext(Constants.CONTEXT_KEY_SOURCE_IP,
ConfigManagerLoader.getConfigManager().getLocalIp());
}
request.setGlobalValues(ContextUtils.getGlobalContext());
ContextUtils.initRequestContext();
request.setRequestValues(ContextUtils.getRequestContext());
}
private void transferContextValueToRequest0(final UnifiedRequest request) {
if (ContextUtils.getGlobalContext(Constants.CONTEXT_KEY_SOURCE_APP) == null) {
ContextUtils.putGlobalContext(Constants.CONTEXT_KEY_SOURCE_APP,
ConfigManagerLoader.getConfigManager().getAppName());
ContextUtils.putGlobalContext(Constants.CONTEXT_KEY_SOURCE_IP,
ConfigManagerLoader.getConfigManager().getLocalIp());
}
Map<String, String> _globalContext = request.getGlobalContext();
if (_globalContext == null) {
_globalContext = new HashMap<String, String>();
request.setGlobalContext(_globalContext);
}
Map<String, Serializable> globalContext = ContextUtils.getGlobalContext();
ContextUtils.convertContext(globalContext, _globalContext);
Map<String, String> _localContext = request.getLocalContext();
if (_localContext == null) {
_localContext = new HashMap<String, String>();
request.setLocalContext(_localContext);
}
Map<String, Serializable> localContext = ContextUtils.getRequestContext();
ContextUtils.convertContext(localContext, _localContext);
}
}