Search This Blog

Saturday, 9 March 2013

Accessing the Request object outside Controllers

We have seen that when working with controllers, Spring automatically gives us access to the HttpServletRequest object. But what if we are in some utility class and would like to access the request ? For this Spring includes the ServletRequestAttributes class.
Consider the below method that I called from a controller instance:
public void test() {
    final ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder
            .currentRequestAttributes();
    final HttpServletRequest request = attr.getRequest();
    System.out.println("Request object is " + request);
}
 
@RequestMapping(method = RequestMethod.POST, value = "test.do")
public @ResponseBody
    Status test(final HttpServletRequest request) {
    System.out.println("Request object is " + request);
    this.test();
    //..other code
}
The output is :
Request object is org.apache.catalina.connector.RequestFacade@b0cf230
Request object is org.apache.catalina.connector.RequestFacade@b0cf230
As can be seen both statements indicate the same request object. This works becauce Spring binds the request object to the executing thread. So what if I changed threads ?
@RequestMapping(method = RequestMethod.POST, value = "login.srvc")
public @ResponseBody
    Status signIn(final HttpServletRequest request) {
    System.out.println("Request object is " + request);
    new Thread() {
        @Override
        public void run() {
            UserAccountController.this.test();
        };
    }.start();
}
The output indicates that the code still worked:
Request object is org.apache.catalina.connector.RequestFacade@7cf7e9dd
Request object is org.apache.catalina.connector.RequestFacade@7cf7e9dd
I looked in the code of DispatcherServlet. Actually I went up the hierarchy to FrameworkServlet:
protected final void processRequest(HttpServletRequest request,
                              HttpServletResponse response)
        throws ServletException, IOException {
    long startTime = System.currentTimeMillis();
    Throwable failureCause = null;

    // Expose current LocaleResolver and request as LocaleContext.
    LocaleContext previousLocaleContext = LocaleContextHolder.
                                  getLocaleContext();
    LocaleContextHolder.setLocaleContext(buildLocaleContext(request),
                     this.threadContextInheritable);

    // Expose current RequestAttributes to current thread.
    RequestAttributes previousRequestAttributes = RequestContextHolder.
             getRequestAttributes();
    ServletRequestAttributes requestAttributes = null;
    if (previousRequestAttributes == null 
           || previousRequestAttributes.getClass().equals(
                            ServletRequestAttributes.class)) {
        requestAttributes = new ServletRequestAttributes(request);
        RequestContextHolder.setRequestAttributes(requestAttributes,
               this.threadContextInheritable);
    }

    if (logger.isTraceEnabled()) {
        logger.trace("Bound request context to thread: " + request);
    }
    try {
    doService(request, response);
    }
//remaining code
As can be seen the RequestContextHolder class uses a ThreadLocal instance to hold the ServletRequestAttributes instance. The ServletRequestAttributes class includes the ServletRequest among other members:
public class ServletRequestAttributes extends AbstractRequestAttributes {

    private final HttpServletRequest request;
    private volatile HttpSession session;
    private final Map<String, Object> sessionAttributesToUpdate = new HashMap<String, Object>();
        //other code
}

No comments:

Post a Comment