DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Decorating Microservices
  • Combining gRPC With Guice
  • Spring Boot: Cross-Origin AJAX HTTP Requests
  • Distributed Tracing System (Spring Cloud Sleuth + OpenZipkin)

Trending

  • Developers Beware: Slopsquatting and Vibe Coding Can Increase Risk of AI-Powered Attacks
  • Chat With Your Knowledge Base: A Hands-On Java and LangChain4j Guide
  • Intro to RAG: Foundations of Retrieval Augmented Generation, Part 1
  • How Can Developers Drive Innovation by Combining IoT and AI?
  1. DZone
  2. Coding
  3. Frameworks
  4. Effective Advice on Spring @Async: Final Part

Effective Advice on Spring @Async: Final Part

Learn more about how Spring @Async works with HttpRequest.

By 
Shamik Mitra user avatar
Shamik Mitra
·
May. 07, 19 · Tutorial
Likes (13)
Comment
Save
Tweet
Share
22.4K Views

Join the DZone community and get the full member experience.

Join For Free

In my previous articles, I discussed the Spring Async concept and how to use it effectively. If you want to revisit these posts, check out the links below:

Part 1: How Spring Async works internally and how to use wisely so that it assigns tasks to new threads.

Part 2:  How to handle exceptions, particularly if something goes wrong while executing a task.

In this part, we will discuss how Spring Async works with the web.

I am very excited to share an experience with you about Spring Async and HttpRequest, as an interesting incident happened in one of my projects. I believe that by sharing my experience, I can save some valuable time of yours in the future.

Let me try to depict the scenario:

Objective

My objective was to pass information from the UI to a back-end controller, which will do some work and eventually calls an async mail service that triggers a mail.

One of my juniors did the following code. I tried to replicate the code intention with the below code snippet, but this is not the actual code. Can you spot where the problem lies?

The Controller

The controller collects information from the UI as a form HTTP Servelet request, completes some operations, and passes it to the Async Mail Service.

package com.example.demo; 

import javax.servlet.http.HttpServletRequest; 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class GreetController {


@Autowired
private AsyncMailTrigger greeter;
@RequestMapping(value = "/greet", method = RequestMethod.GET)
public String greet(HttpServletRequest request) throws Exception {
String name = request.getParameter("name");
greeter.asyncGreet(request);
System.out.println(Thread.currentThread() + " Says Name is " + name);
System.out.println(Thread.currentThread().getName() + " Hashcode" + request.hashCode());
return name;

}
}


For the Async Mail Service, I marked it as @Component; you can easily change it to @Service. Here, I have one method called asyncGreet, which takes the HttpRequest, fetches the information from there, and triggers the mail (this part is omitted for simplicity). Notice I put a Thread.sleep() here. I will discuss the reasoning behind that later.

package com.example.demo;

import java.util.Map; 
import javax.servlet.http.HttpServletRequest; 
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class AsyncMailTrigger {

@Async
public void asyncGreet(HttpServletRequest request) throws Exception {
System.out.println("Trigger mail in a New Thread :: "  + Thread.currentThread().getName());
System.out.println(Thread.currentThread().getName() + " greets before sleep" + request.getParameter("name"));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " greets" + request.getParameter("name"));
System.out.println(Thread.currentThread().getName() + " Hashcode" + request.hashCode());
} 

}


Now, the main class:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class SpringAsyncWebApplication {

public static void main(String[] args) {
SpringApplication.run(SpringAsyncWebApplication.class, args);
}

}


If I run this program, the output will be very similar to below:

Thread[http-nio-8080-exec-1,5,main] Says Name is Shamik
http-nio-8080-exec-1 Hashcode 821691136
Trigger mail in a New Thread:: task-1
task-1 greets before sleep Shamik
task-1 greets null task-1 Hashcode 821691136


Pay attention to the output. The request still has the information before sleep, but after that, it magically disappears? Strange, isn't?  But it is the same request object that hashcode proves the same.

What happened ? what is the reason behind the disappearance of the information from request? That was happening to my junior, the mail recipients, recipients name disappear from the request and mail is not triggered.

Let's Put Our Sherlock Hats On and Investigate the Problem

It is very common to have problems with the request. To understand the problem, let's have a look at how the request lifecycle works.

The request is created by the servlet container right before the call to servlet service method. In Spring, the request passes through a dispatcher servlet. In the Dispatcher servlet, it identifies the controller by request mapping and calls the desired method in the controlle. And when the request has been served, the servlet container either deletes the request object or resets the state of the request object. (This totally depends on the container implementation — it actually maintains a pool of requests). However, I am not going to dive into how the container maintains the request object in this post. 

But keep one thing in mind: Once the request has been served and the response is committed, the container resets its state or destroys the request object. 

Now, let's put the Spring Async part into the consideration. What async does is it picks one thread from the thread pool and assigns the task to it. In our case, we pass the request object to the async thread, and in the asyncGreet method, we are trying to extract info directly from the request.

But as this is async, our main thread (the Controller part) will be not waiting for this thread to complete, so it prints the statement, commits the response, and refreshes the state of the request object.

Ironically, we pass the request object directly to the async thread. Still, at the point where the response is not committed in the main thread, the request holds the data. I explicitly put in a sleep statement, so in the main thread, the response can be committed and refreshes the request state. After sleep, we experience that there is no data in the request; it vanishes, which is a great experiment that proves the incident.

What We Will Learn From This Experiment?

Never pass a Request object or any object related to Request/Response (headers) directly while using async; you never know when your response will be committed and refresh the state. If you do, you will face an intermittent error.

What Can Be Done?

If you need to pass a bunch of information from the request, you can create a value object, set the information, and pass the value object to Spring Async. In this manner, you can create a concrete solution:

RequestVO Object

package com.example.demo; 

public class RequestVO {

String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
} 

}


Async Mail Service

package com.example.demo;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class AsyncMailTrigger {

@Async
public void asyncGreet(RequestVO reqVO) throws Exception {
System.out.println("Trigger mail in a New Thread :: "  + Thread.currentThread().getName());
System.out.println(Thread.currentThread().getName() + " greets before sleep" + reqVO.getName());
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " greets" + reqVO.getName());

} 

}


Greet Controller

package com.example.demo; 

import javax.servlet.http.HttpServletRequest; 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetController {

@Autowired
private AsyncMailTrigger greeter;
@RequestMapping(value = "/greet", method = RequestMethod.GET)
public String greet(HttpServletRequest request) throws Exception {
String name = request.getParameter("name");
RequestVO vo = new RequestVO();
vo.setName(name);
//greeter.asyncGreet(request);
greeter.asyncGreet(vo);
System.out.println(Thread.currentThread() + " Says Name is " + name);
System.out.println(Thread.currentThread().getName() + " Hashcode" + request.hashCode());
return name;
}
}


Output

Thread[http-nio-8080-exec-1,5,main] Says Name is Shamik
http-nio-8080-exec-1 Hashcode 1669579896
Trigger mail in a New Thread:: task-1
task-1 greets before sleep Shamik
task-1 greets Shamik


Conclusion 

Hope you enjoyed the article If you have questions, please leave any questions in the comment box.

Spring Framework Requests Object (computer science) Mail (Apple) Pass (software) Advice (programming)

Published at DZone with permission of Shamik Mitra, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Decorating Microservices
  • Combining gRPC With Guice
  • Spring Boot: Cross-Origin AJAX HTTP Requests
  • Distributed Tracing System (Spring Cloud Sleuth + OpenZipkin)

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!