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
Related
I'm trying to perform a .findAll() on a repository interface that extends from CrudRepository. I'm trying to do this inside a #Async method using a #Autowire implementation of the repository. When I run the following code the #Async thread waits forever at the UpdateTasksService
List<Task> tasks = (List<Task>)taskRepo.findAll();
When I remove the #Async annotation from the updateTasks() method, the program runs as expected and prints all .toString() data.
My questions are:
Why can't I use the #Autowired TaskRepository taskRepo; inside a #Async method?
How can I use the repository inside a #Async method?
Thank you in advance!
ScheduleComponent
#Component
public class ScheduleComponent {
#Autowired
UpdateTasksService updateTasks;
#PostConstruct
public void update(){
Future<Void> updateTasksFuture = updateTasks.updateTasks();
try {
updateTasksFuture.get();
System.out.println("Done");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
UpdateTaskService
#Service
public class UpdateTasksService {
#Autowired
TaskRepository taskRepo;
#Async
public Future<Void> updateTasks() {
System.out.println("The method starts");
List<Task> tasks = (List<Task>)taskRepo.findAll();
for(Task task: tasks){
System.out.println(task.toString());
}
return new AsyncResult<Void>(null);
}
}
TaskRepository
#Repository
public interface TaskRepository extends CrudRepository<Task, String> {
}
I had the same issue and noticed that this behavior does not occur when a query is based on the JDBC API (not the JPA repository) or when the query is defined with #Query in the SpringData repository. As a workaround, I called a flush on one of the repositories in PostConstruct. I still don't know why this happens.
Workaround:
#Service
public class UpdateTasksService {
#Autowired
TaskRepository taskRepo;
#PostConstruct
public void init() {
//Workaround to a async method that waits forever when called
//from PostConstruct from another component
taskRepo.flush();
}
#Async
public Future<Void> updateTasks() {
System.out.println("The method starts");
List<Task> tasks = (List<Task>)taskRepo.findAll();
for(Task task: tasks){
System.out.println(task.toString());
}
return new AsyncResult<Void>(null);
}
}
I want to assert an exception that should be thrown within an #Async void method.
The following fails, even though I already add a SyncTaskExecutor explicit.
org.opentest4j.AssertionFailedError: Expected RuntimeException to be thrown, but nothing was thrown.
#TestConfiguration
public class SyncTaskExecutorTestConfiguration {
#Bean
#Primary
public TaskExecutor asyncExecutor() {
return new SyncTaskExecutor();
}
}
#SpringBootTest
#Import(SyncTaskExecutorTestConfiguration.class)
public class MyTest {
#Test
public void test() {
assertThrows(RuntimeException.class, () -> service.run());
}
}
#Service
#Async //also #EnableAsync existing on #Configuration class
public class AsyncService {
public void run() {
//of course real world is more complex with multiple sub calls here
throw new RuntimeException("junit test");
}
}
I'm facing the same problem.
bilak's post gave the idea of having my custom AsyncUncaughtExceptionHandler declared with a #Component annotation.
Then, in my custom implmentation of AsyncConfigurer I was injecting my custom AsyncUncaughtExceptionHandler.
In my tests, I used the #MockBean annotation on my custom AsyncUncaughtExceptionHandler, so I was able to verify that the handleUncaughtException was called with the appropriate exception.
Code sample:
AsyncExceptionHandler
#Slf4j
#Component
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
log.error("Exception while executing with message: {} ", throwable.getMessage());
log.error("Exception happen in {} method ", method.getName());
}
}
CustomAsyncConfigurer
#Configuration
public class CustomAsyncConfigurer implements AsyncConfigurer {
final private AsyncExceptionHandler asyncExceptionHandler;
#Autowired
public TaskExecutorConfiguration(AsyncExceptionHandler asyncExceptionHandler) {
this.asyncExceptionHandler = asyncExceptionHandler;
}
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("AsyncThread::");
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return asyncExceptionHandler;
}
}
My unit test:
class FooServiceTest extends FooApplicationTests {
#MockBean
private AsyncExceptionHandler asyncExceptionHandler;
#Autowired
private FooService fooService;
#Test
void testCreateEnrollmentBioStoreException() throws Exception {
fooService.doBar();
ArgumentCaptor<FooException> argumentCaptor = ArgumentCaptor.forClass(FooException.class);
verify(asyncExceptionHandler, times(1)).handleUncaughtException(argumentCaptor.capture(), any(), any());
FooException exception = argumentCaptor.getValue();
assertEquals("Foo error message", exception.getMessage());
}
}
I'm not sure if this is the right way, but I have a void method that was turned into async, so I didn't want to change the return value just for the tests.
Since the #Async method get executed asynchronously by a thread from asyncExecutor and it is terminated due to RuntimeException which doesn't have any impact on Main thread, the actually Main-Test thread competes successfully with the rest of flow once after it trigger the async call. So i will recommend to use the CompletableFuture to hold the reference of Async process always even it's required or not and truthfully will help in test cases
#Service
#Async
public class AsyncService {
public CompletableFuture<Void> run() {
//of course real world is more complex with multiple sub calls here
throw new RuntimeException("junit test");
}
}
So in the test you can wait for Async thread to complete assert the cause from ExecutionException, Since the get method throws ExecutionException if this future completed exceptionally
CompletableFuture.allOf(wait);
One more note you can refer link for asserting wrapped exceptions
What about using AsyncUncaughtExceptionHandler that will be defined for your AsyncConfigurer?
So basically when you execute your method which throws exception you can verify that exception was handled inside handler? Just an idea, didn't tried this.
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);
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.
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.