I have a Spring Boot Application where one of the property in application.properties file is:
pro.perty=no
We read this value in a class and print values based on this properties value:
#Service
class MyClass {
private String property;
public MyClass(#Value("${pro.perty}") String property) {
this.property = property;
}
public void display() {
if(property.equals("yes")) {
System.out.println("Today");
} else {
System.out.println("Tomorrow");
}
}
}
value of pro.perty is "no" in the local application property file. But on Pivotal Cloud Foundry(PCF), where it is deployed, I have set the value of this property to "yes". As external property variables (external to jar) have higher priority than inner ones, so it prints "Today". This works fine.
Yesterday mistakenly, I changed the key of property in PCF to "pro_perty=yes" instead of "pro.perty=yes". Now, the code should print "Tomorrow" as it is not getting any key named "pro.perty" in PCF variables. It only has that key in local application properties file where it is set to "no".
However, it is still printing "Today". How is it able to read "pro_perty" when what it expects is "pro.perty"?
Related
I'm using the #PropertyInject annotation to get properties from the application.properties file to use in my beans.
This normally works fine, but now I need to be able to change the injected property based on a header value.
In my head it looks something like this:
#PropertyInject(PROPERTY_NAME)
private String property;
public void pickProperty(String msgVersion) {
if (msgVersion.equals("A")) {
PROPERTY_NAME = "property.firstType.name";
} else {
PROPERTY_NAME = "property.secondType.name";
}
}
I've considered just injecting both properties and deciding in the main method which one to use, but that seems like a roundabout way of doing things and will get a bit bloated if more versions are added.
Is there an easy way this can be done?
now I need to be able to change the injected property based on a header value
Properties and Beans are created on application startup and typically do not change while the application is running. They both have application scope.
Header values on the other hand can change for every message that is processed by your application.
As you suggested yourself: You can inject both properties into the Bean and provide a method that is called once per message to get the correct value
#PropertyInject(PROPERTY_A)
private String propertyA;
#PropertyInject(PROPERTY_B)
private String propertyB;
// called for every message processing
public String pickProperty(#Header("msgVersion") String msgVersion) {
if (msgVersion.equals("A")) {
return propertyA;
} else {
return propertyB;
}
}
This is not at all a workaround, but simply a method that returns a different result based on the input.
I have a large Spring web application, dating back several years. I need to update it to Spring Boot (corporate requirement). I'm well on my way - it starts (!), although there are some issues with properties being injected, which causes the app to fail.
Specifically, there are three huge config files per profile, eg qa_config.properties, qa_ehcache.xml, qa_monitoring.properties. At present I only care about qa_config.properties, which I have renamed to Spring Boot's preferred name, application-qa.properties, and application-qa_monitoring.properties
The application has a number of classes annotated with #Named (from the javax.ws.rs-api ) which are loaded early - so early that I need to inject properties in the constuctor:
package com.domain.app;
import org.springframework.beans.factory.annotation.Value;
import javax.inject.Named;
#Named
public class Foo {
// Cant use #Value here, it is not yet in the context
protected String bar; // lives in application-qa.properties
protected String qux; // lives in application-qa_monitoring.properties
public Foo(#Value("${application.property.named.bar}") String bar,
#Value("${monitoring.property.named.qux}") String qux) {
this.bar = bar;
this.qux = qux;
doSomeWork();
}
}
Properties files:
#application-qa.properties
application.property.named.bar=something
and
#application-qa_monitoring.properties
monitoring.property.named.qux=another_thing
My problem: I want to have both application-qa.properties and application-qa_monitoring.properties in context as soon as possible, and before the #Named classes are loaded.
To achieve this, I am running the application with an active profile of qa, which successfully adds that set of properties into the context.
I added this line to the application.properties file, to ensure that the other properties are loaded:
spring.profiles.include=${spring.profiles.active}_monitoring.properties
When I run the Spring Boot app, the output tells me
The following profiles are active: qa_monitoring.properties,qa
When debugging the Foo class, the value of bar is correct
But, the value of qux is null.
Am I missing something about the order in which properties files are loaded? I would have thought that the include line in application.properties would be sufficient to "flatten" the two files very early on, if one is in context, so both should be available?
What I could do instead is just throw all the vars in the two properties files into one, the application-qa.properties but I'd like, if possible, to keep them seperate and as close to the original structure as possible.
Thanks to pvpkiran and Andy Brown.
My application.properties file should have read
spring.profiles.include=${spring.profiles.active}_monitoring
ie, just adding another profile, in this case qa_monitoring - Spring automagically adds the application- prefix and the .properties suffix
The issue you are having is because you use a literal value instead of a lookup key in the #Value annotation of your qux value.
Replace
public Foo(#Value("${application.property.named.bar}") String bar,
#Value("monitoring.property.named.qux") String qux) {
With
public Foo(#Value("${application.property.named.bar}") String bar,
#Value("${monitoring.property.named.qux}") String qux) {
And it should work.
I have one java class:
public static class ErrorMessagesLocalized {
#Value("${INVALID_ARGUMENT}")
private String value;
...
}
I have 2 property files which contains same key(INVALID_ARGUMENT).
Does spring allow to create 2 instances of ErrorMessagesLocalized (instance per file) ?
Spring picks up the value from the last property file it reads. If you want to read both the files and decide on runtime from where you need to read the value, follow this post :
Reading multiple properties having same keys in spring
I am trying to build up a multiple-module project, every module would be loaded by executing the PropertyPlaceholderConfigure properties, then I found the properties were not injected when I started to run. Instead, it's still using the default property, for example, I configure the input expression in the main class as Value("${esb.mas.path:/home/mas}")
, and define esb.mas.path=/home/vfs/mas in the corresponding module property file, but the modified values were never used.
By debugging and tracing the code, I found the problem, but I am still not sure if this is a bug from spring framework, in the following lines,
public String org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(String value) {
String result = value;
for (StringValueResolver resolver : this.embeddedValueResolvers) {
if (result == null) {
return null;
}
result = resolver.resolveStringValue(result);
}
return result;
}
I noticed that if the value of first place of embeddedValueResolvers list cannot be caught, they would use the default /home/mas, when continually resolving the embeddedValueResolvers, the parameter for method of resolveStringValue is the returned default value which is /home/mas, the real value of esb.mas.path was not used.
org.springframework.util.PropertyPlaceholderHelper.parseStringValue(String, PlaceholderResolver, Set<String>)
I need change properties in my application at runtime.
For example I have a service which send a e-mail with resset password. Request is valid 12 hours. But I want to change this time to 24 or more at runtime. I need to give the opportunity to this action for admin.
My property file has
hours.expired=12
My service
private int hoursExpiredPassword;
public void setHoursExpiredPassword(int hoursExpiredPassword) {
this.hoursExpiredPassword = hoursExpiredPassword;
}
#Override
public ERequests checkRequest(String number, Date date) {
PasswordResetRequest findedObject = passwordResetRequestDao.getObjectByElement(PasswordResetRequest.class, "requestId", number);
if (findedObject == null){
return ERequests.BAD_REQUEST;
}else{
long result = getDateDiff(findedObject.getRequestDate(),date,TimeUnit.HOURS);
if(result >= hoursExpiredPassword){
return ERequests.EXPIRED_REQUEST;
}
}
return ERequests.CORRECT_REQUEST;
}
my spring xml configuration
<bean id="passwordResetRequestService" class="pl.lublin.example.services.servicesDAO.PasswordResetRequestService">
<property name="passwordResetRequestDao" ref="passwordResetRequestDao"></property>
<property name="hoursExpiredPassword" value="${hours.expired}"></property>
</bean>
Could I change this value in someway at runtime?
Just move away from xml configuration its almost 2017.
#Service
public class PasswordResetRequestService {
#Value("${hours.expired:12}")
private int hoursExpiredPassword;
#Autowired
private PasswordResetRequestDao passwordResetRequestDao;
public void setHoursExpiredPassword(int hoursExpiredPassword) {
this.hoursExpiredPassword = hoursExpiredPassword;
}
#Override
public ERequests checkRequest(String number, Date date) {
PasswordResetRequest findedObject = passwordResetRequestDao.getObjectByElement(PasswordResetRequest.class, "requestId", number);
if (findedObject == null){
return ERequests.BAD_REQUEST;
}else{
long result = getDateDiff(findedObject.getRequestDate(),date,TimeUnit.HOURS);
if(result >= hoursExpiredPassword){
return ERequests.EXPIRED_REQUEST;
}
}
return ERequests.CORRECT_REQUEST;
}
}
With #Value you are pulling hours.expired value from properties file, if there is no value default will be 12. You can also call setHoursExpired at runtime and set new value and expose that functionality to your admins.
This is convenient for one time actions. If you want your admins to permanently change password expiration time i would instead persist hours.expired value in mysql or whatver db you are using.
EDIT : answering to perfectly valid #matt remark . If that is the case and moving to Java confing is not an option. For custom behavior you can just autowire your XML-defined beans in your service and perform whatever logic you want to.
#Autowired
private pl.lublin.zeto.zetoRA.services.servicesDAO.PasswordResetRequestService passwordResetRequestService;
EDIT: 2020
Defacto standard way of doing this in 2020 is setup of Cloud Config server backed by a git repository. Example:
spring:
cloud:
config:
server:
git:
uri: https://github.com/spring-cloud-samples/config-repo
as explained here : https://cloud.spring.io/spring-cloud-config/reference/html/
You need standalone spring config app which will be used by all the client apps. The most robust solution is to back spring config server by git repository. By doing this we have version control on production settings and avoid the risk of changing something then forgetting what the previous value was etc.
I have to use XML configuration. Our project is based on it.
Finally I need to get all configuration values from db. The simplest solution is used service for configuration and always invoke configuration state from db.
configurationService.findAllConfigurations().get("hours.expired")
this return value what is stored in db.
But I think there is better solution.