Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What happens if I forget to mark the Spring SessionStatus as "Complete"?

In Spring MVC, suppose I define a SessionAttribute, using the @SessionAttribute tag like so:

@SessionAttributes(value = "myModel")
public class MyController{
   ...
}

Suppose that I forget to call status.setComplete() on the SessionStatus like so:

@RequestMapping(method = RequestMethod.POST)
public void doSomething(@ModelAttribute("myModel") MyModel model, SessionStatus status){
   ...
   //status.setComplete(); <-- Never gets called
}

Will the model stay in the session forever? Will it ever get cleaned out, or will the session keep growing larger and larger as the user navigates the site?

like image 592
Daniel Alexiuc Avatar asked May 19 '09 02:05

Daniel Alexiuc


2 Answers

There is a big debate on whether the session attributes are cleared after controller exit.

To clarify once and for all, we can look at the Spring MVC 3.1.0 RELEASE source code.

The interface org.springframework.web.bind.support.SessionAttributeStore exposes the following methods:

void storeAttribute(WebRequest request, String attributeName, Object attributeValue);

Object retrieveAttribute(WebRequest request, String attributeName);

void cleanupAttribute(WebRequest request, String attributeName);

The default implementation is org.springframework.web.bind.support.DefaultSessionAttributeStore

By doing a "Open Call Hierarchy" on cleanupAttribute() in Eclipse, we can see that the method is called by 2 different flows:

1) org.springframework.web.method.annotation.ModelFactory

public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception {

        if (mavContainer.getSessionStatus().isComplete()){
            this.sessionAttributesHandler.cleanupAttributes(request);
        }
        else {
            this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel());
        }

        if (!mavContainer.isRequestHandled()) {
            updateBindingResult(request, mavContainer.getModel());
        } 
    }

2) org.springframework.web.bind.annotation.support.HandlerMethodInvoker

public final void updateModelAttributes(Object handler, Map<String, Object> mavModel,
            ExtendedModelMap implicitModel, NativeWebRequest webRequest) throws Exception {

        if (this.methodResolver.hasSessionAttributes() && this.sessionStatus.isComplete()) {
            for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
                this.sessionAttributeStore.cleanupAttribute(webRequest, attrName);
            }
        }
...
}

It is clear that in both cases, the session attribute is removed only when this.sessionStatus.isComplete() is called.

I dug into the code of DefaultSessionAttributeStore. Under the hood, it gets the real HTTP Session object to store attributes, so they can potentially be accessed by other controllers in the same session.

So no, the session attributes are not removed after a clean POST.

like image 180
doanduyhai Avatar answered Oct 29 '22 17:10

doanduyhai


EDIT #2: Note that this answer is no longer correct. See @doanduyhai's answer below.

EDIT: Please note that this is for Spring 2.5 and may, but does not necessarily ensure that the same is for Spring 3.x. Double check the docs!

This is along the lines of what @Gandalf said.

Form controllers model a form request lifespan, from initial viewing of the form through form submission. After the form is submitted, the form controller's job is done, and it will remove the command object from the session.

So, to keep the command object in the session between form workflows you will need to manage the session manually. After a clean POST, the object is removed from session.

In short, I believe the setComplete() method is just good practice but is not necessarily required.

EDIT: I just looked in my Spring book to confirm this. I'll quote it:

When @SessionAttribute is not used, a new command object will be created on each request, even when rendering the form again due to binding errors. If this annotation is enabled, the command object will be stored in the session for subsequent uses, until the form completes successfully. Then this command object will be cleared from the session. This is usually used when the command object is a persistent object that needs to be identical across different requests for tracking changes.

Essentially that's what I was saying above. It stores it in the session until you either A) call setComplete() or B) the controller successfully completes a POST.

like image 28
Alex Beardsley Avatar answered Oct 29 '22 17:10

Alex Beardsley