Can I define System Properties within Spring Boot configuration files? - java

I have a single application.yml configuration file for my Spring Boot app that defines two profiles (as described in the documentation).
When the production profile is enabled, I would like to set the http.maxConnections system property to a custom value, e.g.
spring:
profiles:
active: dev
---
spring:
profiles: dev
---
spring:
profiles: production
http:
maxConnections: 15
But this doesn't actually set the system level property; it appears to just create an application-level property. I've verified this through both http://locahost:8080/env and a JMX Console when comparing launching by
java -jar -Dspring.profiles.active=production myapp.jar
versus
java -Dhttp.maxConnections=15 myapp.jar
I suppose I could create a bean that's #Conditional on the "production" profile that programmatically callsSystem.setProperty based on my application.yml-defined property, but is there a simpler way through configuration files alone?

You may try.
#Profile("production")
#Component
public class ProductionPropertySetter {
#PostConstruct
public void setProperty() {
System.setProperty("http.maxConnections", "15");
}
}

I suppose I could create a bean that's #Conditional on the "production" profile that programmatically callsSystem.setProperty based on my application.yml-defined property, but is there a simpler way through configuration files alone?
I think that's your best bet here. Spring Boot does that itself in its LoggingSystem where various logging.* properties are mapped to System properties.
Note that you'll probably want to set the system properties as early as possible, probably as soon as the Environment is prepared. To do so, you can use an ApplicationListener that listens for the ApplicationEnvironmentPreparedEvent. Your ApplicationListener implementation should be registered via an entry in spring.factories.

You can inject the environment into the constructor of the class that specifies the beans. This allows you to write application properties to the system properties before the beans are being created.
#Configuration
public class ApplicationBeans {
#Autowired
public ApplicationBeans(Environment environment) {
// set system properties before the beans are being created.
String property = "com.application.property";
System.getProperties().setProperty(property, environment.getProperty(property));
}
/**
* Bean that depends on the system properties
*/
#Bean
public SomeBean someBean() {
return new SomeBean();
}
}

You can also use PropertyPlaceholderConfigurer from org.springframework.beans.factory.config to get handle over your properties file

Related

Spring Boot Property file precedence

In my Spring Boot program I'm getting a failure due to a bad property value on load. In particular, it uses the DB2 hibernate dialect but it's not defined in the property file I thought I was using.
Assuming no annotations, where does Spring look for the properties file? Yes I know it normally resides in src/main/resources/application.properties
What if I have a property in my test cases; does it ignore the one in main and use the one in test? Or does it start with the main version and let the test one override the main where it applies?
Does the application profile affect the property file used? Some people use the same application.properties file name in both main and test.
If I do have a TestSource annotation with a class path location, does it still augment it with something somewhere else?
Finally, how can I get Spring to tell me everywhere it looked for properties and not just one of them?
#Woodsman
It's possible to use many settings in each profile/environment in spring application on src/main/resources/ folder
application-dev.properties
application-local.properties
application-onlytests.properties
application-prd.properties
application.properties
the name after hyphen will be the name profile got by spring
Ex: application-{myenviroment}.properties
When you start spring application will be used application.properties by default.
You can tell spring to use an specific properties passing the environment in one of ways below:
Putting spring.profiles.active=prd inside application.properties file
Passing by parameters when start spring app --spring.profiles.active=local
Running your jar on command line java -jar myjar.jar --spring.profiles.active=dev
Setting an environment var in your machine/docker/container SET SPRING_ACTIVES_PROFILE=local
There are other ways using annotations on beans, passing jvm arguments and others
If you need run your tests in a specific configuration ( vars, database, settings ), It's possible to pass which .properties will be used
#ExtendWith(SpringExtension.class)
#AutoConfigureMockMvc
#SpringBootTest
#TestPropertySource(locations = "classpath:application-onlytests.properties")
public class RunTest_from_onlytests_properties {
#Autowired
private MockMvc mockMvc;
#Test // org.junit.jupiter.api.Test
public void test() throws Exception{
// ...
}
}

How to resolve placeholder in properties file with values from another properties file in spring boot application

