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
Related
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;
I'm playing around with the ZK 8 MVVM form validation system and generally it seems to do what I want, but I wonder what the definition of the dependent property index is...
Let's take a simple validator...
public class FormValidator extends AbstractValidator {
#Override
public void validate(final ValidationContext ctx) {
Property[] properties = ctx.getProperties("firstName");
Object value0 = properties[0].getValue();
Object value1 = properties[1].getValue();
}
}
So, when this is called before the save command, for every property, I get a Property[] array of length 2. But somehow, I have yet to find out what is stored in [0] and what is stored in [1]. Sometimes it seems that [0] stores the current value (which may or may not be valid according the field validator there) and [1] the last valid entry... But sometimes it seems to be the other way round...
The examples in the documentation always seem to simply take the first element ([0]) for validation, but I would like the understand what both parts of this pair actually mean...
Anyone got an idea for that?
I might be off the mark with my answer, but if you are using ZK8, you should look into using Form binding
That way you do not have to handle Properties in your validator and can retrieve a proxy object matching the bean you use for your form.
If you are using a User POJO with a firstName and lastName attribut.
User myProxy= (User ) ctx.getProperty().getValue();
And then you can validate both fields by simply doing getFirstName and getLastName on myProxy.
Hope it helps.
In login Action I am checking user authentication, and if it is validated, I am putting the user bean into sessionMap:
public String execute()
{
if(userValid)
sessionMap.put("userBean", userBean); //userBean retrieved from DB
}
Now on the landing jsp, when trying to retrieve the session items:
<s:property value="#session.userBean.name" />
Obviously this would return an Object type, as I am storing it that way, so how can I type caste this to UserBean class.
I was expecting to get a solution for this on Google, but found it nowhere since this seems to be a basic implementation. So please let me know if there is any other way to implement this functionality using Struts2.
This works fine for me...
<sp:property value="#session.usertype"/>
<sp:property value="#session.bean.loginID"/>
This both worked fine for me...
sessionMap.put("bean", loginBean);
sessionMap.put("usertype", loginBean.getUserType());
I declared something like this....
Just make sure that in property tag you you same name you used while setting the bean in sessionMap ....
This should probably work....
Obviously you can't cast it to UserBean class if the object is not the instance of that class. In the value attribute you have put a string "#session.userBean.name". Struts parse this string for OGNL expression and if it's a valid expression that returns a value, it will replace it with that value. The returned type is Object, but this type is determined by ValueStack implementation.
Then property tag writes this object to the out. It uses toString() to convert the object to string. And if your object implements this method, then this value would be written.
Looks like your expression returns an Object, which has instance type String, so it's already implemented this method.
#RequestMapping(value = "/Fin_AddBankAccount", method = RequestMethod.POST)
public #ResponseBody JsonResponse addCoaCategory(
#RequestParam(value="code", required=true) long code,
#RequestParam(value="startFrom", required=true) long startFrom,
#RequestParam(value="name", required=true, defaultValue="N/A") String name)
{
}
defaultValue="N/A" not working , As I did not provide any text in name field , it store null in database instead of "N/A"?
What is the point of setting a default value if you really want that parameter.
if you mark it as required true(not needed as it is default) then no need of a default value.
If that parameter is not mandatory then mark it as false and give a default value.
Documentation of Spring RequestParam.required
Default is true, leading to an exception thrown in case of the parameter missing in the request. Switch this to false if you prefer a null in case of the parameter missing.
From your question I figured out that you are sending parameter name with empty value using POST request. According to the Spring documentation you should not send name parameter in the request in order to use default value. Simply remove name field from HTML form if it is empty.
It seems that default values makes more sense for GET requests.
make sure you don't pass empty string value
Valid Methods:
1. Fin_AddBankAccount?name=
O/P: name="N/A"
Fin_AddBankAccount?
O/P: name="N/A"
Invalid Methods:
Fin_AddBankAccount?name=""
this will set empty string to variable i.e. name="";
In my project
#RequestParam(value="name", required=true, defaultValue="N/A") String name
This code correctly sets name variable as defaultvalue N/A when requestparam "name" was not provided. My guess is you are not inserting this name variable into the table properly so database is storing null instead of "N/A". Please show us or double check the data access object code. Good luck
Thanks #TiarĂª Balbi, in fact you do not need "required=true" because defaultValue="N/A" implicitly sets this variable as required=false anyways.
I have the following JSON, where can be either true or false:
{"flag1":<boolean value>, "flag2":<boolean value>}
And I have tried to bind it to a Java class using Jersey and the following JAXB annotations:
#XmlRootElement
public class MyClass {
#XmlElement(name = "flag1", type = Boolean.class)
private Boolean flag1;
#XmlElement(name = "flag2", type = Boolean.class)
private Boolean flag2;
...
}
The problem is that when I assign a non-boolean value to 'flag1' or 'flag2', like in the example below, JAXB automatically assigns a false value to the 'flag1' and 'flag2' fields of MyClass.
{"flag1":"foo", "flag2":"bar"}
Is there a way to annotate 'MyClass' so that when JSON's 'flag1' and 'flag2' are not boolean I get an exception?
It looks like Jersey is simply using Boolean.valueOf, which treats everything other than a literal "true" as false. Since JavaScript doesn't have a notion of variable type, this is an arguably valid behavior.
An XML mapping, by comparison, is based on a schema definition, which does have a very specific notion of boolean values.
Not having used Jersey (or JAXB since the 1.x days), I'm wondering if you have to annotate the actual variables, or if you could annotate the setters. Or perhaps you could provide a setter that takes a String and parses it, instead of / along with a setter that takes a boolean.
What you showed would work the way you want (throw an exception) if you used pure Jackson JAX-RS provider. It does accept some variations (1 and 0, since some languages do not have native boolean type), but not things that have no meaningful equivalent.
Alternatively, as suggested, a setter method with type String would make sense, since then you could manually control conversions.