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

org.zodiac.autoconfigure.context.AppRefreshAutoConfiguration Maven / Gradle / Ivy

There is a newer version: 1.6.8
Show newest version
package org.zodiac.autoconfigure.context;

import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.PostConstruct;

import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
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.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.weaving.LoadTimeWeaverAware;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.instrument.classloading.LoadTimeWeaver;
import org.springframework.stereotype.Component;
import org.zodiac.commons.constants.Constants;
import org.zodiac.commons.constants.SystemPropertiesConstants;
import org.zodiac.commons.util.Colls;
import org.zodiac.commons.util.lang.Strings;
import org.zodiac.core.context.refresh.AppContextRefreshHistory;
import org.zodiac.core.context.refresh.AppContextRefreshListener;
import org.zodiac.core.context.refresh.AppContextRefresher;
import org.zodiac.core.event.context.AppRefreshEventListener;
import org.zodiac.core.logging.AppLoggingRebinder;

@SpringBootConfiguration
@ConditionalOnClass(value = {org.zodiac.core.context.scope.refresh.AppRefreshScope.class})
@ConditionalOnProperty(name = SystemPropertiesConstants.Zodiac.SPRING_BOOTSTRAP_REFRESH_SCOPE_ENABLED, havingValue = "true", matchIfMissing = true)
@AutoConfigureBefore(value = {HibernateJpaAutoConfiguration.class})
public class AppRefreshAutoConfiguration {

    public AppRefreshAutoConfiguration() {
        super();
    }

    @Bean
    @ConditionalOnMissingBean
    @ConfigurationProperties(prefix = SystemPropertiesConstants.Zodiac.SPRING_BOOTSTRAP_REFRESH_SCOPE_PREFIX, ignoreInvalidFields = true)
    protected AppRefreshProperties appRefreshProperties() {
        return new AppRefreshProperties();
    }

    @Bean
    @ConditionalOnMissingBean(value = {org.zodiac.core.context.scope.refresh.AppRefreshScope.class})
    protected static org.zodiac.core.context.scope.refresh.AppRefreshScope appRefreshScope(AppRefreshProperties appRefreshProperties) {
        return new org.zodiac.core.context.scope.refresh.AppRefreshScope().setEager(appRefreshProperties.isScopeEager());
    }

    @Bean
    @ConditionalOnMissingBean(value = {AppLoggingRebinder.class})
    protected static AppLoggingRebinder appLoggingRebinder() {
        return new AppLoggingRebinder();
    }

    @Bean
    @ConditionalOnMissingBean(value = {AppContextRefreshHistory.class})
    protected AppContextRefreshHistory appContextRefreshHistory(AppRefreshProperties appRefreshProperties) {
        return new AppContextRefreshHistory().setMaxSize(appRefreshProperties.getHistoryMaxSize());
    }

    @Bean
    @ConditionalOnMissingBean(value = {AppContextRefresher.class})
    protected AppContextRefresher appContextRefresher(ConfigurableApplicationContext context,
        org.zodiac.core.context.scope.refresh.AppRefreshScope scope,
        AppRefreshProperties appRefreshProperties, AppContextRefreshHistory appContextRefreshHistory,
        ObjectProvider listenersProvider) {
        return new AppContextRefresher(context, scope, appRefreshProperties, appContextRefreshHistory, listenersProvider.stream().collect(Collectors.toList()));
    }

    @Bean
    protected AppRefreshEventListener appRefreshEventListener(AppContextRefresher contextRefresher) {
        return new AppRefreshEventListener(contextRefresher);
    }

    @SpringBootConfiguration
    @ConditionalOnClass(name = {"javax.persistence.EntityManagerFactory"})
    protected static class AppJpaInvokerConfiguration implements LoadTimeWeaverAware {

        // @Autowired
        private ListableBeanFactory beanFactory;

//        public JpaInvokerConfiguration(ListableBeanFactory beanFactory) {
//            this.beanFactory = beanFactory;
//        }

        public AppJpaInvokerConfiguration(ListableBeanFactory beanFactory) {
           this.beanFactory = beanFactory;
        }

        @PostConstruct
        public void init() {
            String cls = "org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker";
            if (this.beanFactory.containsBean(cls)) {
                this.beanFactory.getBean(cls);
            }
        }

        @Override
        public void setLoadTimeWeaver(LoadTimeWeaver ltw) {}

    }

    @Component
    protected static class AppRefreshScopeBeanDefinitionEnhancer
        implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {

        private Environment environment;

        private boolean bound = false;

        /**
         * Class names for beans to post process into refresh scope. Useful when you don't control the bean definition
         * (e.g. it came from auto-configuration).
         */
        private Set refreshables = Colls.set("com.zaxxer.hikari.HikariDataSource");

        public AppRefreshScopeBeanDefinitionEnhancer() {
            super();
        }

        @Override
        public void setEnvironment(Environment environment) {
            this.environment = environment;
        }

        public Set getRefreshable() {
            return this.refreshables;
        }

        public void setRefreshable(Set refreshables) {
            if (this.refreshables != refreshables) {
                this.refreshables.clear();
                this.refreshables.addAll(refreshables);
            }
        }

        public void setExtraRefreshable(Set refreshables) {
            this.refreshables.addAll(refreshables);
        }

        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}

        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            bindEnvironmentIfNeeded(registry);
            for (String name : registry.getBeanDefinitionNames()) {
                BeanDefinition definition = registry.getBeanDefinition(name);
                if (isApplicable(registry, name, definition)) {
                    BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, name);
                    BeanDefinitionHolder proxy = ScopedProxyUtils.createScopedProxy(holder, registry, true);
                    definition.setScope(Constants.Zodiac.BOOTSTRAP_REFRESH_SCOPE_NAME);
                    if (registry.containsBeanDefinition(proxy.getBeanName())) {
                        registry.removeBeanDefinition(proxy.getBeanName());
                    }
                    registry.registerBeanDefinition(proxy.getBeanName(), proxy.getBeanDefinition());
                }
            }
        }

        private boolean isApplicable(BeanDefinitionRegistry registry, String name, BeanDefinition definition) {
            String scope = definition.getScope();
            if (Constants.Zodiac.BOOTSTRAP_REFRESH_SCOPE_NAME.equals(scope)) {
                /*Already refresh scoped*/
                return false;
            }
            String type = definition.getBeanClassName();
            if (!Strings.hasText(type) && registry instanceof BeanFactory) {
                Class cls = ((BeanFactory)registry).getType(name);
                if (cls != null) {
                    type = cls.getName();
                }
            }
            if (type != null) {
                return this.refreshables.contains(type);
            }
            return false;
        }

        private void bindEnvironmentIfNeeded(BeanDefinitionRegistry registry) {
            if (!this.bound) {
                /*only bind once*/
                if (this.environment == null) {
                    this.environment = new StandardEnvironment();
                }
                Binder.get(this.environment).bind(
                    SystemPropertiesConstants.Zodiac.SPRING_BOOTSTRAP_REFRESH_SCOPE_PREFIX, Bindable.ofInstance(this));
                this.bound = true;
            }
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy