Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to override profile-specific properties with a different profile?

Tags:

spring-boot

I currently have the following config setup in spring boot:

application.properties

app.database.host=${DB_HOST}
app.database.port=${DB_PORT}
app.database.name=${DB_NAME}
app.database.user=${DB_USER}
app.database.password=${DB_PASSWORD}
app.database.schema=${DB_SCHEMA:public}
spring.datasource.url=jdbc:postgresql://${app.database.host}:${app.database.port}/${app.database.name}
spring.datasource.username=${app.database.user}
spring.datasource.password=${app.database.password}
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect

application-local-dev.properties:

app.database.host=${DB_HOST:localhost}
app.database.port=${DB_PORT:5432}
app.database.name=${DB_NAME:db_name}
app.database.user=${DB_USER:root}
app.database.password=${DB_PASSWORD:root}
app.database.schema=${DB_SCHEMA:public}

application-load-fixtures.properties:

spring.profiles.include=local-dev
spring.profiles.active=load-fixtures,local-dev
app.database.name=${DB_NAME:db_name}_fixtures

The idea here is that when starting the app in default mode, it will fail to boot when critical properties like database name are missing. They should be passed via environment variables.

For development purposes, this is unnecessary overhead when setting up the project because we have a docker container with static credentials and I'd like to provide them as defaults. Therefore, I created a profile local-dev that will use default values to be able to connect to our docker database and still have the ability to override them via environment variables in case someone needs to. Until here, everything works fine.

But now, we also have a profile that is used to load fixtures into the database (drop all tables, recreate and fill them with data). For obvious reasons, I want to ensure that this cannot be done on an arbitrary database, so I created a profile load-fixtures that should inherit all properties from local-dev and override the database name. However, this approach seems to be wrong. I can see in the spring log that the profiles are loaded properly:

2017-11-16 13:32:11.508  INFO 23943 --- [           main] Main:
The following profiles are active: load-fixtures,local-dev

But it still uses the database name provided by the local-dev profile.

When I remove the line

app.database.name=${DB_NAME:db_name}

from the local-dev config file, it works.

However, what I want to avoid is having to add new properties to both, local-dev and load-fixtures, whenever we add a new configuration property to the project.

I understand that profile specific properties take precedence over non-profile specific ones. And also that non-default location properties take precedence over properties from the default locations. But here, both profiles (local-dev and load-fixtures) are in the same location, and they are also both profile specific.

What are proper ways to go about this problem?

Thanks in advance!

like image 446
Pasukaru Avatar asked Nov 16 '17 12:11

Pasukaru


People also ask

How many profiles can be defined for a spring application?

In the following application, we have three profiles (local, dev, prod) and two profile-specific property files. We use the spring. profiles. active to set active profiles and SpringApplicationBuilder's profiles method to add new active profiles.

How do I change app properties?

To edit the properties of an app, click the gear icon next to the app name in the app design page and select Properties.


1 Answers

I recently came across quite the same problem and had to figure out which precedence Spring applies to several profile specific property files. Unfortunately this is not well documented and I did not find the location of the code that is responsible for that.

However after some tests and tries I'm pretty sure it works like this (or at least in a similar way): Probably some kind of map is used to gather up all properties of all the different places and possibilites where you could define them like documented here. So for example a property my.value is defined in application.properties and so stored in the mentioned map. Then the same property is found as Java system property. Since this way of defining a property is higher in the PropertySource-order it will override the value found before in the map. Until here it is clear according to the documentation that the Java system property will win.

But as we come to two different sources on the same precedence level like two different profile specific property files the documentation is not a 100% clear in my opinion. However it says in 24.4:

If several profiles are specified, a last-wins strategy applies. For example, profiles specified by the spring.profiles.active property are added after those configured through the SpringApplication API and therefore take precedence.

Maybe it is just the example that is not optimal here or I just do not understand it correctly. But I guess the "last-wins" strategy also applies to all profiles defined for example in spring.profiles.active. That means if you run java -jar -Dspring.profiles.active=dev,fix application.jar, the properties in application-fix.properties will overwrite the values of properties having the same key in application-dev.properties.

So in your case considering the output of your application I guess you specified something like java -jar -Dspring.profiles.active=load-fixtures,local-dev application.jar. If I was correct, you would just have to change that into java -jar -Dspring.profiles.active=local-dev,load-fixtures application.jar.

like image 161
Fencer Avatar answered Sep 28 '22 07:09

Fencer