System environment properties overriding spring boot application.properties - java

In a spring boot application, I have a property in application.properties
spring.datasource.url: jdbc:mysql://host1:3306/test?useSSL=false&autoReconnect=true
However when I start the application, it is connecting to a totally different database.
So when I checked the env values in /actuator/env it url, it shows that the value is getting overrided by an environment property
SPRING_DATASOURCE_URL": {
"value": "jdbc:mysql://host2:3306/test",
"origin": "System Environment Property \"SPRING_DATASOURCE_URL\""
}
I have no idea where this environment property is configured.
I just want it to pick from my property file and not the environment. Is there any way to do that?

there are 17 ways to set and override properties in spring boot and they are ordered,
for example- application.properties outside the jar comes before application.properties inside the jar.
you could search potential source that override your properties in all those ways-
(ordered from strongest to weakest ) - copied from spring boot reference -https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config
Devtools global settings properties in the $HOME/.config/spring-boot directory when devtools is active.
#TestPropertySource annotations on your tests.
properties attribute on your tests. Available on #SpringBootTest and the test annotations for testing a particular slice of your application.
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 has properties only 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. Please note that such property sources are not added to the Environment until the application context is being refreshed. This is too late to configure certain properties such as logging.* and spring.main.* which are read before refresh begins.
Default properties (specified by setting SpringApplication.setDefaultProperties).

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.

What is the loading precedence for properties from Spring Cloud Config?

Spring has an explicit order for the loading of externalized configurations.
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 has properties only 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 by setting SpringApplication.setDefaultProperties).
However, there seems to be a glaring omission for configurations coming from Spring Cloud Config. Does anyone know where Spring Cloud Config fit above
As others have stated, the config-server comes first. If you are trying to override the config-server's properties with local properties (i.e. application-local.yml), then you need to add two properties to the config-server**:
spring.cloud.config.allowOverride=true
spring.cloud.config.overrideNone=true
Per the documentation:
The property sources that are added to you application by the
bootstrap context are often "remote" (e.g. from a Config Server), and
by default they cannot be overridden locally. If you want to allow your applications to override the remote
properties with their own System properties or config files, the
remote property source has to grant it permission by setting
spring.cloud.config.allowOverride=true (it doesn’t work to set this
locally). Once that flag is set there are some finer grained settings
to control the location of the remote properties in relation to System
properties and the application’s local configuration:
spring.cloud.config.overrideNone=true to override with any local
property source, and
spring.cloud.config.overrideSystemProperties=false if only System
properties and env vars should override the remote settings, but not
the local config files.
Also see this, regarding using spring.cloud.config.override-system-properties=false to override via system / command line properties. The documentation quoted above had/has an inconsistency, which I removed from the quote.
Note, if you want the remote config server to override your local properties file sources but not your local system properties or environment properties, add the following in the config server:
spring.cloud.config.allowOverride=true
spring.cloud.config.overrideNone=false
spring.cloud.config.overrideSystemProperties=false
** In this case the overrideSystemProperties value is ignored. See org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration#insertPropertySources
Note: All the above applies to Spring Boot 2.3.x. Version 2.4.x uses an alternate loading priority. See https://github.com/spring-cloud/spring-cloud-config/issues/1856
The documentation states that:
The bootstrap properties show up in the /env endpoint as a high-priority property source, as shown in the following example
So it would be position #0
Points 12 and 14 cover Spring Cloud Config.
12.Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants).
14.Application properties outside of your packaged jar (application.properties and YAML variants).

springboot external configuration - profile specific configuration

