Home > database >  mutliple application.yaml in config directory of mutliple libraries
mutliple application.yaml in config directory of mutliple libraries

Time:05-20

I'm developing multiple tiny libraries (external jars) and I need to have them add their application.yml so that they can get discovered by 1 spring boot having them as dependencies. This worked fine for the first library, but it seems that as soon as I have more than 1, the 2nd one is ignored (spring boot overwrites the first one because of classpath collision?).

What's the proper way to do this?

CodePudding user response:

I suggest you rename them to be more specific like abc.yml and abc-prod.yml (for all your stages). You could then create an EnvironmentPostProcessor that reads your YAML files and adds all properties to the context. This is a working example for an EnvironmentPostProcessor that does this exact thing:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;
import java.util.Arrays;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;

import static java.util.function.Predicate.not;

public class MyPropertiesEnvironmentPostProcessor implements EnvironmentPostProcessor {

    private static final Set<String> KNOWN_STAGES = Set.of("local", "dev", "ref", "abn", "prod");
    private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        Stream.concat(Arrays.stream(environment.getActiveProfiles())
                                .map(String::toLowerCase)
                                .filter(KNOWN_STAGES::contains),
                        Stream.of(""))
                .map(String::toLowerCase)
                .distinct()
                .map(this::loadYaml)
                .forEach(environment.getPropertySources()::addLast);
    }

    private PropertySource<?> loadYaml(final String stage) {
        final String stageSuffix = Optional.ofNullable(stage).filter(not(String::isEmpty)).map("-"::concat).orElse("");
        final ClassPathResource configFile = new ClassPathResource(String.format("abc%s.yml", stageSuffix));
        if (!configFile.exists())
            throw new IllegalArgumentException(String.format("Config file %s does not exist", configFile));
        try {
            return loader.load(String.format("abc%s", stageSuffix), configFile).get(0);
        } catch (IOException e) {
            throw new IllegalStateException(String.format("Failed to load %s", configFile), e);
        }
    }

}

Note that the properties are added with the addLast method. You could also use addFirst depending on whether you want your properties to be able to be overwritten by the Spring Boot application using your library or whether you want to override application.yml values of the Spring Boot application.

Make sure to create a spring.factories file in the src/main/resources/META-INF directory of your library where you register your EnvironmentPostProcessor as well so that the Spring Boot application using your library knows that this EnvironmentPostProcessor needs to be called:

org.springframework.boot.env.EnvironmentPostProcessor=\
com.myproject.MyPropertiesEnvironmentPostProcessor

This Baeldung article is also a good read.

  • Related