Set/override Spring / Spring Boot properties at runtime - spring

At the project with Spring Boot we use application.properties but need to configure some of these properties (like port number of logging level) based on an external configuration. We access the configuration via API so it is known only at runtime.
Is there a way to override or set some Spring properties at runtime (for example using a bean) and if yes how can this be achieved?

You could do this with Spring Cloud Config
Just for the purpose of illustration, here's a relatively quick way to see dynamic property overrides at runtime:
First, for your bean to be able to pick up changed properties, you need to annotate it with
#RefreshScope
Add the spring cloud dependency to your spring boot app, eg for gradle
compile group: 'org.springframework.cloud', name: 'spring-cloud-starter', version: '1.1.1.RELEASE'
( NB You also need the spring boot actuator dependency.)
With the app running, you can view your current config at eg
http://localhost:8080/env
eg if you have a property 'my.property' in application.properties, you'll see something like:
"applicationConfig: [classpath:/application.properties]": {
"my.property": "value1",
etc
To change the value, POST my.property=value2 to /env as application/x-www-form-urlencoded
eg
curl -X POST http://localhost:8080 -d my.property=value2
GET /env again and you'll see the new value appears under the "manager" section
To apply the changed properties, do an empty POST to /refresh. Now your bean will have the new value.

Could you use system properties to pass in the variable? If you configure the PropertyPlaceholderConfigurer you can set the precedence of system properties vs file properties.
For example, something like:
#Bean public PropertyPlaceholderConfigurer placeHolderConfigurer() {
PropertyPlaceholderConfigurer props = new PropertyPlaceholderConfigurer()
props.setSystemPropertiesMode( PropertyPlaceholderConfigurer.SYSTEM_PROPERTIES_MODE_OVERRIDE )
props.setLocations(new
PathMatchingResourcePatternResolver().getResources("classpath:/**.properties"));
props
}
The above would load your .properties file, but we set the priority to be system variables first, so if you set a system variable that will override the same variable in the config.
Alternatively, looking at the docs, Spring recommends defining a search order in your Environment:
[PropertyPlaceholderConfigurer is still appropriate for use when]
existing configuration makes use of the "systemPropertiesMode" and/or "systemPropertiesModeName" properties. Users are encouraged to
move away from using these settings, and rather configure property
source search order through the container's Environment; however,
exact preservation of functionality may be maintained by continuing to
use PropertyPlaceholderConfigurer.
Hopefully one of the above should sort out what you need?

Related

How to set arbitrary system.property from Spring Boot?

If I run my app as java jar -Dsomething=anything thejar.jar then I have set a system property.
Can I do that via Spring Boot Configuration files or is my only option to defined a #Configuration class that reads a property and then sets the system property from that?
Spring configuration is highly flexible and provides a hierarchy for resolving configuration properties. You can set properties using the spring config server, environment variables (my preferred approach), system properties, application.yaml/propteries, etc. Check out the docs on externalized configuration
Say you wanted to set a property: app.some.property=foo. You can access this property from any bean using the value annotation:
#Value("${app.some.property}")
private String someProperty;
And then you can set it at runtime using one of the approaches defined above:
1. System Property
java -jar -Dapp.some.property=foo thejar.jar
2. Environment Variable
export APP_SOME_PROPERTY="foo"
java -jar thejar.jar

How to set properties of Tomcat JDBC Pool when using Spring Cloud Connector config?

