4

Is it possible to load multiple Spring-Boot .yml config files from a config folder within parent module of a multi-module project?

So, structure looks like this:

parent-module/
  pom.xml
  config/
    application-prd.yml
    application-dev.yml
  module1
    pom.xml
    src/main/resources/
      logback-spring.xml
      bootstrap.yml

Is this possible? How can it be done?

So, if I execute from root folder of multi-module project, I would use this command:

mvn -pl module1 spring-boot:run
   OR
mvn spring-boot:run

And I would hope that the config folder would be included in the classpath? I am trying to do this but not getting it to work. Am I missing something?

We know this to be true: Child POMs inherit properties, dependencies, and plugin configurations from the parent. But shouldn't that mean that {parent}/config/application.yml is in the classpath already?

Example project to use for proving: https://github.com/djangofan/spring-boot-maven-multi-module-example . Clone it and modify if you think you can solve it.

djangofan
  • 28,471
  • 61
  • 196
  • 289
  • 1
    what if you copy the files from `parent` to `module1` using `maven-resources-plugin` at `package` phase ? – eHayik Feb 05 '21 at 19:55

3 Answers3

1

Yes, it is possible.

Maven changes the working directory to the module's directory when you use the -pl. So it is no longer the root which has the config/ dir.

Either you can refactor your maven multi-module setup to a way that you can package the common application.yml files and than use them. I wouldn't recommend that as it has many pitfalls.

Probably easier to use the --spring.config-location

$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties

I can not test it at the moment but if it doesn't work you can always try the -Dspring.config.location or the SPRING_CONFIG_LOCATION environment variable.

Gebezs
  • 696
  • 3
  • 9
  • Looking for an answer that doesn't require overriding the classpath in any way. Simplest possible command line. I've seen it done before, but I just don't remember how they did it. – djangofan Feb 02 '21 at 01:47
  • Defining an environment variable or specifying system variable according my answer does not override classpath. – Gebezs Feb 03 '21 at 07:48
  • My point is that I believe there is a way to do this without specifying spring.config.location on command line. I know because I have seen it work; I just don't know the specifics. I'll work on making a Github example project and if I can solve it on my own, ill share. – djangofan Feb 03 '21 at 18:13
  • The question could be where did you seen that working. Afaik intelij idea does that by default. – bilak Feb 07 '21 at 20:21
  • Discovered that passing `workingDirectory` arg to `spring-boot-maven-plugin` fixed my issue. Spring now auto-scans the `/config` folder from where I set working directory. – djangofan Feb 10 '21 at 19:02
  • I'm a bit puzzled as to why that would be simpler than just setting spring config location, which is pretty much the standard way of doing it @djangofan – eis Feb 10 '21 at 21:38
1

I found a customized way of doing it, which I still consider to be a hack, as you can see below, but I am still looking for an actual answer to my question, if it exists.

@Slf4j
@SpringBootApplication
public class Application {

    private static String DEV_PROPS = "application-dev.properties";
    private static String PRD_PROPS = "application-prd.properties";
    private static String DEFAULT_PROPS = "application.properties";

    private static Path CONFIG = Paths.get(System.getProperty("user.dir"))
            .resolve(Paths.get(".."))
            .resolve("config");

    private static final String EXT_CONFIG_DIR = CONFIG.toString() + File.separator;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        PropertySourcesPlaceholderConfigurer properties = new PropertySourcesPlaceholderConfigurer();
        Resource[] resources = new Resource[] {
                        new FileSystemResource(EXT_CONFIG_DIR + PRD_PROPS),
                        new FileSystemResource(EXT_CONFIG_DIR + DEV_PROPS),
                        new ClassPathResource(DEFAULT_PROPS)
                };
        log.info("Properties: " + Arrays.deepToString(resources));
        properties.setIgnoreResourceNotFound(true);
        properties.setLocations(resources);
        return properties;
    }

}

The problem with this method, is that configuration has to be changed to support additional environment profile names.

djangofan
  • 28,471
  • 61
  • 196
  • 289
1

No need to write code. You can use Exec Maven Plugin for this. Add the plugin to the parent module:

<build>
  <plugins>
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>exec-maven-plugin</artifactId>
      <version>1.6.0</version>
      <configuration>
        <mainClass>PACKAGE.MODULE_MAIN_CLASS</mainClass>
        <arguments>
          <argument>--spring.profiles.active=dev</argument>
        </arguments>
      </configuration>
    </plugin>
  </plugins>
</build>

then run mvn install once and then mvn exec whenever you want to start the application.

mvn exec:java -pl module1

For more on Maven goals in a multi-module project, check this answer https://stackoverflow.com/a/11091569/512667 .

Another way to configure this is like so, which requires the workingDirectory arg:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <mainClass>com.example.Application</mainClass>
        <workingDirectory>${maven.multiModuleProjectDirectory}</workingDirectory>
        <arguments>
            <argument>--spring.profiles.active=dev</argument>
        </arguments>
    </configuration>
</plugin>

In this case, execute:

mvn spring-boot:run -pl module1
djangofan
  • 28,471
  • 61
  • 196
  • 289
Ahmed Rezk
  • 301
  • 1
  • 4
  • 10
  • Thanks for the suggestion. I tried it and it did not work. Here is the branch where I tried it: https://github.com/djangofan/spring-boot-maven-multi-module-example/blob/d7f537f2bd4937890fb1ee4c30e9f57437edccef/spring-boot-module/pom.xml#L79-L86 – djangofan Feb 08 '21 at 17:04
  • 1
    @djangofan The github URL is not accessible – Ahmed Rezk Feb 08 '21 at 19:52
  • Thanks. Fixed the url. Still getting used to Github defaulting to private. – djangofan Feb 09 '21 at 00:39
  • Alright, so I checked out your branch and did mvn install > mvn exec:java -pl spring-boot-module and it works. I can call your GET /all and I can see 2 JSON objects. Is this what you expect? – Ahmed Rezk Feb 09 '21 at 09:38
  • It just looks like it works. The red-herring is that it starts on the wrong port: 8080 . It's not actually reading the properties file. – djangofan Feb 09 '21 at 19:30
  • 1
    my bad, just append the profile argument to the mvn command: mvn exec:java -pl spring-boot-module -Dspring.profiles.active=dev – Ahmed Rezk Feb 09 '21 at 20:33
  • 1
    Yep, that worked. I think I will award you the bounty. Thank you. Also, the spring "default profile" can be put in a properties file instead of on command line, making the execution simpler. I would rather use spring-boot-maven-plugin but it is nice to know this solution works. – djangofan Feb 10 '21 at 18:03
  • I updated your answer. I hope you don't mind. Just added more info. – djangofan Feb 10 '21 at 19:03
  • Not at all, glad you found the answer you were looking for – Ahmed Rezk Feb 10 '21 at 19:18
  • Yep, you helped me discover the typo in my initial POC. I just needed that `workingDirectory` parameter to `maven-spring-boot-plugin`. Thanks for being my rubber ducky and showing me a 2nd way to do it. – djangofan Feb 10 '21 at 21:26
  • 1
    no problems. Doesn't your rubber ducky deserve your promised bounty? ;-) – Ahmed Rezk Feb 11 '21 at 10:19
  • I'm not totally sure how bounty gets auto-rewarded. If I could just award you myself I would be I think we were in the after bounty expiration period. I'm pretty sure you should have got at least half of it. – djangofan Feb 14 '21 at 21:41
  • Alright, I just thought you forgot to do it. No problems. – Ahmed Rezk Feb 15 '21 at 12:32