/** * Dianping.com Inc. * Copyright (c) 2003-2013 All Rights Reserved. */ package com.dianping.pigeon.remoting.provider.service.method; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import com.dianping.pigeon.config.ConfigChangeListener; import com.dianping.pigeon.config.ConfigManager; import com.dianping.pigeon.config.ConfigManagerLoader; import org.apache.commons.lang.StringUtils; import com.dianping.pigeon.log.Logger; import com.dianping.pigeon.log.LoggerLoader; import com.dianping.pigeon.remoting.common.domain.CompactRequest; import com.dianping.pigeon.remoting.common.domain.InvocationRequest; import com.dianping.pigeon.remoting.common.domain.ServiceId; import com.dianping.pigeon.remoting.common.exception.BadRequestException; import com.dianping.pigeon.remoting.provider.config.ProviderConfig; import com.dianping.pigeon.remoting.provider.exception.InvocationFailureException; import com.dianping.pigeon.remoting.provider.process.filter.ContextTransferProcessFilter; import com.dianping.pigeon.remoting.provider.publish.ServicePublisher; import com.dianping.pigeon.util.LangUtils; public final class ServiceMethodFactory { private static final Logger logger = LoggerLoader.getLogger(ContextTransferProcessFilter.class); private static Map<String, ServiceMethodCache> methods = new ConcurrentHashMap<String, ServiceMethodCache>(); private static Set<String> ingoreMethods = new HashSet<String>(); private static final String KEY_COMPACT = "pigeon.invoker.request.compact"; private static final ConfigManager configManager = ConfigManagerLoader.getConfigManager(); private static volatile boolean isCompact = configManager.getBooleanValue(KEY_COMPACT, true); static { Method[] objectMethodArray = Object.class.getMethods(); for (Method method : objectMethodArray) { ingoreMethods.add(method.getName()); } Method[] classMethodArray = Class.class.getMethods(); for (Method method : classMethodArray) { ingoreMethods.add(method.getName()); } configManager.registerConfigChangeListener(new InnerConfigChangeListener()); } public static ServiceMethod getMethod(InvocationRequest request) throws InvocationFailureException { String serviceName = request.getServiceName(); String methodName = request.getMethodName(); if (StringUtils.isBlank(methodName)) { throw new IllegalArgumentException("method name is required"); } String[] paramClassNames = request.getParamClassName(); String version = request.getVersion(); String newUrl = ServicePublisher.getServiceUrlWithVersion(serviceName, version); if (logger.isDebugEnabled()) { logger.debug("get method for service url:" + request); } ServiceMethodCache serviceMethodCache = getServiceMethodCache(newUrl); if (serviceMethodCache == null) { if (logger.isDebugEnabled()) { logger.debug("no service found for version:" + version + ", use the default version of service:" + serviceName); } serviceMethodCache = getServiceMethodCache(serviceName); } if (serviceMethodCache == null) { throw new BadRequestException("cannot find service for request:" + request); } return serviceMethodCache.getMethod(methodName, new ServiceParam(paramClassNames)); } public static ServiceMethodCache getServiceMethodCache(String url) { ServiceMethodCache serviceMethodCache = methods.get(url); if (serviceMethodCache == null) { Map<String, ProviderConfig<?>> services = ServicePublisher.getAllServiceProviders(); ProviderConfig<?> providerConfig = services.get(url); if (providerConfig != null) { Object service = providerConfig.getService(); Method[] methodArray = service.getClass().getMethods(); serviceMethodCache = new ServiceMethodCache(url, service); for (Method method : methodArray) { if (!ingoreMethods.contains(method.getName())) { method.setAccessible(true); serviceMethodCache.addMethod(method.getName(), new ServiceMethod(service, method)); if (isCompact) { int id = LangUtils.hash(url + "#" + method.getName(), 0, Integer.MAX_VALUE); ServiceId serviceId = new ServiceId(url, method.getName()); ServiceId lastId = CompactRequest.PROVIDER_ID_MAP.putIfAbsent(id, serviceId); if (lastId != null && !serviceId.equals(lastId)) { throw new IllegalArgumentException("same id for service:" + url + ", method:" + method.getName()); } } } } methods.put(url, serviceMethodCache); } } return serviceMethodCache; } public static void init(String url) { getServiceMethodCache(url); } public static Map<String, ServiceMethodCache> getAllMethods() { return methods; } 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); } } } @Override public void onKeyAdded(String key, String value) { } @Override public void onKeyRemoved(String key) { } } }