com.careerly.common.support.ReloadablePropertySourcesPlaceholderConfigurer.java Source code

Java tutorial

Introduction

Here is the source code for com.careerly.common.support.ReloadablePropertySourcesPlaceholderConfigurer.java

Source

/*
 * Copyright 2014 Qunar.com All right reserved. This software is the
 * confidential and proprietary information of Qunar.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Qunar.com.
 */
package com.careerly.common.support;

import com.careerly.common.thread.NamedThreadFactory;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.*;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.StringValueResolver;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * ???????reload
 *
 * @author chaoyi.he
 * @version v1.0.0
 * @see 
 * @since 201416 ?2:19:43
 */
public class ReloadablePropertySourcesPlaceholderConfigurer extends PropertySourcesPlaceholderConfigurer
        implements BeanFactoryAware, InitializingBean, DisposableBean, Runnable {

    private static final Logger logger = LoggerFactory
            .getLogger(ReloadablePropertySourcesPlaceholderConfigurer.class);
    private AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor;
    private ConfigurableListableBeanFactory beanFactory;
    private PropertyStringValueResolver valueResolver;
    private Resource[] locations;
    private Map<Resource, Long> lastModifiedResources = Maps.newHashMap();
    private List<String> beanNames = Lists.newArrayList();
    private ScheduledExecutorService scheduler;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
    }

    @Override
    public void setLocations(Resource[] locations) {
        super.setLocations(locations);
        this.locations = locations;
    }

    public void setAutowiredAnnotationBeanPostProcessor(
            AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor) {
        this.autowiredAnnotationBeanPostProcessor = autowiredAnnotationBeanPostProcessor;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notEmpty(locations, "properties reload, locations not given");
        // ??
        for (Resource resource : locations) {
            long modified = resource.lastModified();
            String modifiedTime = new DateTime(modified).toString("yyyy-MM-dd HH:mm:ss");
            logger.info("properties monitor, file: {}, modified: {}", resource.getFile().getPath(), modifiedTime);
            lastModifiedResources.put(resource, modified);
        }
        // ??Constant?
        for (String beanName : beanFactory.getBeanDefinitionNames()) {
            if (beanName.endsWith("Constant")) {
                logger.info("properties monitor, bean: {}", beanName);
                beanNames.add(beanName);
            }
        }
        // ??10??JDK7?nio2 Watch Service
        scheduler = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("properties-reload"));
        scheduler.scheduleWithFixedDelay(this, 120L, 10L, TimeUnit.SECONDS);
    }

    @Override
    public void destroy() throws Exception {
        scheduler.shutdown();
    }

    @Override
    public void run() {
        try {
            boolean changed = false;
            for (Resource resource : lastModifiedResources.keySet()) {
                long lastModified = lastModifiedResources.get(resource);
                long modified = resource.lastModified();
                if (modified > lastModified) {
                    String modifiedTime = new DateTime(modified).toString("yyyy-MM-dd HH:mm:ss");
                    logger.info("properties changed, file: {}, modified: {}", resource.getFile().getPath(),
                            modifiedTime);
                    changed = true;
                    lastModifiedResources.put(resource, modified);
                }
            }
            // ?????
            if (changed) {
                reload();
            }
        } catch (IOException e) {
            logger.error("properties reload failure", e);
        }
    }

    private void reload() throws IOException {
        logger.info("properties changed, reloading..");

        // Properties?
        Properties properties = super.mergeProperties();
        MutablePropertySources propertySources = new MutablePropertySources();
        propertySources.addLast(new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, properties));
        ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(propertySources);
        // BeanFactory??removeEmbeddedValueResolver??Resolver?
        valueResolver.expire();
        valueResolver = new PropertyStringValueResolver(propertyResolver);
        beanFactory.addEmbeddedValueResolver(valueResolver);

        // ??
        for (String beanName : beanNames) {
            Object bean = beanFactory.getBean(beanName);
            autowiredAnnotationBeanPostProcessor.processInjection(bean);
        }
    }

    @Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
            ConfigurablePropertyResolver propertyResolver) throws BeansException {
        // ?ValueResolver?expire?
        valueResolver = new PropertyStringValueResolver(propertyResolver);
        doProcessProperties(beanFactoryToProcess, valueResolver);
    }

    private class PropertyStringValueResolver implements StringValueResolver {

        private PropertyResolver propertyResolver;
        private boolean expired = false;

        public PropertyStringValueResolver(PropertyResolver propertyResolver) {
            this.propertyResolver = propertyResolver;
        }

        @Override
        public String resolveStringValue(String strVal) {
            if (expired) {
                return strVal;
            }
            String resolved = ignoreUnresolvablePlaceholders ? propertyResolver.resolvePlaceholders(strVal)
                    : propertyResolver.resolveRequiredPlaceholders(strVal);
            return (resolved.equals(nullValue) ? null : resolved);
        }

        public void expire() {
            expired = true;
        }

    }

}