I have a property in config file, which contains a list of values in such format. The format has to remain the same.
myapp.values=value1,value2,value2
I use these values in my config class like that:
#Value("#{'{myapp.values}'.split(,)}")
private List<String> values;
My question is there a way to make a default value (empty list or null) it there is no such property in config file or the property is empty?
I tried to google this with no success. People either use myapp.values={value1,value2,value3} which doesn't suit me or format for that or use default values for simple variables, not lists.
My question is only about achieving the goal by changing the #Value annotation. Pls do not suggest workarounds
#Value("${myapp.values:}#{T(java.util.Collections).emptyList()}")
private List<String> defaultToEmpty;
or
#Value("#{T(java.util.Arrays).asList('${myapp.values:}')}")
private List<String> defaultToEmpty = new ArrayList();
Actually, just one symbol : with an empty default value will work:
#Value("${myapp.values:}")
private List<String> values;
Related
This question already has answers here:
Reading a List from properties file and load with spring annotation #Value
(17 answers)
Closed 3 years ago.
I need to set two values in Spring bean property value having same name.
Currently I have
I am using this property name as this in Java class : private String siteUid;
My requirement is to add another property name with diffrent value
Please suggest, if I can write both property values, and how can I use the same in Java class
You can use the #Value annotation.
If you have a properties file that contains:
foo.bar.property1=hello
foo.bar.property2=world
You can use in your Java class:
#Component
public class SomeClass {
#Value("${foo.bar.property1}")
private String variable1; // will be set to "hello"
#Value("${foo.bar.property2}")
private String variable2; // will be set to "world"
}
Note that the names of the actual variables (i.e. variable1 and variable2) are irrelevant; they can be whatever you want. The important part is that the string contained in #Value matches the key in your properties file.
Its not really clear what do you mean by adding. In general as long as you're supposed to use #Value annotation the most flexible approach by far, is using Values in autowired constructor:
#Component
public class MyComponent {
private String myProperty;
public MyComponent(#Value('${app.value1}') String value1, #Value('${app.value2}') String value2) {
// here you can even concatenate the value (hence I've said that its not clear what is 'add' but in general you can manipulate however you want
this.myProperty = value1 + value2;
}
}
You could use Spring Expression Language here to add/concat two property
#Value("#{'${property1}'.concat('${property2}')}") String parameter
Here concat is the String method. As the requirement is not very clear to me, so you could experiment with other string methods to achieve what you want.
I'm already familiar with the base behavior of Spring's #Value annotation to set a field to the value of a project property like so:
Project's Property File
foo.bar=value
Project's Configuration Class
#Configuration
public class MyConfig {
#Value("${foo.bar}")
private String myValue;
}
However I'm trying to make a SpringBoot starter project with conditional configuration and would like to standardize the property names to something useful such as "com.mycompany.propertygroup.propertyname", but to ease transition and encourage adoption, I want to support the old property names for a time as well, and was thus wondering if there was some way to allow multiple property names to set the same field? For instance:
My Theoretical Starter's Config
#Configuration
public class MyConfig {
#Value("${com.mycompany.propertygroup.propertyname}" || "${oldconvention.property}")
private String myValue;
}
Project A's Property
oldconvention.property=value
Project B's Property
com.mycompany.propertygroup.propertyname=value
I can't seem to find any documentation or SO answers on whether or not this is possible and how to achieve it if so... So I'm wondering if it is possible, or if it's not, is there an alternative to the #Value annotation that can be used to achieve the same effect?
Edit to Clarify:
I would not want to keep track of multiple values so I do not need instruction on how to get multiple values... the objective is to consolidate into a SINGLE VALUE that which may have multiple names. In practice, it would only ever have one name-value per project that uses the starter... only in rare cases when someone perhaps forgot to delete the old property would each property name be used (and it would probably have the same value anyway). In such cases, the NEW CONVENTION NAME-VALUE WOULD BE THE ONLY ONE USED.
Update
While the SpEL expression answers provided works when both properties are present, the application context cannot load when only one of the property names is present. Example:
Updated Configuration Class
#Value("#{'${com.mycompany.propertygroup.propertyname}' != null ? '${com.mycompany.propertygroup.propertyname}' : '${oldconvention.propertyname}'}"
private String myProperty;
Updated Property File
com.mycompany.propertygroup.propertyname=somevalue
Error
Caused by: java.lang.IllegalArgumentException:
Could not resolve placeholder 'oldconvention.propertyname' in value
"#{'${com.mycompany.propertygroup.propertyname}' != null ? '${com.mycompany.propertygroup.propertyname}' : '${oldconvention.propertyname}'}"
Requiring both property names to be present defeats the purpose, which is to allow an implementing project to configure this starter using EITHER the old convention OR the new convention...
Another Update...
I've been playing around with the SpEL expression a bit, and I've got the conditional check working when the property is present and when it's not, but I'm having trouble with property resolution after the fact. I think the problem is because property defaults and complex SpEL expressions don't play nice together.
#Value("#{${com.mycompany.propertygroup.propertyname:null} != null ? '${com.mycompany.propertygroup.propertyname}' : '${oldconvention.propertyname}'}")
private String myProperty;
When my SpEL is written like the above, I get a cannot resolve property placeholder exception, meaning that both properties have to be present in order for the SpEL expression to evaluate. So I got to thinking, I could use the default property syntax that I've seen for resolving optional properties: #Value("${myoptionalproperty:defaultValue}")
So below is my attempt to combine the default property resolution with the SpEL expression:
#Value("#{${com.mycompany.propertygroup.propertyname:null} != null ? '${com.mycompany.propertygroup.propertyname:}' : '${oldconvention.propertyname:}'}")
private String myProperty;
When using the default property notation, I keep getting this error:
org.springframework.expression.spel.SpelParseException:
EL1041E: After parsing a valid expression, there is still more data in the expression: 'colon(:)'
and when I Googled that error, the popular answer was that properties had to be wrapped in single quotes so that they evaluate to a string... but they're already wrapped (except the first one.. I had to unwrap that one since I wanted that to evaluate to a literal null for the null check). So I'm thinking that defaults can't be used with properties when they're wrapped in a spell expression. In truth, I've only ever seen the default property set when a #Value annotation is set with just a pure property holder, and all properties I've seen used in a SpEL expression never had a default set.
You can use the following #Value annotation:
#Configuration
public class MyConfig {
#Value("#{'${com.mycompany.propertygroup.propertyname:${oldconvention.propertyname:}}'}")
private String myValue;
}
This #Value annotation uses com.mycompany.propertygroup.propertyname if it is provided and defaults to oldconvention.property if com.mycompany.propertygroup.propertyname is not provided. If neither is provided, the property is set to null. You can set this default to another value by replacing null with another desired value.
For more information, see the following:
Spring Expression Language (SpEL)
Spring Expression Language Guide
As an alternative, you can capture both values and do a selection before returning the value:
#Configuration
public class MyConfig {
#Value("${com.mycompany.propertygroup.propertyname:}")
private String newValue;
#Value("${oldconvention.propertyname:}")
private String oldValue;
public String getValue() {
if (newValue != null && !newValue.isEmpty()) {
// New value is provided
System.out.println("New Value: " + newValue);
return newValue;
}
else {
// Default to the old value
return oldValue;
}
}
}
Using SPEL is the best way to solve this. This should work
#Value("#{'${com.mycompany.propertygroup.propertyname}' != null ? '${com.mycompany.propertygroup.propertyname}' : '${oldconvention.property}'}")
private String myValue;
No that's not possible I believe but yes you can define property as comma separated. For example
com.mycompany.propertygroup.propertyname=value1,value2,value3
And instead of receiving a String you can annotate #Value over String[] like this:
#Value("#{'${com.mycompany.propertygroup.propertyname}'.split(',')}")
private String[] propertyNames;
Another way you can also store key and value as a comma-separated string in the property file and use #Value annotation you can map into Map, For example, you want group name as key and value as group details so in the property file you can store string like this
group.details.property= {'group1':'group1.details','group2':'group2.details'}
And you can annotate #Value as
#Value("#{${group.details.property}}")
private Map<String, String> groupMap;
Basically I do not want any empty JSON arrays or objects to show up in my generated JSON files. I have already configured my ObjectMapper accordingly using the following method:
objectMapper.setSerializationInclusion(Include.NON_EMPTY);
This works fine for arrays, collections and Strings.
However if i have an empty object (= all properties are null or empty) it will still show up in the generated JSON like this:
"MyObject":{}
Here is a possible example of what I mean with an empty object:
class MyClass
{
String property1 = "";
Object property2 = null;
}
In this case I want the object to be excluded completely from the generated JSON file.
Is this possible? If yes, how to I have to configure my ObjectMapper in order to get the desired behavior?
To ignore the empty values such as you may have initialized the arrayList but there are no elements in that list. In that time using NOT_EMPTY annotation to ignore those empty value fields
#JsonInclude(Include.NON_EMPTY)
class Foo
{
String bar;
}
It's been a few years since the question was asked, but I hit this page looking for a solution. So here it is.
You need to annotate your class with NON_DEFAULT:
#JsonInclude(NON_DEFAULT)
class MyClass
{
String property1 = "";
Object property2 = null;
}
Global config is not enough as explicitly stated in the documentation:
http://fasterxml.github.io/jackson-annotations/javadoc/2.7/com/fasterxml/jackson/annotation/JsonInclude.Include.html#NON_DEFAULT
The new NON_DEFAULT is available since 2.7
Just as we can read the property using #Value annotation, is there any way to change the value of the property through the code?
#Value("#{envProps['" + Keys.Env.updatedDate + "']}")
private String date;
value in environment.properties
updatedDate =2013-10-01
I want to change the value to 2013-10-16. How to do it?
Thanks
Sorry you cant do that
#Annotations and its values in java are constants.
the #Annotations are stored in class file (at RuntimeVisibleAnnotations) while compiling.
the values of them can be String, Class, or other value and can't be change at Runtime.
see jvm class file spec
Alternative solution
use a place holder like
#Value("${updatedDate.envProps}")
private String date;
and write your custom PropertyPlaceholderConfigurer to parse updatedDate.envProps to the value you want
I have a Struts 2 (JDK 1.7, Struts 2.2.1) application that contains a list of filtering criteria, stored as strings in a map.
Map< String, String > m_filters = new HashMap< String, String >();
public Map< String, String > getFilters() {
return m_filters;
}
I pass a URL formatted like this:
http://myserver.com/myapp/GenerateReport.action?reportID=Whatever&filters.fromDate=0&filters.toDate=2000000000&filters.FcsType=piv_cardholder_3kp&detailed=true
Even though the Map has both key & value types specified as String, attempting to get a value from this
Map< String, String > filters = getFilters();
String value = filters.get( "fromDate" );
causes this exception to be thrown:
java.lang.ClassCastException: [Ljava.lang.String; cannot be cast to java.lang.String
I reproduced this in a unit test, and confirmed in a debugger that Struts 2 seems to be creating a String[1] instead of a String for each of the parameters. i.e. it is a length-1 string array with the only string being the expected value ("0" in this case).
My question is: Is this a bug in Struts2, or am I doing something incorrectly?
If it is a bug, is there a known workaround?
There are no issues if you follow Java bean conventions.
Here are some guidelines to resolve the issue:
This issue does not happen if you name the private member "filter" and provide only a getter for filter.
This issue does not happen if you provide a public setter (even if you use a different name for the private member such as m_filter) in addition to the getter
this issue only happens if you do not provide a setter and the property does not have the same name as the getter.
Long story short follow conventions. Above behaviour tested with Struts 2.3.4
What I'm guessing is happening: The getter allows for setting too (If there was only a setter you could only set one item in the map, well with the current parameter interceptor this is the case). The bean is inspected for the property to see how it should do type conversion probably first it looks for a setter failing that it looks at the action for a propterty of that name to determine the type failing that it will need to use the default. The default parameter type is a mapping of String to String[] and this is what you are seeing.
You're using the wrong notation. filters.fromDate would be the equivalent of getFilters().setFromDate(), which is not actually what you want. Dot notation is for JavaBeans.
Try using brackets, such as filters['fromDate'].
Refer to: http://struts.apache.org/2.2.1/docs/type-conversion.html#TypeConversion-RelationshiptoParameterNames.
Try this:
Object[] obj = (Object[]) filters.get( "fromDate" );
String value = (String)obj[0]);
It's not a bug you can consider it's a feature. Access RequestParameters
ParameterAware Interceptor
Let us suppose /http://localhost:8080/myApp/login.action?name=struts2&name=rocks
if you try to access the name parameter string[0] = struts2, string1=rocks