JSP to hashmap? [duplicate] - java

Can a Spring form command be a Map? I made my command a Map by extending HashMap and referenced the properties using the ['property'] notation but it didn't work.
Command:
public class MyCommand extends HashMap<String, Object> {
}
HTML form:
Name: <form:input path="['name']" />
Results in the error:
org.springframework.beans.NotReadablePropertyException: Invalid property '[name]' of bean class [com.me.MyCommand]: Bean property '[name]' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
Is this not allowed or do I have incorrect syntax?

Springn MVC commands need to use JavaBeans naming conventins (ie getXXX() and setXXX()) so no you can't use a map for that.
One alternative is to have a bean with a single Map property ie:
public class MyCommand {
private final Map<String, Object> properties = new HashMap<String, Object>();
public Map<String, Object> getProperties() { return properties; }
// setter optional
}
Then you can do something like this (not 100% sure on the syntax but it is possible):
Name: <form:input path="properties['name']" />

Combining the answer of cletus and dbell I could actually make it work and would like to share the solution with you (including binding of values when submitting the form, a flaw reported for cletus solution)
You cannot use directly a map as command, however have another domain object that needs to wrap a lazy map
public class SettingsInformation {
private Map<String, SettingsValue> settingsMap= MapUtils.lazyMap(new HashMap<String, SettingsValue>(),FactoryUtils.instantiateFactory(SettingsValue.class));
public Map<String, SettingsValue> getSettingsMap() {
return settingsMap;
}
public void setSettingsMap(Map<String, SettingsValue > settingsMap) {
this.settingsMap = settingsMap;
}
}
SettingsValue is a class that actually wraps the value.
public class SettingsValue {
private String value;
public SettingsValue(String value) {
this.value = value;
}
public SettingsValue() {
}
public String getValue() {
return value;
}
public void setValue(String propertyValue) {
this.value = propertyValue;
}
The controller method providing the model looks like this:
#RequestMapping(value="/settings", method=RequestMethod.GET)
public ModelAndView showSettings() {
ModelAndView modelAndView = new ModelAndView("settings");
SettingsDTO settingsDTO = settingsService.getSettings();
Map<String, String> settings = settingsDTO.getSettings();
SettingsInformation settingsInformation = new SettingsInformation();
for (Entry<String, String> settingsEntry : settings.entrySet()) {
SettingsValue settingsValue = new SettingsValue(settingsEntry.getValue());
settingsInformation.getSettingsMap().put(settingsEntry.getKey(), settingsValue);
}
modelAndView.addObject("settings", settingsInformation);
return modelAndView;
}
Your form should look like this
<form:form action="${actionUrl}" commandName="settings">
<form:input path="settingsMap['exampleKey'].value"/>
<input type="submit" value="<fmt:message key="settings.save"/>"/>
</form:form>
The controller method handling form submission works as usual
#RequestMapping(value="/settings", method=RequestMethod.POST)
public ModelAndView updateSettings(#ModelAttribute(value="settings") SettingsInformation settings) {
[...]
}
I verified the SettingsInformation bean is actually filled with the values in the form.
Thanks for helping me with this one; If you have any questions feel free to ask.

Ok i have a solution that works for me i use MapUtils.lazyMap
//My Root domain
public class StandardDomain {
private Map<String, AnotherDomainObj> templateMap= MapUtils.lazyMap(new HashMap<String, AnotherDomainObj>(),FactoryUtils.instantiateFactory(AnotherDomainObj.class));
public Map<String, AnotherDomainObj> getTemplateContentMap() {
return templateMap;
}
public void setTemplateContentMap(Map<String, AnotherDomainObj > templateMap) {
templateMap = templateMap;
}
}
//my second domain
public class AnotherDomainObj {
String propertyValue="";
public String getPropertyValue() {
return propertyValue;
}
public void setPropertyValue(String propertyValue) {
this.propertyValue = propertyValue;
}
}
//In my JSP
<input type="text" value="testthis" name="templateMap['keyName'].propertyValue"/>

Yes it can but... You need to reference it as <form:input path="name[index].key|value"/>
e.g.
<form:input path="name[0].value"/>

Related

DynamoDBMapper unconvert attribute error

I am getting json from dynamoDb that looks like this -
{
"id": "1234",
"payment": {
"payment_id": "2345",
"user_defined": {
"some_id": "3456"
}
}
}
My aim is to get the user_defined field in a Java HashMap<String, Object> as user_defined field can contain any user defined fields, which would be unknown until the data arrives. Everything works fine except my DynamoDBMapper cannot convert the user_defined field to a Java HashMap. It is throwing this error -
Exception occured Response[payment]; could not unconvert attribute
This is how the classes looks like -
#DynamoDBTable(tableName = "PaymentDetails")
public class Response {
private String id;
public Response() {
}
private Payment payment = new Payment();
#DynamoDBHashKey(attributeName="id")
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public Payment getPayment() {
return payment;
}
public void setPayment(Payment payment) {
this.payment = payment;
}
}
The payment field mapper -
#DynamoDBDocument
public class Payment {
private String payment_id:
private HashMap<String, Object> user_defined;
public Payment() {}
public getPayment_id() {
return payment_id;
}
public setPayment_id(String payment_id) {
this.payment_id = payment_id;
}
#DynamoDBTypeConverted(converter = HashMapMarshaller.class)
public HashMap<String, Object> getUser_defined() {
return user_defined;
}
public void setUser_defined(HashMap<String, Object> user_defined) {
this.user_defined = user_defined;
}
}
The HashMapMarshaller(Just to check if Hashmap marshaller wasn't working with gson, I just defined a Hashmap, put in a value and return it, but seems to still not working) -
public class HashMapMarshaller implements DynamoDBTypeConverter<String, HashMap<String, Object>> {
#Override
public String convert(HashMap<String, Object> hashMap) {
return new Gson().toJson(hashMap);
}
#Override
public HashMap<String, Object> unconvert(String jsonString) {
System.out.println("jsonString received for unconverting is " + jsonString);
System.out.println("Unconverting attribute");
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("key", "value");
return hashMap;
//return new Gson().fromJson(jsonString, new TypeToken<HashMap<String, Object>>(){}.getType());
}
}
Marshaller approach is till now not working for me. It is also not printing any of the printlns I've put in there. I've also tried using #DynamoDBTyped(DynamoDBMapperFieldModel.DynamoDBAttributeType.M) and using Map instead of HashMap above my user_defined getter to no avail.
I want to find out how to convert the user_defined field to Java HashMap or Map. Any help is appreciated. Thank you!
Make Map<String, Object> to Map<String, String>. It should work without any custom converters. Otherwise be specific about Map's value type. For example, Map<String, SimplePojo> should work. Don't forget to annotate SimplePojo class with #DynamoDBDocument.
With Object as a type of Map's value, DynamoDB will not able to decide which object it has to create while reading entry from DynamoDB. It should know about specific type like String, Integer, SimplePojo etc.

struts multi-dimensional map-backed action form [duplicate]

I'm trying to have a dynamic form and, depending on an attribute type, I would like to display a different input style (textfield, radio buttons, dropdown, checklist, ...).
In order to have the dynamic form, I've set up the ActionForm with a Map.
Map<String, Object> values;
public void setValue(String key, Object value);
public Object getValue(String key);
My problem comes when I try to set up a checklist or multibox. The ActionForm only passes one value, although I would have expected that the String[] would be mapped to the Object argument.
Any idea about how can I solve this?
EDIT: in the JSP:
<input type=checkbox name="value(foo)" />
I looked into this issue and found out what was happening. The problem is not with Struts but with BeanUtils (which Struts uses for populating the form with the request parameters).
I managed to duplicate this by extracting a (test only) snippet of code from the framework:
public class MyForm {
// assume this is your Struts ActionForm
public void setValue(String key, Object val) {
System.out.println(key + "=" + val);
}
}
public class Test {
public static void main(String[] args)
throws IllegalAccessException, InvocationTargetException {
MyForm s = new MyForm();
Map<String, Object> properties = new HashMap<String, Object>();
// Your request should be like yourActionUrl?value(foo)=1&value(foo)=2&value(foo)=3
// and Struts calls bean utils with something like:
properties.put("value(foo)", new String[] {"1", "2", "3"});
BeanUtils.populate(s, properties);
}
}
When your run this you get printed one value only (just as you desrbibed):
foo=1
The thing is that BeanUtils considers this a mapped property and treats it as such, going for a scalar value for the key. Since your value is an array it just uses the first element:
...
} else if (value instanceof String[]) {
newValue = getConvertUtils().convert(((String[]) value)[0], type);
...
What you can do is modify your JSP and ActionForm to treat the list of values separately. Example modified:
public class MyForm {
private Map<String, Object> map = new HashMap<String, Object>();
public void setValue(String key, Object val) {
map.put(key, val);
}
public void setPlainValue(String[] values) {
// this is correctly called; now delegate to what you really wanted
setValue("foo", values);
}
}
public class Test {
public static void main(String[] args)
throws IllegalAccessException, InvocationTargetException {
MyForm s = new MyForm();
Map<String, Object> properties = new HashMap<String, Object>();
// Notice the change to your URL..
// yourActionUrl?plainValue=1&plainValue=2&plainValue=3
properties.put("plainValue", new String[] {"1", "2", "3"});
BeanUtils.populate(s, properties);
}
}
The above means that you use
<input type="..." name="value(foo)" ... />
for all the single elements in your JSP, while for your checkboxes (extending it to multivalue elements in general) you use
<input type="checkbox" name="plainValue" ... />
and you delegate to the map once in your ActionForm.

I'm getting "Value is not a valid option" for SelectManyCheckBox

I have this code in my JSP page:
<h:selectManyCheckbox id="chb" value="#{MyBean.selectedCheckBoxes}" layout="pageDirection">
<f:selectItems value="#{MyBean.checkBoxItems}"/>
</h:selectManyCheckbox>
And in my MyBean:
public class MyBean {
public MyBean() {
for (Elem section : sections) {
checkBoxItems.put(section.getName(), section.getObjectID());
}
}
private String[] selectedCheckBoxes;
private Map<String, Object> checkBoxItems = new LinkedHashMap<String, Object>();
public String save() {
//save is not being executed....
return FORWARD;
}
public Map<String, Object> getCheckBoxItems() {
return checkBoxItems;
}
public void setCheckBoxItems(Map<String, Object> checkBoxItems) {
this.checkBoxItems = checkBoxItems;
}
public String[] getSelectedCheckBoxes() {
return selectedCheckBoxes;
}
public void setSelectedCheckBoxes(String[] selectedCheckBoxes) {
this.selectedCheckBoxes = selectedCheckBoxes;
}
}
When I click save it is giving the below message in <t:message for="chb"/>
"chb": Value is not a valid option.
Even though I did not add the required attribute for h:selectManyCheckbox, it is trying to validate or doing something else...
I've changed checkBoxItems variable type(with getter/setters) to List<SelectItem>, but it is not working as well.
What can be the reason, how can I solve it?
PS: I'm using JSF 1.1
You will get this error when the equals() test on a selected item has not returned true for any of the available items. So, when roughly the following happens under JSF's covers:
boolean valid = false;
for (Object availableItem : availableItems) {
if (selectedItem.equals(availableItem)) {
valid = true;
break;
}
}
if (!valid) {
// Validation error: Value is not valid!
}
That can in your particular case only mean that section.getObjectID() does not return a String which is what your selectedCheckboxes is declared to, but a different type or a custom type where equals() is not implemented or broken.
Update as per your comment, the getObjectID() returns Integer. It's thus been treated as String because selectedCheckBoxes is declared as String[]. You should change the following
private String[] selectedCheckBoxes;
private Map<String, Object> checkBoxItems = new LinkedHashMap<String, Object>();
to
private Integer[] selectedCheckBoxes;
private Map<String, Integer> checkBoxItems = new LinkedHashMap<String, Integer>();
and maybe (not sure, can't tell from top of head now) also explicitly supply a converter:
<h:selectManyCheckbox ... converter="javax.faces.Integer">
i didnt find any problem in th code, i thought there is the problem the list u passed to oneManyCheckBox.
hardcode some values in list in getter than check
public Map<String, Object> getCheckBoxItems() {
checkBoxItems.clear();
checkBoxItems.put("aaaa", "aaaa");
checkBoxItems.put("bbbb", "bbbb");
checkBoxItems.put("cccc", "cccc");
checkBoxItems.put("dddd", "dddd");
checkBoxItems.put("eeee", "eeee");
return checkBoxItems;
}

JavaBean set attributes. Multiple if-statements alternative

Again I would like to get your opinion about a design issue.
I have a JavaBean with 15 attributes. For feeding the attributes I have a for loop that iterates over a collection of key-value pairs (concretely SAML attributes, I am mapping the attributes response to principals attributes). I am invoking the appropriate setter method basis on the key value, this is:
.../...
for (SAML2AttributeInfo attr : attrs) {
if (attr.getAttributeName().equals("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn")) {
customPrincipal.setUpn(attr.getAttributeValues().iterator().next());
}
.../... so on and so forth
}
It works, ok, but I have an ugly piece of code, 15 if-statements like above do not look very elegant.
I am thinking on using Reflection, this is, develop a unique set method and pass it the name of the attribute and his value.
Another option could be store the attributes in a Map, but I am not sure...
Any ideas?
Thanks in advance,
Luis
I would use a Map and declare static variables with the attribute keys:
public class Bean {
public static final String ATTRIBUTE_1 = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn";
public static final String ATTRIBUTE_2 = "...";
...
public static final String ATTRIBUTE_N = "...";
private Map<String, Object> map = new HashMap<String, Object>();
public void put(String key, Object value) {
map.put(key, value);
}
public Object get(String key) {
map.get(key);
}
}
Then, you coud store / retrieve values using the static variables:
Bean bean = new Bean();
bean.set(Bean.ATTRIBUTE_1, someValue);
Object value = bean.get(Bean.ATTRIBUTE_1);
Polymorphism for the rescue
enum Attribute {
UPN("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn") {
void setValue(Principal principal, String value) {
principal.setUpn(value);
}
},
...
;
private final String name;
private Attribute(String name) {
this.name = name;
}
public abstract setValue(Principal principal, String name);
public static Attribute getByName(String name) {
for (Attribute attribute : values())
if (attribute.name.equals(name))
return attribute;
return null;
}
public static void setByName(Principal principal, String name, String value) {
Attribute attribute = getByName(name);
if (attribute == null)
throw new IllegalArgumentException("No such attribute");
attribute.setValue(principal, value);
}
}
If you know the attribute you want to set there is no reason to go go via the name:
Attribute.UPN.setValue(principal, "something");

Combining Mapped properties with Indexed properties in Struts

I'm trying to have a dynamic form and, depending on an attribute type, I would like to display a different input style (textfield, radio buttons, dropdown, checklist, ...).
In order to have the dynamic form, I've set up the ActionForm with a Map.
Map<String, Object> values;
public void setValue(String key, Object value);
public Object getValue(String key);
My problem comes when I try to set up a checklist or multibox. The ActionForm only passes one value, although I would have expected that the String[] would be mapped to the Object argument.
Any idea about how can I solve this?
EDIT: in the JSP:
<input type=checkbox name="value(foo)" />
I looked into this issue and found out what was happening. The problem is not with Struts but with BeanUtils (which Struts uses for populating the form with the request parameters).
I managed to duplicate this by extracting a (test only) snippet of code from the framework:
public class MyForm {
// assume this is your Struts ActionForm
public void setValue(String key, Object val) {
System.out.println(key + "=" + val);
}
}
public class Test {
public static void main(String[] args)
throws IllegalAccessException, InvocationTargetException {
MyForm s = new MyForm();
Map<String, Object> properties = new HashMap<String, Object>();
// Your request should be like yourActionUrl?value(foo)=1&value(foo)=2&value(foo)=3
// and Struts calls bean utils with something like:
properties.put("value(foo)", new String[] {"1", "2", "3"});
BeanUtils.populate(s, properties);
}
}
When your run this you get printed one value only (just as you desrbibed):
foo=1
The thing is that BeanUtils considers this a mapped property and treats it as such, going for a scalar value for the key. Since your value is an array it just uses the first element:
...
} else if (value instanceof String[]) {
newValue = getConvertUtils().convert(((String[]) value)[0], type);
...
What you can do is modify your JSP and ActionForm to treat the list of values separately. Example modified:
public class MyForm {
private Map<String, Object> map = new HashMap<String, Object>();
public void setValue(String key, Object val) {
map.put(key, val);
}
public void setPlainValue(String[] values) {
// this is correctly called; now delegate to what you really wanted
setValue("foo", values);
}
}
public class Test {
public static void main(String[] args)
throws IllegalAccessException, InvocationTargetException {
MyForm s = new MyForm();
Map<String, Object> properties = new HashMap<String, Object>();
// Notice the change to your URL..
// yourActionUrl?plainValue=1&plainValue=2&plainValue=3
properties.put("plainValue", new String[] {"1", "2", "3"});
BeanUtils.populate(s, properties);
}
}
The above means that you use
<input type="..." name="value(foo)" ... />
for all the single elements in your JSP, while for your checkboxes (extending it to multivalue elements in general) you use
<input type="checkbox" name="plainValue" ... />
and you delegate to the map once in your ActionForm.

Categories