My spring boot application has below properties files.
src/main/resources/config/DEV/env.properties
mail.server=dev.mail.domain
src/main/resources/config/QA/env.properties
mail.server=qa.mail.domain
src/main/resources/config/common/env.properties
mail.url=${mail.server}/endpoint
Is it possible to load "common/env.properties" so that it's placeholders will be resolved using the given environment specific properties file. For DEV environment, we want the placeholders in "common/env.properties" to be resolved using values from "DEV/env.properties".
There are answers about how to load multiple properties files and profile based loading but could not find an answer for this particular use case.
Thanks in advance.
2 Options :
Generate the common/application.properties using configuration-maven-plugin and filter files for each environment. It is outdated now.
Use application-<env>.properties for each environment and pass the -Dspring.profiles.active=<env> as VM option in application start up. Spring will automatically take the property from correct file.
In option 2, you will be overwriting whatever is present in application.properties with application-.properties. So you dont have to add only the properties which you need to change per environment.
for eg:
Your application.properties can have
logging.level.root=WARN
logging.level.org.apache=WARN
logging.level.org.springframework=WARN
Your application-dev.properties can have
logging.level.org.springframework=DEBUG
which means, when you are starting application using dev profile, spring takes
logging.level.root=WARN
logging.level.org.apache=WARN
logging.level.org.springframework=DEBUG
edit :
Also, you can try something like below on your class. (Spring will overwrite value in config.properties with values from config-dev.properties). ignoreResourceNotFound will make sure, application will still start with default values even if the corresponding file is not found.
#Configuration
#PropertySource("classpath:config.properties")
#PropertySource(value = "classpath:config-${spring.profiles.active}.properties", ignoreResourceNotFound = true)
You can add resources/application.yml file where you can have multiple profiles in one File.
MultiProfile Yaml
e.g.here are two different profiles 'dev' and 'qa' with different applicationNames 'DEV' and 'QA' and one defaultName 'Default'
spring:
application:
name: Default
profiles:
active: qa
---
spring:
profiles: dev
application:
name: DEV
---
spring:
profiles: qa
application:
name: QA
You can achieve this by declaring a property source on a class configuration and setting up an environment variable in the path :
#PropertySource({ "classpath:config/${env}/env.properties" })
#Configuration
public class config{}
And then you launch the spring boot app with the command line variable -env=dev
UPDATE
You can use #PropertySources annotation to load several properties.
#PropertySources({
#PropertySource("classpath:config/${env}/env.properties"),
#PropertySource("classpath:config/common/env.properties")
})
public class config{}

adding new property based on spring profile

If spring boot is run in override profile , can we have application-override.properties having properties like foo.baz that is not defined in application.properties ?
application.properties
foo.bar=1
application-override.properties
spring.profiles.include=default
foo.baz=1
That is correct. When you have new properties in application-override.properties and and the override profile is the active profile, then yes in your program the properties from application.properties as wel as application-override.properties is loaded.
Using spring.profiles.include=default in your override profile is not needed.
In the case of loading multiple specific profiles with same properties:
Also, in the context of property overriding with profiles, something to keep in mind when you have multiple active profiles and they contain the same property. The last profile in the list will be used.
Let's say you start up your program with mvn spring-boot:run -Drun.profiles=profile1,profile2
Both application-profile1.properties and application-profile2.properties
contains the property my.custom-property=x (for profile1) and my.custom-property=y (for profile2). The value of my.custom-property will be y, as that was the last profile in the provided profiles.
You can create configuration class for your custom profile and load the appropriate properties file in it like this:
#Configuration
#Profile("override")
#PropertySource("classpath:application-override.properties")
public class OverrideConfig {
}
This way, all the configuration you do in OverrideConfig (including taking properties from application-override.properties), will only load if override profile is enabled in application.properties like this:
spring.profiles.active=override
Long story short : Spring boot overrides values of properties with same name according to their evaluation order. But here you don't override any property, you add a new.
That is still simpler : Spring boot just adds it into the Spring Environment.
Just run the app by specifying this profile and makes sure that the properties are located in the locations expected by Spring Boot.
Example from a fat jar (Java system property) :
java -Dspring.profiles.active=override -jar foo.jar
Example from the source code (Maven property) :
mvn spring-boot:run -Dspring-boot.run.profiles=override
Yes, you can do this by simply add the profile name to the application.properties:
application-override.properties
Then you can load profile from the command line:
java -jar foo.jar --spring.profiles=override
source: https://docs.spring.io/spring-boot/docs/current/reference/html/howto-properties-and-configuration.html#howto-change-configuration-depending-on-the-environment
Spring will load the application.properties first followed by any application-{profile}.properties.
Another option is to use yaml, and load everything into one file:
foo:
bar: 1
---
spring:
profiles: override
foo:
baz: 1
---
spring:
profiles: otherOverride
foo:
bar: 2
baz: 2

ImportResource conditional choose of spring configuration

I have spring application that is configured by Spring annotation and I need to load complex spring xml configuration that is dependent on env system property. is it possible to do it in annotation? I need something like is in #ImportResource (it is only my idea):
#ImportResource("classpath:spring/${env:dev} == dev ? config-dev.xml : config.xml")
#Configuration
public class AppConfig{
}
I can have these env: dev, prod, uat and I need to ensuere that for dev I will load config-dev.xml otherwise I need to load config.xml. Is it possible to do something like this in #ImportResource?

Loading environment specific properties file in spring

I am working on a spring-boot application, I need your assistance on below scenario.
I have properties files for each environment something like application-dev.properties, application-prod.properties etc. Is there way that my application can load environment specific properties file by using spring #Profile annotation.
Please help.
You don't need to use #Profiles annotation at all. Just use
#ConfigurationProperties(locations = "classpath:myapp-${environment.type}.properties")
and define environment type via system property. E.g. via command line -Denvironment.type=dev.
#Profile is not for loading environment specific properties file. It is for specifying profile of a bean. For example,
#Profile("dev")
#Component
class Foo {
}
It means the bean of Foo is only available when the active profiles include dev. Or the opposite #Profile("!dev"), which means the bean is available when dev is not an active profile.
So for loading environment specific properties file, since it is spring-boot, you can just specify the active profiles. There are several ways to specify the active profiles.
Environment variable: SPRING_PROFILES_ACTIVE=dev,prod
command line argument: java -jar app.jar --spring.profiles.active=dev,prod
Programmatically : SpringApplicationBuilder(...).properties("spring.profiles.active=dev,prod).run(...)
Default application.properties or yaml: spring.profiles.active:dev, prod

Categories