#Async in spring mvc working without a Future return type? - spring

Sometimes when I am using #Async without return type of Future it returns null but sometimes it returns String. In documentation it is mentioned that return type of Future is must. I am confused why this is happening?
e.g.this one is working
#Service
public class MyClass implements MyClass2{
#Async
#Override
public String getString() {
return "hello";
}
}

Please don't return a String from an #Async method. This is against the intentions of the makers. Excerpt from the Spring Execution & Scheduling docu:
Even methods that return a value can be invoked asynchronously. However, such methods are required to have a Future typed return value. This still provides the benefit of asynchronous execution so that the caller can perform other tasks prior to calling get() on that Future.
If your asynchronous method has a return type, it MUST be a Future object!
Why? Because otherwise the method invoker won't be able to wait for the completion of the Thread that has handled the #Async method. The waiting for Thread-completion is handled by Future.get

Related

Is CompletableFuture is must use return type for AAsync spring boot annotation?

I want to Use #Async annotation on method for running it asynchronously. I have defined my ThreadExecutor as follows:
#Bean("threadPoolTaskExecutor")
public TaskExecutor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);
executor.setMaxPoolSize(200);
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(30);
executor.setThreadNamePrefix("async-");
executor.setQueueCapacity(50);
executor.initialize();
return executor;
}
My question is, is it mandatory to use CompletableFuture as return type for the method using #Async annotation? Will it work if my third party REST calls returns different/Custom types? for e.g.
#Async("threadPoolTaskExecutor")
public ResponseDTO getCapa(final List<String> vins) {
for (String vin : vins) {
CapabilityDTO capabilityDTO = new CapabilityDTO();
// call third party
Optional<ResponseDTO> response=thirdPartyClient.getInfo();
..........
return response.get();
}
}
or I must use CompletableFuture<ResponseDTO> ??
As per the Spring Async javadoc:
the return type is constrained to either void or Future. In the latter case, you may declare the more specific ListenableFuture or CompletableFuture types which allow for richer interaction with the asynchronous task and for immediate composition with further processing steps.
In practice, I believe that if you declare another type, the caller will receive a null value since no value will be available at the moment of the call and it has to return immediately, so the caller won’t be able to retrieve the result.
If you want to be able to access the result, you must thus return a Future containing it. Easiest is probably to just use
return CompletableFuture.completedFuture(response.get())
Spring Async will then take care of forwarding your result to the caller when your method returns.

Mono response from a method which returns void

I have a service method which does not result anything, but can return an HttpException.
example
class Service{
public void myService() throws HttpException{
//do something
}
}
My calling class has a method which is supposed to return a Mono. This method calls myService().
class Caller{
#Autowire
Service service;
public Mono<Response> callMyService(){
return Mono.just("abc")
.doOnSuccess(service.myService())
.thenReturn(new Response()); //this should return Mono<Response>
}
}
My question, is how can I write callMyService() in a good way? Mono.just("abc") doesn't seem right implementation.
You should use Mono<Void> for this purpose. This mono will not forward any data, it will only signal error or completion.
You can create it using then()
Also, remember that doOnSuccess() is side effect. You should not use it for data processing, maybe use map() or flatMap(). For you case, maybe you can use Mono.fromCallable(()->service.myService()), but that may not be correct depending on what service actually does.

#Async is not working if I give it at service level in spring-boot