I want to configure the properties of the Tomcat JDBC Pool with custom parameter values. The pool is bootstrapped by the spring-cloud (Spring Cloud Connector) environment (Cloud Foundry) and connected to a PostgreSQL database. In particular, I want to set the minIdle, maxIdle and initialSize properties for the given pool.
In a "spring-vanilla" environment (non-cloud) the properties can be easily set by using
application.properties / .yaml files with environment properties,
#ConfigurationProperties annotation.
However, this approach doesn't transfer to my Cloud environment, where the URL (and other parameters) are injected from the environment variable VCAP_SERVICES (via the ServiceInfo instances). I don't want to re-implement the logic which Spring Cloud already did with its connectors.
After some searching I also stumbled over some tutorials / guides, which suggest to make use of the PoolConfig object (e.g. http://cloud.spring.io/spring-cloud-connectors/spring-cloud-spring-service-connector.html#_relational_database_db2_mysql_oracle_postgresql_sql_server). However, that way one cannot set the properties I need but merely the following three:
minPoolSize,
maxPoolSize,
maxWaitTime.
Note that I don't want to set connection-related properties (such as charset), but the properties are associated with the pool itself.
In essence, I would like to do the configuration similarly to https://www.baeldung.com/spring-boot-tomcat-connection-pool (using spring.datasource.tomcat.* properties). The problem with that approach is that the properties are not considered if the datasource was created by Spring Cloud. The article https://dzone.com/articles/binding-data-services-spring, section "Using a CloudFactory to create a DataSource", claims that the following code snippet makes it so that the configuration "can be tweaked using application.properties via spring.datasource.* properties":
#Bean
#ConfigurationProperties(DataSourceProperties.PREFIX)
public DataSource dataSource() {
return cloud().getSingletonServiceConnector(DataSource.class, null);
}
However, my own local test (with spring-cloud:Greenwich.RELEASE and spring-boot-starter-parent:2.1.3.RELEASE) showed that those property values are simply ignored.
I found an ugly way to solve my problem but I think it's not appropriate:
Let spring-cloud create the DataSource, which is not the pooled DataSource directly,
check that the reference is a descendant of a DelegatingDataSource,
resolve the delegate, which is then the pool itself,
change the properties programmatically directly at the pool itself.
I do not believe that this is the right way as I am using internal knowledge (on the layering of datasources). Additionally, this approach does not work for the property initialSize, which is only considered when the pool is created.

Define Spring application properties default values on external library

In our company we have 100+ services built using Spring Boot. Most of these services share configuration values that allow us to configure how these applications register on Eureka, consume from Kafka and so on. This configuration is specified on the application.yml file in most of the services, and we'd like to avoid repeating these values, so we want to move this common configuration to a dependency that, when added to the classpath, will make that you no longer have to configure those values.
I achieved it by creating a new library which contains different #Configuration classes that configure the different beans with the right values. So now applications just need to add this library to the build.gradle file, and things are configured.
The problem is that, if one of the applications need to override any of the values for those beans, specifying the configuration in their application.yml file won't work, because I'm directly setting the right values to the bean.
For example, if my library declares this Bean
#Bean
#Profile("aws")
public EurekaInstanceConfigBean eurekaInstanceConfigAWS(InetUtils inetUtils) {
EurekaInstanceConfigBean eurekaInstanceConfigBean = new EurekaInstanceConfigBean(inetUtils);
AmazonInfo info = AmazonInfo.Builder.newBuilder().autoBuild("eureka");
eurekaInstanceConfigBean.setDataCenterInfo(info);
eurekaInstanceConfigBean.setInstanceId(info.get(AmazonInfo.MetaDataKey.instanceId));
eurekaInstanceConfigBean.setHostname(info.get(AmazonInfo.MetaDataKey.localHostname));
eurekaInstanceConfigBean.setIpAddress(info.get(AmazonInfo.MetaDataKey.localIpv4));
return eurekaInstanceConfigBean;
}
And someone specifies on the application.yml that they want a different hostname like
eureka:
instance:
hostname: some-custom-value.example.com
It will be ignored: the configuration set in the bean will be used, not the local application.yml values.
Is there a way to achieve an order of precedence here where values specified on application.yml are more important than the values on the bean coming from the library? I'd like a precedence like:
local application.yml > bean config in library > bean defaults coming from spring-boot/spring-cloud

How to get access and customize Freemarker's Configuration object in spring-boot 2.x?

How to get access and customize Freemarker's Configuration object in spring-boot 2.x?
It allows to set square bracket syntax as default option with something like:
Configuration#setTagSyntax(Configuration.SQUARE_BRACKET_TAG_SYNTAX)
which is not possible with any of spring.freemarker.* configuration property.
Also, it should be possible to introduce default imports having access to direct configuration of this object.
It's possible to set any FreeMarker configuration settings with spring.freemarker.settings.<settingName>, like spring.freemarker.settings.tagSyntax = square_bracket. See the JavaDoc of Configuration.setSetting(String, String) for more (https://freemarker.apache.org/docs/api/freemarker/core/Configurable.html#setSetting-java.lang.String-java.lang.String-). This is the method to which Spring delegates the assignments under spring.freemarker.settings; Spring itself doesn't know what "settings" exist or how to parse them.

Viewing current Spring (Boot) properties

I run a Spring Boot application as a .jar file which partly takes its properties from application.yml residing inside the jar while the other part of properties is being provided from another application.yml residing outside the jar. Some of the properties from the outside overwrite the properties from the inside. In order to test whether the properties were overwritten properly I would like to see the currently active ones. Is that achieveable at all out of the box? Or is the only solution to extend my application by property output logic?
If you add Spring Boot Actuator to your dependencies, you can view a lot of configuration and other info at actuator endpoints. You can view properties at /configprops endpoint.
At least as of spring boot 2.0 actuator/env will return the list of all properties per propertySources in order of their precedence, ie. if a property is redefined in >1 sources then the 1st occurrence reading from the top is the one that is active.
For a single property actuator/env/<property-name> will return the effective value and in which source it's defined
{
"property": {
"source": "applicationConfig: [file:../application-tom.properties]",
"value": "DEBUG"
},
...
}
Note: I dont know if this would reflect any changes that might happen when programmatically modifying the spring context. But that is smth. one should not do anyhow.

Resources