Spring MVC - Intercepting Async process lifecycle using CallableProcessingInterceptor

[Updated: Dec 14, 2016, Created: Dec 12, 2016]

We saw in the last tutorial, using AsyncHandlerInterceptor was a way to intercept async processing.

To have more interceptions around user defined 'Callable' process, we can register CallableProcessingInterceptor

Let's understand that with an example. We going to modify the previous tutorial example to see a clear difference between the two.



Creating CallableProcessingInterceptor

package com.logicbig.example;

import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import java.util.concurrent.Callable;

 public class MyCallableProcessingInterceptor implements
                                           CallableProcessingInterceptor {
   @Override
   public <T> void beforeConcurrentHandling (
                             NativeWebRequest request,
                             Callable<T> task) throws Exception {
       System.out.println("callableInterceptor#beforeConcurrentHandling called. " +
                              "Thread: " + Thread.currentThread().getName());
   }

   @Override
   public <T> void preProcess (
                             NativeWebRequest request,
                             Callable<T> task) throws Exception {

       System.out.println("callableInterceptor#preProcess called. "+
                             " Thread: " + Thread.currentThread().getName());
   }

   @Override
   public <T> void postProcess (NativeWebRequest request,
                             Callable<T> task,
                             Object concurrentResult) throws Exception {
       System.out.println("callableInterceptor#postProcess called. "+
                             " Thread: " + Thread.currentThread().getName());
   }

   @Override
   public <T> Object handleTimeout (NativeWebRequest request,
                             Callable<T> task) throws Exception {

       System.out.println("callableInterceptor#handleTimeout called."+
                             " Thread: " + Thread.currentThread().getName());

       return RESULT_NONE;
   }

   @Override
   public <T> void afterCompletion (NativeWebRequest request,
                             Callable<T> task) throws Exception {
       System.out.println("callableInterceptor#afterCompletion called."+
                            " Thread: " + Thread.currentThread().getName());
   }
}


Registering the interceptor

We can register the above interceptor in the HandlerInterceptor#preHandle method on the fly. We are going to modify our previous example MyAsyncHandlerInterceptor a little for that:

package com.logicbig.example;

import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class MyAsyncHandlerInterceptor extends HandlerInterceptorAdapter {
    private static final Object CALLABLE_INTERCEPTOR_KEY = new Object();

    @Override
    public boolean preHandle (HttpServletRequest request,
                              HttpServletResponse response,
                              Object handler) throws Exception {

        System.out.println("interceptor#preHandle called." +
                                     " Thread: " + Thread.currentThread()
                                                         .getName());

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(CALLABLE_INTERCEPTOR_KEY,
                                      new MyCallableProcessingInterceptor());
        return true;

    }

   .......
}

The controller and the main classes are same as our last example. On running the main class and accessing the application in the browser. We will have following output:

interceptor#preHandle called. Thread: http-nio-8080-exec-1
controller#handler called. Thread: http-nio-8080-exec-1
controller#handler finished
callableInterceptor#beforeConcurrentHandling called. Thread: http-nio-8080-exec-1
interceptor#afterConcurrentHandlingStarted called. Thread: http-nio-8080-exec-1
callableInterceptor#preProcess called. Thread: MvcAsync1
controller-callable#async task started. Thread: MvcAsync1
controller-callable#async task finished
callableInterceptor#postProcess called. Thread: MvcAsync1
interceptor#preHandle called. Thread: http-nio-8080-exec-2
interceptor#postHandle called. Thread: http-nio-8080-exec-2
interceptor#afterCompletion called Thread.: http-nio-8080-exec-2
callableInterceptor#afterCompletion called. Thread: http-nio-8080-exec-2

Here we can see that CallableProcessingInterceptor is integrated more deeply with the lifecycle of an asynchronous request.

Here's the flow diagram to see a clear difference from the last example:




Example Project

Dependencies and Technologies Used :

  • Spring Boot Web Starter 1.4.2.RELEASE: Starter for building web, including RESTful, applications using Spring MVC. Uses Tomcat as the default embedded container.
    Corresponding Spring version: 4.3.4.RELEASE
  • JDK 1.8
  • Maven 3.3.9

Async Processing Select All Download
  • async-callable-process-interceptor
    • src
      • main
        • java
          • com
            • logicbig
              • example

See Also