How to run #Async method inside the #Scheduled one - java

I have read many questions and answers about using #Scheduled with #Async in Spring but no one resolves my problem and my asynchronous method still runs single-threaded. So here is my Configuration class:
#EnableScheduling
#EnableAsync
#Configuration
#RequiredArgsConstructor
public class SchedulerConfiguration {
private final ThreadPoolProperties threadPoolProperties;
#Bean
public TaskExecutor commonTaskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(threadPoolProperties.getCorePoolSize()); // 10
taskExecutor.setMaxPoolSize(threadPoolProperties.getMaxPoolSize()); // 20
taskExecutor.setQueueCapacity(threadPoolProperties.getQueueCapacity()); // 5
taskExecutor.setThreadNamePrefix("TEST");
taskExecutor.initialize();
return taskExecutor;
}
}
Then we have a bean with the #Scheduled method:
#Component
#RequiredArgsConstructor
public class ScheduledTask {
private final ConfirmReservationTask confirmReservationTask;
#Scheduled(cron = "${booking.scheduler.confirmReservationsCron}")
public void process() {
confirmReservationTask.confirmReservations();
}
}
And finally, another bean (to avoid self-injection and proxy problems with asynchronous processing) with #Async method:
#Log4j2
#Component
#RequiredArgsConstructor
public class ConfirmReservationTask {
private final ReservationService reservationService;
#Async("commonTaskExecutor")
public void confirmReservations() {
...
}
}
unfortunately, this solution works in only one thread, however, the method uses the correct ThreadExecutor. How to solve it?

Related

How to make Aysnc annotation work in Spring Boot?

I created a simple spring boot application with scheduled (#Scheduled) task. In that scheduled task, I would like to call async function with #Async, but I can see it still runs on the scheduling thread without switch to another thread. I also tried to customise executor, but no luck. Here are some codes.
I also already enable async in main class
public class scheduledService {
#Scheduled(fixedRateString = "${config.scheduleInterval}")
public void pollDataWithFixSchedule() {
AsyncService service = new AsyncService();
service.asyncCall();
service.asyncCall();
service.asyncCall();
asyncCall();
}
}
public class AsyncService {
#Async()
public void asyncCall(){
System.out.printly("Current thread -- {}",Thread.currentThread().getName()))
Thread.sleep(10000);
}
}
#Bean(name = "MyThreadPoolExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("MyThreadPoolExecutor-");
executor.initialize();
return executor;
}
#SpringBootApplication
#EnableScheduling
#EnableAsync
public class ScheduledApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(ScheduledApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}
according to Baeldung:
#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.
so you can put your async method in a service and use it from there
you need to autowire AsyncService, do not create new object like
AsyncService service = new AsyncService();
Also, annotate your scheduledService class with #Service or #Component
#Service
public class scheduledService {
#Autowired
private AsyncService service ;
#Scheduled(fixedRateString = "${config.scheduleInterval}")
public void pollDataWithFixSchedule() {
service.asyncCall();
service.asyncCall();
service.asyncCall();
}
}
不要在同一个类中调用异步方法
Do not call asynchronous methods in the same class.
将异步任务单独放到一个类 并且在这个类上加上#Component
Put asynchronous tasks into a single class and add # component to this class
Use #EnableAsync on the top of class where you are creating async bean, not on the ScheduledApplication.

Spring inject task executor in bean

I'm using Spring Boot and I'm not able to inject a task executor in a service bean.
Here's some code:
#Service
public class ClassA {
#Autowired
private ThreadPoolTaskExecutor taskExecutor;
public void doSht(){
for(int i = 0; i<this.taskExecutor.getMaxPoolSize(); i++){
this.taskExecutor.execute(new ClassB());
}
}
}
Class B:
public class ClassB implements Runnable {
#Override
public void run() {
System.out.println("Class B running");
}
}
Controller:
#Controller
public class IndexController {
#Autowired
ClassA ca;
#RequestMapping("/")
public String index(){
return "index";
}
#RequestMapping("test")
public String test(ClassA ca){
ca.doSht();
return "test";
}
}
And here's the task executor configuration:
#SpringBootApplication
public class App{
#Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(10);
taskExecutor.setQueueCapacity(30);
return taskExecutor;
}
public static void main(String[] args) throws Exception{
SpringApplication app = new SpringApplication(App.class);
app.run(args);
}
}
I want the ClassB instances being executed when a request comes to /test, but I get a NullPointerException because the task executor being not autowired into ClassA bean.
What am I doing wrong?
To fix the error please see the following instructions:
Go to IndexController class
Go to public String test(ClassA ca) method
Remove Class ca input parameter form test method
test method should be like this
test method changed:
#RequestMapping("test")
public String test(){
ca.doSht();
return "test";
}
The null pointer exception is caused because test method is using ca method argument instead of ca object that comes from #Autowired annotation
As your using #SpringBootApplication in App class , it is registering your custom bean definition to IOC and also it is autowiring the bean correctly. Otherwise you'll get error on autowiring class.
So its not the issue with autowiring. You can set a debug point to check that. Now in your controller you messed up your instance variable with method argument by using same name. Moreover #RequestMapping not supplying your custom class objects. So it is coming as null and you're get the exception.
Hope you understand the issue.

JUnit-testing a Spring #Async void service method

I have a Spring service:
#Service
#Transactional
public class SomeService {
#Async
public void asyncMethod(Foo foo) {
// processing takes significant time
}
}
And I have an integration test for this SomeService:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#IntegrationTest
#Transactional
public class SomeServiceIntTest {
#Inject
private SomeService someService;
#Test
public void testAsyncMethod() {
Foo testData = prepareTestData();
someService.asyncMethod(testData);
verifyResults();
}
// verifyResult() with assertions, etc.
}
Here is the problem:
as SomeService.asyncMethod(..) is annotated with #Async and
as the SpringJUnit4ClassRunner adheres to the #Async semantics
the testAsyncMethod thread will fork the call someService.asyncMethod(testData) into its own worker thread, then directly continue executing verifyResults(), possibly before the previous worker thread has finished its work.
How can I wait for someService.asyncMethod(testData)'s completion before verifying the results? Notice that the solutions to How do I write a unit test to verify async behavior using Spring 4 and annotations? don't apply here, as someService.asyncMethod(testData) returns void, not a Future<?>.
For #Async semantics to be adhered, some active #Configuration class will have the #EnableAsync annotation, e.g.
#Configuration
#EnableAsync
#EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {
//
}
To resolve my issue, I introduced a new Spring profile non-async.
If the non-async profile is not active, the AsyncConfiguration is used:
#Configuration
#EnableAsync
#EnableScheduling
#Profile("!non-async")
public class AsyncConfiguration implements AsyncConfigurer {
// this configuration will be active as long as profile "non-async" is not (!) active
}
If the non-async profile is active, the NonAsyncConfiguration is used:
#Configuration
// notice the missing #EnableAsync annotation
#EnableScheduling
#Profile("non-async")
public class NonAsyncConfiguration {
// this configuration will be active as long as profile "non-async" is active
}
Now in the problematic JUnit test class, I explicitly activate the "non-async" profile in order to mutually exclude the async behavior:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#IntegrationTest
#Transactional
#ActiveProfiles(profiles = "non-async")
public class SomeServiceIntTest {
#Inject
private SomeService someService;
#Test
public void testAsyncMethod() {
Foo testData = prepareTestData();
someService.asyncMethod(testData);
verifyResults();
}
// verifyResult() with assertions, etc.
}
I have done by injecting
ThreadPoolTaskExecutor
and then
executor.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);
before verifying results,
it as below:
#Autowired
private ThreadPoolTaskExecutor executor;
#Test
public void testAsyncMethod() {
Foo testData = prepareTestData();
someService.asyncMethod(testData);
executor.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);
verifyResults();
}
If you are using Mockito (directly or via Spring testing support #MockBean), it has a verification mode with a timeout exactly for this case:
https://static.javadoc.io/org.mockito/mockito-core/2.10.0/org/mockito/Mockito.html#22
someAsyncCall();
verify(mock, timeout(100)).someMethod();
Much more capable is the great library Awaitility, which has many options how to handle async assertions. Example:
someAsyncCall();
await().atMost(5, SECONDS)
.untilAsserted(() -> assertThat(userRepo.size()).isEqualTo(1));
In case your method returns CompletableFuture use join method - documentation CompletableFuture::join.
This method waits for the async method to finish and returns the result. Any encountered exception is rethrown in the main thread.
Just to extend the answer by #bastiat, which in my opinion should be considered the correct one, you should also specified the TaskExecutor, if you are working with multiple executors. So you would need to inject the correct one that you wish to wait for. So, let's imagine we have the following configuration class.
#Configuration
#EnableAsync
public class AsyncConfiguration {
#Bean("myTaskExecutor")
public TaskExecutor myTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(15);
executor.setCoreCapacity(10);
executor.setQueueCapacity(Integer.MAX_VALUE);
executor.setThreadNamePrefix("MyTaskExecutor-");
executor.initialize();
return executor;
}
// Everything else
}
Then, you would have a service that would look like the following one.
#Service
public class SomeServiceImplementation {
#Async("myTaskExecutor")
public void asyncMethod() {
// Do something
}
// Everything else
}
Now, extending on #bastiat answer, the test would look like the following one.
#Autowired
private SomeService someService;
#Autowired
private ThreadPoolTaskExecutor myTaskExecutor;
#Test
public void testAsyncMethod() {
Foo testData = prepareTestData();
this.someService.asyncMethod(testData);
this.myTaskExecutor.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);
this.verifyResults();
// Everything else
}
Also, I have a minor recommendation that has nothing to do with the question. I wouldn't add the #Transactional annotation to a service, only to the DAO/repository. Unless you need to add it to a specific service method that must be atomic.
Just addition to the above solutions:
#Autowired
private ThreadPoolTaskExecutor pool;
#Test
public void testAsyncMethod() {
// call async method
someService.asyncMethod(testData);
boolean awaitTermination = pool.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);
assertThat(awaitTermination).isFalse();
// verify results
}

Injecting the TaskScheduler with Spring

Is it possible to inject the TaskScheduler instance created by Spring?
I would like to schedule tasks programatically and for that, I guess I need to access the TaskScheduler but for some reason, it's not found by Spring for autowiring.
#Configuration
#EnableScheduling
public class MySpringConfig {
}
#Component
public class MyClass implements InitializingBean {
#Autowired
private TaskScheduler taskScheduler;
#Override
public void afterPropertiesSet() throws Exception {
...
}
}
Any idea?
Thanks!
#Configuration
#EnableScheduling
public class MySpringConfig {
#Bean
public TaskScheduler taskScheduler() {
//org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
return new ThreadPoolTaskScheduler();
}
}
You can choose which ever implementation you like. ThreadPoolTaskScheduler is the simpler one as mentioned in this link.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html#scheduling-task-scheduler-implementations

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