I am autowiring service in controller. And in service, I have a scenario where I need to throw an exception and DB changes also. So, I tried #Async
#Transactional
public void verifyOtp(OtpDto otpDto)
...
if(xyz){
deactivateOtp(emp.getId());
throw new ServException("Mobile no requested is already assigned", "error-code");
}
}
#Async
#Transactional //with or without
public void deactivateOtp(Integer id){
otpRepo.deactivateOtp(id);
}
public interface OtpRepository extends JpaRepository<Otp, Integer> {
#Modifying
#Query("UPDATE Otp SET isActive = 0 WHERE id = :id")
public void deactiveOtp(#Param("id") Integer id);
This is not creating new thread. But, if I gives at repo, it works
public void deactivateOtp(Integer id){
otpRepo.deactivateOtp(id);
}
public interface OtpRepository extends JpaRepository<Otp, Integer> {
#Async
#Transactional
#Modifying
#Query("UPDATE Otp SET isActive = 0 WHERE id = :id")
public void deactiveOtp(#Param("id") Integer id);
First of all check that the service is wrapped into proxy (you can place a breakpoint in controller and see the reference to the service, it will be with proxy). Otherwise there is something wrong with the configuration and #Transactional/#Async won't work till you fix that.
Now, assuming this is not an issue, there is an issue in the code:
When the controller calls service.verifyOtp it goes to the proxy (to handle the transaction) and then to your implementation.
But when it reaches your implementation and you call the method that belongs to the same impl, it doesn't pass through the proxy again, instead it directly goes to the deactivateOtp as if there is no spring at all here. Of course, #Async doesn't work.
In terms of resolution:
Consider using self injection if you work with spring 4.3+. Read this thread for more information.
Alternatively, refactor your code so that the deactivateOtp will be a public method of another class. In this case the call won't be "internal" anymore, it will path through Proxy hence the #Async will work.
This is discussed many times.
Basically, Spring AOP will not intercept local method call
(in your case call deactivateOtp() within the same class)
You can read more about this behavior here: Understanding AOP proxies
Highlight:
The key thing to understand here is that the client code inside the main(..) of the Main class has a reference to the proxy. This means that method calls on that object reference will be calls on the proxy, and as such the proxy will be able to delegate to all of the interceptors (advice) that are relevant to that particular method call. However, once the call has finally reached the target object, the SimplePojo reference in this case, any method calls that it may make on itself, such as this.bar() or this.foo(), are going to be invoked against the this reference, and not the proxy. This has important implications. It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.

Is #Recovery method necessary when using Spring-Retry?

My question is regarding spring-retry.
Assume a simple sample code where I have a the Service layer and Controller class.,
This is the testService Interface
public interface testService{
#Retryable(value = { KnownExceptiomn.class }, backoff = #Backoff(delay = 1000), maxAttempts = 2)
Address getAddress(String emailAddress);
}
This is the implementation of the service
public class testServiceImpl{
public Address getAddress(String emailAddress){
//addressRepository is a crud repository
return addressRepository.getAddressFromEmail(emailAddress);
}
}
And the controller is
#GetMapping("path/{emailId}")
public ResponseEntity<?> getAddress(#PathVariable("emailId") final String email){
final Address address;
try{
address= testService.getAddress(String emailAddress);
if(address != null) return new ResponseEntity<Address>(address,HttpStatus.OK);
return new ResponseEntity<String>("Invalid Email", HttpStatus.BAD_REQUEST);
}catch(KnownException e){
return errorMessage("Error");
}
}
As seen the #Retryble is in the Service interface. However I have not implemented an #Recover method. My thought here was since i dont really have any secondary DB and If the DB is down there really isn't a recovery option I did not add a #Recovery method. Instead the exception was caught in the controller and handled.
My questions are:
Is the above approach wrong. If so, how to do it the right way?
Is it always necessary to have a recovery method? If so what would be recovery in these kind of scenarios like DB being down and no alternative source of Data.
Is it wrong to catch exceptions in controller and handle
them accordingly? (I was told the service lay should handle all the
exceptions in some discussions).
Everywhere I've see is some sort of recovery method but couldn't find a solid example with proper recovery handler for this type of scenario if there is.
#Recovery is optional; if there is none, after retries are exhausted, the last exception is thrown to the caller, who has to handle the exception.
It's perfectly normal to not have a #Recovery and handle the exception in the caller, by whatever means you like.
#Vipin Menon: I think you are referring to #Recover annotation, so the answer is No. #Recover annotation is provided just to give the programmer flexibility to create a recovery method to return a default (fallback) response if retry attempts also fails.
In a nutshell, don't create a recovery method using #Recover annotation if you don't need any default behavior when retry attempts fail. Simply use #Retryable annotation on the actual service method.

Spring #Async annotation

I have a question about Spring #Async annotation.
I have a controller autowired a service(GnInsuranceDetailsService)
#RequestMapping(value="/agn/AP0W01A_010/insertDetail.do")
public ResponseEntity<?> insertDetail(#ModelAttribute("dto")GnInsuranceDetailsDTO dto,HttpSession session) throws Exception {
gnInsuranceDetailsDTO.setBkFlag(getAgnUserInfo(session).getBkFlag());
gnInsuranceDetailsService.insertGnInsuranceDetail(dto);//open another thread to insert data
MessageDTO dto = new MessageDTO(AgnConstant.INSERT_SUCCESS);
return new ResponseEntity<MessageDTO>(dto,HttpStatus.OK);// increase return time for client
}
And the Service insertGnInsuranceDetail method I declare #Async up method.
#Transactional(readOnly = false)
#Async
public void insertGnInsuranceDetail(GnInsuranceDetailsDTO gnInsuranceDetailsDTO) throws Exception{
GnInsuranceDetails entity = gnInsuranceDetailsDTO.convert();
gnInsuranceDetailsDAO.save(detailsEntity);
}
I put the #Async for the service method to increase controller response time for client side,but it does not work as I think.
Do I lose somethings?Or How can I modify in the easiest way to use?
You would not loose anything, when you put #Async in the method service will be executed in a different thread, Controllers insertDetail method will not be returned until your insertGnInsuranceDetail is returned or thrown any exception.
I put the #Async for the service method to increase controller response time for client side,but it does not work as I think.
#Async - Annotation that marks a method as a candidate for asynchronous execution. Can also be used at the type level, in which case all of the type's methods are considered as asynchronous.
This #Async annotation will not help you in delaying the response time. To introduce a delay, use Thread.sleep(milliseconds);
If you want main thread(controller) to wait for insert data thread to get a result (successful/failure) you can just invoke db layer code from the controller. Whereas if you want the client response to be sent earlier, then create a new thread inside the controller and use that thread to insert data. In the latter approach of creating a new thread for insert data your client will not know about the status of data insertion, since the thread is created in the controller and it will end in the controller, no feedback/response can be given to the client, since we will not be having client details.
Hope it Helps!
Reference: https://www.tutorialspoint.com/java/lang/thread_sleep_millis.htm
#Async should be first enabled in your #Configuration bean:
#Configuration
#EnableAsync
public class AppConfiguration {
[...]
}
for xml configuration add this: <task:annotation-driven>

Resources