Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I reload properties file in Spring 4 using annotations?

I have a simple application where i am using several properties-files to fetch content edited by other users (links to sites etc).

The class where i load the properties look like this:

@Configuration
@PropertySource("classpath:salestipsWhitelist.properties")
public class SalestipsWhitelist {

@Autowired
Environment env;

public Environment getEnv() {
    return env;
}

public void setEnv(Environment env) {
    this.env = env;
}

@Bean
   public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
      return new PropertySourcesPlaceholderConfigurer();
   }
}

Some of the properties file:

UPS_MPP_M_L=True
UPS_MPP_M_M=True
UPS_MPP_M_MP=True
UPS_MPP_M_S=True

This works fine, but if i make changes to the properties-file, i have to reload the app to visualise any changes made.

Is it possible, if i move the location to disk instead of classpath, to reload this periodically or manually? I do not want this to be done automatically on change, as i want to have control over when this is done / updated.

like image 219
Jørgen Skår Fischer Avatar asked Oct 01 '14 20:10

Jørgen Skår Fischer


People also ask

How do I refresh properties in spring boot?

For Reloading properties, spring cloud has introduced @RefreshScope annotation which can be used for refreshing beans. Spring Actuator provides different endpoints for health, metrics. but spring cloud will add extra end point /refresh to reload all the properties.

How reload properties file without restarting server in spring?

include=refresh: this is actuator property which will help us to refresh the properties without restarting the server.

Do we need to restart server after changing properties file?

As expected, in case of any changes in the property files, we have to restart the weblogic server for the changes to take effect.


2 Answers

This works a treat. Requires Java 7, Apache commons logging, Apache commons lang (v2.6) and Apache commons Configuration:

package corejava.reloadTest;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;

public class MyApplicationProperties {
    private static PropertiesConfiguration configuration = null;

    static {
        try {
            configuration = new PropertiesConfiguration("test.properties");
        } catch (ConfigurationException e) {
            e.printStackTrace();
        }
        configuration.setReloadingStrategy(new FileChangedReloadingStrategy());
    }

    public static synchronized String getProperty(final String key) {
        return (String) configuration.getProperty(key);
    }
}

and test it with:

package corejava.reloadTest;

public class TestReloading {
    public static void main(String[] args) {
        while (true) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(MyApplicationProperties.getProperty("key"));
        }
    }
}

Output when you change test.properties is something like this (original contents of test.props was key=value, later changed to key=value1 mid-program execution):

value
value
value
value
value
Jan 17, 2015 2:05:26 PM org.apache.commons.configuration.PropertiesConfiguration reload
INFO: Reloading configuration. URL is file:/D:/tools/workspace   /AutoReloadConfigUsingApacheCommons/resources/test.properties
value1
value1
value1

You could also consider Official Spring Framework Reference Documentation Refreshable beans , using a DSL like Groovy for this.

like image 113
Mikus111 Avatar answered Oct 11 '22 13:10

Mikus111


the PropertyPlaceholderConfigurer need to be override to reload the new Properties

You need to rewrite processProperties method to make the StringValueResolver which contains the properties became to loadable. This is my code

import java.io.IOException;
import java.util.Properties;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
import org.springframework.util.StringValueResolver;


public class ReloadablePropertyPlaceholderConfigurer 
                                    extends PropertyPlaceholderConfigurer {

    private ReloadablePlaceholderResolvingStringValueResolver reloadableValueResolver;


    public void reloadProperties() throws IOException {
        Properties props = mergeProperties();
        this.reloadableValueResolver.refreshProperties(props);
    }


    @Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
            throws BeansException {
        this.reloadableValueResolver = new ReloadablePlaceholderResolvingStringValueResolver(props);
        StringValueResolver valueResolver = this.reloadableValueResolver;
        this.doProcessProperties(beanFactoryToProcess, valueResolver);
    }


    private class ReloadablePlaceholderResolvingStringValueResolver 
            implements StringValueResolver {

        private final PropertyPlaceholderHelper helper;
        private final ReloadablePropertyPlaceholderConfigurerResolver resolver;

        public ReloadablePlaceholderResolvingStringValueResolver(Properties props) {
            this.helper = new PropertyPlaceholderHelper(placeholderPrefix, placeholderSuffix, valueSeparator, ignoreUnresolvablePlaceholders);
            this.resolver = new ReloadablePropertyPlaceholderConfigurerResolver(props);
        }

        @Override
        public String resolveStringValue(String strVal) throws BeansException {
            String value = this.helper.replacePlaceholders(strVal, this.resolver);
            return (value.equals(nullValue) ? null : value);
        }

        private void refreshProperties(Properties props){
            this.resolver.setProps(props);
        }
    }

    private class ReloadablePropertyPlaceholderConfigurerResolver 
            implements PlaceholderResolver {

        private Properties props;
        private ReloadablePropertyPlaceholderConfigurerResolver(Properties props) {
            this.props = props;
        }

        @Override
        public String resolvePlaceholder(String placeholderName) {
            return ReloadablePropertyPlaceholderConfigurer.this.resolvePlaceholder(placeholderName, props, SYSTEM_PROPERTIES_MODE_FALLBACK);
        }

        public void setProps(Properties props) {
            this.props = props;
        }
   }
}

here is the configure for properties-config.xml .all of those properties can be reload in runtime as a prototype bean.

<bean id="propertyConfigurer" class="com.cn21.mail189.analysis.commons.expand.ReloadablePropertyPlaceholderConfigurer">
    <property name="ignoreUnresolvablePlaceholders" value="true" />
    <property name="locations">
        <list>
            <!-- database config -->
            <value>classpath:spring/dbconfig.properties</value>
            <!-- app config -->
            <value>classpath:spring/app.properties</value>
            <!-- some other config -->
            <value>classpath:xxxx.properties</value>
        </list>
    </property>
</bean>`
like image 27
Nonlone Avatar answered Oct 11 '22 13:10

Nonlone