How to maintain, update application properties dynamically in Spring? [duplicate] - spring

This question already has answers here:
How can I reload properties file in Spring 4 using annotations?
(3 answers)
Closed 6 years ago.
I would like to maintain a list of application properties like service endpoints, application variables, etc. in a Spring application. These properties should be able to updated dynamically (possibly through an web page by system administrator).
Does spring has an inbuilt feature to accomplish this requirement?

I am not sure spring has an implementation for updating the properties file dynamically.
You can do something like reading the properties file using FileInputStream into a Properties object. Then you will be able to update the properties. Later you can write back the properties to the same file using the FileOutputStream.
// reading the existing properties
FileInputStream in = new FileInputStream("propertiesFile");
Properties props = new Properties();
props.load(in);
in.close();
// writing back the properties after updation
FileOutputStream out = new FileOutputStream("propertiesFile");
props.setProperty("property", "value");
props.store(out, null);
out.close();

Externalizing properties, take a look here
Spring loads these properties which can be configured at runtime and accessed in your application in different ways.

Add your own implementation of a PropertySource to your Environment.
Warning: Properties used by #ConfigurationProperties and #Value annotations are only read once on application startup, so changing the actual property values at runtime will have no effect (until restarted).

I am not sure, but check if you can make use #ConfigurationProperties of Spring boot framework.
#ConfigurationProperties(locations = "classpath:application.properties", ignoreUnknownFields = false, prefix = "spring.datasource")
You can keep this application.properties file in you classpath
Change the properties in this file without redeploying the application
Java Experts - I am just trying to explore my view. Corrections are always welcome.
Edit - I read a good examples on #PropertySource here

Related

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.

Spring boot application properties load process change programatically to improve security

I have spring boot micro-service with database credentials define in the application properties.
spring.datasource.url=<<url>>
spring.datasource.username=<<username>>
spring.datasource.password=<<password>>
We do not use spring data source to create the connection manually. Only Spring create the database connection with JPA.(org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration)
We only provide the application properties, but spring create the connections automatically to use with the database connection pool.
Our requirement to enhance the security without using db properties in clear text. Two possible methods.
Encrypt the database credentials
Use the AWS secret manager. (then get the credential with the application load)
For the option1, jasypt can be used, since we are just providing the properties only and do not want to create the data source manually, how to do to understand by the spring framework is the problem. If better I can get some working sample or methods.
Regarding the option-2,
first we need to define secretName.
use the secertName and get the database credentials from AWS secret manager.
update the application.properties programatically to understand by spring framework. (I need to know this step)
I need to use either option1 and option2. Mentioned the issues with each option.
What you could do is use environment variables for your properties. You can use them like this:
spring.datasource.url=${SECRET_URL}
You could then retrieve these and start your Spring process using a ProcessBuilder. (Or set the variables any other way)
I have found the solution for my problem.
We need to define org.springframework.context.ApplicationListenerin spring.factories file. It should define the required application context listener like below.
org.springframework.context.ApplicationListener=com.sample.PropsLoader
PropsLoader class is like this.
public class PropsLoader implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
#Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
String appEnv = environment.getProperty("application.env");
//set new properties based on the application environment.
// calling other methods and depends on the enviornment and get the required value set
Properties props = new Properties();
props.put("new_property", "value");
environment.getPropertySources().addFirst(new PropertiesPropertySource("props", props));
}
}
spring.factories file should define under the resources package and META-INF
folder.
This will set the application context with new properties before loading any other beans.

Spring Reload Properties on Demand

I am looking to load properties from file On Demand in my spring boot application.
I have a requirement to load the properties from the file when there is a change in the properties file.
I am using #Configuration properties to load the properties, by setting the spring.config.location=file:/../test-properties.yml.
Bean class that holds the data
#Configuration
#ConfigurationProperties
public class GetValues {
List<String> values = new ArrayList<String>();
//getters and setters
}
test-properties.yml
values:
- X1
- X2
- X3
I have a rest service to load the data loaded from properties file to backend, within which I am Autowiring the GetValues Bean.
Whenever there is a change in the property file, I will call this service which would load the properties to the Backend.
Right Now, It is loading the same properties everytime, as the properties are already loaded in to the context.
Is there a way to reload the properties from the file everytime I hit the service? I don't want to use the Spring-Cloud, #RefreshScope.
There are two solutions
Using spring devtools you can achieve auto restart when config file change. Only desirable in development environment.
Make that when you hit the service the properties file get loaded, and update your config class attributes. Abstract:
Properties prop = new Properties();
InputStream input = null;
input = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileNameProperties);
prop.load(input);
bean.setValue(prop.getProperty("attribute1"));
If you don't want to use Spring Cloud Config / #RefreshScope, one option would be to the usage of a FileChangeListener / WatchService found in Apache commons (or similar library) or JDK 7 and reload the properties. Make sure you synchronize the usage of the loaded properties and updating them when the file changes, maybe through a ReentrantReadWriteLock.

Spring Boot Custom Properties - How to include externalize properties when class is not in the application context

It is hard to understand but for my application a required format. I have some custom libraries which are included at runtime and so they are not in the spring application context. To get apis from spring boot application I catched required apis and overhand this to my external classes.
To show an example:
HashValueService hashValueService
= (HashValueService) appContext.getBean("hashValueServiceImpl");
ServiceList srvList = new ServiceList();
srvList.setHashValueService(hashValueService);
In this way I'm able to get access to my database, which is in my application context.
I have a lot of properties distributed in the whole application. So I want to use the default application.properties to centralized often used properties in my application, like the keystore.
For that I edited application.properties with this line:
application.keystore=server.jks
But of course the usage of the Spring's #Value does show me a null for that attribute, because this class is not in my application context:
#Value("${application.keystore}")
private String keystore;
Do you have an idea to overhand this properties to this customer libraries? Maybe the creation of a new property file whould help? Thank u a lot.
Majority of Spring magic is done by BeanPostProcessors. Take a good look at them - link.
#Value wiring (and much more) is performed by AutowiredAnnotationBeanPostProcessor, you can use it for your purpose:
AutowiredAnnotationBeanPostProcessor beanPostProcessor =
appContext.getBean(AutowiredAnnotationBeanPostProcessor.class);
ServiceList srvList = new ServiceList();
beanPostProcessor.processInjection(srvList);
After that, your ServiceList should have String keystore field initialized.

Set/override Spring / Spring Boot properties at runtime

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?

Resources