In one of my projects, I had an abstract controller class that simulated the form controller of Spring 1.2 for session forms. Annotations do not accept dynamic parameters. They have to be static. So, in the class, the command object name had to be a constant.
The class looks somewhat like this.
When I have two different controllers extending this class, both of them use the same command object name to store the command object in the session. So far so good. Now, if I have a main form which would pop-up another modal form, that's when the problem starts. Once, the modal form is closed, you would be unable to submit the main form. The command object in the session would be replaced with the modal form backing object.
Quick troubleshooting revealed the following. Spring (or rather the
So, for my situation what I would need is a
Here is a custom
To use this store, it needs to be injected to the
Note that this store still will not work for opening two form windows with the same URL.
The class looks somewhat like this.
@SessionAttributes("COMMANDOBJECT")
public abstract class FormController {
@InitBinder("COMMANDOBJECT")
public void initBinder(WebDataBinder binder, WebRequest request) {
.....
}
@RequestMapping(value = "/form.do", method=RequestMethod.GET)
public ModelAndView initForm(WebRequest request) {
....
}
@RequestMapping(value = "/form.do", method=RequestMethod.POST)
public ModelAndView submitForm(@ModelAttribute(value = "COMMANDOBJECT") CommandObject commandObj, BindingResult bindingResult, SessionStatus status, WebRequest request) {
......
}
}
When I have two different controllers extending this class, both of them use the same command object name to store the command object in the session. So far so good. Now, if I have a main form which would pop-up another modal form, that's when the problem starts. Once, the modal form is closed, you would be unable to submit the main form. The command object in the session would be replaced with the modal form backing object.
Quick troubleshooting revealed the following. Spring (or rather the
AnnotationMethodHandlerAdapter
) uses SessionAttributeStore
to store the session attributes. The default implementation for SessionAttributeStore
is DefaultSessionAttributeStore
. DefaultSessionAttributeStore
uses the value provided in @SessionAttributes
to store the command object (when the form view is rendered). And then uses the value provided in the @ModelAttributes
to retrieve the command object (during the form submit). Hence, multiple form windows supported by controllers with the same command object name would not work.So, for my situation what I would need is a
SessionAttributeStore
which can store the attribute with a unique key for every form. A custom SessionAttributeStore
can be created and injected to the AnnotationMethodHandlerAdapter
.Here is a custom
SessionAttributeStore
which appends the requestURI to the attributeName before storing, retrieving and removing.public class CustomSessionAttributeStore extends DefaultSessionAttributeStore {
@Override
public void storeAttribute(WebRequest request, String attributeName,
Object attributeValue) {
super.storeAttribute(request, getCustomAttributeName(request, attributeName), attributeValue);
}
@Override
public Object retrieveAttribute(WebRequest request, String attributeName) {
return super.retrieveAttribute(request, getCustomAttributeName(request, attributeName));
}
@Override
public void cleanupAttribute(WebRequest request, String attributeName) {
super.cleanupAttribute(request, getCustomAttributeName(request, attributeName));
}
public String getCustomAttributeName(WebRequest request, String attributeName) {
String result;
if(request instanceof ServletWebRequest) {
result = ((HttpServletRequest)((ServletWebRequest)request).getNativeRequest()).getRequestURI() + attributeName;
}
else {
result = attributeName;
}
return result;
}
}
To use this store, it needs to be injected to the
HandlerAdapter
.<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="sessionAttributeStore">
<bean class="com.codelooru.CustomSessionAttributeStore">
</bean>
</property>
<!-- Other properties would be set here -->
</bean>
Note that this store still will not work for opening two form windows with the same URL.