1

I work on a SpringBoot application which has to run on different environments. The property files are created and once I modify the environment the default values are overridden with the proper ones. That's okay.

In the next step I want to check whether the logged in user System.getProperty("user.name") has a custom property file. If so, these properties must be overriden with his ones. So the steps should be (let's say the active profile is dev):

  1. Load application.properties
  2. Load and override properties from application-dev.properties
  3. In case the user has a custom property file (user.properties), load this and override the properties

I read many topcis and found two possible solutions, but none of them worked.

  1. Add the annotation @PropertySource("user.properties") to a configuration class, which should load the user specific property file and override the values. For testing purposes, I added server.port=1234 to user.properties, but this was ignored.
  2. Create a custom PropertyPlaceholderConfigurer. Although this bean was created successfully, the server port wasn't changed.

`

@Bean
public PropertyPlaceholderConfigurer propertyPlaceholder() {
    PropertyPlaceholderConfigurer propertyPlaceholder = new PropertyPlaceholderConfigurer();
    propertyPlaceholder.setLocations(
            new ClassPathResource("application.properties"),
            new ClassPathResource("application-dev.properties"),
            new ClassPathResource("user.properties"));

    propertyPlaceholder.setIgnoreResourceNotFound(true);
    propertyPlaceholder.setIgnoreUnresolvablePlaceholders(true);
    return propertyPlaceholder;
}

I don't know how to go forward. So any idea is really welcomed.

Update: I've justed pushed the demo code to GitHub. Maybe is helps to find the solution: https://github.com/aszidien/springboot.

Dimnox
  • 441
  • 1
  • 6
  • 13
  • `System.getProperty("user.name")` will not looking for currently logged in user,This will look for `user.name` key currently activated profile.So pls make clear your question first. – TheCurious Aug 16 '17 at 19:03
  • As for my understanding what you are looking for say john is currently logged in user so you want to override keys in `application-profile.property` by `john.properties` if `john.properties` exists? Is this your requirement? – TheCurious Aug 16 '17 at 19:07
  • Sorry for misunderstanding, your supposition is correct. That's the requirement. – Dimnox Aug 16 '17 at 19:23
  • Please look into [this](https://stackoverflow.com/questions/25855795/spring-boot-and-multiple-external-configuration-files) ,or try to set priority on it.Lets try. – TheCurious Aug 16 '17 at 19:28
  • Thank for the suggestion, I already tried playing with setOrder, setSystemPropertiesMode and setLocalOverride methods. But none of them helped. In the meantime, I pushed the demo code to GitHub. Maybe it helps to find the solution: https://github.com/aszidien/springboot. – Dimnox Aug 16 '17 at 20:11

1 Answers1

1

Correct way to customise environment in Spring Boot is with an EnvironmentPostProcessor that will run very early in ApplicationContext start-up and allow you to manage the property sources.

Step 1. Create file src/main/resources/META-INF/spring.factories with the following:

org.springframework.boot.env.EnvironmentPostProcessor=\
com.example.YourEnvironmentPostProcessor

Step 2. As an example create a file src/main/resources/custom.properties with:

server.port=8081

Step 3. Now create you post processor class

package com.example;

public class EnvironmentPostProcessorExample implements EnvironmentPostProcessor {

  private final PropertiesPropertySourceLoader loader = new PropertiesPropertySourceLoader();

  @Override
  public void postProcessEnvironment(ConfigurableEnvironment environment,
                                     SpringApplication application) {
    Resource path = new ClassPathResource("custom.properties");
    // ^^^ here you can create the resource however you want
    // construct the name from a user name, use FileSystemResource, anything
    // for example you can ask users to place a file in their home 
    // directory named "my-application.properties" and load it like so

    // Resource path = new FileSystemResource(Paths.get(System.getProperty("user.home"),"my-application.properties").toString());

    PropertySource<?> propertySource = loadProps(path);
    environment.getPropertySources().addFirst(propertySource);
  }

  private PropertySource<?> loadProps(Resource path) {
    if (!path.exists()) {
      throw new IllegalArgumentException("Resource " + path + " does not exist");
    }
    try {
      return this.loader.load("custom-resource", path, null);
    }
    catch (IOException ex) {
      throw new IllegalStateException(
          "Failed to load props configuration from " + path, ex);
    }
  }

}

Now when you run your application the port will change to 8081 and any other properties will override the defaults provided in your main properties.

Strelok
  • 50,229
  • 9
  • 102
  • 115
  • Thank you for the detailed answer. I created both spring.factories and CustomEnvironmentPostProcessor. I see the method postProcessEnvironment was executed and loaded the properties, but for some reason the server.port is still not overriden. I updated the [GitHub repo](https://github.com/aszidien/springboot). Could you have a look at this? – Dimnox Aug 17 '17 at 03:21
  • @aszidien but you named your file `user.properties`, shouldn't you name it `your-actual-username.properties`? Since you're using the `user.name` system prop to build the file name? – Strelok Aug 17 '17 at 04:09
  • I modified `user.properties` to `my-current-username.properties` locally. If I debug `postProcessEnvironment`, I can see the key `server.port` with value `6789`. So this part of the code seems working fine. I think `server.port` cannot be overriden in this way. Somehow I should add this property to the `applicationConfigurationProperties - environment.getPropertySources().get("applicationConfigurationProperties")`, instead of `custom-resource`. Since this list contains the Spring profiles (default and dev). But I don't know the exact way yet how to do it. – Dimnox Aug 17 '17 at 07:02
  • @aszidien ok I finally had a chance to run your project. You need to use `addFirst` instead of `addLast` when adding the new property source. When I tested the answer in my project I had to use `addLast`. I updated my answer. – Strelok Aug 17 '17 at 11:51
  • Thanks a lot for your time and help, you saved my day. It works fine now. – Dimnox Aug 17 '17 at 14:02