Update values on properties file on Runtime - java

I've configuration as below:
#Configuration
public class PropertyConfiguration {
#Bean
#Profile("local")
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer();
configurer.setLocation(new FileSystemResource("path/to/resources/app-local.properties"));
configurer.setIgnoreUnresolvablePlaceholders(true);
return configurer;
}
}
My app-local.properties file contains values as:
cache.time.milliseconds=1000
So, I'm accessing the value as:
#Value("${cache.time.milliseconds}")
private long cachingTime;
I'm getting the correct value.
System.out.println(cachingTime);
Now, I want to update the cachingTime to some other value and serve that updated value. For example, from 1000 to 99.
Is there any way to update this property value at runtime??
Or is there any other way to update this value except restarting app or server?
I'm using Spring Boot 1.4.3.RELEASE.
I tried to google it, but none of answer gave me the solution. :(
Thank you for any help.

If you will change the value on property file it will not effect on runtime because all the configuration is done while server starting, if you don't want to redeploy the code base you can do one thing, change the property file value and just restart the server.

You can have a look at spring-boot admin once. Though it acts as a monitoring server, it gives you ability to update properties and environment variables.
http://codecentric.github.io/spring-boot-admin/1.5.3/
Attaching a screenshot put up a proof of concept by codecentric guys.

Related

how to reload configuration object at runtime in spring mvc

i have a configuration class FTPSchedullingConfiguration which is initialise at app instantiation and get data from table for Starting a scheduler on given Scheduling Expression.
And also my question are not Duplicate as Reloading/Refreshing Spring configuration file without restarting the servlet container like this because i have no any XML File, my application is based on annotation and here Every classes are compiled in .class formate so we can`t change any code at runtime.
#Configuration
#EnableScheduling
public class FTPSchedullingConfiguration {
#Autowired
private FTPTransferServiceInterface ftpTransferServiceInterface;
#Bean
public String getCronFTPTransferValue()
{
return ftpTransferServiceInterface.getFTPTransferById((long) 1).getTransferCroneTime();
}
#Scheduled(cron="#{#getCronFTPTransferValue}")
public void ftpTransfer() {
ftpTransferServiceInterface.transferFTPRecording();
}
}
But when i change the Schedule Expression of table then no changing on scheduled time. if i restart server then changed Expression load.
by using updateFTP(),
#RequestMapping(value= {"/updateFTPTransfer"})
private String updateFTP(...){
ftpTransferServiceInterface.updateFTP(ftpDomain);
//here i want to change the value of #Scheduled
}
my actual need like when i changed Expression value in table by updateFTP(...){...} at that time also my schedulling value changed (OR) Refreshed FTPSchedullingConfiguration class.
Thanks in Advance.

Spring boot properties to be loaded at initialization and respect all and control #Aspect based on the value from property file

We are loading properties from an external file using #PropertySources. Now I want to enable/disable #Aspect based on a property. I tried using #ConditionalOnExpression which didn't work. I tried the same by creating a bean of propertyplaceholderconfig. Even in the same case, it didn't work. Then I tried #profile which also didn't work initially.
What I Figured out is that these variables are not initialized at the starting when propertysource or propertyplaceholder bean is used at startup. Some variables are always ignored like (logging.file). But #Value works fine. In order to set these variables, I've to pass them as JVM parameters.
So my questions are:
1. How can I make spring to always read specified property files at startup and respect all of them?
2. Which is the best way to enable/disable #Aspect. Using #profile or #ConditionalOnExpression or something else?
Currently, we are setting logging.file in the main method since this also behaves the same way. But you guys know that it's not the proper way as I may end up adding the properties one by one like this. I want to put all the properties into external files such that spring reads those files and sets its properties.
Our properties structure:
common.properties #This has all common properties
service.properties #Property specific to a service. This will also contain existing property from common.properties which will be overridden.
I understand that I can use profiles. But, we want to keep the properties outside such you need to restart service if you are changing the properties. I also don't want to pass the variables as JVM parameters then I've to pass most of the variables in this way. Passing -Dspring.config.location is also difficult as common.properties and service.properties are used and 'service.properties' filename varies for each service.
sample codes:
Mainclass:
#PropertySources({
#PropertySource(value = "file:${property_path}/common.properties", ignoreResourceNotFound = false),
#PropertySource(value = "file:${property_path}/service1.properties", ignoreResourceNotFound = true) })
public class MainClass {
static String logDirectory = ApplicationContext.getGlobalProperty("logging.file");
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(MainClass.class);
Properties properties = new Properties();
properties.put("logging.file", logDirectory);
springApplication.setDefaultProperties(properties);
springApplication.run(args);
}
}
Application Context:
#Configuration
#EnableAutoConfiguration
public class ApplicationContext implements EnvironmentAware {
private static Environment environment;
#Override
public void setEnvironment(Environment environment) {
ApplicationContext.environment = environment;
}
public static String getGlobalProperty(String propertyName) {
return environment.getProperty(propertyName);
}
}
Here you can see any way I've used environment to get property. Is there any way to set the property using the environment such that while spring boot initialization itself the properties are populated?
We can also implement ApplicationContextInitializer and override initialize method to read properties. But how can I make it read 2 property files and override the duplicate property with the latest value? Reference(I'm not sure how to implement my requirements in this way.). Even in this case doesn't sound like you are trying to kill a mosquito with a hammer?
Current working Solution:
#Aspect
#Profile("!production")
#Configuration
public class ControllerAspect {
#pointcut(....)
} //Here also I've to pass spring.profiles.active as JVM params.
//setting the value in common.properties or service1.properties is not working.
I'm a newbie to spring boot so please let me know for additional clarifications.
It seems Spring by default loads some properties at initialization and unless until you specifically write logic to overwrite them (like the one I wrote in MainClass.java) there is no option to override those. Some of these include (logging.file, key used in #ConditionalonExpression).
Some tricks with their own challenges:
Specify the properties in application.properties in your classpath. The variables loaded at the earlier stages are always read from this file. challenge: I've tight coupled all my properties into the jar and in order to change the values I've to recompile and relaunch the Jar.
Use profiles and define application.properties as application-profile.properties. challenge: I've to create so many profiles and still the previous challenge exists.
Pass the property value as JVM parameter as -Dproperty.key=value. challenge:seriously? How many properties am I supposed to send as JVM parameter?
Implement ApplicationContextInitialize and override initialize method.challenge:Overriding Spring's default behaviour is not recommended as well as isn't it an overkill to use this just for reading property file?
Solution:
Use -Dspring.config.location to specify the property files. In this case, always spring reads the properties only from the specified location(s). You can provide multiple property files as well. Refer this for much more details. It seems if you give property locations as Directories spring loads them in reverse order. But if you specify files it follows the order specified.
Note: All these can be combined together. To know about precedence refer this.

Spring Boot ignores spring.config.name/location properties

I've got quite a simple application.yml file:
spring:
config:
name: android,ios,test,web
I expected to gain an ability to name config files like android.yml and put them into
classpath:/,classpath:/config/,file:./,file:./config/
as the DEFAULT_SEARCH_LOCATIONS constant from the ConfigFileApplicationListener class specifies. I created a file in the same directory with the main config:
android:
clientId: 0
clientSecret: clientSecret
Then I wrote a #Configuration class with one method to get an instance of ClientDetails by the #ConfigurationProperties:
#Configuration
public class TrustedClientInformationConfiguration {
#Bean(name = ANDROID)
#ConfigurationProperties(prefix = ANDROID)
public ClientDetails getAndroidClientDetails() {
return new BaseClientDetails();
}
}
Unfortunately, after autowiring, I got the instance with unfilled fields. What have I missed?
EDIT1: I found and debugged a method where CONFIG_NAME_PROPERTY = "spring.config.name" is used (it's only one usage), the containsProperty condition always returns false:
private Set<String> getSearchNames() {
if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) {
return asResolvedSet(this.environment.getProperty(CONFIG_NAME_PROPERTY),
null);
}
return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
}
EDIT2: It is a try to create an oauth2 client configuration by moving properties to a separate file for each trusted client. They should be always instantiated despite the active profile.
The spring.config properties should be set on the command-line as they're needed before any config files are loaded.
However it looks like the feature you actually want is profiles. They're a much easier way to organise different configuration for different environments. Files can be named application-android, application-test, etc.
Documentation

Spring PropertyPlaceHolder Java Config external properties file

So this has to be some silly mistake, which I've not been able to pass through. I'm trying to externalize my properties file, currently placed in my user home. I'm loading the properties file using #PropertySource like this:
#Configuration
#PropertySources(value = { #PropertySource("file:#{systemProperties['user.home']}/.invoice/config.properties") })
public class PropertiesConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertiesPlaceHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
But unfortunately, that is not loading the properties file. Throws FileNotFoundException. But if I change the path to:
#PropertySources(value = { #PropertySource("file:/home/rohit/.invoice/config.properties") })
it works properly. And that is the path which the earlier path resolves to. I've logged it to verify. So it seems to me that SpEL is not getting evaluated in the #PropertySource annotation. Is it supposed to work that way?
If yes, then is there any other way to read the external properties file, which sits in /home/rohit? I don't want to give absolute path, for obvious reasons. And I would like to avoid extending PropertyPlaceHolderConfigurer class.
One other option I tried was adding the /home/rohit/.invoice folder to tomcat classpath. But seems like Spring doesn't use System Classpath to resolve classpath: suffix. Any pointers on this?
In a #PropertySoure annotation EL expressions won't work. You are allowed to use placeholders ${...} however that is also limited to system or environment variables. However as you want to resolve the home directory of the user you can use the ${user.home} placeholder.
#PropertySource("file:${user.home}/.invoice/config.properties")
This should work as desired.

How to autowire Enviroment into a PropertySourcesPlaceholderConfigurer?

I'm trying to set up my Spring app such that a different .properties files is read depending on the configuration profile. I'm using java config and so what I'm trying to do is this:
#Autowired
private static Environment env;
#Bean
public static PropertySourcesPlaceholderConfigurer properties(){
PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
String[] profiles = env.getActiveProfiles();
String filestring = "environment."+profiles[0]+".properties";
ClassPathResource properties = new ClassPathResource( filestring );
Resource[] resources = new ClassPathResource[] { properties };
pspc.setLocations( resources );
return pspc;
}
However the env.getActiveProfiles() is giving me a NullPointerException, which I assume means that the environment hasn't been injected. Any one got any ideas how I can fix this? Or alternatively if this is dumb/impossible how I could go about this better?
Just to give you an alternate perspective on your approach (clearly everyone's business case may differ from project to project,) but the type of configuration you are pursuing might lead to other headaches down the road. Security comes to mind. Usually multiple environments means you are dealing with usernames and passwords for various connections to databases and such. Storing those values for a production environment in line with your other configurations could expose sensitive data to developers who need have no knowledge of such things. Rather, if you switch using SPeL expressions and referencing the environment directly, then you can still achieve your runtime configuration but move your settings for each environment to the server (or what-have-you) where those specific configs apply. Example:
<bean id="myDatabase" class="mypackage.MyDatabase" p:username="#{environment['DB_USERNAME']}" p:password="#{environment['DB_PASSWORD']}" .../>
Then on your server, you can pass in system properties OR set environment variables with your desired username and password, and they will be configured at runtime. (The environment expression resolves directly to your Environment instance.)
Just a thought. =)
As #kungfuters rightly suggested, the business case may differ from application to application. Here is another alternative that worked for my application.
Provide an implementation of following interface:
ApplicationContextInitializer<ConfigurableApplicationContext>
Provide implementation of the following method. The logic to identify the profile goes in this method.
initialize(ConfigurableApplicationContext ctx)
Based on the identification, set the active profile:
this.applicationContext.getEnvironment().setActiveProfiles(<<yourProfileName>>)

Categories