Run a process asynchronously after completion of initial process in Spring boot - java

I have a requirement. I have 2 processes
Contact creation and
Associating contact to the Department
Currently I have a spring boot API which has a REST POST call to perform both in one thread. Since process 2 is taking more time I wanted to run that in the
background immediately after finishing the step 1.
#PostMapping(value = "/processDeptContact", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<PayloadResponse> processDeptContact(#RequestBody String payload) {
ResponseEntity response = new ResponseEntity(new ErrorResponse("Exception"),
new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
try {
response = myService.processPayload(payload);
} catch (Exception e) {
logger.error("Exception in the controller");
}
return response;
}
I want to return the response to the user as soon as step 1 is done and performing step 2 at the background. How do I achieve that
Thanks in advance

In your main class, or a #Configuration class, use #EnableAsync to bootstrap a thread pool:
#EnableAsync
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
You can optionally set Thread Pool properties under spring.task.execution.pool property. Example:
spring:
task:
execution:
pool:
core-size: 8
max-size 16
Here's a stack post detailing what each property means: Core pool size vs maximum pool size in ThreadPoolExecutor
Inside your controller:
#RestController
public class TestController {
private final ContactService contactService;
private final DepartmentService departmentService;
// Constructor Injection
public TestController(ContactService contactService, DepartmentService departmentService) {
this.contactService = contactService;
this.departmentService = departmentService;
}
#PostMapping(value = "/processDeptContact", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<PayloadResponse> processDeptContact(#RequestBody String payload) {
List<Contact> contacts = contactService.processPayload(payload);
departmentService.associateContacts(contacts); // This is an asynchronous call
return ResponseEntity.ok(new PayloadResponse(contacts));
}
}
I've removed the try/catch from the controller method since error handling is a cross cutting concern and is handled by AOP. More on that here: Baeldung
And finally in your DepartmentService, you use the #Async annotation to turn it into an asynchronous method:
#Service
public class DepartmentService {
#Async
public void associateContacts(List<Contact> contacts) {
// method
}
}
I see other answers are basically saying the same thing and are correct, but not complete so I felt the need to put everything together for you.

Spring framework provides support for asynchronous processing out of the box. Spring can create & manage threads for us by providing support for various TaskExecutor abstraction.
We can create a method in a new class that will do the second process (associate contact to the Department) and annotate that method with #Aysnc. The annotation ensures the spring executes this method as a Runnable/Future depending on return type.
Sample Implementation (We have to add #EnableAsync in any of our configuration class)
#Component
class ContactManager {
#Async
public void associateContactToDepartment(){
//method implementation goes here
}
}
class MyService {
#Autowired
private ContactManager contactManager;
public PayloadResponse processPayload(String payload){
payloadResponse payloadResponse = createContact();//first process
contactManager.associateContactToDepartment() // this call will be executed asynchronously.
return payloadResponse;
}
}
Refer this for quick intro to async methods.

Follow the below steps:
Add #EnableAsync annotation and Add TaskExecutor Bean to main spring boot application class
#SpringBootApplication
#EnableAsync
public class AsynchronousSpringBootApplication {
private static final Logger logger = LoggerFactory.getLogger(SpringBootApplication.class);
#Bean(name="processExecutor")
public TaskExecutor workExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("Async-");
threadPoolTaskExecutor.setCorePoolSize(3);
threadPoolTaskExecutor.setMaxPoolSize(3);
threadPoolTaskExecutor.setQueueCapacity(600);
threadPoolTaskExecutor.afterPropertiesSet();
logger.info("ThreadPoolTaskExecutor set");
return threadPoolTaskExecutor;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SpringBootApplication.class,args);
}
Add the contact to department method as below:
#Service
public class DepartmentProcess {
private static final Logger logger = LoggerFactory.getLogger(ProcessServiceImpl.class);
#Async("processExecutor")
#Override
public void processDepartment() {
logger.info("Received request to process in DepartmentProcess.processDepartment()");
try {
Thread.sleep(15 * 1000);
logger.info("Processing complete");
}
catch (InterruptedException ie) {
logger.error("Error in ProcessServiceImpl.process(): {}", ie.getMessage());
}
}
}
Call the method from the controller as below:
#PostMapping(value = "/processDeptContact", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<PayloadResponse> processDeptContact(#RequestBody String payload) {
ResponseEntity response = new ResponseEntity(new ErrorResponse("Exception"),
new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
try {
response = myService.processPayload(payload);
myService.processDepartment();//async method
} catch (Exception e) {
logger.error("Exception in the controller");
}
return response;
}