According to the SpringBoot documentation, the order of configuration is as:
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).
On my project I have a profile called "prod" and the following files:
application.yml (inside the jar)
application-prod.yml (inside the jar)
And I also want to override some of the properties using an external file. Since according to the docs, an external application.yml will be overridden by the internal application-prod.yml, I need to make sure that the external file is considered as a profile specific config file.
I have tried to use:
-Dspring.config.location=<my path>/application-prod.yml
and I have also tried:
-Dspring.config.location=file:<my path>/application-prod.yml
In all cases I get the value from the internal application-prod.yml
If I totally remove the internal config file then I get the value from the external (so I know that the config picks up the file).
I understand that this external file is considered as the equivalent to the generic application.yml and not a profile specific.
How can I configure it to be considered as a profile specific external config?
Found the answer:
You need to use a Directory externally to set the profile specific configuration files, not using the file directly and it needs to end in /. So it has to be:
-Dspring.profiles.active=prod
-Dspring.config.location=/<some-path>/config/ (any path that ends in /)
and in there have a :
application-prod.yml

How to change Spring Boot properties default path in Tomcat development environment

I need to use two *.properties file to determine config of Spring Boot application server. How can I set the second configuration path?
I use spring-boot-starter-parent version 1.5.10 and such *.properties file:
spring.datasource.url=url
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialec
spring.jpa.properties.hibernate.current_session_context_class=
org.springframewok.orm.hibernate4.SpringSessionContext
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.idle-timeout=30000
Now, I need to address database info from another properties file.
Edit: Note that I need to put second properties file outside of my WAR file in WEB-INF folder.
(1) This is best practices when switching between development mode and production environment.
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html#boot-features-profiles
Reference for version 1.5.10.RELEASE: https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/#boot-features-external-config-profile-specific-properties
For more specific, you create 3 files
application.properties for common properties.
application-dev.properties for only own properties what used in profile dev
application-production.properties for only own properties what used in profile production
(Notice: Has convention over naming)
Point profile what used, in application.properties has line spring.profiles.active=dev (in development) or spring.profiles.active=production (in production)
(2)
Note that I need to put second properties file outside of my WAR file
in WEB-INF folder.
Assumption, your file is foo.properties. It is outside WAR file, it has not nature of Spring's properties file. Therefore, Spring Framework/Spring Boot can not read it automatically. You must write few lines of Java code to reading the content of foo.properties, then assign to configuration manually (see example configuration class)
You can access multiple of properties file using #PropertySource
Follow the following stackoverflow thread.
https://stackoverflow.com/a/47178674/7538821

Spring boot deployed as WAR on tomcat, How to Externalize application.properties for different profiles(Dev,Test,Staging,Prod)

I am in a scenario where I am deploying by spring boot as a WAR on tomcat. Here in this application I have application.properties which has database username/password , some URL which help in consuming rest services(urls vary depending on the environment). Now I need to get the DB credentials and URL's depending on the environment it is deployed to. how to achieve it.
On external container ex tomcat
You can pass configuration using Jndi variable in context.xml
This var override local property defined in application.properties
Or pass -Dspring.profiles.active=env
in tomcat startup script ,for select environment specific application.properties
if you prefer to have configuration inside the war
As per 24. Externalized Configuration it should be enough to place a profile specific properties e.g. application-dev.properties on the classpath. The property precedence is:
...
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).
...
This can however work slight differently if you are packaging as JAR as per 24.3 Application Property Files. The property precedence is:
A /config subdirectory of the current directory
The current directory
A classpath /config package
The classpath root
according to the discussion with Karol, I guess using properties sources referencing file system path should be ok:
#Configuration
#PropertySource("${mywebapp.config.dir}/application.properties")
public class SpringConfig {
}
And just path JVM args at the startup of tomcat like
-Dmywebapp.config.dir=file:/etc/mywebapp
This way you can deploy one application.properties different for each environment.
As for your comment:
#PropertySource(value={"file:C:/Users/foo/apache-tomcat-8.5.28/webapps/application.properties}"})
as it's a windows system path may you have to double backslash your path: C:\\Users\\foo ...
I am able to read the properties file from tomcat/webapp location
for every environment I can ask system engineers to drop the file at the location,
I dont know if its a good solution. Please suggest
#PropertySource(value={"file:C:/Users/foo/Downloads/apache-tomcat-8.5.28/webapps/application.properties"})

Categories

Resources