Spring Reload Properties on Demand - spring

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.

Related

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 : How to give properties file to Java.exe

I am using Spring to develop a Java application. I have stored certain properties in a properties file, which is packaged as a JAR. The properties are read in code using #Value annotation. Now when i deploy the JAR i want to supply new values for some of these properties. I know that we can give the new property value as "-Dproperty-name=property-value". But is there a way to give a new property file itself as input which has many properties to be overridden together?
Thanks
Chenbaga
You can have a number of different property files and then load then with a specific system parameter.
#Configuration
#PropertySource(value = {"classpath:/app.properties", "file:/${configLocation}/app.properties"}, ignoreResourceNotFound = true)
public class AppConfig {
Add your default app.properties to /src/resources/app.properties
Then when you start your JVM you can override with
-DconfigLocation=/home/config
If the configLocation is present it will first pick up the default properties, then override then with the ones from the file. If the system parameter is not present it will not file the file and use the defaults.
See http://docs.spring.io/spring/docs/4.3.9.RELEASE/javadoc-api/org/springframework/context/annotation/PropertySource.html for more examples

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

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

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.

Spring Cloud Refresh event does not recognize removed properties?

Background:
I have a Spring Boot 1.4 application running with Spring Cloud. My app is using the native profile to pull settings from an external config.properties file. The config server is embedded inside the same application.
In the config.properties file I have added the following setting:
app.setting=helloworld
What works:
When I change the property and send a REFRESH event, relevant beans marked are reloaded and the change is recognized correctly.
What doesn't work:
If I actually remove the property from config.properties, (by commenting it out for instance), the REFRESH event does nothing to actually refresh the application. Spring Cloud seems to not recognize the fact that the property is removed and when the data-binder proceeds to update the state of the world it misses the fact that the property is removed, and the corresponding bean linked to it must also be refreshed and its field set to blank/null, etc.
It looks like the data-binder only looks at what is at the moment available in the configuration and does not keep record of what was vs what is.
The only way to actually disable that setting in the bean configuration state is not by removing it, but by actually setting it to a blank value (which is a new value, given the setting is just a String). Note the field in Java bean mapped to this property has no default value other than null, and the value is not defined anywhere else (as in an embedded application.properties file, etc)
What might I be missing?
Is this a feature? Bug?
Thanks for your time.
Not sure if this is applicable to you, but I had a similar issue with beans annotated with #ConfigurationProperties and registered using #EnableAutoConfiguration:
#ConfigurationProperties(prefix="example")
#RefreshScope
public class MyConfig {
private List<String> values;
}
#EnableAutoConfiguration(MyConfig.class)
public class ApplicationConfiguration {
}
The problem I was experiencing is when you had a YAML configuration like:
example:
- Some
- Values
- Here
removing items from the list did not remove them from MyConfig.values when the context is refreshed.
The cause of this was that registering MyConfig using #EnableAutoConfiguration does not allow you to change the bean's scope, meaning that the bean does not get recreated when refreshing the context. See Github Issue.
My Fix
I removed MyConfig from #EnableAutoConfiguration and explicitly added a #Component annotation:
#Component
#ConfigurationProperties(prefix="example")
#RefreshScope
public class MyConfig {
private List<String> values;
}
#EnableAutoConfiguration
public class ApplicationConfiguration {
}
After this, removing items from the YAML list gets reflected in MyConfig when the context is refreshed.
I've run into a similar issue when refreshing an external config.properties file. The issue manifested itself with my config.properties because it only had a single entry in it.
To demonstrate:
Start by overriding the application properties
application.properties
some.value=abc
config.properties
some.value=xyz
Initially, using some.value will render "xyz"
To show that the value can be updated, change the value in the config.properties
config.properties
some.value=123
Using the /refresh endpoint, refresh the context and then using some.value will render "123"
Now, by removing the property, then we can see the value does not get updated
config.properties
// now empty
Using the /refresh endpoint, refresh the context and then using some.value will still render "123". It hadn't recognised that the field had been removed, nor used the "abc" value from the application.properties.
The issue stems from the class ConfigFileApplicationListener which on line 428, identifies the properties file as empty, so doesn't load the file into the property sources that are later used to compare the new properties to the old in the ContextRefresher class. The scenario appears to keep the old one in memory.
To workaround this issue, when you only have a single property, you could add property like a.b which would force the file to be loaded with the no value and result in the correct functionality.
config.properties
a.b=true
// item removed, but use some property to make sure it's read later
Hope this helps

Resources