All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.zodiac.sentinel.base.datasource.SentinelDataSourceHandler Maven / Gradle / Ivy

package org.zodiac.sentinel.base.datasource;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.slf4j.Logger;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.env.Environment;
import org.zodiac.commons.logging.SmartSlf4jLoggerFactory;
import org.zodiac.commons.util.Reflections;
import org.zodiac.commons.util.lang.Strings;
import org.zodiac.sentinel.base.config.SentinelConfigInfo;
import org.zodiac.sentinel.base.constants.SentinelDataSourceConstants;
import org.zodiac.sentinel.base.datasource.model.AbstractDataSourceInfo;
import org.zodiac.sentinel.base.support.SentinelDataSourcesContext;

import com.alibaba.csp.sentinel.datasource.AbstractDataSource;

public class SentinelDataSourceHandler implements SmartInitializingSingleton {

    private static final Logger LOG = SmartSlf4jLoggerFactory.getLogger(SentinelDataSourceHandler.class);

    private List dataTypeList = Arrays.asList("json", "xml");

    private final String DATA_TYPE_FIELD = "dataType";

    private final String CUSTOM_DATA_TYPE = "custom";

    private final String CONVERTER_CLASS_FIELD = "converterClass";

    private final DefaultListableBeanFactory beanFactory;

    private final SentinelConfigInfo sentinelConfigInfo;

    private final Environment env;

    public SentinelDataSourceHandler(DefaultListableBeanFactory beanFactory, SentinelConfigInfo sentinelConfigInfo,
        Environment env) {
        this.beanFactory = beanFactory;
        this.sentinelConfigInfo = sentinelConfigInfo;
        this.env = env;
    }

    @Override
    public void afterSingletonsInstantiated() {
        sentinelConfigInfo.getDatasource().forEach((dataSourceConfigName, dataSourceConfigInfo) -> {
            try {
                List validFields = dataSourceConfigInfo.getValidField();
                if (validFields.size() != 1) {
                    LOG.error("[Sentinel Starter] DataSource {} multi datasource active and won't loaded: {} .",
                        dataSourceConfigName, dataSourceConfigInfo.getValidField());
                    return;
                }
                AbstractDataSourceInfo abstractSentinelDataSourceInfo = dataSourceConfigInfo.getValidDataSourceInfo();
                /*abstractSentinelDataSourceInfo 为 'SentinelConfigInfo.datasource'的Map中 的value,即 AbstractSentinelDataSourceInfo的各个实现类。*/
                abstractSentinelDataSourceInfo.setEnvironment(env);
                abstractSentinelDataSourceInfo.preCheck(dataSourceConfigName);
                /*格式为 “{SentinelConfigInfo.datasource的Map key}-sentinel-{file|apollo|nacos|redis}-datasource” 。*/
                String dataSourceBeanName = String.format(SentinelDataSourceConstants.DATASOURCE_BEAN_NAME_TEMPLATE,
                    dataSourceConfigName, validFields.get(0));
                registerBean(dataSourceConfigName, abstractSentinelDataSourceInfo, dataSourceBeanName);
            } catch (Exception e) {
                LOG.error(String.format("[Sentinel Starter] DataSource %s build error: %s", dataSourceConfigName, e.getMessage()), e);
            }
        });

    }

    private void registerBean(final String dataSourceConfigName, final AbstractDataSourceInfo dataSourceInfo, String dataSourceName) {
        Map propertyMap =
            Arrays.stream(dataSourceInfo.getClass().getDeclaredFields()).collect(HashMap::new, (m, v) -> {
                try {
                    v.setAccessible(true);
                    m.put(v.getName(), v.get(dataSourceInfo));
                } catch (IllegalAccessException e) {
                    String error = String.format("[Sentinel Starter] DataSource %s field: %s invoke error", dataSourceName, v.getName());
                    LOG.error(error);
                    throw new RuntimeException(error, e);
                }
            }, HashMap::putAll);
        propertyMap.put(CONVERTER_CLASS_FIELD, dataSourceInfo.getConverterClass());
        propertyMap.put(DATA_TYPE_FIELD, dataSourceInfo.getDataType());

        BeanDefinitionBuilder builder =
            BeanDefinitionBuilder.genericBeanDefinition(dataSourceInfo.getFactoryBeanName());

        propertyMap.forEach((propertyName, propertyValue) -> {
            Field field = Reflections.findField(dataSourceInfo.getClass(), propertyName);
            if (null == field) {
                return;
            }
            if (DATA_TYPE_FIELD.equals(propertyName)) {
                String dataType = Strings.trimAllWhitespace(propertyValue.toString());
                if (CUSTOM_DATA_TYPE.equals(dataType)) {
                    try {
                        if (Strings.isEmpty(dataSourceInfo.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-" + dataSourceInfo.getConverterClass();
                        if (!this.beanFactory.containsBean(customConvertBeanName)) {
                            this.beanFactory.registerBeanDefinition(customConvertBeanName,
                                BeanDefinitionBuilder
                                    .genericBeanDefinition(Class.forName(dataSourceInfo.getConverterClass()))
                                    .getBeanDefinition());
                        }
                        builder.addPropertyReference("converter", customConvertBeanName);
                    } catch (ClassNotFoundException e) {
                        LOG.error("[Sentinel Starter] DataSource " + dataSourceName + " handle "
                            + dataSourceInfo.getClass().getSimpleName() + " error, class name: "
                            + dataSourceInfo.getConverterClass());
                        throw new RuntimeException("[Sentinel Starter] DataSource " + dataSourceName + " handle "
                            + dataSourceInfo.getClass().getSimpleName() + " error, class name: "
                            + dataSourceInfo.getConverterClass(), e);
                    }
                } else {
                    if (!dataTypeList.contains(Strings.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}-{ruleType}-converter'*/
                    builder.addPropertyReference("converter", "sentinel-" + propertyValue.toString() + "-"
                        + dataSourceInfo.getRule().getName() + "-converter");
                }
            } else if (CONVERTER_CLASS_FIELD.equals(propertyName)) {
                return;
            } else {
                /*Wired properties.*/
                Optional.ofNullable(propertyValue).ifPresent(v -> builder.addPropertyValue(propertyName, v));
            }
        });

        this.beanFactory.registerBeanDefinition(dataSourceName, builder.getBeanDefinition());
        /*Init in Spring.*/
        AbstractDataSource newDataSource = (AbstractDataSource)this.beanFactory.getBean(dataSourceName);

        /*Register property in RuleManager.*/
        dataSourceInfo.postRegister(newDataSource);

        /*Add dataSouece bean to Context.*/
        SentinelDataSourcesContext.addDataSource(dataSourceConfigName, dataSourceInfo, dataSourceName, newDataSource);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy