How to externalize application.yml in spring boot - spring-boot

Hi Currently my project is using application.yml from src/main/resources by default. I want to use the application.yml file in a different location, so as I can edit the properties whenever I want. Please suggest any idea

Spring Boot lets you externalize your configuration so that you can work with the same application code in different environments. You can use properties files, YAML files, environment variables, and command-line arguments to externalize configuration. Property values can be injected directly into your beans by using the #Value annotation, accessed through Spring’s Environment abstraction, or be bound to structured objects through #ConfigurationProperties.
Detailed Information: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-yaml
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
Set JAVA_OPTS environment variable example
-Dspring.profiles.active=dev -Dspring.config.location=file:C:/application-external.yml
This will allow you to have provide multiple profiles inside of a YML file and let spring do the heavy lifting of evaluating the correct properties:
spring:
profiles: dev
someproperty: devproperty
---
spring:
profiles: test
someproperty: testproperty
To use external configuration files in your Maven build : configure the maven surefire plugin like this in your pom.xml:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Dspring.config.location=file:${home}/conf/application-external.yml
</configuration>
</plugin>

Related

Spring Boot Externalize properties from within application.properties

I want to set my property source location to point to an external directory from within application.propoerties itself.
I know passing command line argument as follows does the job.
java -jar myApp.jar --spring.config.location=/Users/tony/Desktop/override.properties
But I want to set this path from within the application itself rather than passing a command-line argument. I tried adding spring.config.location=/Users/tony/Desktop/override.properties to my application.properties but it doesn't work.
How can I do this?
You can try adding the following annotation to your main class where you call SpringApplication.run() :
#PropertySource("/Users/tony/Desktop/override.properties")
On running the SpringBoot application, it should pick up the properties from there. Excluding the application.properties from src/main/resources using build - resources - resource - excludes - exclude in pom.xml would help to ensure that the packaged jar doesn't include the applcation.properties and would force Spring Boot to pick it up from path specified in #PropertySource
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>**/application.properties</exclude>
</excludes>
</resource>
</resources>
</build>
But keep in mind that in this way, you are creating tight coupling of your properties file with your application. If you keep it as a runtime argument, it would be having enough flexibility to run elsewhere and maintenance would be easier too.
spring.config.location and spring.config.additional-location are only useful when set as Environment Variable, System Property, or command line argument.
The best you can do to specify additional configuration files to be loaded directly in your application.properties file, is to set the spring.config.import property like so:
spring.config.import=file:/Users/tony/Desktop/override.properties
References: https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-files

How to set Flyway migration file location using Spring profiles

