Polish #51. Add a new module spring-cloud-alibaba-sentinel-datasource separate from spring-cloud-alibaba-sentinel.
parent
1ce28f00f3
commit
1d15cdaee5
@ -0,0 +1,69 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-alibaba</artifactId>
|
||||||
|
<version>0.2.0</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-alibaba-sentinel-datasource</artifactId>
|
||||||
|
<name>Spring Cloud Alibaba Sentinel DataSource</name>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-datasource-extension</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-datasource-nacos</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-datasource-zookeeper</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-datasource-apollo</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--spring boot-->
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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 org.springframework.cloud.alibaba.sentinel.datasource;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||||
|
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||||
|
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
|
import static org.springframework.core.io.support.ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ReadableDataSource} Loader
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||||
|
*/
|
||||||
|
public class DataSourceLoader {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(DataSourceLoader.class);
|
||||||
|
|
||||||
|
private final static String PROPERTIES_RESOURCE_LOCATION = "META-INF/sentinel-datasource.properties";
|
||||||
|
|
||||||
|
private final static String ALL_PROPERTIES_RESOURCES_LOCATION = CLASSPATH_ALL_URL_PREFIX
|
||||||
|
+ PROPERTIES_RESOURCE_LOCATION;
|
||||||
|
|
||||||
|
private final static ConcurrentMap<String, Class<? extends ReadableDataSource>> dataSourceClassesCache
|
||||||
|
= new ConcurrentHashMap<String, Class<? extends ReadableDataSource>>(
|
||||||
|
4);
|
||||||
|
|
||||||
|
static void loadAllDataSourceClassesCache() {
|
||||||
|
Map<String, Class<? extends ReadableDataSource>> dataSourceClassesMap = loadAllDataSourceClassesCache(
|
||||||
|
ALL_PROPERTIES_RESOURCES_LOCATION);
|
||||||
|
|
||||||
|
dataSourceClassesCache.putAll(dataSourceClassesMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, Class<? extends ReadableDataSource>> loadAllDataSourceClassesCache(
|
||||||
|
String resourcesLocation) {
|
||||||
|
|
||||||
|
Map<String, Class<? extends ReadableDataSource>> dataSourcesMap
|
||||||
|
= new HashMap<String, Class<? extends ReadableDataSource>>(
|
||||||
|
4);
|
||||||
|
|
||||||
|
ClassLoader classLoader = DataSourceLoader.class.getClassLoader();
|
||||||
|
|
||||||
|
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
Resource[] resources = resolver.getResources(resourcesLocation);
|
||||||
|
|
||||||
|
for (Resource resource : resources) {
|
||||||
|
if (resource.exists()) {
|
||||||
|
Properties properties = PropertiesLoaderUtils
|
||||||
|
.loadProperties(resource);
|
||||||
|
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
|
||||||
|
|
||||||
|
String type = (String)entry.getKey();
|
||||||
|
String className = (String)entry.getValue();
|
||||||
|
|
||||||
|
if (!ClassUtils.isPresent(className, classLoader)) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug(
|
||||||
|
"Sentinel DataSource implementation [ type : "
|
||||||
|
+ type + ": , class : " + className
|
||||||
|
+ " , url : " + resource.getURL()
|
||||||
|
+ "] was not present in current classpath , "
|
||||||
|
+ "thus loading will be ignored , please add dependency if required !");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.isTrue(!dataSourcesMap.containsKey(type),
|
||||||
|
"The duplicated type[" + type
|
||||||
|
+ "] of SentinelDataSource were found in "
|
||||||
|
+ "resource [" + resource.getURL() + "]");
|
||||||
|
|
||||||
|
Class<?> dataSourceClass = ClassUtils.resolveClassName(className,
|
||||||
|
classLoader);
|
||||||
|
Assert.isAssignable(ReadableDataSource.class, dataSourceClass);
|
||||||
|
|
||||||
|
dataSourcesMap.put(type,
|
||||||
|
(Class<? extends ReadableDataSource>)dataSourceClass);
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Sentinel DataSource implementation [ type : "
|
||||||
|
+ type + ": , class : " + className
|
||||||
|
+ "] was loaded.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (logger.isErrorEnabled()) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataSourcesMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class<? extends ReadableDataSource> loadClass(String type)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
|
||||||
|
Class<? extends ReadableDataSource> dataSourceClass = dataSourceClassesCache.get(type);
|
||||||
|
|
||||||
|
if (dataSourceClass == null) {
|
||||||
|
if (dataSourceClassesCache.isEmpty()) {
|
||||||
|
loadAllDataSourceClassesCache();
|
||||||
|
dataSourceClass = dataSourceClassesCache.get(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataSourceClass == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Sentinel DataSource implementation [ type : " + type
|
||||||
|
+ " ] can't be found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataSourceClass;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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 org.springframework.cloud.alibaba.sentinel.datasource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||||
|
*/
|
||||||
|
public interface SentinelDataSourceConstants {
|
||||||
|
|
||||||
|
String PROPERTY_PREFIX = "spring.cloud.sentinel";
|
||||||
|
|
||||||
|
String PROPERTY_ITEM_SEPARATOR = ".";
|
||||||
|
|
||||||
|
String PROPERTY_DATASOURCE_NAME = "datasource";
|
||||||
|
|
||||||
|
String PROPERTY_DATASOURCE_PREFIX = PROPERTY_PREFIX + PROPERTY_ITEM_SEPARATOR
|
||||||
|
+ PROPERTY_DATASOURCE_NAME;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,258 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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 org.springframework.cloud.alibaba.sentinel.datasource;
|
||||||
|
|
||||||
|
import java.beans.PropertyDescriptor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.datasource.Converter;
|
||||||
|
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
|
||||||
|
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
||||||
|
import com.alibaba.csp.sentinel.slots.system.SystemRule;
|
||||||
|
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.PropertyValues;
|
||||||
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
|
||||||
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||||
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
|
import org.springframework.boot.context.event.ApplicationStartedEvent;
|
||||||
|
import org.springframework.cloud.alibaba.sentinel.datasource.annotation.SentinelDataSource;
|
||||||
|
import org.springframework.cloud.alibaba.sentinel.datasource.util.PropertySourcesUtils;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import static org.springframework.core.annotation.AnnotationUtils.getAnnotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link SentinelDataSource @SentinelDataSource} Post Processor
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||||
|
* @see ReadableDataSource
|
||||||
|
* @see SentinelDataSource
|
||||||
|
*/
|
||||||
|
public class SentinelDataSourcePostProcessor
|
||||||
|
extends InstantiationAwareBeanPostProcessorAdapter
|
||||||
|
implements MergedBeanDefinitionPostProcessor {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory
|
||||||
|
.getLogger(SentinelDataSourcePostProcessor.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ConfigurableEnvironment environment;
|
||||||
|
|
||||||
|
private final Map<String, List<SentinelDataSourceField>> dataSourceFieldCache = new ConcurrentHashMap<>(
|
||||||
|
64);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition,
|
||||||
|
Class<?> beanType, String beanName) {
|
||||||
|
// find all fields using by @SentinelDataSource annotation
|
||||||
|
ReflectionUtils.doWithFields(beanType, new ReflectionUtils.FieldCallback() {
|
||||||
|
@Override
|
||||||
|
public void doWith(Field field)
|
||||||
|
throws IllegalArgumentException, IllegalAccessException {
|
||||||
|
SentinelDataSource annotation = getAnnotation(field,
|
||||||
|
SentinelDataSource.class);
|
||||||
|
if (annotation != null) {
|
||||||
|
if (Modifier.isStatic(field.getModifiers())) {
|
||||||
|
if (logger.isWarnEnabled()) {
|
||||||
|
logger.warn(
|
||||||
|
"@SentinelDataSource annotation is not supported on static fields: "
|
||||||
|
+ field);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (dataSourceFieldCache.containsKey(beanName)) {
|
||||||
|
dataSourceFieldCache.get(beanName)
|
||||||
|
.add(new SentinelDataSourceField(annotation, field));
|
||||||
|
} else {
|
||||||
|
List<SentinelDataSourceField> list = new ArrayList<>();
|
||||||
|
list.add(new SentinelDataSourceField(annotation, field));
|
||||||
|
dataSourceFieldCache.put(beanName, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PropertyValues postProcessPropertyValues(PropertyValues pvs,
|
||||||
|
PropertyDescriptor[] pds, Object bean, String beanName)
|
||||||
|
throws BeanCreationException {
|
||||||
|
if (dataSourceFieldCache.containsKey(beanName)) {
|
||||||
|
List<SentinelDataSourceField> sentinelDataSourceFields = dataSourceFieldCache
|
||||||
|
.get(beanName);
|
||||||
|
sentinelDataSourceFields.forEach(sentinelDataSourceField -> {
|
||||||
|
try {
|
||||||
|
// construct DataSource field annotated by @SentinelDataSource
|
||||||
|
Field field = sentinelDataSourceField.getField();
|
||||||
|
ReflectionUtils.makeAccessible(field);
|
||||||
|
String dataSourceBeanName = constructDataSource(
|
||||||
|
sentinelDataSourceField.getSentinelDataSource());
|
||||||
|
field.set(bean, applicationContext.getBean(dataSourceBeanName));
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return pvs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String constructDataSource(SentinelDataSource annotation) {
|
||||||
|
String prefix = annotation.value();
|
||||||
|
if (StringUtils.isEmpty(prefix)) {
|
||||||
|
prefix = SentinelDataSourceConstants.PROPERTY_DATASOURCE_PREFIX;
|
||||||
|
}
|
||||||
|
Map<String, Object> propertyMap = PropertySourcesUtils
|
||||||
|
.getSubProperties(environment.getPropertySources(), prefix);
|
||||||
|
String alias = propertyMap.get("type").toString();
|
||||||
|
Class dataSourceClass = DataSourceLoader.loadClass(alias);
|
||||||
|
|
||||||
|
String beanName = StringUtils.isEmpty(annotation.name())
|
||||||
|
? StringUtils.uncapitalize(dataSourceClass.getSimpleName()) + "_" + prefix
|
||||||
|
: annotation.name();
|
||||||
|
if (applicationContext.containsBean(beanName)) {
|
||||||
|
return beanName;
|
||||||
|
}
|
||||||
|
|
||||||
|
Class targetClass = null;
|
||||||
|
// if alias exists in SentinelDataSourceRegistry, wired properties into
|
||||||
|
// FactoryBean
|
||||||
|
if (SentinelDataSourceRegistry.checkFactoryBean(alias)) {
|
||||||
|
targetClass = SentinelDataSourceRegistry.getFactoryBean(alias);
|
||||||
|
} else {
|
||||||
|
// if alias not exists in SentinelDataSourceRegistry, wired properties into
|
||||||
|
// raw class
|
||||||
|
targetClass = dataSourceClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerDataSource(beanName, targetClass, propertyMap);
|
||||||
|
|
||||||
|
return beanName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerDataSource(String beanName, Class targetClass,
|
||||||
|
Map<String, Object> propertyMap) {
|
||||||
|
BeanDefinitionBuilder builder = BeanDefinitionBuilder
|
||||||
|
.genericBeanDefinition(targetClass);
|
||||||
|
for (String propertyName : propertyMap.keySet()) {
|
||||||
|
Field field = ReflectionUtils.findField(targetClass, propertyName);
|
||||||
|
if (field != null) {
|
||||||
|
if (field.getType().isAssignableFrom(Converter.class)) {
|
||||||
|
// Converter get from ApplicationContext
|
||||||
|
builder.addPropertyReference(propertyName,
|
||||||
|
propertyMap.get(propertyName).toString());
|
||||||
|
} else {
|
||||||
|
// wired properties
|
||||||
|
builder.addPropertyValue(propertyName, propertyMap.get(propertyName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory)applicationContext
|
||||||
|
.getAutowireCapableBeanFactory();
|
||||||
|
beanFactory.registerBeanDefinition(beanName, builder.getBeanDefinition());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventListener(classes = ApplicationStartedEvent.class)
|
||||||
|
public void appStartedListener(ApplicationStartedEvent event) throws Exception {
|
||||||
|
Map<String, ReadableDataSource> dataSourceMap = event.getApplicationContext().getBeansOfType(
|
||||||
|
ReadableDataSource.class);
|
||||||
|
if (dataSourceMap.size() == 1) {
|
||||||
|
ReadableDataSource dataSource = dataSourceMap.values().iterator().next();
|
||||||
|
Object ruleConfig = dataSource.loadConfig();
|
||||||
|
SentinelProperty sentinelProperty = dataSource.getProperty();
|
||||||
|
if (checkRuleType(ruleConfig, FlowRule.class)) {
|
||||||
|
FlowRuleManager.register2Property(sentinelProperty);
|
||||||
|
}
|
||||||
|
if (checkRuleType(ruleConfig, DegradeRule.class)) {
|
||||||
|
DegradeRuleManager.register2Property(sentinelProperty);
|
||||||
|
}
|
||||||
|
if (checkRuleType(ruleConfig, SystemRule.class)) {
|
||||||
|
SystemRuleManager.register2Property(sentinelProperty);
|
||||||
|
}
|
||||||
|
if (checkRuleType(ruleConfig, AuthorityRule.class)) {
|
||||||
|
AuthorityRuleManager.register2Property(sentinelProperty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkRuleType(Object ruleConfig, Class type) {
|
||||||
|
if (ruleConfig.getClass() == type) {
|
||||||
|
return true;
|
||||||
|
} else if (ruleConfig instanceof List) {
|
||||||
|
List ruleList = (List)ruleConfig;
|
||||||
|
if (ruleList.stream().filter(rule -> rule.getClass() == type).toArray().length == ruleList.size()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SentinelDataSourceField {
|
||||||
|
private SentinelDataSource sentinelDataSource;
|
||||||
|
private Field field;
|
||||||
|
|
||||||
|
public SentinelDataSourceField(SentinelDataSource sentinelDataSource,
|
||||||
|
Field field) {
|
||||||
|
this.sentinelDataSource = sentinelDataSource;
|
||||||
|
this.field = field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SentinelDataSource getSentinelDataSource() {
|
||||||
|
return sentinelDataSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSentinelDataSource(SentinelDataSource sentinelDataSource) {
|
||||||
|
this.sentinelDataSource = sentinelDataSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Field getField() {
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setField(Field field) {
|
||||||
|
this.field = field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
package org.springframework.cloud.alibaba.sentinel.datasource.factorybean;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.datasource.Converter;
|
||||||
|
import com.alibaba.csp.sentinel.datasource.apollo.ApolloDataSource;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link FactoryBean} for creating {@link ApolloDataSource} instance.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||||
|
* @see ApolloDataSource
|
||||||
|
*/
|
||||||
|
public class ApolloDataSourceFactoryBean implements FactoryBean<ApolloDataSource> {
|
||||||
|
|
||||||
|
private String namespaceName;
|
||||||
|
private String flowRulesKey;
|
||||||
|
private String defaultFlowRuleValue;
|
||||||
|
private Converter converter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ApolloDataSource getObject() throws Exception {
|
||||||
|
return new ApolloDataSource(namespaceName, flowRulesKey, defaultFlowRuleValue,
|
||||||
|
converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getObjectType() {
|
||||||
|
return ApolloDataSource.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNamespaceName() {
|
||||||
|
return namespaceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNamespaceName(String namespaceName) {
|
||||||
|
this.namespaceName = namespaceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFlowRulesKey() {
|
||||||
|
return flowRulesKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFlowRulesKey(String flowRulesKey) {
|
||||||
|
this.flowRulesKey = flowRulesKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDefaultFlowRuleValue() {
|
||||||
|
return defaultFlowRuleValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultFlowRuleValue(String defaultFlowRuleValue) {
|
||||||
|
this.defaultFlowRuleValue = defaultFlowRuleValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Converter getConverter() {
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConverter(Converter Converter) {
|
||||||
|
this.converter = Converter;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
package org.springframework.cloud.alibaba.sentinel.datasource.factorybean;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.datasource.Converter;
|
||||||
|
import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link FactoryBean} for creating {@link FileRefreshableDataSource} instance.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||||
|
* @see FileRefreshableDataSource
|
||||||
|
*/
|
||||||
|
public class FileRefreshableDataSourceFactoryBean
|
||||||
|
implements FactoryBean<FileRefreshableDataSource> {
|
||||||
|
|
||||||
|
private String file;
|
||||||
|
private String charset;
|
||||||
|
private long recommendRefreshMs;
|
||||||
|
private int bufSize;
|
||||||
|
private Converter converter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileRefreshableDataSource getObject() throws Exception {
|
||||||
|
return new FileRefreshableDataSource(new File(file), converter,
|
||||||
|
recommendRefreshMs, bufSize, Charset.forName(charset));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getObjectType() {
|
||||||
|
return FileRefreshableDataSource.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFile() {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFile(String file) {
|
||||||
|
this.file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCharset() {
|
||||||
|
return charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCharset(String charset) {
|
||||||
|
this.charset = charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getRecommendRefreshMs() {
|
||||||
|
return recommendRefreshMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRecommendRefreshMs(long recommendRefreshMs) {
|
||||||
|
this.recommendRefreshMs = recommendRefreshMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBufSize() {
|
||||||
|
return bufSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBufSize(int bufSize) {
|
||||||
|
this.bufSize = bufSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Converter getConverter() {
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConverter(Converter Converter) {
|
||||||
|
this.converter = Converter;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
package org.springframework.cloud.alibaba.sentinel.datasource.factorybean;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.datasource.Converter;
|
||||||
|
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link FactoryBean} for creating {@link NacosDataSource} instance.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||||
|
* @see NacosDataSource
|
||||||
|
*/
|
||||||
|
public class NacosDataSourceFactoryBean implements FactoryBean<NacosDataSource> {
|
||||||
|
|
||||||
|
private String serverAddr;
|
||||||
|
private String groupId;
|
||||||
|
private String dataId;
|
||||||
|
private Converter converter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NacosDataSource getObject() throws Exception {
|
||||||
|
return new NacosDataSource(serverAddr, groupId, dataId, converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getObjectType() {
|
||||||
|
return NacosDataSource.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServerAddr() {
|
||||||
|
return serverAddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServerAddr(String serverAddr) {
|
||||||
|
this.serverAddr = serverAddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroupId(String groupId) {
|
||||||
|
this.groupId = groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDataId() {
|
||||||
|
return dataId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDataId(String dataId) {
|
||||||
|
this.dataId = dataId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Converter getConverter() {
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConverter(Converter Converter) {
|
||||||
|
this.converter = Converter;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
package org.springframework.cloud.alibaba.sentinel.datasource.factorybean;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.datasource.Converter;
|
||||||
|
import com.alibaba.csp.sentinel.datasource.zookeeper.ZookeeperDataSource;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link FactoryBean} for creating {@link ZookeeperDataSource} instance.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||||
|
* @see ZookeeperDataSource
|
||||||
|
*/
|
||||||
|
public class ZookeeperDataSourceFactoryBean implements FactoryBean<ZookeeperDataSource> {
|
||||||
|
|
||||||
|
private String serverAddr;
|
||||||
|
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
private String groupId;
|
||||||
|
private String dataId;
|
||||||
|
|
||||||
|
private Converter converter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ZookeeperDataSource getObject() throws Exception {
|
||||||
|
if (StringUtils.isNotEmpty(groupId) && StringUtils.isNotEmpty(dataId)) {
|
||||||
|
// the path will be /{groupId}/{dataId}
|
||||||
|
return new ZookeeperDataSource(serverAddr, groupId, dataId, converter);
|
||||||
|
} else {
|
||||||
|
// using path directly
|
||||||
|
return new ZookeeperDataSource(serverAddr, path, converter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getObjectType() {
|
||||||
|
return ZookeeperDataSource.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServerAddr() {
|
||||||
|
return serverAddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServerAddr(String serverAddr) {
|
||||||
|
this.serverAddr = serverAddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPath(String path) {
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroupId(String groupId) {
|
||||||
|
this.groupId = groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDataId() {
|
||||||
|
return dataId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDataId(String dataId) {
|
||||||
|
this.dataId = dataId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Converter getConverter() {
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConverter(Converter Converter) {
|
||||||
|
this.converter = Converter;
|
||||||
|
}
|
||||||
|
}
|
@ -1,151 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 the original author or authors.
|
|
||||||
*
|
|
||||||
* 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 org.springframework.cloud.alibaba.sentinel.datasource;
|
|
||||||
|
|
||||||
import static org.springframework.core.io.support.ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.core.io.Resource;
|
|
||||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
|
||||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
|
||||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.util.ClassUtils;
|
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link ReadableDataSource} Loader
|
|
||||||
*
|
|
||||||
* @author fangjian
|
|
||||||
*/
|
|
||||||
public class DataSourceLoader {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(DataSourceLoader.class);
|
|
||||||
|
|
||||||
private final static String PROPERTIES_RESOURCE_LOCATION = "META-INF/sentinel-datasource.properties";
|
|
||||||
|
|
||||||
private final static String ALL_PROPERTIES_RESOURCES_LOCATION = CLASSPATH_ALL_URL_PREFIX
|
|
||||||
+ PROPERTIES_RESOURCE_LOCATION;
|
|
||||||
|
|
||||||
private final static ConcurrentMap<String, Class<? extends ReadableDataSource>> dataSourceClassesCache = new ConcurrentHashMap<String, Class<? extends ReadableDataSource>>(
|
|
||||||
4);
|
|
||||||
|
|
||||||
static void loadAllDataSourceClassesCache() {
|
|
||||||
Map<String, Class<? extends ReadableDataSource>> dataSourceClassesMap = loadAllDataSourceClassesCache(
|
|
||||||
ALL_PROPERTIES_RESOURCES_LOCATION);
|
|
||||||
|
|
||||||
dataSourceClassesCache.putAll(dataSourceClassesMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map<String, Class<? extends ReadableDataSource>> loadAllDataSourceClassesCache(
|
|
||||||
String resourcesLocation) {
|
|
||||||
|
|
||||||
Map<String, Class<? extends ReadableDataSource>> dataSourcesMap = new HashMap<String, Class<? extends ReadableDataSource>>(
|
|
||||||
4);
|
|
||||||
|
|
||||||
ClassLoader classLoader = DataSourceLoader.class.getClassLoader();
|
|
||||||
|
|
||||||
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
Resource[] resources = resolver.getResources(resourcesLocation);
|
|
||||||
|
|
||||||
for (Resource resource : resources) {
|
|
||||||
if (resource.exists()) {
|
|
||||||
Properties properties = PropertiesLoaderUtils
|
|
||||||
.loadProperties(resource);
|
|
||||||
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
|
|
||||||
|
|
||||||
String type = (String) entry.getKey();
|
|
||||||
String className = (String) entry.getValue();
|
|
||||||
|
|
||||||
if (!ClassUtils.isPresent(className, classLoader)) {
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug(
|
|
||||||
"Sentinel DataSource implementation [ type : "
|
|
||||||
+ type + ": , class : " + className
|
|
||||||
+ " , url : " + resource.getURL()
|
|
||||||
+ "] was not present in current classpath , "
|
|
||||||
+ "thus loading will be ignored , please add dependency if required !");
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.isTrue(!dataSourcesMap.containsKey(type),
|
|
||||||
"The duplicated type[" + type
|
|
||||||
+ "] of SentinelDataSource were found in "
|
|
||||||
+ "resource [" + resource.getURL() + "]");
|
|
||||||
|
|
||||||
Class<?> dataSourceClass = ClassUtils.resolveClassName(className,
|
|
||||||
classLoader);
|
|
||||||
Assert.isAssignable(ReadableDataSource.class, dataSourceClass);
|
|
||||||
|
|
||||||
dataSourcesMap.put(type,
|
|
||||||
(Class<? extends ReadableDataSource>) dataSourceClass);
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Sentinel DataSource implementation [ type : "
|
|
||||||
+ type + ": , class : " + className
|
|
||||||
+ "] was loaded.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
if (logger.isErrorEnabled()) {
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataSourcesMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Class<? extends ReadableDataSource> loadClass(String type)
|
|
||||||
throws IllegalArgumentException {
|
|
||||||
|
|
||||||
Class<? extends ReadableDataSource> dataSourceClass = dataSourceClassesCache.get(type);
|
|
||||||
|
|
||||||
if (dataSourceClass == null) {
|
|
||||||
if (dataSourceClassesCache.isEmpty()) {
|
|
||||||
loadAllDataSourceClassesCache();
|
|
||||||
dataSourceClass = dataSourceClassesCache.get(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dataSourceClass == null) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Sentinel DataSource implementation [ type : " + type
|
|
||||||
+ " ] can't be found!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataSourceClass;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,264 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 the original author or authors.
|
|
||||||
*
|
|
||||||
* 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 org.springframework.cloud.alibaba.sentinel.datasource;
|
|
||||||
|
|
||||||
import static org.springframework.core.annotation.AnnotationUtils.getAnnotation;
|
|
||||||
|
|
||||||
import java.beans.PropertyDescriptor;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.PropertyValues;
|
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
|
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
|
||||||
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
|
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
|
||||||
import org.springframework.boot.context.event.ApplicationStartedEvent;
|
|
||||||
import org.springframework.cloud.alibaba.sentinel.SentinelConstants;
|
|
||||||
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelDataSource;
|
|
||||||
import org.springframework.cloud.alibaba.sentinel.util.PropertySourcesUtils;
|
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
import org.springframework.context.event.EventListener;
|
|
||||||
import org.springframework.core.env.ConfigurableEnvironment;
|
|
||||||
import org.springframework.util.ReflectionUtils;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.datasource.Converter;
|
|
||||||
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
|
|
||||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
|
||||||
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
|
|
||||||
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
|
|
||||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
|
|
||||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
|
|
||||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
|
||||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
|
||||||
import com.alibaba.csp.sentinel.slots.system.SystemRule;
|
|
||||||
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link SentinelDataSource @SentinelDataSource} Post Processor
|
|
||||||
*
|
|
||||||
* @author fangjian
|
|
||||||
* @see com.alibaba.csp.sentinel.datasource.ReadableDataSource
|
|
||||||
* @see SentinelDataSource
|
|
||||||
*/
|
|
||||||
public class SentinelDataSourcePostProcessor
|
|
||||||
extends InstantiationAwareBeanPostProcessorAdapter
|
|
||||||
implements MergedBeanDefinitionPostProcessor {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory
|
|
||||||
.getLogger(SentinelDataSourcePostProcessor.class);
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ApplicationContext applicationContext;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ConfigurableEnvironment environment;
|
|
||||||
|
|
||||||
private final Map<String, List<SentinelDataSourceField>> dataSourceFieldCache = new ConcurrentHashMap<>(
|
|
||||||
64);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition,
|
|
||||||
Class<?> beanType, String beanName) {
|
|
||||||
// find all fields using by @SentinelDataSource annotation
|
|
||||||
ReflectionUtils.doWithFields(beanType, new ReflectionUtils.FieldCallback() {
|
|
||||||
@Override
|
|
||||||
public void doWith(Field field)
|
|
||||||
throws IllegalArgumentException, IllegalAccessException {
|
|
||||||
SentinelDataSource annotation = getAnnotation(field,
|
|
||||||
SentinelDataSource.class);
|
|
||||||
if (annotation != null) {
|
|
||||||
if (Modifier.isStatic(field.getModifiers())) {
|
|
||||||
if (logger.isWarnEnabled()) {
|
|
||||||
logger.warn(
|
|
||||||
"@SentinelDataSource annotation is not supported on static fields: "
|
|
||||||
+ field);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (dataSourceFieldCache.containsKey(beanName)) {
|
|
||||||
dataSourceFieldCache.get(beanName)
|
|
||||||
.add(new SentinelDataSourceField(annotation, field));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
List<SentinelDataSourceField> list = new ArrayList<>();
|
|
||||||
list.add(new SentinelDataSourceField(annotation, field));
|
|
||||||
dataSourceFieldCache.put(beanName, list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PropertyValues postProcessPropertyValues(PropertyValues pvs,
|
|
||||||
PropertyDescriptor[] pds, Object bean, String beanName)
|
|
||||||
throws BeanCreationException {
|
|
||||||
if (dataSourceFieldCache.containsKey(beanName)) {
|
|
||||||
List<SentinelDataSourceField> sentinelDataSourceFields = dataSourceFieldCache
|
|
||||||
.get(beanName);
|
|
||||||
sentinelDataSourceFields.forEach(sentinelDataSourceField -> {
|
|
||||||
try {
|
|
||||||
// construct DataSource field annotated by @SentinelDataSource
|
|
||||||
Field field = sentinelDataSourceField.getField();
|
|
||||||
ReflectionUtils.makeAccessible(field);
|
|
||||||
String dataSourceBeanName = constructDataSource(
|
|
||||||
sentinelDataSourceField.getSentinelDataSource());
|
|
||||||
field.set(bean, applicationContext.getBean(dataSourceBeanName));
|
|
||||||
}
|
|
||||||
catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return pvs;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String constructDataSource(SentinelDataSource annotation) {
|
|
||||||
String prefix = annotation.value();
|
|
||||||
if (StringUtils.isEmpty(prefix)) {
|
|
||||||
prefix = SentinelConstants.PROPERTY_DATASOURCE_PREFIX;
|
|
||||||
}
|
|
||||||
Map<String, Object> propertyMap = PropertySourcesUtils
|
|
||||||
.getSubProperties(environment.getPropertySources(), prefix);
|
|
||||||
String alias = propertyMap.get("type").toString();
|
|
||||||
Class dataSourceClass = DataSourceLoader.loadClass(alias);
|
|
||||||
|
|
||||||
String beanName = StringUtils.isEmpty(annotation.name())
|
|
||||||
? StringUtils.uncapitalize(dataSourceClass.getSimpleName()) + "_" + prefix
|
|
||||||
: annotation.name();
|
|
||||||
if (applicationContext.containsBean(beanName)) {
|
|
||||||
return beanName;
|
|
||||||
}
|
|
||||||
|
|
||||||
Class targetClass = null;
|
|
||||||
// if alias exists in SentinelDataSourceRegistry, wired properties into
|
|
||||||
// FactoryBean
|
|
||||||
if (SentinelDataSourceRegistry.checkFactoryBean(alias)) {
|
|
||||||
targetClass = SentinelDataSourceRegistry.getFactoryBean(alias);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// if alias not exists in SentinelDataSourceRegistry, wired properties into
|
|
||||||
// raw class
|
|
||||||
targetClass = dataSourceClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
registerDataSource(beanName, targetClass, propertyMap);
|
|
||||||
|
|
||||||
return beanName;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void registerDataSource(String beanName, Class targetClass,
|
|
||||||
Map<String, Object> propertyMap) {
|
|
||||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder
|
|
||||||
.genericBeanDefinition(targetClass);
|
|
||||||
for (String propertyName : propertyMap.keySet()) {
|
|
||||||
Field field = ReflectionUtils.findField(targetClass, propertyName);
|
|
||||||
if (field != null) {
|
|
||||||
if (field.getType().isAssignableFrom(Converter.class)) {
|
|
||||||
// Converter get from ApplicationContext
|
|
||||||
builder.addPropertyReference(propertyName,
|
|
||||||
propertyMap.get(propertyName).toString());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// wired properties
|
|
||||||
builder.addPropertyValue(propertyName, propertyMap.get(propertyName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext
|
|
||||||
.getAutowireCapableBeanFactory();
|
|
||||||
beanFactory.registerBeanDefinition(beanName, builder.getBeanDefinition());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@EventListener(classes = ApplicationStartedEvent.class)
|
|
||||||
public void appStartedListener(ApplicationStartedEvent event) throws Exception {
|
|
||||||
Map<String, ReadableDataSource> dataSourceMap = event.getApplicationContext().getBeansOfType(ReadableDataSource.class);
|
|
||||||
if(dataSourceMap.size() == 1) {
|
|
||||||
ReadableDataSource dataSource = dataSourceMap.values().iterator().next();
|
|
||||||
Object ruleConfig = dataSource.loadConfig();
|
|
||||||
SentinelProperty sentinelProperty = dataSource.getProperty();
|
|
||||||
if(checkRuleType(ruleConfig, FlowRule.class)) {
|
|
||||||
FlowRuleManager.register2Property(sentinelProperty);
|
|
||||||
}
|
|
||||||
if(checkRuleType(ruleConfig, DegradeRule.class)) {
|
|
||||||
DegradeRuleManager.register2Property(sentinelProperty);
|
|
||||||
}
|
|
||||||
if(checkRuleType(ruleConfig, SystemRule.class)) {
|
|
||||||
SystemRuleManager.register2Property(sentinelProperty);
|
|
||||||
}
|
|
||||||
if(checkRuleType(ruleConfig, AuthorityRule.class)) {
|
|
||||||
AuthorityRuleManager.register2Property(sentinelProperty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkRuleType(Object ruleConfig, Class type) {
|
|
||||||
if(ruleConfig.getClass() == type) {
|
|
||||||
return true;
|
|
||||||
} else if(ruleConfig instanceof List) {
|
|
||||||
List ruleList = (List)ruleConfig;
|
|
||||||
if(ruleList.stream().filter(rule -> rule.getClass() == type).toArray().length == ruleList.size()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
class SentinelDataSourceField {
|
|
||||||
private SentinelDataSource sentinelDataSource;
|
|
||||||
private Field field;
|
|
||||||
|
|
||||||
public SentinelDataSourceField(SentinelDataSource sentinelDataSource,
|
|
||||||
Field field) {
|
|
||||||
this.sentinelDataSource = sentinelDataSource;
|
|
||||||
this.field = field;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SentinelDataSource getSentinelDataSource() {
|
|
||||||
return sentinelDataSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSentinelDataSource(SentinelDataSource sentinelDataSource) {
|
|
||||||
this.sentinelDataSource = sentinelDataSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Field getField() {
|
|
||||||
return field;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setField(Field field) {
|
|
||||||
this.field = field;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
package org.springframework.cloud.alibaba.sentinel.datasource.factorybean;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.datasource.Converter;
|
|
||||||
import com.alibaba.csp.sentinel.datasource.apollo.ApolloDataSource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author fangjian
|
|
||||||
* @see ApolloDataSource
|
|
||||||
*/
|
|
||||||
public class ApolloDataSourceFactoryBean implements FactoryBean<ApolloDataSource> {
|
|
||||||
|
|
||||||
private String namespaceName;
|
|
||||||
private String flowRulesKey;
|
|
||||||
private String defaultFlowRuleValue;
|
|
||||||
private Converter converter;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ApolloDataSource getObject() throws Exception {
|
|
||||||
return new ApolloDataSource(namespaceName, flowRulesKey, defaultFlowRuleValue,
|
|
||||||
converter);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getObjectType() {
|
|
||||||
return ApolloDataSource.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getNamespaceName() {
|
|
||||||
return namespaceName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNamespaceName(String namespaceName) {
|
|
||||||
this.namespaceName = namespaceName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFlowRulesKey() {
|
|
||||||
return flowRulesKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFlowRulesKey(String flowRulesKey) {
|
|
||||||
this.flowRulesKey = flowRulesKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDefaultFlowRuleValue() {
|
|
||||||
return defaultFlowRuleValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDefaultFlowRuleValue(String defaultFlowRuleValue) {
|
|
||||||
this.defaultFlowRuleValue = defaultFlowRuleValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Converter getConverter() {
|
|
||||||
return converter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setConverter(Converter Converter) {
|
|
||||||
this.converter = Converter;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
package org.springframework.cloud.alibaba.sentinel.datasource.factorybean;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.datasource.Converter;
|
|
||||||
import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author fangjian
|
|
||||||
* @see FileRefreshableDataSource
|
|
||||||
*/
|
|
||||||
public class FileRefreshableDataSourceFactoryBean
|
|
||||||
implements FactoryBean<FileRefreshableDataSource> {
|
|
||||||
|
|
||||||
private String file;
|
|
||||||
private String charset;
|
|
||||||
private long recommendRefreshMs;
|
|
||||||
private int bufSize;
|
|
||||||
private Converter converter;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FileRefreshableDataSource getObject() throws Exception {
|
|
||||||
return new FileRefreshableDataSource(new File(file), converter,
|
|
||||||
recommendRefreshMs, bufSize, Charset.forName(charset));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getObjectType() {
|
|
||||||
return FileRefreshableDataSource.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFile() {
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFile(String file) {
|
|
||||||
this.file = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCharset() {
|
|
||||||
return charset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCharset(String charset) {
|
|
||||||
this.charset = charset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getRecommendRefreshMs() {
|
|
||||||
return recommendRefreshMs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRecommendRefreshMs(long recommendRefreshMs) {
|
|
||||||
this.recommendRefreshMs = recommendRefreshMs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBufSize() {
|
|
||||||
return bufSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBufSize(int bufSize) {
|
|
||||||
this.bufSize = bufSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Converter getConverter() {
|
|
||||||
return converter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setConverter(Converter Converter) {
|
|
||||||
this.converter = Converter;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
package org.springframework.cloud.alibaba.sentinel.datasource.factorybean;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.datasource.Converter;
|
|
||||||
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author fangjian
|
|
||||||
* @see NacosDataSource
|
|
||||||
*/
|
|
||||||
public class NacosDataSourceFactoryBean implements FactoryBean<NacosDataSource> {
|
|
||||||
|
|
||||||
private String serverAddr;
|
|
||||||
private String groupId;
|
|
||||||
private String dataId;
|
|
||||||
private Converter converter;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public NacosDataSource getObject() throws Exception {
|
|
||||||
return new NacosDataSource(serverAddr, groupId, dataId, converter);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getObjectType() {
|
|
||||||
return NacosDataSource.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getServerAddr() {
|
|
||||||
return serverAddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setServerAddr(String serverAddr) {
|
|
||||||
this.serverAddr = serverAddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getGroupId() {
|
|
||||||
return groupId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGroupId(String groupId) {
|
|
||||||
this.groupId = groupId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDataId() {
|
|
||||||
return dataId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDataId(String dataId) {
|
|
||||||
this.dataId = dataId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Converter getConverter() {
|
|
||||||
return converter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setConverter(Converter Converter) {
|
|
||||||
this.converter = Converter;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
package org.springframework.cloud.alibaba.sentinel.datasource.factorybean;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.datasource.Converter;
|
|
||||||
import com.alibaba.csp.sentinel.datasource.zookeeper.ZookeeperDataSource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author fangjian
|
|
||||||
* @see ZookeeperDataSource
|
|
||||||
*/
|
|
||||||
public class ZookeeperDataSourceFactoryBean implements FactoryBean<ZookeeperDataSource> {
|
|
||||||
|
|
||||||
private String serverAddr;
|
|
||||||
|
|
||||||
private String path;
|
|
||||||
|
|
||||||
private String groupId;
|
|
||||||
private String dataId;
|
|
||||||
|
|
||||||
private Converter converter;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ZookeeperDataSource getObject() throws Exception {
|
|
||||||
if (StringUtils.isNotEmpty(groupId) && StringUtils.isNotEmpty(dataId)) {
|
|
||||||
// the path will be /{groupId}/{dataId}
|
|
||||||
return new ZookeeperDataSource(serverAddr, groupId, dataId, converter);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// using path directly
|
|
||||||
return new ZookeeperDataSource(serverAddr, path, converter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getObjectType() {
|
|
||||||
return ZookeeperDataSource.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getServerAddr() {
|
|
||||||
return serverAddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setServerAddr(String serverAddr) {
|
|
||||||
this.serverAddr = serverAddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPath() {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPath(String path) {
|
|
||||||
this.path = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getGroupId() {
|
|
||||||
return groupId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGroupId(String groupId) {
|
|
||||||
this.groupId = groupId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDataId() {
|
|
||||||
return dataId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDataId(String dataId) {
|
|
||||||
this.dataId = dataId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Converter getConverter() {
|
|
||||||
return converter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setConverter(Converter Converter) {
|
|
||||||
this.converter = Converter;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue