Spring boot external configuration of property file - java

I have a spring boot application that I can package in a war that I want to deploy to different environments. To automate this deployment it'd be easier to have the configuration file externalized.
Currently everything works fine with a application.properties file in src/main/resources. Then I use ´mvn install´ to build a war deployable to tomcat.
But I would like to use a .yml file that does not need to be present on mvn install but that would be read from during deployment of the war and is in the same or a directory relative to my war.
24. externalized configuration shows where spring boot will look for files and 72.3 Change the location of external properties of an application gives more detail on how to configure this but I just do not understand how to translate this to my code.
My application class looks like this:
package be.ugent.lca;
Updated below
Do I need to add a #PropertySource to this file? How would I refer to a certain relative path?
I feel like it's probably documented in there as most spring boot documentation but I just don't understand how they mean me to do this.
EDIT
Not sure if this should be a separate issue but I think it's still related.
Upon setting the os variable the error of yaml file not found went away. Yet I still get the same error again as when I had no application .properties or .yml file.
Application now looks like this:
#Configuration
**#PropertySource("file:${application_home}/application.yml")**
#ComponentScan({"be.ugent.lca","be.ugent.sherpa.configuration"})
#EnableAutoConfiguration
#EnableSpringDataWebSupport
public class Application extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
The application_home OS variable
$ echo $application_home
C:\Masterproef\clones\la15-lca-web\rest-service\target
My application.yml file(part it complains about):
sherpa:
package:
base: be.ugent.lca
Error upon java -jar *.war
All variations upon:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'sherpa.package.base' in string value "${sherpa.package.base}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:204)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:178)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$2.resolveStringValue(PropertySourcesPlaceholderConfigurer.java:172)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:808)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1027)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545)
... 142 more

Using external properties files
The answer lies in the Spring Boot Docs, I'll try to break it down for you.
First of all, no you should not use #PropertySource when working with Yaml configuration, as mentioned here under the Yaml shortcomings :
YAML files can’t be loaded via the #PropertySource annotation. So in the case that you need to load values that way, you need to use a properties file.
So, how to load propery files? That is explained here Application Property Files
One is loaded for you: application.yml , place it in one of the directories as mentioned in the link above. This is great for your general configuration.
Now for your environment specific configuration (and stuff like passwords) you want to use external property files, how to do that is also explained in that section :
If you don’t like application.properties as the configuration file name you can switch to another by specifying a spring.config.name environment property. You can also refer to an explicit location using the spring.config.location environment property (comma-separated list of directory locations, or file paths).
So you use the spring.config.location environment property.
Imagine you have an external config file: application-external.yml in the conf/ dir under your home directory, just add it like this:
-Dspring.config.location=file:${home}/conf/application-external.yml as a startup parameter of your JVM.
If you have multiple files, just seperate them with a comma. Note that you can easily use external properties like this to overwrite properties, not just add them.
I would advice to test this by getting your application to work with just your internal application.yml file , and then overwrite a (test) property in your external properties file and log the value of it somewhere.
Bind Yaml properties to objects
When working with Yaml properties I usually load them with #ConfigurationProperties, which is great when working with for example lists or a more complex property structure. (Which is why you should use Yaml properties, for straightforward properties you are maybe better of using regular property files). Read this for more information: Type-Safe Configuration properties
Extra: loading these properties in IntelliJ, Maven and JUnit tests
Sometimes you want to load these properties in your maven builds or when performing tests. Or just for local development with your IDE
If you use IntelliJ for development you can easily add this by adding it to your Tomcat Run Configuration : "Run" -> "Edit Configurations" , select your run configuration under "Tomcat Server" , check the Server tab and add it under "VM Options".
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</argLine>
</configuration>
</plugin>
When running JUnit tests in IntelliJ:
Run → Edit Configurations
Defaults → JUnit
add VM Options -> -ea -Dspring.config.location=file:${home}/conf/application-external.yml

Yes, you need to use #PropertySource as shown below.
The important point here is that you need to provide the application_home property (or choose any other name) as OS environment variable or System property or you can pass as a command line argument while launching Spring boot. This property tells where the configuration file (.properties or .yaml) is exactly located (example: /usr/local/my_project/ etc..)
#Configuration
#PropertySource("file:${application_home}config.properties")//or specify yaml file
#ComponentScan({"be.ugent.lca","be.ugent.sherpa.configuration"})
#EnableAutoConfiguration
#EnableSpringDataWebSupport
public class Application extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

There is a very simple way to achieve this.
Inside your original application.properties file you can just specify the following line:
spring.config.import=file:Directory_To_The_File/Property_Name.properties
It will automatically sync all the properties from the external property file.
Now lets say that you have a situation where you need to get properties from multiple property files. In that case, you can mention the same line in the external property file which in turn will take the remaining properties from the second property file and so on.
Consider the following example.
application.properties:
spring.config.import=file:Resources/Custom1.properties
Custom1.properties:
server.port=8090
.
.
.
spring.config.import=file:Resources/Custom2.properties

One of the easiest way to use externalized property file using system environment variable is, in application.properties file you can use following syntax:
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_PORT}
Now, declare above used environment variables,
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"
This way you can use different value for same variable in different environments.

Use below code in your boot class:
#PropertySource({"classpath:omnicell-health.properties"})
use below code in your controller:
#Autowired
private Environment env;

Related

The best way to make app configs editable for docker image/container?

I have an app which is dockerized like this:
FROM openjdk:11-jre-slim as jdkbase
FROM jdkbase
COPY target/dependency-jars /run/dependency-jars
COPY target/resources /run/resources
ADD target/app-1.0.2.jar /run/app-1.0.2.jar
CMD java -jar run/app-1.0.2.jar
This app uses some configs from application.properties. I push this docker image into my private registry for using it inside Kubernetes cluster.
There is no problems unless i have to change any properties in application.properties. Lets say, my database URL was changed. So, I have to undate it in application.properties and then force my app to use updated configs. I've tried to edit application.properties inside the running docker container and then restart the container. As a result, after i restart my container it has edited application.properties BUT the app still uses old URL.
The only way i've found to force the app to use new configs is to commit changed container into new image and then start the new image.
It works, but it doesn't seems to me to be an optimal solution: like, after every changes in app's configs I have to recreate image, which is +300Mb of data, I have to push this new image into registry, I have to recreate Kubernetes pod from new image... It looks like too much unnecessary actions to just change one URL.
So, is there any other more optimal way to work with needs to change application.properties from time to time?
If this is a spring boot application is easy to define a variable as an environment variable. If an environment variable is present it overwrite the variable with the same name defined in your application.properties.
What happens is called externalized configuration and it is defined here :
Spring Boot allows you to externalize your configuration so 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 using the #Value annotation, accessed via Spring’s Environment abstraction or bound to structured objects via #ConfigurationProperties.
Spring Boot uses a very particular PropertySource order that is designed to allow sensible overriding of values. Properties are considered in the following order:
Devtools global settings properties on your home directory (~/.spring-boot-devtools.properties when devtools is active).
#TestPropertySource annotations on your tests.
#SpringBootTest#properties annotation attribute on your tests.
Command line arguments.
Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property)
ServletConfig init parameters.
ServletContext init parameters.
JNDI attributes from java:comp/env.
Java System properties (System.getProperties()).
OS environment variables.
A RandomValuePropertySource that only has properties in random.*.
Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants)
Profile-specific application properties packaged inside your jar (application-{profile}.properties and YAML variants)
Application properties outside of your packaged jar (application.properties and YAML variants).
Application properties packaged inside your jar (application.properties and YAML variants).
#PropertySource annotations on your #Configuration classes.
Default properties (specified using SpringApplication.setDefaultProperties).
So you can pass at runtime an environment variable to your docker to solve this problem. You don't need to recreate the Docker image each time.

loading of property file outside of war file in spring boot

I need to load the application.properties file from outside the spring boot war which going to be deployed in tomcat.
I tried various solution missing something
Tried setting environmental variable as below in windows
name : SPRING_CONFIG_NAME
value:D:/test/application.properties
i tried multiple values for above value like file:/// in prefix and only file: as perfix .Nothing worked
Tried having context parameter is tomcat like mentioned in below SO answer
https://stackoverflow.com/a/44697239/2751962
Tried loading like this in main file which extends SpringBootServletIntializer
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class)
.properties(getProperties());
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
SpringApplicationBuilder springApplicationBuilder = (SpringApplicationBuilder) (new SpringApplicationBuilder(Application.class))
.sources(Application.class)
.properties(getProperties())
.run(args);
}
static Properties getProperties() {
Properties props = new Properties();
props.put("spring.config.location", "file:///D:/test/application.properties​");
return props;
}
I not sure what i missed , Kindly help.
External Configuration in Spring Boot
When using Spring Boot, there are documented naming conventions and directory structure. A Spring Boot app searches for properties to load from a prioritized list, so there are to suggestions for you to consider:
Use command-line flag spring.config.location to target specific file or directory from which to load properties sources. You can use this to specify directories to search or individual files to load. Be cautious loading individual files though, if you intend to use profile-based properties. (add flag in command like this: java -jar MyJar.jar --spring.config.location=D:\test\)
By default, Spring Boot will look for a ./config/ directory where the WAR is and the directory of the WAR itself, so you may place "application.properties" in either position and it will be loaded.
Pivotal provides a super great reference for Spring Boot. Section 24 covers properties more extensively than I can in a post.
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html (*links to most recent release's reference)
Note: I am not a Windows user, so be careful pasting in that filepath above. Edit Me.
Extending Configuration to Deployable Packages
Normally Spring Boot packages into an executable WAR or JAR that has an embedded servlet container engine that is used for the runtime. In your case, however, you are packaging a conventional WAR and deploying that to an external instance of Tomcat, so the configuration parameters must be propagated through Tomcat, using the JAVA_OPTS variable.
For a Apache Tomcat, the convention is to place your properties in ${catalina_base}/conf where catalina.base points to the location of the Tomcat instance. I created a working demo just now following these steps:
First, follow section 88.1 of the reference to setup a base WAR app
mvn package
Place application.properties in conf directory within Tomcat
set JAVA_OPTS=-Dspring.config.location=${catalina.base}/conf/
"%CATALINA_HOME%"\bin\startup
Deploy
It's not the cleanest deployment pipeline, but if you must use an external Tomcat instance, then this will work. However, to run multiple apps with separate property files on the same Tomcat instance would complicate things. In that case, using Spring Framework (not Boot) would be easier to configure.
You can try setting properties via XML and or Java configuration and #PropertySource.
#Configuration
#PropertySource("classpath:foo.properties")
public class PropertiesWithJavaConfig {
//...
}
source :- https://www.baeldung.com/properties-with-spring

spring boot: add new yml files to application config

i want developers to be able to locally override some configuration properties. (let's say we work on google drive and everyone should test it on its own account).
i don't want to override properties using command line (because it has to be set inside every IDE configuration and on every CLI run).
what i want is: application should use all the standard spring boot config files (application.yml etc) and also look for e.g. local.yml (on the classpath) or some file inside user.home. and those additional files should override other settings.
how to add new yml resources and order them correctly?
edit: i know spring's default orders and locations. question is about adding new ones
If you look in the Spring Boot documentation about the locations for configuration files (http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config), you can see, that they are loaded from the following places (amongst others):
Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants)
Application properties outside of your packaged jar (application.properties and YAML variants).
There are two default locations where they are loaded from ( see http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config-application-property-files):
A /config subdirectory of the current directory.
The current directory
Current directory in this case means the working directory for the Java process (Usually the directory where the JAR is located, or in case of running with in the IDE, usually the project root folder). So the developers just can place their own configuration files in that places and they are automatically loaded (and will override properties within the JARs). Add that files to .gitignore (or .svnignore or ...) and they won't accidentally committed into your repository.
There's a new way to do this, after Spring Boot v2.4, by using spring.config.import: https://spring.io/blog/2020/08/14/config-file-processing-in-spring-boot-2-4#importing-additional-configuration
By adding this part to your application.yml file, you should be able to import the additional configuration:
spring:
config:
import: local.yml
The article also has this section:
Imports can be considered as additional documents inserted just below the document that declares them. They follow the same top-down ordering as regular multi-document files: An import will only be imported once, no matter how many times it is declared.
So the contents of local.yml should be handled as if they were appended to the end of application.yml, thereby allowing you to override any property in application.yml.
From Spring Boot Documentation : Application property files:
SpringApplication will load properties from application.properties files in the following locations and add them to the Spring Environment:
A /config subdirectory of the current directory.
The current directory
A classpath /config package
The classpath root
The list is ordered by precedence (properties defined in locations higher in the list override those defined in lower locations).
This also goes for yaml, so you everyone can add application.yml under config directory, under the directory you run the spring boot jar from.
You can also customize the extra configuration file to be local.yml if you'd like by using spring.config.location:
--spring.config.location=classpath:/application.yml,classpath:/local.yml
Note however:
spring.config.name and spring.config.location are used very early to determine which files have to be loaded so they have to be defined as an environment property (typically OS env, system property or command line argument).
To provide the configuration from external config file in spring-boot application -
-Dspring.config.location=file:/home/vfroot/Workspace/project/MODULE_HOME/application.yaml
this command can be run with terminal:
mvn clean install -Dspring.config.location
= file:/home/vfroot/Workspace/MODULE_HOME/application.yaml
or need to set in Eclipse VM argument.
Also to set the active profiles :
-Dspring.profiles.active=dev
Well, since i am new in Spring Boot & Restfull Web Services. However, i managed to add a new .yml file to mange database and server port.
Instructions that i followed:
Project File.
Other Sources
src/main/resources
default package
right click on "default package"
add new YAML FILE
Or of YAML File option not available
5. right click on "default package"
6. then in categories: other --> File Types: YAML File

Custom config location and config name on Spring Boot

I'm trying to customize Spring Boot config location and config name using spring.config.name and spring.config.location properties as I've saw on Spring Boot reference guide
I've created an Spring Boot basic application to test it.
I'm able to customize it using OS environment variable like export SPRING_CONFIG_NAME=custom and/or export SPRING_CONFIG_LOCATION=classpath:/custom/location.properties. That works fine!
But I want to know, if it's possible to define spring.config.name=custom on default application.properties and then create a custom.properties file where I'll be able to define all application configuration properties.
I've checked it, and seems that it's not working defining spring.config.name property on application.properties... but I want to know if this is a valid way to do it before to create an issue on gitHub.
Regards,
From spring documentation:
spring.config.location environment property (comma-separated list of
directory locations, or file paths)
Moreover, code in ConfigFileApplicationListener shows that if there if no environment property, processing fallbacks to:
DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
And for the name:
DEFAULT_NAMES = "application";
So it is normal that what you are doing is not working.

Access maven project version in Spring config files

I would like to display the currently running version of my web application in the page. The project is based on Maven, Spring, and Wicket.
I'd like to somehow get the value of maven's ${project.version} and use it in my spring XML files, similarly to the way I use the Spring PropertyPlaceholderConfigurer to read a property file for settings that I use in my application.
If I had the maven project.version available as a variable in my Spring config, I could do something like this:
<bean id="applicationBean" class="com.mysite.web.WicketApplication">
<property name="version"><value>${project.version}</value></property>
</bean>
How can I do this?
You can use Maven filtering as already suggested.
Or you could just read the pom.properties file created by Maven under META-INF directory directly with Spring:
<util:properties id="pom"
location="classpath:META-INF/groupId/artifactId/pom.properties" />
and use the bean.
The only drawback of the later approach is that the pom.properties is created at package phase time (and won't be there during, say, test).
One technique would be to use mavens filtering. You can insert placeholders in resource files like which then get replaced with values from the build during the resource phase.
Look up "How do I filter resource files?" in the Maven getting started guide
Use the #PropertySource annotation to add the pom.properties file created by Maven in the META-INF directory. Note that the file doesn't exist until the package phase. To avoid errors during testing set the ignoreResourceNotFound=true and add a default value on the property being read (e.g. none below).
#Service
#PropertySource(value = "classpath:META-INF/maven/io.pivotal.poc.tzolov/hawq-rest-server/pom.properties", ignoreResourceNotFound=true)
public class MyClass {
private String applicationPomVersion;
#Autowired
public MyClass(#Value("${version:none}") String applicationVersion ) {
this.applicationPomVersion = applicationVersion;
}
public String getApplicationPomVersion() {
return this.applicationPomVersion;
}
}
We deploy property files outside of the web app. The files can then be filtered at deployment time.
If using Jetty one can put the file under $JETTY_HOME/resources or use the extraClassPath feature to load the property file at runtime.
I think the right way of gather application version is the one explained in this thread: https://stackoverflow.com/a/2713013/840635 through getClass().getPackage().getImplementationVersion().

Categories

Resources