Using env variable in Spring Boot's application.properties – Dev

The best answers to the question “Using env variable in Spring Boot's application.properties” in the category Dev.

QUESTION:

We are working on a Spring Boot web application, and the database we are using is MySQL;

  • the setup we have is we first test it locally (means we need to install MySQL on our PC);

  • then we push to Bitbucket;

  • Jenkins automatically detects the new push to Bitbucket and does a build on it (for Jenkins mvn build to pass we also need to install MySQL on the virtual machines that is running Jenkins).

  • if Jenkins build passes we push the code to our application on OpenShift (using the Openshift deployment plugin on Jenkins).

The problem we have, as you may have already figured it out, is that:

  • in application.properties we can not hard code the MySQL info. Since our project will be running in 3 different places (local, Jenkins, and OpenShift), we need to make the datasource field dynamic in application.properties (we know there are different ways of doing it but we are working on this solution for now).

      spring.datasource.url = 
      spring.datasource.username = 
      spring.datasource.password = 
    

The solution we came up with is we create system environment variables locally and in the Jenkins VM (naming them the same way OpenShift names them), and assigning them the right values respectively:

export OPENSHIFT_MYSQL_DB_HOST="jdbc:mysql://localhost"
export OPENSHIFT_MYSQL_DB_PORT="3306"
export OPENSHIFT_MYSQL_DB_USERNAME="root"
export OPENSHIFT_MYSQL_DB_PASSWORD="123asd"

We have done this and it works. We have also checked with Map<String, String> env = System.getenv(); that the environment variables can be made into java variables as such:

String password = env.get("OPENSHIFT_MYSQL_DB_PASSWORD");   
String userName = env.get("OPENSHIFT_MYSQL_DB_USERNAME");   
String sqlURL = env.get("OPENSHIFT_MYSQL_DB_HOST"); 
String sqlPort = env.get("OPENSHIFT_MYSQL_DB_PORT");

Now the only thing left is we need to use these java variables in our application.properties, and that is what we are having trouble with.

In which folder, and how, do we need to assign the password, userName, sqlURL, and sqlPort variables for application.properties to be able to see them and how do we include them in application.properties?

We have tried many things one of them being:

spring.datasource.url = ${sqlURL}:${sqlPort}/"nameofDB"
spring.datasource.username = ${userName}
spring.datasource.password = ${password}

No luck so far. We are probably not putting these environment variables in the right class/folder or are using them incorrectly in application.properties.

ANSWER:

The easiest way to have different configurations
for different environments is to use spring profiles.
See externalised configuration.

This gives you a lot of flexibility.
I am using it in my projects and it is extremely helpful.
In your case you would have 3 profiles:
‘local’, ‘jenkins’, and ‘openshift’

You then have 3 profile specific property files:
application-local.properties,
application-jenkins.properties,
and application-openshift.properties

There you can set the properties for the regarding environment.
When you run the app you have to specify the profile to activate like this:
-Dspring.profiles.active=jenkins

Edit

According to the spring doc you can set the system environment variable
SPRING_PROFILES_ACTIVE to activate profiles and don’t need
to pass it as a parameter.

is there any way to pass active profile option for web app at run time ?

No.
Spring determines the active profiles as one of the first steps,
when building the application context.
The active profiles are then used to decide which property files are read and which beans are instantiated.
Once the application is started this cannot be changed.

ANSWER:

You don’t need to use java variables. To include system env variables add the following to your application.properties file:

spring.datasource.url = ${OPENSHIFT_MYSQL_DB_HOST}:${OPENSHIFT_MYSQL_DB_PORT}/"nameofDB"
spring.datasource.username = ${OPENSHIFT_MYSQL_DB_USERNAME}
spring.datasource.password = ${OPENSHIFT_MYSQL_DB_PASSWORD}

But the way suggested by @Stefan Isele is more preferable, because in this case you have to declare just one env variable: spring.profiles.active. Spring will read the appropriate property file automatically by application-{profile-name}.properties template.

ANSWER:

This is in response to a number of comments as my reputation isn’t high enough to comment directly.

You can specify the profile at runtime as long as the application context has not yet been loaded.

// Previous answers incorrectly used "spring.active.profiles" instead of
// "spring.profiles.active" (as noted in the comments).
// Use AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME to avoid this mistake.

System.setProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, environment);
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/META-INF/spring/applicationContext.xml");

ANSWER:

Flyway doesn’t recognize the direct environment variables into the application.properties (Spring-Boot V2.1).
e.g

spring.datasource.url=jdbc:mysql://${DB_HOSTNAME}:${DB_PORT}/${DB_DATABASE}
spring.datasource.username=${DB_USER}
spring.datasource.password=${DB_PASS}

To solve this issue I did this environment variables, usually I create the file .env:

SPRING_DATASOURCE_URL=jdbc:mysql://127.0.0.1:3306/place
SPRING_DATASOURCE_USERNAME=root
SPRING_DATASOURCE_PASSWORD=root

And export the variables to my environment:

export $(cat .env | xargs)

And finally just run the command

mvn spring-boot:run

Or run your jar file

java -jar target/your-file.jar

There another approach here: https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/maven-plugin/examples/run-env-variables.html