Spring asynchronous support for request processing

Spring 3.0 introduced the @Async annotation for executing tasks asynchronously. The asynchronous capabilities are highly useful in situations where we need to execute a long running task before allowing user input.

In this blog post, I will kick off a long running task asynchronously. Then I will use JQuery to periodically check if the task has been completed. When the task is finally completed, a response is shown to the user.

We start by creating a AsyncServices as shown below. The process method returns java.util.concurrent.Future interface. This service allows the caller to retrieve the execution result at later time.
We simulate the “long running process” by calling the sleep method on the current thread. The method returns AsyncResult instance with the newly created string as pass through value.

import java.util.concurrent.Future;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

@Service
public class AsyncServices {

	@Async
	public Future<String> process(){
		System.out.println("##Start processing with Thread id: "+Thread.currentThread().getId());
		
		try {
			Thread.sleep(20000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("********");
		String processInfo="Processing is done with Thread id: "+Thread.currentThread().getId();
		return new AsyncResult<String>(processInfo);
	}
}

Now that we have completed our service layer, let’s create an MVC Controller that uses the AsyncServices to create a new string. The new string is then saved in the user’s session.

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.service.AsyncServices;

@RestController
public class HelloController {

	@Resource 
	AsyncServices services;
	
	@RequestMapping("/helloasync")
	public String sayHiAsync(HttpServletRequest request, HttpServletResponse response, 
       HttpSession session) {
		System.out.println("Request received");
		Future<String> process = services.process();
		session.setAttribute("process", process);
		System.out.println("######");
		return "helloasync";
	} 
}

To this controller, we then add a method that returns the status of the long running task. The method implementation simply retrieves the Future object and invokes the isDone method to check the status of the running task.

	@RequestMapping("/helloasyncstatus")
	public String reportStatus(HttpSession session) {  
	    Future<String> process = (Future<String>)session.getAttribute("process");  
	      
	    if(process.isDone()) {  
	        System.out.println("process Done");  
	        return "COMPLETE";  
	    }  
	    else {  
	        System.out.println("Still Working on process");  
	        return "WORKING";  
	    }  
	}

We then add a method to the controller that shows the result of the long running task as shown below.

	@RequestMapping(value="/helloasyncget", method=RequestMethod.GET)  
    public String showReport(HttpSession session) throws InterruptedException, ExecutionException   
    {  
        Future<String> process = (Future<String>)session.getAttribute("process");  
          
        String strProcess = process.get();   
          
        return strProcess;  
    } 

You can use JQuery’s setInterval method to call the “/helloasyncstatus” URL every two seconds(or any long polling technology). Once we get the “COMPLETE” response, we can use the “/helloasyncget” url to show the result of the long running task.

Leave a Reply

Your email address will not be published. Required fields are marked *