package com.weibo.api.motan.config.springsupport;
import com.weibo.api.motan.cluster.support.ClusterSupport;
import com.weibo.api.motan.config.BasicRefererInterfaceConfig;
import com.weibo.api.motan.config.BasicServiceInterfaceConfig;
import com.weibo.api.motan.config.ConfigUtil;
import com.weibo.api.motan.config.ExtConfig;
import com.weibo.api.motan.config.ProtocolConfig;
import com.weibo.api.motan.config.RegistryConfig;
import com.weibo.api.motan.config.springsupport.annotation.MotanReferer;
import com.weibo.api.motan.config.springsupport.annotation.MotanService;
import com.weibo.api.motan.config.springsupport.util.SpringBeanUtil;
import com.weibo.api.motan.rpc.init.Initializable;
import com.weibo.api.motan.rpc.init.InitializationFactory;
import com.weibo.api.motan.util.ConcurrentHashSet;
import com.weibo.api.motan.util.LoggerUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.Ordered;
import org.springframework.util.ClassUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* @author fld
*
* Annotation bean for motan
* <p>
* <p>
* Created by fld on 16/5/13.
*/
public class AnnotationBean implements DisposableBean, BeanFactoryPostProcessor, BeanPostProcessor, BeanFactoryAware, Ordered {
private String id;
private String annotationPackage;
private String[] annotationPackages;
private BeanFactory beanFactory;
List<ClusterSupport<?>> clusterSupportList = new ArrayList<ClusterSupport<?>>();
public AnnotationBean() {
}
private final Set<ServiceConfigBean<?>> serviceConfigs = new ConcurrentHashSet<ServiceConfigBean<?>>();
private final ConcurrentMap<String, RefererConfigBean> referenceConfigs = new ConcurrentHashMap<String, RefererConfigBean>();
static{
//custom Initializable before motan beans inited
Initializable initialization = InitializationFactory.getInitialization();
initialization.init();
}
/**
* @param beanFactory
* @throws BeansException
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
if (annotationPackage == null || annotationPackage.length() == 0) {
return;
}
if (beanFactory instanceof BeanDefinitionRegistry) {
try {
// init scanner
Class<?> scannerClass = ClassUtils.forName("org.springframework.context.annotation.ClassPathBeanDefinitionScanner",
AnnotationBean.class.getClassLoader());
Object scanner = scannerClass.getConstructor(new Class<?>[]{BeanDefinitionRegistry.class, boolean.class})
.newInstance(new Object[]{(BeanDefinitionRegistry) beanFactory, true});
// add filter
Class<?> filterClass = ClassUtils.forName("org.springframework.core.type.filter.AnnotationTypeFilter",
AnnotationBean.class.getClassLoader());
Object filter = filterClass.getConstructor(Class.class).newInstance(MotanService.class);
Method addIncludeFilter = scannerClass.getMethod("addIncludeFilter",
ClassUtils.forName("org.springframework.core.type.filter.TypeFilter", AnnotationBean.class.getClassLoader()));
addIncludeFilter.invoke(scanner, filter);
// scan packages
Method scan = scannerClass.getMethod("scan", new Class<?>[]{String[].class});
scan.invoke(scanner, new Object[]{annotationPackages});
} catch (Throwable e) {
// spring 2.0
}
}
}
/**
* init reference field
*
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!isMatchPackage(bean)) {
return bean;
}
Class<?> clazz = bean.getClass();
if (isProxyBean(bean)) {
clazz = AopUtils.getTargetClass(bean);
}
Method[] methods = clazz.getMethods();
for (Method method : methods) {
String name = method.getName();
if (name.length() > 3 && name.startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())
&& !Modifier.isStatic(method.getModifiers())) {
try {
MotanReferer reference = method.getAnnotation(MotanReferer.class);
if (reference != null) {
Object value = refer(reference, method.getParameterTypes()[0]);
if (value != null) {
method.invoke(bean, new Object[]{value});
}
}
} catch (Exception e) {
throw new BeanInitializationException("Failed to init remote service reference at method " + name
+ " in class " + bean.getClass().getName(), e);
}
}
}
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
try {
if (!field.isAccessible()) {
field.setAccessible(true);
}
MotanReferer reference = field.getAnnotation(MotanReferer.class);
if (reference != null) {
Object value = refer(reference, field.getType());
if (value != null) {
field.set(bean, value);
}
}
} catch (Exception e) {
throw new BeanInitializationException("Failed to init remote service reference at filed " + field.getName()
+ " in class " + bean.getClass().getName(), e);
}
}
return bean;
}
/**
* init service config and export servcice
*
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (!isMatchPackage(bean)) {
return bean;
}
Class<?> clazz = bean.getClass();
if (isProxyBean(bean)) {
clazz = AopUtils.getTargetClass(bean);
}
MotanService service = clazz.getAnnotation(MotanService.class);
if (service != null) {
ServiceConfigBean<Object> serviceConfig = new ServiceConfigBean<Object>();
if (void.class.equals(service.interfaceClass())) {
if (clazz.getInterfaces().length > 0) {
Class<Object> clz = (Class<Object>) clazz.getInterfaces()[0];
serviceConfig.setInterface(clz);
} else {
throw new IllegalStateException("Failed to export remote service class " + clazz.getName()
+ ", cause: The @Service undefined interfaceClass or interfaceName, and the service class unimplemented any interfaces.");
}
} else {
serviceConfig.setInterface((Class<Object>) service.interfaceClass());
}
if (beanFactory != null) {
serviceConfig.setBeanFactory(beanFactory);
if (service.basicService() != null && service.basicService().length() > 0) {
serviceConfig.setBasicService(beanFactory.getBean(service.basicService(), BasicServiceInterfaceConfig.class));
}
if (service.export() != null && service.export().length() > 0) {
serviceConfig.setExport(service.export());
}
if (service.host() != null && service.host().length() > 0) {
serviceConfig.setHost(service.host());
}
String protocolValue = null;
if (service.protocol() != null && service.protocol().length() > 0) {
protocolValue = service.protocol();
} else if (service.export() != null && service.export().length() > 0) {
protocolValue = ConfigUtil.extractProtocols(service.export());
}
if (!StringUtils.isBlank(protocolValue)) {
List<ProtocolConfig> protocolConfigs = SpringBeanUtil.getMultiBeans(beanFactory, protocolValue, SpringBeanUtil.COMMA_SPLIT_PATTERN,
ProtocolConfig.class);
serviceConfig.setProtocols(protocolConfigs);
}
// String[] methods() default {};
if (service.registry() != null && service.registry().length() > 0) {
List<RegistryConfig> registryConfigs = SpringBeanUtil.getMultiBeans(beanFactory, service.registry
(), SpringBeanUtil.COMMA_SPLIT_PATTERN, RegistryConfig.class);
serviceConfig.setRegistries(registryConfigs);
}
if (service.extConfig() != null && service.extConfig().length() > 0) {
serviceConfig.setExtConfig(beanFactory.getBean(service.extConfig(), ExtConfig.class));
}
if (service.application() != null && service.application().length() > 0) {
serviceConfig.setApplication(service.application());
}
if (service.module() != null && service.module().length() > 0) {
serviceConfig.setModule(service.module());
}
if (service.group() != null && service.group().length() > 0) {
serviceConfig.setGroup(service.group());
}
if (service.version() != null && service.version().length() > 0) {
serviceConfig.setVersion(service.version());
}
if (service.proxy() != null && service.proxy().length() > 0) {
serviceConfig.setProxy(service.proxy());
}
if (service.filter() != null && service.filter().length() > 0) {
serviceConfig.setFilter(service.filter());
}
if (service.actives() > 0) {
serviceConfig.setActives(service.actives());
}
if(service.async()) {
serviceConfig.setAsync(service.async());
}
if (service.mock() != null && service.mock().length() > 0) {
serviceConfig.setMock(service.mock());
}
// 是否共享 channel
if (service.shareChannel()) {
serviceConfig.setShareChannel(service.shareChannel());
}
// if throw exception when call failure,the default value is ture
if (service.throwException()) {
serviceConfig.setThrowException(service.throwException());
}
if(service.requestTimeout()>0) {
serviceConfig.setRequestTimeout(service.requestTimeout());
}
if (service.register()) {
serviceConfig.setRegister(service.register());
}
if (service.accessLog()) {
serviceConfig.setAccessLog("true");
}
if (service.check()) {
serviceConfig.setCheck("true");
}
if (service.usegz()) {
serviceConfig.setUsegz(service.usegz());
}
if(service.retries()>0) {
serviceConfig.setRetries(service.retries());
}
if(service.mingzSize()>0) {
serviceConfig.setMingzSize(service.mingzSize());
}
if (service.codec() != null && service.codec().length() > 0) {
serviceConfig.setCodec(service.codec());
}
try {
serviceConfig.afterPropertiesSet();
} catch (RuntimeException e) {
throw (RuntimeException) e;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
serviceConfig.setRef(bean);
serviceConfigs.add(serviceConfig);
serviceConfig.export();
}
return bean;
}
/**
* release service/reference
*
* @throws Exception
*/
public void destroy() throws Exception {
for (ServiceConfigBean<?> serviceConfig : serviceConfigs) {
try {
serviceConfig.unexport();
} catch (Throwable e) {
LoggerUtil.error(e.getMessage(), e);
}
}
for (RefererConfigBean<?> referenceConfig : referenceConfigs.values()) {
try {
referenceConfig.destroy();
} catch (Throwable e) {
LoggerUtil.error(e.getMessage(), e);
}
}
}
/**
* refer proxy
*
* @param reference
* @param referenceClass
* @param <T>
* @return
*/
private <T> Object refer(MotanReferer reference, Class<?> referenceClass) {
String interfaceName;
if (!void.class.equals(reference.interfaceClass())) {
interfaceName = reference.interfaceClass().getName();
} else if (referenceClass.isInterface()) {
interfaceName = referenceClass.getName();
} else {
throw new IllegalStateException("The @Reference undefined interfaceClass or interfaceName, and the property type "
+ referenceClass.getName() + " is not a interface.");
}
String key = reference.group() + "/" + interfaceName + ":" + reference.version();
RefererConfigBean<T> referenceConfig = referenceConfigs.get(key);
if (referenceConfig == null) {
referenceConfig = new RefererConfigBean<T>();
referenceConfig.setBeanFactory(beanFactory);
if (void.class.equals(reference.interfaceClass())
&& referenceClass.isInterface()) {
referenceConfig.setInterface((Class<T>) referenceClass);
} else if (!void.class.equals(reference.interfaceClass())) {
referenceConfig.setInterface((Class<T>) reference.interfaceClass());
}
if (beanFactory != null) {
if (reference.protocol() != null && reference.protocol().length() > 0) {
//多个PROTOCOL
List<ProtocolConfig> protocolConfigs = SpringBeanUtil.getMultiBeans(beanFactory, reference
.protocol(), SpringBeanUtil.COMMA_SPLIT_PATTERN, ProtocolConfig.class);
referenceConfig.setProtocols(protocolConfigs);
}
if (reference.directUrl() != null && reference.directUrl().length() > 0) {
referenceConfig.setDirectUrl(reference.directUrl());
}
if (reference.basicReferer() != null && reference.basicReferer().length() > 0) {
BasicRefererInterfaceConfig biConfig = beanFactory.getBean(reference.basicReferer(), BasicRefererInterfaceConfig.class);
if (biConfig != null) {
referenceConfig.setBasicReferer(biConfig);
}
}
if (reference.client() != null && reference.client().length() > 0) {
//TODO?
// referenceConfig.setC(reference.client());
}
// String[] methods() default {};
if (reference.registry() != null && reference.registry().length() > 0) {
List<RegistryConfig> registryConfigs = SpringBeanUtil.getMultiBeans(beanFactory, reference
.registry(), SpringBeanUtil.COMMA_SPLIT_PATTERN, RegistryConfig.class);
referenceConfig.setRegistries(registryConfigs);
}
if (reference.extConfig() != null && reference.extConfig().length() > 0) {
referenceConfig.setExtConfig(beanFactory.getBean(reference.extConfig(), ExtConfig.class));
}
if (reference.application() != null && reference.application().length() > 0) {
referenceConfig.setApplication(reference.application());
}
if (reference.module() != null && reference.module().length() > 0) {
referenceConfig.setModule(reference.module());
}
if (reference.group() != null && reference.group().length() > 0) {
referenceConfig.setGroup(reference.group());
}
if (reference.version() != null && reference.version().length() > 0) {
referenceConfig.setVersion(reference.version());
}
if (reference.proxy() != null && reference.proxy().length() > 0) {
referenceConfig.setProxy(reference.proxy());
}
if (reference.filter() != null && reference.filter().length() > 0) {
referenceConfig.setFilter(reference.filter());
}
if (reference.actives() > 0) {
referenceConfig.setActives(reference.actives());
}
if (reference.async()) {
referenceConfig.setAsync(reference.async());
}
if (reference.mock() != null && reference.mock().length() > 0) {
referenceConfig.setMock(reference.mock());
}
if (reference.shareChannel()) {
referenceConfig.setShareChannel(reference.shareChannel());
}
// if throw exception when call failure,the default value is ture
if (reference.throwException()) {
referenceConfig.setThrowException(reference.throwException());
}
if(reference.requestTimeout()>0) {
referenceConfig.setRequestTimeout(reference.requestTimeout());
}
if (reference.register()) {
referenceConfig.setRegister(reference.register());
}
if (reference.accessLog()) {
referenceConfig.setAccessLog("true");
}
if (reference.check()) {
referenceConfig.setCheck("true");
}
if(reference.retries()>0) {
referenceConfig.setRetries(reference.retries());
}
if (reference.usegz()) {
referenceConfig.setUsegz(reference.usegz());
}
if(reference.mingzSize()>0) {
referenceConfig.setMingzSize(reference.mingzSize());
}
if (reference.codec() != null && reference.codec().length() > 0) {
referenceConfig.setCodec(reference.codec());
}
if (reference.mean() != null && reference.mean().length() > 0) {
referenceConfig.setMean(reference.mean());
}
if (reference.p90() != null && reference.p90().length() > 0) {
referenceConfig.setP90(reference.p90());
}
if (reference.p99() != null && reference.p99().length() > 0) {
referenceConfig.setP99(reference.p99());
}
if (reference.p999() != null && reference.p999().length() > 0) {
referenceConfig.setP999(reference.p999());
}
if (reference.errorRate() != null && reference.errorRate().length() > 0) {
referenceConfig.setErrorRate(reference.errorRate());
}
try {
referenceConfig.afterPropertiesSet();
} catch (RuntimeException e) {
throw (RuntimeException) e;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
referenceConfigs.putIfAbsent(key, referenceConfig);
referenceConfig = referenceConfigs.get(key);
}
return referenceConfig.getRef();
}
private boolean isMatchPackage(Object bean) {
if (annotationPackages == null || annotationPackages.length == 0) {
return true;
}
Class clazz = bean.getClass();
if (isProxyBean(bean)) {
clazz = AopUtils.getTargetClass(bean);
}
String beanClassName = clazz.getName();
for (String pkg : annotationPackages) {
if (beanClassName.startsWith(pkg)) {
return true;
}
}
return false;
}
private boolean isProxyBean(Object bean) {
return AopUtils.isAopProxy(bean);
}
public String getPackage() {
return annotationPackage;
}
public void setPackage(String annotationPackage) {
this.annotationPackage = annotationPackage;
this.annotationPackages = (annotationPackage == null || annotationPackage.length() == 0) ? null
: annotationPackage.split(SpringBeanUtil.COMMA_SPLIT_PATTERN);
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public int getOrder() {
return 0;
}
}