What Is a SessionAttributeStore? When to Use It?


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.
@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.