Points 1 and 2 are not here but it doesn't matter, let's call them foo1() and foo2().
In myService.processPayload() you want to do:
ResponseEntity result = foo1();
Runnable runnable = () -> {
foo2()
};
Thread thread = new Thread(runnable);
thread.start(); // the logic in foo2 will happen in a background thread so it will not block on this line, consider using a thread pool instead
return result;
BTW, this sounds like premature optimization and you should think about race conditions with parallel threads but this is not what the question was asking.
One more thing, move this to the catch because it's a waste of instantiations if the try will succeed, which should happen most of the time.
ResponseEntity response = new ResponseEntity(new ErrorResponse("Exception"),
new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);

Related

Java: New thread to skip synchronised method if another thread already in it

Requirements
I need to be able to trigger a (long running) job via a POST call and return immediately.
Only one thread can run the job at one time.
The job being an expensive one, I want all future triggers of this job to not do anything if one job is already in progress.
Code
#RestController
public class SomeTask {
private SomeService someService;
#Autowired
public SomeTask(SomeService someService) {
this.someService = someService;
}
#Async // requirement 1
#RequestMapping(method = RequestMethod.POST, path = "/triggerJob")
public void triggerJob() {
expensiveLongRunningJob();
}
/**
* Synchronized in order to restrict multiple invocations. // requirement 2
*
*/
private synchronized void expensiveLongRunningJob() {
someService.executedJob();
}
}
Question
With the above code requirements 1 and 2 are satisfied. What is the best way to satisfy requirement 3 as well (have the new thread, created as a result of a POST call, skip the synchronised method and return immediately on failure to acquire a lock)?
Synchronization isn't the right tool for the job. You can do it like this:
#RestController
public class SomeTask {
private SomeService someService;
private final AtomicBoolean isTriggered = new AtomicBoolean();
#Autowired
public SomeTask(SomeService someService) {
this.someService = someService;
}
#Async // requirement 1
#RequestMapping(method = RequestMethod.POST, path = "/triggerJob")
public void triggerJob() {
if (!isTriggered.getAndSet(true)) {
try {
expensiveLongRunningJob();
} finally {
isTriggered.set(false);
}
}
}
/**
* only runs once at a time, in the thread that sets isTriggered to true
*/
private void expensiveLongRunningJob() {
someService.executedJob();
}
}
For requirement 1, if you want to use just #Async, you should have it on the service method and not the controller method. But be aware that by making it async, you would lose control over the job and failure handling will be not possible, unless you implement #Async with Future and handle failures by implementing AsyncUncaughtExceptionHandler interface.
For requirement 3, you can have a volatile boolean field in the service, which gets set just before beginning the job process and unset after the job process completes. In your controller method, you can check the service's volatile boolean field to decide if the job is being executed or not and just return with appropriate message if the job is in progress. Also, make sure to unset the boolean field while handling the failure in the implementation of AsyncUncaughtExceptionHandler interface.
Service:
#Service
public class SomeService {
public volatile boolean isJobInProgress = false;
#Async
public Future<String> executeJob() {
isJobInProgress = true;
//Job processing logic
isJobInProgress = false;
}
}
Controller:
#RestController
public class SomeTask {
#Autowired
private SomeService someService;
#RequestMapping(method = RequestMethod.POST, path = "/triggerJob")
public void triggerJob() {
if (!someService.isJobInProgress){
someService.executeJob(); //can have this in a sync block to be on the safer side.
} else {
return;
}
}
}
Implementation of AsyncUncaughtExceptionHandler:
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Autowired
private SomeService someService;
#Override
public void handleUncaughtException(
Throwable throwable, Method method, Object... obj) {
//Handle failure
if (someService.isJobInProgress){
someService.isJobInProgress = false;
}
}
}
#Async configuration:
#Configuration
#EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
return new ThreadPoolTaskExecutor();
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}