I have two Spring profiles dev and test configured for development and test environment. And in each environment I am using different databases viz h2 in dev and postgresql in testing. Following are my properties files for each profile where {vendor} is resolved by spring boot to h2 and postgresql repectively as per datasource configured.
application-dev.properties
spring.flyway.locations=classpath:db/migration/{vendor}
application-test.properties
#Data source
spring.datasource.url=jdbc:postgresql://localhost:5432/test
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
#Flyway
spring.flyway.check-location=false
spring.flyway.locations=classpath:/db/migration/test/{vendor}
Flyway migration files for dev profile are under test/resources and for test profile under main/resources
This is working fine when I run my application with test profile where it picks migration files only under main/resources. However, When I run my unit test using dev profile. I expect it to pick files only under src/test/resources/db/migration/h2. But Flyway is picking up migration files from main/resources and test/resources both leading to error
org.flywaydb.core.api.FlywayException: Found more than one migration with version 1
I don't understand this behavior. Any inputs on how to fix this?
So, here is how I did it.
Requirements:
Use Spring profiles to configure application for different environments viz dev, test and prod.
Use Spring profiles to load flyway migration files as per the environment.
Database per environement:
H2 database for dev environment.
postgresql database for test environment.
postgresql database for prod environment.
Configuration
Create Spring profiles dev, test and prod in pom.xml.
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>test</id>
</profile>
<profile>
<id>prod</id>
</profile>
</profiles>
Create properties files for each profile
application-dev.properties
spring.flyway.locations=classpath:db/migration/{vendor}
Since, H2 database is configured by Spring boot when H2 driver is on classpath. We don't need to configure it explicitly.
application-test.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/db_test
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.flyway.locations=/db/{vendor}/common,/db/{vendor}/test
application-prod.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/db_prod
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.flyway.locations=/db/{vendor}/common,/db/{vendor}/prod
Flyway migration file locations.
If you will notice, I have not used db/migration under src/main/resources to place migration files which is the default location. For simple reason that Flyway picks all the files under this location and which results in version conflict between files for different environments. For e.g V2__data_insertion.sql is present for all three environments and this will not work if these were nested under db/migration. Since, H2 migration files pertain to default profile I have left them at the default flyway migration file location.
Hope that helps !!!
Its not about the flyway only.
In maven there are two different classpathes that it works with during the build:
Compile classpath - used for compilation (including src/main/*)
Testing classpath - effectively including both src/test/* (obviously) and src/main/* because in tests you should have a compile time access to the actual code.
That's why in runtime there are effectively 2 accessible places and flyway finds more than one migration.
Another observation:
Your production code in general should not include anything about tests. But I see that you add: src/main/resources/application-test.properties This file will appear in the production artifact which is wrong.
As a workaround you can work with
src/main/resources/application-prod.properties that defines location "X" for real migrations
As opposed to:
src/test/resources/application-test.properties that defines location "Y" for test migrations
Run integration tests with Profile Test and you won't find production migrations.

Could i use maven profile to swith between different value application.properties

I have application-prod.yml application-dev.yml, and application.properties which containing just one line code like below
spring.profiles.active=dev
for maven production build, it should use spring.profiles.active=prod , then it will build with application-prod.yml, for development build, it should use spring.profiles.active=dev, then maven
will use application-dev.yml to build
could I use pom.xml's different profile to do switch for this value switch in applicaiton.properties?
You can use a Maven property for this, reference it in your yml file (with ${...}) and filter the resource (i.e. the yml file) with the maven resources plugin.
It seems that what you're after is "externalized configuration". According to the excellent 12factor guidelines, it is best not to keep such config inside your code-repository.
Refer to the relevant section in the Spring Boot manual to see which options you have (and there are many). What it comes down to is that you provide your application.yml/properties file on the filesystem and your application will read it from there, rather than from the classpath.
Also, note that spring-profiles are not meant to be used to distinguish between development environments, but rather to put the application in different functional modes (e.g. to enable or disable specific features).
If you want the content of your properties file changed at build time, then you can use Maven filtering. Maven filtering allows to replace a placeholder in your properties (or yaml) file by values from Maven properties.
Assuming you have a property in your POM called targetEnv, which might have either the value dev or prod (depending on the active Maven profile), then you can refer it in your properties file (or yaml file) by using the following syntax :
spring.profiles.active=#targetEnv#
However, if you want to follow Spring Boot recommandations, it is better to enable and disable the Spring profiles by the means of environment variables in your target environment. For instance, you can use an environment variable spring.profiles.active with the desired value and it will override the value in your properties file.
You need to define a custom property in each of your Maven profiles and set their values to match with suffixes of corresponding properties files that you want to load with a particular profile.
<profile>
<id>dev</id>
<properties>
<activatedProperties>dev</activatedProperties>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>release</id>
<properties>
<activatedProperties>release</activatedProperties>
</properties>
</profile>
Next, in the build section of the same file, configure filtering for the Resources Plugin. That will allow you to insert properties defined in the previous step into any file in the resources directory, which is the subsequent step.
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
…
</build>
Finally, add the following line to the application.properties.
spring.profiles.active=#activatedProperties#
For more details, please see spring boot properties per maven profile
For official guide to load from external configLoad from external Config

Utility Jar using Spring Boot

Created a custom jar using spring boot and this is using for CRUD operations on a database table.The purpose is used to make this a utility jar, so that other services or applications can use this jar for any operations on that table. Following are the steps I followed:
1). Added this jar entry in pom.xml of a REST SERVICE and build got successfully.
2). Autowired the service class of Utility jar inside the controller of REST SERVICE.
But when I started the REST SERVICE (service is developed on spring boot), I got the error as 'the ****controller can require a bean of type *****serviceUtility. Consider defining a bean of type in your configuration'. But I am not able to see any configuration class inside the rest service and it is using application.yml for datasource related things. I am new to Spring and Spring Boot. Could any one guide me how to configure the utility jar in external services.
It is hard to tell without source code, but Spring Boot jars do not by default work as utility jars. The packaging is different. To get a Spring Boot jar to work as a utility jar within another project, you'll want to configure the build plugin like this :
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
</plugins>
</build>
Which is described here:
https://docs.spring.io/spring-boot/docs/current/maven-plugin/examples/repackage-classifier.html
If you are using spring boot , by default , the utility jar will not move all its dependencies .
You can add all the dependencies in the pom.xml for the Rest service .
Else you end up packaging some core modules twice increasing the size of your jar .

How can I configure the heap size when starting a Spring Boot application with embedded Tomcat?

I am trying to deploy a Spring Boot powered web app to production. The app is built with Spring Boot 1.0.1 and has the default Tomcat 7 embedded as application server. I want to allocate larger memory to the app when start the app with java -jar myapp.jar command line.
Should I use JVM parameter such as -Xms -Xmx or use environment variable such as JAVA_OPTS? I have tried to look for the answer in documentation or google it, but I did not get an answer. Can anyone give some hints?
If starting the application with the spring-boot plugin:
mvn spring-boot:run -Drun.jvmArguments="-Xmx512m" -Drun.profiles=dev
Otherwise if running java -jar:
java -Xmx512m -Dspring.profiles.active=dev -jar app.jar
Since this is specifically a Spring Boot question, I'd argue that a more useful answer than #DaveSyer's is this:
You can drop a .conf file in the same directory as your WAR file that is effectively a shell script.
For example,
$ ls
myapp.conf
myapp.war
$ cat myapp.conf
export JAVA_OPTS="-Xmx1024m -Xms256m"
Any configuration you do there will be run before the Spring Boot embedded Tomcat starts up. Personally, I version control a .conf.example file in my application itself and then drop a copy of it on each server I deploy to.
Of course, anything you set in that .conf file is overridable with command-line operations.
Just use whatever normal mechanism you would to set up the JVM. Documentation is available on the command line:
$ java -X
...
-Xms<size> Set initial Java heap size
-Xmx<size> Set maximum Java heap size
...
For Spring Boot 2, you have to specify the heap size in the pom.xml file as below:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<jvmArguments>-Xmx64m</jvmArguments>
</configuration>
</plugin>
For Spring Boot 1, the Maven argument to specify in the plugin configuration is jvmArguments, and the user property is run.jvmArguments:
mvn spring-boot:run -Drun.jvmArguments="-Xms2048m -Xmx4096m"
For Spring Boot 2, the Maven argument to specify in the plugin configuration is also jvmArguments, but the user property is now spring-boot.run.jvmArguments:
mvn spring-boot:run -Dspring-boot.run.jvmArguments="-Xms2048m -Xmx4096m"
So if you use the plugin configuration way, both for Spring Boot 1 and 2 you can do that:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<jvmArguments>
-Xms4048m
-Xmx8096m
</jvmArguments>
</configuration>
</plugin>

Resources