Java Code Geeks

Sunday, March 6, 2011

Spring Session Scoped Bean

Somehow my experience with spring was limited so far in my career. Its just that for last one year, I have been using Spring extensively. I recently used Spring's Session Scope beans. We most of the time use Singleton bean or the Prototype beans but session scoped beans are very useful in those use cases where you need to create an object per httpsession. For example, the shopping cart object which holds the list of shopping goods that a customer is buying. We can of course do this manually by creating a new Cart object per user session, however what if the lifecycle of such objects can be controlled by outside IOC containers like Spring? Yes, Spring Session and Request scoped beans are just for that, to create an object and bind that to each unique session/request.
Now the question really comes how to test such a bean in JUnit test cases? One way to test is - test just the functionality of the bean, but if I was more interested in to find out that Spring really does create object per sessions and can maintain the lifecycle of such beans. I created test cases to create a mock httpseesion and httprequest objects and fake one http user session.

Here are the steps to get session scoped beans in Junit test cases
1) Create one Session aware Application context which is XmlWebApplicationContext
2) Create mock session and mock httprequest
3) Write test cases to get the session scoped beans and write the necessary test cases
4) tear down the httpsession and request.




public class AbstractSessionTest extends TestCase {
protected XmlWebApplicationContext ctx;
protected MockHttpSession session;
protected MockHttpServletRequest request;

protected String[] getConfigLocations() {
return new String[] { "file:src/test/resources/test-context.xml" };
}

@BeforeTest
protected void setUp() throws Exception {
super.setUp();
ctx = new XmlWebApplicationContext();
ctx.setConfigLocations(getConfigLocations());
ctx.setServletContext(new MockServletContext(""));
ctx.refresh();
createSession();
createRequest();
// set the session attributes that the bean might look for?
session.setAttribute("varname","value")
}
protected void createSession() {
session = new MockHttpSession(ctx.getServletContext());
}

protected void endSession() {
session.clearAttributes();
session = null;
}

protected void createRequest() {

request = new MockHttpServletRequest();
request.setSession(session);
request.setMethod("GET");
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(
request));
}

protected void endRequest() {
((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.requestCompleted();
RequestContextHolder.resetRequestAttributes();
request = null;

}

@AfterTest
public void tearDown() {
endRequest();
endSession();
}
}


One more useful tips here is - how to access the session variables from the Session Scoped beans? Well the friend here is RequestContextHolder. Using this - we can get hold of the httpsession inside of the bean and we can access any variables that was bound to this session in some http filter or any upper layer which might want to put session scoped variables to indicate one user specific data.


ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpSession session = attr.getRequest().getSession(false); // true == allow create
String value= (String)session.getAttribute("varname");