Friday, October 31, 2014

Enable Conversation using Session attributes in Spring

In a Spring framework project we use form objects saved as session attributes to achieve a conversational style of creating and editing business objects.
But recently I realized that this functionality does not work if you have more than one tab open in the same browser. Reason is simple, if you are editing an object in first tab and start editing another object in second tab then the session attributes gets replaced by the second object. And now if you save first object then actually the second object will be updated with the information of first object.

This happens because spring saves the objects into session with same attribute name, so the object saved later will replace any other object. And when POST request is made from already loaded UI then it will always update the object which was saved later.

There is a very easy solution to the above issue. We can extend the class "DefaultSessionAttributeStore" and override just one method,  which is "getAttributeNameInSession(WebRequest request, String attributeName)", as shown below:
  
@Override
  protected String getAttributeNameInSession(WebRequest request, String attributeName) {
    String cid = request.getParameter(attributeName + "_cid") == null ? ""
        + request.getAttribute(attributeName + "_cid", WebRequest.SCOPE_REQUEST) : request.getParameter(attributeName
        + "_cid");
    if (cid != null || !"".equals(cid)) {
      return super.getAttributeNameInSession(request, attributeName + "_" + cid);
    }
    return super.getAttributeNameInSession(request, attributeName);
  }


This class should also implement interface "InitializingBean" and override method "afterPropertiesSet()" as shown below:
  @Override
  public void afterPropertiesSet() throws Exception {
    annotationMethodHandlerAdapter.setSessionAttributeStore(this);
  }

This will make sure that this custom session attribute store is added to the annotation handler adaptor.

Now when ever you save the form into model map, make sure that you add another attribute with name "{your form name}_cid" and value as the unique id for the form.
And from the JSP add a hidden input which will be sent along with the POST request.
<input name="{your form name}_cid" type="hidden" value="<c:out value='${your form unique id}'/>" />

Thats it! You can now edit different entities in different tabs under same browser session.

Please add comment if you have any question.

Thanks,
Manish

No comments:

Post a Comment