Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Environment backed by Typesafe Config

Tags:

I want to use typesafe config (HOCON config files) in my project, which facilitate easy and organized application configuration. Currently I am using normal Java properties file(application.properties) and which is difficult to handle on big project.

My project is a Spring MVC (Not a spring boot project). Is there a way to back my Spring Environment (that I am getting injected to my services) to be backed by typesafe config. Which should not brake my existing Environment usage Like @Value annotation, @Autowired Environment etc.

How can I do this with minimal effort and changes on my code.

This is my current solution: Looking for is there any other better way

@Configuration
public class PropertyLoader{
    private static Logger logger = LoggerFactory.getLogger(PropertyLoader.class);

    @Bean
    @Autowired
    public static PropertySourcesPlaceholderConfigurer properties(Environment env) {
        PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();

        Config conf = ConfigFactory.load();
        conf.resolve();
        TypesafePropertySource propertySource = new TypesafePropertySource("hoconSource", conf);

        ConfigurableEnvironment environment = (StandardEnvironment)env;
        MutablePropertySources propertySources = environment.getPropertySources();
        propertySources.addLast(propertySource);
        pspc.setPropertySources(propertySources);

        return pspc;
    }
}

class TypesafePropertySource extends PropertySource<Config>{
    public TypesafePropertySource(String name, Config source) {
        super(name, source);
    }

    @Override
    public Object getProperty(String name) {
        return this.getSource().getAnyRef(name);
    }
}
like image 607
Ysak Avatar asked Aug 06 '16 10:08

Ysak


People also ask

What is Typesafe configuration?

Typesafe Config allows you to store configuration into separated files and use include keyword to include configuration of those files. Here is a concrete example for myApp which relies on the configuration of moduleA and moduleB .

Does spring provide externalized configuration?

Spring Boot lets you externalize your configuration so that you can work with the same application code in different environments. You can use properties files, YAML files, environment variables, and command-line arguments to externalize configuration.

What is type-safe configuration in spring boot?

Spring Boot offers another approach for managing properties known as type-safe configuration properties. You can create a class to hold all related properties instead of adding a @Value annotation. The @ConstructorBinding annotation tells Spring Boot to use the constructor to inject the properties.

How do I set environment specific properties in spring boot?

Environment-Specific Properties File. If we need to target different environments, there's a built-in mechanism for that in Boot. We can simply define an application-environment. properties file in the src/main/resources directory, and then set a Spring profile with the same environment name.


2 Answers

I think I came up with a slightly more idiomatic way than manually adding the PropertySource to the property sources. Creating a PropertySourceFactory and referencing that with @PropertySource

First, we have a TypesafeConfigPropertySource almost identical to what you have:

public class TypesafeConfigPropertySource extends PropertySource<Config> {
    public TypesafeConfigPropertySource(String name, Config source) {
        super(name, source);
    }

    @Override
    public Object getProperty(String path) {
        if (source.hasPath(path)) {
            return source.getAnyRef(path);
        }
        return null;
    }
}

Next, we create a PropertySource factory that returns that property source

public class TypesafePropertySourceFactory implements PropertySourceFactory {

    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        Config config = ConfigFactory.load(resource.getResource().getFilename()).resolve();

        String safeName = name == null ? "typeSafe" : name;
        return new TypesafeConfigPropertySource(safeName, config);
    }

}

And finally, in our Configuration file, we can just reference the property source like any other PropertySource instead of having to add the PropertySource ourselves:

@Configuration
@PropertySource(factory=TypesafePropertySourceFactory.class, value="someconfig.conf")
public class PropertyLoader {
    // Nothing needed here
}
like image 200
Laplie Anderson Avatar answered Sep 18 '22 17:09

Laplie Anderson


You create a PropertySource class as follows, it is similar to yours with the difference that you have to return the value or null and not let the lib throw a missing exception

public class TypesafeConfigPropertySource extends PropertySource<Config> {

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

    public TypesafeConfigPropertySource(String name, Config source) {
        super(name, source);
    }

    @Override
    public Object getProperty(String name) {
        try {
            return source.getAnyRef(name);
        } catch (ConfigException.Missing missing) {
            LOG.trace("Property requested [{}] is not set", name);
            return null;
        }
    }
}

Second step is to define a bean as follows

    @Bean
    public TypesafeConfigPropertySource provideTypesafeConfigPropertySource(
        ConfigurableEnvironment env) {

        Config conf = ConfigFactory.load().resolve();
        TypesafeConfigPropertySource source = 
                          new TypesafeConfigPropertySource("typeSafe", conf);
        MutablePropertySources sources = env.getPropertySources();
        sources.addFirst(source); // Choose if you want it first or last
        return source;

    }

In cases where you want to autowire properties to other beans you need to use the annotation @DependsOn to the propertysource bean in order to ensure it is first loaded

Hope it helps

like image 36
tbo Avatar answered Sep 19 '22 17:09

tbo