A Spring boot application fails to `Autowire` a Service class that is proxied as a new Thread

I am using Spring Boot [v. 2.1.4.RELEASE] and the #Async annotation along with CompletableFuture in order to implement asynchronous and non blocking calls. 
I have implemented an Config class for running a TaskExecutor that will provide the multi-threading environment, as well as pair of Service-Controller classes that implement a POST request.
Config
#Configuration
#EnableAsync
public class AsyncConfig implements AsyncConfigurer {
#Bean(name = "musterExecutor")
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("AppExecutor-");
executor.initialize();
return new ThreadPoolTaskExecutor();
}
}
A service class where Status is a pojo used for mapping the response.
Service
#Service
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class MusterService<T> {
private static final String request_status = RestURIConstants.STATUS;
private static final String request_execution = RestURIConstants.EXECUTE;
private static final String request_async = Constants.ASYNC;
private static final String DATA_FUSION = Constants.DATA_FUSION;
#Autowired
private RestTemplate restTemplate;
#Async("musterExecutor")
public CompletableFuture<Status> execute(Payload payload) {
HttpHeaders headers = setHeaders();
String input = Constructor.createPayload(payload);
HttpEntity<String> requestEntity = new HttpEntity<String>(input, headers);
Status s = restTemplate.postForObject(execution_url, request_execution, Status.class);
return CompletableFuture.completedFuture(s);
}
#Async("musterExecutor")
public CompletableFuture<Status[]> getStatus() {
log.debug("Sending Status Request");
final String status_url = request_status;
HttpHeaders headers = setHeaders();
HttpEntity<String> requestEntity = new HttpEntity<String>(request_async, headers);
Status[] s = restTemplate.postForObject(status_url, requestEntity, Status[].class);
return CompletableFuture.completedFuture(s);
}
private HttpHeaders setHeaders() {
// code for setting the Headers
return headers;
}
}
Along with a simple
Service interface
public interface GenericService<T> {
public abstract CompletableFuture<Status[]> getStatus();
public abstract CompletableFuture<Status> execute(Payload payload);
}
Controller
#RestController
#RequestMapping(path = "/api")
public class MusterController {
#Autowired
private MusterService<Status> musterService;
#RequestMapping(value = "/status", method = RequestMethod.GET)
#ResponseBody
public CompletableFuture<Status[]> status() {
CompletableFuture<Status[]> cf_status = new CompletableFuture<Status[]>();
try {
cf_status = musterService.getStatus();
return cf_status;
} catch (RestClientException e) {
e.printStackTrace();
// TODO: handle exception
}
return cf_status;
}
#RequestMapping(value = "/execute", method = RequestMethod.POST, produces = "application/json", consumes = "application/json")
#ResponseBody
public CompletableFuture<Status> execute(#RequestBody Payload payload) throws SchedulerException, IOException {
CompletableFuture<Status> cf_execution_body = new CompletableFuture<Status>();
Status execution_body = new Status();
try {
cf_execution_body = musterService.execute(payload);
execution_body = cf_execution_body.get();
return cf_execution_body;
} catch (RestClientException e) {
log.error("RestClientException:{} \t Cause:{}", e.getMessage(), e.getCause());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return cf_execution_body;
}
}
The instance of the class is correctly proxied in order to launch the thread, but when i try to implement the Service class with the interface class (aka. public class MusterService<T> implements GenericService<T>..) it gives me the following error:
Caused by:
org.springframework.beans.factory.BeanNotOfRequiredTypeException
The service is proxied as a new Thread and for some reason cannot be
Autowired
When i remove the implements then everything works as expected.
NOTE: I've put the #EnableAspectJAutoProxy(proxyTargetClass = true) annotation in order to make it work as suggested but it doesn't do anything. How can i make it work?
Stackoverflow references:
BeanNotOfRequiredTypeException due to autowired fields
Original interface is lost in Spring AOP introduction
It's #EnableAsync that's causing MusterService to be proxied so that its #Async methods can be asynchronous. By default, #EnableAsync will create JDK proxies if the class being proxied implements a single interface. This is the case for MusterService when it implements GenericService. As a result the muster service bean has to be consumed as a GenericService as that's the contract that the proxy conforms to.
If you want your controller to be able to inject a MusterService instance you should modify #EnableAsync to use CGLib proxies:
#EnableAsync(proxyTargetClass = true)

Why does adding Spring AOP aspects break this asynchronous call during Spring context initialization?

I am having a hard time understanding the issue at hand, which I believe is a problem with the way how Spring proxies get created.
In this minimal example, I have two classes, AccountLoader and BankImpl, which implements an interface Bank. Upon start-up, AccountLoader executes some concurrent calls to an autowired Bank-instance, where the method in BankImpl is advised with an aspect.
In this setup the call to complete the future (Future.get) finishes with a TimeoutException, because the call appears to never terminate. However, if I call the same method before the callables get submitted to the executor, all calls finish successfully.
What is going on with Spring here? Why does this async call not terminate? And why in all seven hells does it terminate, if I add a synchronous call before the async one?
You may find the code below, a complete working example is also available on Github
public interface Bank {
Map<String, String> getAccounts(String q);
}
The simple implementation
#Service
public class BankImpl implements Bank {
private static final Logger LOGGER = LoggerFactory.getLogger(BankImpl.class);
#Override
public Map<String, String> getAccounts(String q) {
LOGGER.info("Listing accounts for {}", q);
return Collections.singletonMap(q, "q");
}
}
And finally the caller
#Service
public class AccountLoader {
private static final Logger LOGGER = LoggerFactory.getLogger(AccountLoader.class);
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
#Autowired
private Bank bank;
#PostConstruct
public void refresh() {
LOGGER.info("Refreshing accounts");
// Uncommenting the following line will let the calls terminate
// bank.getAccounts("sync");
try {
executorService.submit(() -> { bank.getAccounts("async"); })
.get(5L, TimeUnit.SECONDS);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
For the sake of completeness, here are the aspect
#Aspect
#Component
public class SomeAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(SomeAspect.class);
#AfterReturning(pointcut = "execution(* com.github.mtritschler.aspects.BankImpl.getAccounts(..))", returning = "returnValue")
public Map<String, String> logCallee(Map<String, String> returnValue) {
LOGGER.info("Result is {}", returnValue);
return returnValue;
}
}
and last but not least the configuration
#EnableAspectJAutoProxy
#Configuration
public class MyConfig {
}
Update: if I remove the #EnableAspextJAutoProxy, I also don't get an exception. Switching to load-time weaving did not change anything either.
It turned out that there was a race condition between the application initialization in the main thread and the concurrent access to the injected dependency.
Once we switched the #PostConstruct for a listener on ContextRefreshedEvent it worked just fine.

Spring Boot REST - requests are not executing with ThreadPoolTaskExecutor configuration

I am trying to develop a spring boot app. I have written all core implementations in core java without spring framework. I am using that jar in this spring boot app. I would like to manage the concurrency of my rest controller. So, configured ThreadPoolTaskExecutor accordingly in the main class. Ideally, I want only 2 concurrent requests to get into the execute() method, which I annotated Async. I was testing for 2 concurrent requests at a time but I see in the log that my requests are entering execute() all at once. All the tasks are memory intensive. So those are failing with heap memory issues. I am trying to figure out the ideal concurrency number. I would like to know if my configuration is correct or am I missing something? Thank you.
Here's my main class:
#SpringBootApplication
#EnableAsync
public class RestapiApplication implements AsyncConfigurer {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(RestapiApplication.class, args);
System.out.println("Rightdata Middleware ready to accept requests:");
}
#Bean(name = "executor1")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setMaxPoolSize(2);
taskExecutor.setCorePoolSize(2);
taskExecutor.setThreadNamePrefix("LULExecutor-");
taskExecutor.setQueueCapacity(100);
taskExecutor.initialize();
return taskExecutor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
And here's my REST controller:
#RestController
#RequestMapping("/end2end")
public class End2EndRestController {
/**
* The log.
*/
private final Logger log = LoggerFactory.getLogger(this.getClass());
#RequestMapping(method = RequestMethod.POST)
public JSONObjectPOJO process(#RequestBody String end2EndScenarioString) throws InterruptedException, ExecutionException {
final JSONObjectPOJO jsonObjectPOJO = convertToJavaObject(end2EndScenarioString);
final ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
#Override
public void run() {
try {
execute(jsonObjectPOJO);
} catch (Exception e) {
e.getMessage();
}
}});
executor.shutdown();
return jsonObjectPOJO;
}
#Async("executor1")
private void execute(JSONObjectPOJO jsonObjectPOJO) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Future<?> futureTarget;
Future<?> futureSource;
futureSource = processSource(executorService);
futureTarget = processTarget(executorService);
manageSourceProcessingResults(futureSource);
manageTargetProcessingResults(futureTarget);
executorService.shutdown();
//Do rest of the tasks.
}
#SuppressWarnings({"unchecked", "rawtypes"})
protected Future<?> processSource(executorService){
//Get appropriate class instance with call() - coreActionClass.
Future<?> futureSource = executorService.submit(coreActionClass);
return futureSource;
}
#SuppressWarnings({"unchecked", "rawtypes"})
protected Future<?> processTarget(executorService){
//Get appropriate class instance with call() - coreActionClass.
Future<?> futureTarget = executorService.submit(coreActionClass); //callable method in core.
return futureTarget;
}
private void manageSourceProcessingResults(Future<?> futureSource) {
try{
futureSource.get();
} catch(Exception e){
e.printStackTrace();
}
}
private void manageTargetProcessingResults(Future<?> futureTarget) {
try{
futureTarget.get();
} catch(Exception e){
e.printStackTrace();
}
}
}
UPDATE- 1:
I have now changed the code to following:
#RestController
#RequestMapping("/end2end")
public class End2EndRestController {
/**
* The log.
*/
private final Logger log = LoggerFactory.getLogger(this.getClass());
#RequestMapping(method = RequestMethod.POST)
public JSONObjectPOJO process(#RequestBody String end2EndScenarioString) throws InterruptedException, ExecutionException {
final JSONObjectPOJO jsonObjectPOJO = convertToJavaObject(end2EndScenarioString);
final ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
#Override
public void run() {
try {
execute(jsonObjectPOJO);
} catch (Exception e) {
e.getMessage();
}
}});
executor.shutdown();
return jsonObjectPOJO;
}
}
And AsyncService class:
public class AsyncService {
#Async("executor1")
public void execute(JSONObjectPOJO jsonObjectPOJO) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Future<?> futureTarget;
Future<?> futureSource;
futureSource = processSource(executorService);
futureTarget = processTarget(executorService);
manageSourceProcessingResults(futureSource);
manageTargetProcessingResults(futureTarget);
executorService.shutdown();
//Do rest of the tasks.
}
#SuppressWarnings({"unchecked", "rawtypes"})
protected Future<?> processSource(executorService){
//Get appropriate class instance with call() - coreActionClass.
Future<?> futureSource = executorService.submit(coreActionClass);
return futureSource;
}
#SuppressWarnings({"unchecked", "rawtypes"})
protected Future<?> processTarget(executorService){
//Get appropriate class instance with call() - coreActionClass.
Future<?> futureTarget = executorService.submit(coreActionClass); //callable method in core.
return futureTarget;
}
private void manageSourceProcessingResults(Future<?> futureSource) {
try{
futureSource.get();
} catch(Exception e){
e.printStackTrace();
}
}
private void manageTargetProcessingResults(Future<?> futureTarget) {
try{
futureTarget.get();
} catch(Exception e){
e.printStackTrace();
}
}
}
My understanding is that when I configure maxpoolsize(2) no more
than 2 requests would be in the execute() method at one time. For a
new request to enter, one of the earlier requests has to complete
its execution. Is my understanding correct? Would the async apply
to the inner executor service?
I am of the view that at one time only 2 requests are handled and
each of those requests can spawn 2 different threads and complete
its task. Please clarify.
I see two problems.
1) In your process method you are creating a new ExecutorService. This is not needed. Instead just call the execute method after the jsonObjectPOJO is retrieved.
2) You cannot use #Async int he same class that it is implemented. You'll need to create a new class, lets called MyAsyncService to contain the #Async method. This is because of the aspect orientated programming that is going on under the covers.
Check out this link for more info. Below is a quote from the link.
First – let’s go over the rules – #Async has two limitations:
it must be applied to public methods only
self-invocation – calling the async method from within the same class – won’t work
The reasons are simple – the method needs to be public so that it can be proxied. And self-invocation doesn’t work because it bypasses the proxy and calls the underlying method directly.
EDIT 1:
#RestController
#RequestMapping("/end2end")
public class End2EndRestController {
#AutoWired
AsyncService asyncService;
private final Logger log = LoggerFactory.getLogger(this.getClass());
#RequestMapping(method = RequestMethod.POST)
public JSONObjectPOJO process(#RequestBody String end2EndScenarioString) throws InterruptedException, ExecutionException {
final JSONObjectPOJO jsonObjectPOJO = convertToJavaObject(end2EndScenarioString);
asyncService.execute(jsonObjectPOJO);
return jsonObjectPOJO;
}
public class AsyncService {
#Async("executor1")
public void execute(JSONObjectPOJO jsonObjectPOJO) throws Exception {
//No Futures, no ExecutorServices, just process that request.
}
}
By creating and configuring the ThreadPoolTaskExecutor to use only 2 threads, you have accomplished your goal.
EDIT2: Spring #Async limit number of threads

Spring Boot #Async method executing synchronously

This is very similar to the other question here: Spring Boot #Async method in controller is executing synchronously. However my #Service method annotated with #Async is still executing synchronously. I've tried all methods from different forums to no use. Hopefully someone could help me figure out why. A simple spring boot project as below doesn't work.
AsyncConfiguration.java
#Configuration
#EnableAsync
public class AsyncConfiguration(){}
SomeService.java
#Service
public class SomeService() {
#Async
public void doSomething() {
try {
Thread.sleep(5000L);
} catch (Exception ignore){}
}
}
SomeController.java
#Controller
public class SomeController() {
#Inject SomeService someService;
#RequestMapping(value="/", method=RequestMethod.POST)
public String doStuff() {
someService.doSomething();
return "mytemplate";
}
}
Here is a simple example with #Async. Follow these steps to get #Async to work in your Spring Boot application:
Step 1: Add #EnableAsync annotation and Add TaskExecutor Bean to Application Class.
Example:
#SpringBootApplication
#EnableAsync
public class AsynchronousSpringBootApplication {
private static final Logger logger = LoggerFactory.getLogger(AsynchronousSpringBootApplication.class);
#Bean(name="processExecutor")
public TaskExecutor workExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("Async-");
threadPoolTaskExecutor.setCorePoolSize(3);
threadPoolTaskExecutor.setMaxPoolSize(3);
threadPoolTaskExecutor.setQueueCapacity(600);
threadPoolTaskExecutor.afterPropertiesSet();
logger.info("ThreadPoolTaskExecutor set");
return threadPoolTaskExecutor;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(AsynchronousSpringBootApplication.class,args);
}
}
Step 2: Add Method which executes an Asynchronous Process
#Service
public class ProcessServiceImpl implements ProcessService {
private static final Logger logger = LoggerFactory.getLogger(ProcessServiceImpl.class);
#Async("processExecutor")
#Override
public void process() {
logger.info("Received request to process in ProcessServiceImpl.process()");
try {
Thread.sleep(15 * 1000);
logger.info("Processing complete");
}
catch (InterruptedException ie) {
logger.error("Error in ProcessServiceImpl.process(): {}", ie.getMessage());
}
}
}
Step 3: Add an API in the Controller to execute the asynchronous processing
#Autowired
private ProcessService processService;
#RequestMapping(value = "ping/async", method = RequestMethod.GET)
public ResponseEntity<Map<String, String>> async() {
processService.process();
Map<String, String> response = new HashMap<>();
response.put("message", "Request is under process");
return new ResponseEntity<>(response, HttpStatus.OK);
}
I have also written a blog and a working application on GitHub with these steps. Please check:
http://softwaredevelopercentral.blogspot.com/2017/07/asynchronous-processing-async-in-spring.html
Sorry for my English.
The same problem happened to me.
The solution was to add
#Autowired
private SomeService someService;
In the Controller class, in this way it allows the class to contain the Beam Configurations associated with "SomeService", thus being able to execute the asynchronous method perfectly.
Here is a project with a functional asynchronous method:
https://github.com/JColmenares/async-method-api-rest.git

Categories

Resources