From dcbabd70fb8e17bb855b9bd37a5ceb0d2f894a18 Mon Sep 17 00:00:00 2001 From: fangjian0423 Date: Tue, 20 Nov 2018 14:14:00 +0800 Subject: [PATCH] fixes #91 --- .../pom.xml | 20 +- .../sentinel/datasource/DataSourceLoader.java | 152 --------- .../SentinelDataSourcePostProcessor.java | 272 --------------- .../SentinelDataSourceRegistry.java | 68 ---- .../annotation/SentinelDataSource.java | 50 --- .../config/AbstractDataSourceProperties.java | 41 +++ .../config/ApolloDataSourceProperties.java | 44 +++ .../DataSourcePropertiesConfiguration.java | 79 +++++ .../config/FileDataSourceProperties.java | 53 +++ .../config/NacosDataSourceProperties.java | 44 +++ .../config/ZookeeperDataSourceProperties.java | 56 ++++ .../datasource/converter/JsonConverter.java | 157 +++++++++ .../datasource/converter/XmlConverter.java | 157 +++++++++ .../datasource/util/PropertySourcesUtils.java | 75 ----- spring-cloud-alibaba-sentinel/pom.xml | 6 + .../alibaba/sentinel/SentinelProperties.java | 18 + .../custom/SentinelAutoConfiguration.java | 38 ++- .../custom/SentinelBeanPostProcessor.java | 2 +- .../custom/SentinelDataSourceHandler.java | 315 ++++++++++++++++++ .../sentinel/endpoint/SentinelEndpoint.java | 35 +- 20 files changed, 1054 insertions(+), 628 deletions(-) delete mode 100644 spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/DataSourceLoader.java delete mode 100644 spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelDataSourcePostProcessor.java delete mode 100644 spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelDataSourceRegistry.java delete mode 100644 spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/annotation/SentinelDataSource.java create mode 100644 spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/AbstractDataSourceProperties.java create mode 100644 spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/ApolloDataSourceProperties.java create mode 100644 spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/DataSourcePropertiesConfiguration.java create mode 100644 spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/FileDataSourceProperties.java create mode 100644 spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/NacosDataSourceProperties.java create mode 100644 spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/ZookeeperDataSourceProperties.java create mode 100644 spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/converter/JsonConverter.java create mode 100644 spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/converter/XmlConverter.java delete mode 100644 spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/util/PropertySourcesUtils.java create mode 100644 spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java diff --git a/spring-cloud-alibaba-sentinel-datasource/pom.xml b/spring-cloud-alibaba-sentinel-datasource/pom.xml index f2878dfe1..5d7d85154 100644 --- a/spring-cloud-alibaba-sentinel-datasource/pom.xml +++ b/spring-cloud-alibaba-sentinel-datasource/pom.xml @@ -41,8 +41,27 @@ true + + com.fasterxml.jackson.core + jackson-databind + provided + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + provided + + + + org.springframework.boot + spring-boot-configuration-processor + provided + true + + org.springframework.boot spring-boot @@ -63,7 +82,6 @@ test - diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/DataSourceLoader.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/DataSourceLoader.java deleted file mode 100644 index 40f17197d..000000000 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/DataSourceLoader.java +++ /dev/null @@ -1,152 +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 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 Jim - */ -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> dataSourceClassesCache - = new ConcurrentHashMap>( - 4); - - static void loadAllDataSourceClassesCache() { - Map> dataSourceClassesMap = loadAllDataSourceClassesCache( - ALL_PROPERTIES_RESOURCES_LOCATION); - - dataSourceClassesCache.putAll(dataSourceClassesMap); - } - - static Map> loadAllDataSourceClassesCache( - String resourcesLocation) { - - Map> dataSourcesMap - = new HashMap>( - 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 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)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 loadClass(String type) - throws IllegalArgumentException { - - Class 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; - - } - -} diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelDataSourcePostProcessor.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelDataSourcePostProcessor.java deleted file mode 100644 index d54dd0299..000000000 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelDataSourcePostProcessor.java +++ /dev/null @@ -1,272 +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 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 Jim - * @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> 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 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 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 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 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 { - logger.info("[Sentinel Starter] Start to find ReadableDataSource"); - Map dataSourceMap = event.getApplicationContext().getBeansOfType( - ReadableDataSource.class); - if (dataSourceMap.size() == 1) { - logger.info("[Sentinel Starter] There exists only one ReadableDataSource named {}, start to load rules", - dataSourceMap.keySet().iterator().next()); - ReadableDataSource dataSource = dataSourceMap.values().iterator().next(); - Object ruleConfig = dataSource.loadConfig(); - SentinelProperty sentinelProperty = dataSource.getProperty(); - Integer rulesNum; - if ((rulesNum = checkRuleType(ruleConfig, FlowRule.class)) > 0) { - FlowRuleManager.register2Property(sentinelProperty); - logger.info("[Sentinel Starter] load {} flow rules", rulesNum); - } - if ((rulesNum = checkRuleType(ruleConfig, DegradeRule.class)) > 0) { - DegradeRuleManager.register2Property(sentinelProperty); - logger.info("[Sentinel Starter] load {} degrade rules", rulesNum); - } - if ((rulesNum = checkRuleType(ruleConfig, SystemRule.class)) > 0) { - SystemRuleManager.register2Property(sentinelProperty); - logger.info("[Sentinel Starter] load {} system rules", rulesNum); - } - if ((rulesNum = checkRuleType(ruleConfig, AuthorityRule.class)) > 0) { - AuthorityRuleManager.register2Property(sentinelProperty); - logger.info("[Sentinel Starter] load {} authority rules", rulesNum); - } - } else if (dataSourceMap.size() > 1) { - logger.warn( - "[Sentinel Starter] There exists more than one ReadableDataSource, can not choose which one to load"); - } else { - logger.warn( - "[Sentinel Starter] No ReadableDataSource exists"); - } - } - - private Integer checkRuleType(Object ruleConfig, Class type) { - if (ruleConfig.getClass() == type) { - return 1; - } else if (ruleConfig instanceof List) { - List ruleList = (List)ruleConfig; - if (ruleList.stream().filter(rule -> rule.getClass() == type).toArray().length == ruleList.size()) { - return ruleList.size(); - } - } - return -1; - } - - 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; - } - } - -} diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelDataSourceRegistry.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelDataSourceRegistry.java deleted file mode 100644 index 5108196b5..000000000 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelDataSourceRegistry.java +++ /dev/null @@ -1,68 +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 java.util.HashMap; - -import com.alibaba.csp.sentinel.datasource.ReadableDataSource; - -import org.springframework.beans.factory.FactoryBean; -import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.ApolloDataSourceFactoryBean; -import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.FileRefreshableDataSourceFactoryBean; -import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.NacosDataSourceFactoryBean; -import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.ZookeeperDataSourceFactoryBean; - -/** - * Registry to save DataSource FactoryBean - * - * @author Jim - * @see ReadableDataSource - * @see FileRefreshableDataSourceFactoryBean - * @see ZookeeperDataSourceFactoryBean - * @see NacosDataSourceFactoryBean - * @see ApolloDataSourceFactoryBean - */ -public class SentinelDataSourceRegistry { - - private static HashMap> cache = new HashMap<>( - 32); - - static { - SentinelDataSourceRegistry.registerFactoryBean("file", - FileRefreshableDataSourceFactoryBean.class); - SentinelDataSourceRegistry.registerFactoryBean("zk", - ZookeeperDataSourceFactoryBean.class); - SentinelDataSourceRegistry.registerFactoryBean("nacos", - NacosDataSourceFactoryBean.class); - SentinelDataSourceRegistry.registerFactoryBean("apollo", - ApolloDataSourceFactoryBean.class); - } - - public static synchronized void registerFactoryBean(String alias, - Class clazz) { - cache.put(alias, clazz); - } - - public static Class getFactoryBean(String alias) { - return cache.get(alias); - } - - public static boolean checkFactoryBean(String alias) { - return cache.containsKey(alias); - } - -} diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/annotation/SentinelDataSource.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/annotation/SentinelDataSource.java deleted file mode 100644 index b6b33226b..000000000 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/annotation/SentinelDataSource.java +++ /dev/null @@ -1,50 +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.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import com.alibaba.csp.sentinel.datasource.ReadableDataSource; - -import org.springframework.core.annotation.AliasFor; - -/** - * An annotation to inject {@link ReadableDataSource} instance - * into a Spring Bean. The Properties of DataSource bean get from config files with - * specific prefix. - * - * Jim - * @see ReadableDataSource - */ -@Target({ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface SentinelDataSource { - - @AliasFor("prefix") - String value() default ""; - - @AliasFor("value") - String prefix() default ""; - - String name() default ""; // spring bean name - -} diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/AbstractDataSourceProperties.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/AbstractDataSourceProperties.java new file mode 100644 index 000000000..6e879b21d --- /dev/null +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/AbstractDataSourceProperties.java @@ -0,0 +1,41 @@ +package org.springframework.cloud.alibaba.sentinel.datasource.config; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** + * Abstract class Using by {@link DataSourcePropertiesConfiguration} + * + * @author Jim + */ +public class AbstractDataSourceProperties { + + private String dataType = "json"; + private String converterClass; + @JsonIgnore + private final String factoryBeanName; + + public AbstractDataSourceProperties(String factoryBeanName) { + this.factoryBeanName = factoryBeanName; + } + + public String getDataType() { + return dataType; + } + + public void setDataType(String dataType) { + this.dataType = dataType; + } + + public String getConverterClass() { + return converterClass; + } + + public void setConverterClass(String converterClass) { + this.converterClass = converterClass; + } + + public String getFactoryBeanName() { + return factoryBeanName; + } + +} diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/ApolloDataSourceProperties.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/ApolloDataSourceProperties.java new file mode 100644 index 000000000..28d803eaf --- /dev/null +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/ApolloDataSourceProperties.java @@ -0,0 +1,44 @@ +package org.springframework.cloud.alibaba.sentinel.datasource.config; + +import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.ApolloDataSourceFactoryBean; + +/** + * Apollo Properties class Using by {@link DataSourcePropertiesConfiguration} and + * {@link ApolloDataSourceFactoryBean} + * + * @author Jim + */ +public class ApolloDataSourceProperties extends AbstractDataSourceProperties { + + private String namespaceName; + private String flowRulesKey; + private String defaultFlowRuleValue; + + public ApolloDataSourceProperties() { + super(ApolloDataSourceFactoryBean.class.getName()); + } + + 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; + } +} diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/DataSourcePropertiesConfiguration.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/DataSourcePropertiesConfiguration.java new file mode 100644 index 000000000..16485872f --- /dev/null +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/DataSourcePropertiesConfiguration.java @@ -0,0 +1,79 @@ +package org.springframework.cloud.alibaba.sentinel.datasource.config; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import org.springframework.util.ObjectUtils; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** + * Using By ConfigurationProperties. + * + * @author Jim + * @see NacosDataSourceProperties + * @see ApolloDataSourceProperties + * @see ZookeeperDataSourceProperties + * @see FileDataSourceProperties + */ +public class DataSourcePropertiesConfiguration { + + private FileDataSourceProperties file; + + private NacosDataSourceProperties nacos; + + private ZookeeperDataSourceProperties zk; + + private ApolloDataSourceProperties apollo; + + public FileDataSourceProperties getFile() { + return file; + } + + public void setFile(FileDataSourceProperties file) { + this.file = file; + } + + public NacosDataSourceProperties getNacos() { + return nacos; + } + + public void setNacos(NacosDataSourceProperties nacos) { + this.nacos = nacos; + } + + public ZookeeperDataSourceProperties getZk() { + return zk; + } + + public void setZk(ZookeeperDataSourceProperties zk) { + this.zk = zk; + } + + public ApolloDataSourceProperties getApollo() { + return apollo; + } + + public void setApollo(ApolloDataSourceProperties apollo) { + this.apollo = apollo; + } + + @JsonIgnore + public List getInvalidField() { + return Arrays.stream(this.getClass().getDeclaredFields()).map(field -> { + try { + if (!ObjectUtils.isEmpty(field.get(this))) { + return field.getName(); + } + return null; + } + catch (IllegalAccessException e) { + // won't happen + } + return null; + }).filter(Objects::nonNull).collect(Collectors.toList()); + } + +} diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/FileDataSourceProperties.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/FileDataSourceProperties.java new file mode 100644 index 000000000..ab70b8164 --- /dev/null +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/FileDataSourceProperties.java @@ -0,0 +1,53 @@ +package org.springframework.cloud.alibaba.sentinel.datasource.config; + +import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.FileRefreshableDataSourceFactoryBean; + +/** + * File Properties class Using by {@link DataSourcePropertiesConfiguration} and + * {@link FileRefreshableDataSourceFactoryBean} + * + * @author Jim + */ +public class FileDataSourceProperties extends AbstractDataSourceProperties { + + private String file; + private String charset = "utf-8"; + private long recommendRefreshMs = 3000L; + private int bufSize = 1024 * 1024; + + public FileDataSourceProperties() { + super(FileRefreshableDataSourceFactoryBean.class.getName()); + } + + 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; + } +} diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/NacosDataSourceProperties.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/NacosDataSourceProperties.java new file mode 100644 index 000000000..3448b410f --- /dev/null +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/NacosDataSourceProperties.java @@ -0,0 +1,44 @@ +package org.springframework.cloud.alibaba.sentinel.datasource.config; + +import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.NacosDataSourceFactoryBean; + +/** + * Nacos Properties class Using by {@link DataSourcePropertiesConfiguration} and + * {@link NacosDataSourceFactoryBean} + * + * @author Jim + */ +public class NacosDataSourceProperties extends AbstractDataSourceProperties { + + private String serverAddr; + private String groupId; + private String dataId; + + public NacosDataSourceProperties() { + super(NacosDataSourceFactoryBean.class.getName()); + } + + 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; + } +} diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/ZookeeperDataSourceProperties.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/ZookeeperDataSourceProperties.java new file mode 100644 index 000000000..a1d616674 --- /dev/null +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/ZookeeperDataSourceProperties.java @@ -0,0 +1,56 @@ +package org.springframework.cloud.alibaba.sentinel.datasource.config; + +import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.ZookeeperDataSourceFactoryBean; + +/** + * Zookeeper Properties class Using by {@link DataSourcePropertiesConfiguration} and + * {@link ZookeeperDataSourceFactoryBean} + * + * @author Jim + */ +public class ZookeeperDataSourceProperties extends AbstractDataSourceProperties { + + public ZookeeperDataSourceProperties() { + super(ZookeeperDataSourceFactoryBean.class.getName()); + } + + private String serverAddr; + + private String path; + + private String groupId; + + private String dataId; + + 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; + } +} diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/converter/JsonConverter.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/converter/JsonConverter.java new file mode 100644 index 000000000..0247ba80d --- /dev/null +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/converter/JsonConverter.java @@ -0,0 +1,157 @@ +package org.springframework.cloud.alibaba.sentinel.datasource.converter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; + +import com.alibaba.csp.sentinel.datasource.Converter; +import com.alibaba.csp.sentinel.slots.block.AbstractRule; +import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; +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.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Convert sentinel rules for json array Using strict mode to parse json + * + * @author Jim + * @see FlowRule + * @see DegradeRule + * @see SystemRule + * @see AuthorityRule + * @see ObjectMapper + */ +public class JsonConverter implements Converter> { + + private static final Logger logger = LoggerFactory.getLogger(JsonConverter.class); + + private final ObjectMapper objectMapper; + + public JsonConverter(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + @Override + public List convert(String source) { + List ruleList = new ArrayList<>(); + if (StringUtils.isEmpty(source)) { + logger.info( + "Sentinel JsonConverter can not convert rules because source is empty"); + return ruleList; + } + try { + List jsonArray = objectMapper.readValue(source, + new TypeReference>() { + }); + jsonArray.stream().forEach(obj -> { + + String itemJson = null; + try { + itemJson = objectMapper.writeValueAsString(obj); + } + catch (JsonProcessingException e) { + // won't be happen + } + + List rules = Arrays.asList(convertFlowRule(itemJson), + convertDegradeRule(itemJson), convertSystemRule(itemJson), + convertAuthorityRule(itemJson)); + + List convertRuleList = rules.stream() + .filter(rule -> !ObjectUtils.isEmpty(rule)) + .collect(Collectors.toList()); + + if (convertRuleList.size() == 0) { + logger.warn( + "Sentinel JsonConverter can not convert {} to any rules, ignore", + itemJson); + } + else if (convertRuleList.size() > 1) { + logger.warn( + "Sentinel JsonConverter convert {} and match multi rules, ignore", + itemJson); + } + else { + ruleList.add(convertRuleList.get(0)); + } + + }); + if (jsonArray.size() != ruleList.size()) { + logger.warn( + "Sentinel JsonConverter Source list size is not equals to Target List, maybe a " + + "part of json is invalid. Source List: " + jsonArray + + ", Target List: " + ruleList); + } + } + catch (Exception e) { + logger.error("Sentinel JsonConverter convert error: " + e.getMessage()); + throw new RuntimeException( + "Sentinel JsonConverter convert error: " + e.getMessage(), e); + } + logger.info("Sentinel JsonConverter convert {} rules: {}", ruleList.size(), + ruleList); + return ruleList; + } + + private FlowRule convertFlowRule(String json) { + try { + FlowRule rule = objectMapper.readValue(json, FlowRule.class); + if (FlowRuleManager.isValidRule(rule)) { + return rule; + } + } + catch (Exception e) { + // ignore + } + return null; + } + + private DegradeRule convertDegradeRule(String json) { + try { + DegradeRule rule = objectMapper.readValue(json, DegradeRule.class); + if (DegradeRuleManager.isValidRule(rule)) { + return rule; + } + } + catch (Exception e) { + // ignore + } + return null; + } + + private SystemRule convertSystemRule(String json) { + SystemRule rule = null; + try { + rule = objectMapper.readValue(json, SystemRule.class); + } + catch (Exception e) { + // ignore + } + return rule; + } + + private AuthorityRule convertAuthorityRule(String json) { + AuthorityRule rule = null; + try { + rule = objectMapper.readValue(json, AuthorityRule.class); + } + catch (Exception e) { + // ignore + } + return rule; + } + +} diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/converter/XmlConverter.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/converter/XmlConverter.java new file mode 100644 index 000000000..b4a95a992 --- /dev/null +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/converter/XmlConverter.java @@ -0,0 +1,157 @@ +package org.springframework.cloud.alibaba.sentinel.datasource.converter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; + +import com.alibaba.csp.sentinel.datasource.Converter; +import com.alibaba.csp.sentinel.slots.block.AbstractRule; +import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; +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.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; + +/** + * Convert sentinel rules for xml array Using strict mode to parse xml + * + * @author Jim + * @see FlowRule + * @see DegradeRule + * @see SystemRule + * @see AuthorityRule + * @see XmlMapper + */ +public class XmlConverter implements Converter> { + + private static final Logger logger = LoggerFactory.getLogger(XmlConverter.class); + + private final XmlMapper xmlMapper; + + public XmlConverter(XmlMapper xmlMapper) { + this.xmlMapper = xmlMapper; + } + + @Override + public List convert(String source) { + List ruleList = new ArrayList<>(); + if (StringUtils.isEmpty(source)) { + logger.info( + "Sentinel XmlConverter can not convert rules because source is empty"); + return ruleList; + } + try { + List xmlArray = xmlMapper.readValue(source, + new TypeReference>() { + }); + xmlArray.stream().forEach(obj -> { + + String itemXml = null; + try { + itemXml = xmlMapper.writeValueAsString(obj); + } + catch (JsonProcessingException e) { + // won't be happen + } + + List rules = Arrays.asList(convertFlowRule(itemXml), + convertDegradeRule(itemXml), convertSystemRule(itemXml), + convertAuthorityRule(itemXml)); + + List convertRuleList = rules.stream() + .filter(rule -> !ObjectUtils.isEmpty(rule)) + .collect(Collectors.toList()); + + if (convertRuleList.size() == 0) { + logger.warn( + "Sentinel XmlConverter can not convert {} to any rules, ignore", + itemXml); + } + else if (convertRuleList.size() > 1) { + logger.warn( + "Sentinel XmlConverter convert {} and match multi rules, ignore", + itemXml); + } + else { + ruleList.add(convertRuleList.get(0)); + } + + }); + if (xmlArray.size() != ruleList.size()) { + logger.warn( + "Sentinel XmlConverter Source list size is not equals to Target List, maybe a " + + "part of xml is invalid. Source List: " + xmlArray + + ", Target List: " + ruleList); + } + } + catch (Exception e) { + logger.error("Sentinel XmlConverter convert error: " + e.getMessage()); + throw new RuntimeException( + "Sentinel XmlConverter convert error: " + e.getMessage(), e); + } + logger.info("Sentinel XmlConverter convert {} rules: {}", ruleList.size(), + ruleList); + return ruleList; + } + + private FlowRule convertFlowRule(String xml) { + try { + FlowRule rule = xmlMapper.readValue(xml, FlowRule.class); + if (FlowRuleManager.isValidRule(rule)) { + return rule; + } + } + catch (Exception e) { + // ignore + } + return null; + } + + private DegradeRule convertDegradeRule(String xml) { + try { + DegradeRule rule = xmlMapper.readValue(xml, DegradeRule.class); + if (DegradeRuleManager.isValidRule(rule)) { + return rule; + } + } + catch (Exception e) { + // ignore + } + return null; + } + + private SystemRule convertSystemRule(String xml) { + SystemRule rule = null; + try { + rule = xmlMapper.readValue(xml, SystemRule.class); + } + catch (Exception e) { + // ignore + } + return rule; + } + + private AuthorityRule convertAuthorityRule(String xml) { + AuthorityRule rule = null; + try { + rule = xmlMapper.readValue(xml, AuthorityRule.class); + } + catch (Exception e) { + // ignore + } + return rule; + } + +} diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/util/PropertySourcesUtils.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/util/PropertySourcesUtils.java deleted file mode 100644 index fde8fc44f..000000000 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/util/PropertySourcesUtils.java +++ /dev/null @@ -1,75 +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.util; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Properties; - -import org.springframework.core.env.EnumerablePropertySource; -import org.springframework.core.env.PropertySource; -import org.springframework.core.env.PropertySources; - -/** - * {@link PropertySources} Utilities - * - * @author Mercy - */ -public abstract class PropertySourcesUtils { - - /** - * Get Sub {@link Properties} - * - * @param propertySources {@link PropertySource} Iterable - * @param prefix the prefix of property name - * @return Map - * @see Properties - */ - public static Map getSubProperties(Iterable> propertySources, String prefix) { - - Map subProperties = new LinkedHashMap(); - - String normalizedPrefix = normalizePrefix(prefix); - - for (PropertySource source : propertySources) { - if (source instanceof EnumerablePropertySource) { - for (String name : ((EnumerablePropertySource)source).getPropertyNames()) { - if (!subProperties.containsKey(name) && name.startsWith(normalizedPrefix)) { - String subName = name.substring(normalizedPrefix.length()); - if (!subProperties.containsKey(subName)) { // take first one - Object value = source.getProperty(name); - subProperties.put(subName, value); - } - } - } - } - } - - return subProperties; - - } - - /** - * Normalize the prefix - * - * @param prefix the prefix - * @return the prefix - */ - public static String normalizePrefix(String prefix) { - return prefix.endsWith(".") ? prefix : prefix + "."; - } -} diff --git a/spring-cloud-alibaba-sentinel/pom.xml b/spring-cloud-alibaba-sentinel/pom.xml index 3c3b41ead..b4f63c9f2 100644 --- a/spring-cloud-alibaba-sentinel/pom.xml +++ b/spring-cloud-alibaba-sentinel/pom.xml @@ -40,6 +40,12 @@ spring-cloud-alibaba-sentinel-datasource + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + provided + + diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelProperties.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelProperties.java index bd7120957..fb3cba2e4 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelProperties.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelProperties.java @@ -17,9 +17,12 @@ package org.springframework.cloud.alibaba.sentinel; import java.util.List; +import java.util.Map; +import java.util.TreeMap; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.NestedConfigurationProperty; +import org.springframework.cloud.alibaba.sentinel.datasource.config.DataSourcePropertiesConfiguration; import org.springframework.core.Ordered; import com.alibaba.csp.sentinel.config.SentinelConfig; @@ -29,6 +32,7 @@ import com.alibaba.csp.sentinel.transport.config.TransportConfig; * @author xiaojing * @author hengyunabc * @author jiashuai.xie + * @author Jim */ @ConfigurationProperties(prefix = SentinelConstants.PROPERTY_PREFIX) public class SentinelProperties { @@ -49,6 +53,12 @@ public class SentinelProperties { */ private String charset = "UTF-8"; + /** + * configurations about datasource, like 'nacos', 'apollo', 'file', 'zookeeper' + */ + private Map datasource = new TreeMap<>( + String.CASE_INSENSITIVE_ORDER); + /** * transport configuration about dashboard and client */ @@ -145,6 +155,14 @@ public class SentinelProperties { this.filter = filter; } + public Map getDatasource() { + return datasource; + } + + public void setDatasource(Map datasource) { + this.datasource = datasource; + } + public static class Flow { /** diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java index 2f0ef6084..093cab8be 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java @@ -21,13 +21,15 @@ import java.util.Optional; import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.alibaba.sentinel.SentinelProperties; -import org.springframework.cloud.alibaba.sentinel.datasource.SentinelDataSourcePostProcessor; +import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter; +import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.StringUtils; @@ -42,9 +44,13 @@ import com.alibaba.csp.sentinel.init.InitExecutor; import com.alibaba.csp.sentinel.transport.config.TransportConfig; import com.alibaba.csp.sentinel.util.AppNameUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; + /** * @author xiaojing * @author jiashuai.xie + * @author Jim */ @Configuration @ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true) @@ -135,9 +141,33 @@ public class SentinelAutoConfiguration { } @Bean - @ConditionalOnMissingBean - public SentinelDataSourcePostProcessor sentinelDataSourcePostProcessor() { - return new SentinelDataSourcePostProcessor(); + public SentinelDataSourceHandler sentinelDataSourceHandler() { + return new SentinelDataSourceHandler(); + } + + @Bean("sentinel-json-converter") + public JsonConverter jsonConverter( + @Qualifier("sentinel-object-mapper") ObjectMapper objectMapper) { + return new JsonConverter(objectMapper); + } + + @Bean("sentinel-object-mapper") + public ObjectMapper objectMapper() { + return new ObjectMapper(); + } + + @ConditionalOnClass(XmlMapper.class) + protected static class SentinelXmlConfiguration { + @Bean("sentinel-xml-converter") + public XmlConverter xmlConverter( + @Qualifier("sentinel-xml-mapper") XmlMapper xmlMapper) { + return new XmlConverter(xmlMapper); + } + + @Bean("sentinel-xml-mapper") + public XmlMapper xmlMapper() { + return new XmlMapper(); + } } } diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java index 2d95c06c4..0932ac85d 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java @@ -34,7 +34,7 @@ import org.springframework.web.client.RestTemplate; /** * PostProcessor handle @SentinelProtect Annotation, add interceptor for RestTemplate * - * @author fangjian + * @author Jim * @see SentinelProtect * @see SentinelProtectInterceptor */ diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java new file mode 100644 index 000000000..66c44e4eb --- /dev/null +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java @@ -0,0 +1,315 @@ +package org.springframework.cloud.alibaba.sentinel.custom; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.cloud.alibaba.sentinel.SentinelProperties; +import org.springframework.cloud.alibaba.sentinel.datasource.config.AbstractDataSourceProperties; +import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter; +import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter; +import org.springframework.context.event.EventListener; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.ResourceUtils; +import org.springframework.util.StringUtils; + +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; + +/** + * Sentinel {@link ReadableDataSource} Handler Handle the configurations of + * 'spring.cloud.sentinel.datasource' + * + * @author Jim + * @see SentinelProperties#datasource + * @see JsonConverter + * @see XmlConverter + */ +public class SentinelDataSourceHandler { + + private static final Logger logger = LoggerFactory + .getLogger(SentinelDataSourceHandler.class); + + private List dataTypeList = Arrays.asList("json", "xml"); + + private List rulesList = Arrays.asList(FlowRule.class, DegradeRule.class, + SystemRule.class, AuthorityRule.class); + + private List dataSourceBeanNameList = Collections + .synchronizedList(new ArrayList<>()); + + private final String DATATYPE_FIELD = "dataType"; + private final String CUSTOM_DATATYPE = "custom"; + private final String CONVERTERCLASS_FIELD = "converterClass"; + + @Autowired + private SentinelProperties sentinelProperties; + + @EventListener(classes = ApplicationStartedEvent.class) + public void buildDataSource(ApplicationStartedEvent event) throws Exception { + + DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) event + .getApplicationContext().getAutowireCapableBeanFactory(); + + sentinelProperties.getDatasource() + .forEach((dataSourceName, dataSourceProperties) -> { + if (dataSourceProperties.getInvalidField().size() != 1) { + logger.error("[Sentinel Starter] DataSource " + dataSourceName + + " multi datasource active and won't loaded: " + + dataSourceProperties.getInvalidField()); + return; + } + Optional.ofNullable(dataSourceProperties.getFile()) + .ifPresent(file -> { + try { + dataSourceProperties.getFile().setFile(ResourceUtils + .getFile(StringUtils.trimAllWhitespace( + dataSourceProperties.getFile() + .getFile())) + .getAbsolutePath()); + } + catch (IOException e) { + logger.error("[Sentinel Starter] DataSource " + + dataSourceName + " handle file error: " + + e.getMessage()); + throw new RuntimeException( + "[Sentinel Starter] DataSource " + + dataSourceName + + " handle file error: " + + e.getMessage(), + e); + } + registerBean(beanFactory, file, + dataSourceName + "-sentinel-file-datasource"); + }); + Optional.ofNullable(dataSourceProperties.getNacos()) + .ifPresent(nacos -> { + registerBean(beanFactory, nacos, + dataSourceName + "-sentinel-nacos-datasource"); + }); + Optional.ofNullable(dataSourceProperties.getApollo()) + .ifPresent(apollo -> { + registerBean(beanFactory, apollo, + dataSourceName + "-sentinel-apollo-datasource"); + }); + Optional.ofNullable(dataSourceProperties.getZk()).ifPresent(zk -> { + registerBean(beanFactory, zk, + dataSourceName + "-sentinel-zk-datasource"); + }); + }); + + dataSourceBeanNameList.forEach(beanName -> { + ReadableDataSource dataSource = beanFactory.getBean(beanName, + ReadableDataSource.class); + Object ruleConfig; + try { + logger.info("[Sentinel Starter] DataSource " + beanName + + " start to loadConfig"); + ruleConfig = dataSource.loadConfig(); + } + catch (Exception e) { + logger.error("[Sentinel Starter] DataSource " + beanName + + " loadConfig error: " + e.getMessage(), e); + return; + } + SentinelProperty sentinelProperty = dataSource.getProperty(); + Class ruleType = getAndCheckRuleType(ruleConfig, beanName); + if (ruleType != null) { + if (ruleType == FlowRule.class) { + FlowRuleManager.register2Property(sentinelProperty); + } + else if (ruleType == DegradeRule.class) { + DegradeRuleManager.register2Property(sentinelProperty); + } + else if (ruleType == SystemRule.class) { + SystemRuleManager.register2Property(sentinelProperty); + } + else { + AuthorityRuleManager.register2Property(sentinelProperty); + } + } + }); + } + + private void registerBean(DefaultListableBeanFactory beanFactory, + final AbstractDataSourceProperties dataSourceProperties, + String dataSourceName) { + + Map propertyMap = Arrays + .stream(dataSourceProperties.getClass().getDeclaredFields()) + .collect(HashMap::new, (m, v) -> { + try { + v.setAccessible(true); + m.put(v.getName(), v.get(dataSourceProperties)); + } + catch (IllegalAccessException e) { + logger.error("[Sentinel Starter] DataSource " + dataSourceName + + " field: " + v.getName() + " invoke error"); + throw new RuntimeException( + "[Sentinel Starter] DataSource " + dataSourceName + + " field: " + v.getName() + " invoke error", + e); + } + }, HashMap::putAll); + propertyMap.put(CONVERTERCLASS_FIELD, dataSourceProperties.getConverterClass()); + propertyMap.put(DATATYPE_FIELD, dataSourceProperties.getDataType()); + + BeanDefinitionBuilder builder = BeanDefinitionBuilder + .genericBeanDefinition(dataSourceProperties.getFactoryBeanName()); + + propertyMap.forEach((propertyName, propertyValue) -> { + Field field = ReflectionUtils.findField(dataSourceProperties.getClass(), + propertyName); + if (field != null) { + if (DATATYPE_FIELD.equals(propertyName)) { + String dataType = StringUtils + .trimAllWhitespace(propertyValue.toString()); + if (CUSTOM_DATATYPE.equals(dataType)) { + try { + if (StringUtils + .isEmpty(dataSourceProperties.getConverterClass())) { + throw new RuntimeException( + "[Sentinel Starter] DataSource " + dataSourceName + + "dataType is custom, please set converter-class " + + "property"); + } + // construct custom Converter with 'converterClass' + // configuration and register + String customConvertBeanName = "sentinel-" + + dataSourceProperties.getConverterClass(); + if (!beanFactory.containsBean(customConvertBeanName)) { + beanFactory.registerBeanDefinition(customConvertBeanName, + BeanDefinitionBuilder + .genericBeanDefinition( + Class.forName(dataSourceProperties + .getConverterClass())) + .getBeanDefinition()); + } + builder.addPropertyReference("converter", + customConvertBeanName); + } + catch (ClassNotFoundException e) { + logger.error("[Sentinel Starter] DataSource " + dataSourceName + + " handle " + + dataSourceProperties.getClass().getSimpleName() + + " error, class name: " + + dataSourceProperties.getConverterClass()); + throw new RuntimeException( + "[Sentinel Starter] DataSource " + dataSourceName + + " handle " + + dataSourceProperties.getClass() + .getSimpleName() + + " error, class name: " + + dataSourceProperties.getConverterClass(), + e); + } + } + else { + if (!dataTypeList.contains(StringUtils + .trimAllWhitespace(propertyValue.toString()))) { + throw new RuntimeException("[Sentinel Starter] DataSource " + + dataSourceName + " dataType: " + propertyValue + + " is not support now. please using these types: " + + dataTypeList.toString()); + } + // converter type now support xml or json. + // The bean name of these converters wrapped by + // 'sentinel-{converterType}-converter' + builder.addPropertyReference("converter", + "sentinel-" + propertyValue.toString() + "-converter"); + } + } + else if (CONVERTERCLASS_FIELD.equals(propertyName)) { + return; + } + else { + // wired properties + builder.addPropertyValue(propertyName, propertyValue); + } + } + }); + + beanFactory.registerBeanDefinition(dataSourceName, builder.getBeanDefinition()); + // init in Spring + beanFactory.getBean(dataSourceName); + dataSourceBeanNameList.add(dataSourceName); + } + + private Class getAndCheckRuleType(Object ruleConfig, String dataSourceName) { + if (rulesList.contains(ruleConfig.getClass())) { + logger.info("[Sentinel Starter] DataSource {} load {} {}", dataSourceName, 1, + ruleConfig.getClass().getSimpleName()); + return ruleConfig.getClass(); + } + else if (ruleConfig instanceof List) { + List convertedRuleList = (List) ruleConfig; + if (CollectionUtils.isEmpty(convertedRuleList)) { + logger.warn("[Sentinel Starter] DataSource {} rule list is empty.", + dataSourceName); + return null; + } + if (convertedRuleList.stream() + .allMatch(rule -> rulesList.contains(rule.getClass()))) { + if (rulesList.contains(convertedRuleList.get(0).getClass()) + && convertedRuleList.stream() + .filter(rule -> rule.getClass() == convertedRuleList + .get(0).getClass()) + .toArray().length == convertedRuleList.size()) { + logger.info("[Sentinel Starter] DataSource {} load {} {}", + dataSourceName, convertedRuleList.size(), + convertedRuleList.get(0).getClass().getSimpleName()); + return convertedRuleList.get(0).getClass(); + } + else { + logger.warn( + "[Sentinel Starter] DataSource {} all rules are not same rule type and it will not be used. " + + "Rule List: {}", + dataSourceName, convertedRuleList.toString()); + } + } + else { + List classList = (List) convertedRuleList.stream() + .map(Object::getClass).collect(Collectors.toList()); + logger.error("[Sentinel Starter] DataSource " + dataSourceName + + " rule class is invalid. Class List: " + classList); + throw new RuntimeException( + "[Sentinel Starter] DataSource " + dataSourceName + + " rule class is invalid. Class List: " + classList); + } + } + else { + logger.error("[Sentinel Starter] DataSource " + dataSourceName + + " rule class is invalid. Class: " + ruleConfig.getClass()); + throw new RuntimeException("[Sentinel Starter] DataSource " + dataSourceName + + " rule class is invalid. Class: " + ruleConfig.getClass()); + } + return null; + } + + public List getDataSourceBeanNameList() { + return dataSourceBeanNameList; + } + +} diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpoint.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpoint.java index 9a806ed05..f20098c2b 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpoint.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpoint.java @@ -20,16 +20,20 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; +import org.springframework.cloud.alibaba.sentinel.SentinelProperties; +import org.springframework.cloud.alibaba.sentinel.custom.SentinelDataSourceHandler; +import org.springframework.context.ApplicationContext; + +import com.alibaba.csp.sentinel.datasource.ReadableDataSource; 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.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.actuate.endpoint.annotation.Endpoint; -import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; -import org.springframework.cloud.alibaba.sentinel.SentinelProperties; /** * Endpoint for Sentinel, contains ans properties and rules @@ -41,9 +45,15 @@ public class SentinelEndpoint { @Autowired private SentinelProperties sentinelProperties; + @Autowired + private SentinelDataSourceHandler dataSourceHandler; + + @Autowired + private ApplicationContext applicationContext; + @ReadOperation public Map invoke() { - Map result = new HashMap<>(); + final Map result = new HashMap<>(); List flowRules = FlowRuleManager.getRules(); List degradeRules = DegradeRuleManager.getRules(); @@ -52,6 +62,21 @@ public class SentinelEndpoint { result.put("FlowRules", flowRules); result.put("DegradeRules", degradeRules); result.put("SystemRules", systemRules); + result.put("datasources", new HashMap()); + dataSourceHandler.getDataSourceBeanNameList().forEach(dataSourceBeanName -> { + ReadableDataSource dataSource = applicationContext.getBean(dataSourceBeanName, + ReadableDataSource.class); + try { + ((HashMap) result.get("datasources")).put(dataSourceBeanName, + dataSource.loadConfig()); + } + catch (Exception e) { + ((HashMap) result.get("datasources")).put(dataSourceBeanName, + "load error: " + e.getMessage()); + } + + }); + return result; }