Why #Async is not working if i keep async method in same class - spring

Here is my code to work with #Async with CompletableFuture in Spring Boot:
#RestController
#EnableAsync
public class DemoController {
#Autowired
UtilService service;
#GetMapping("/test/{count}")
public void test(#PathVariable int count) throws InterruptedException, ExecutionException {
List<CompletableFuture<String>> list = new ArrayList<>();
long start = System.currentTimeMillis();
for(int i=0; i< count; i++) {
//CompletableFuture<String> res = calculate(i);
CompletableFuture<String> res = service.calculate(i);
list.add(res);
}
List<String> res = list.stream().map(com -> com.join()).collect(Collectors.toList());
res.forEach(System.out:: println);
System.out.println("Elapsed time: " + (System.currentTimeMillis() - start));
}
#Async
public CompletableFuture<String> calculate(int counter) throws InterruptedException{
Thread.sleep(1000L);
System.out.println("------Util------counter->" + counter);
return CompletableFuture.completedFuture("Async -" + counter);
}
}
#Service
public class UtilService {
#Async
public CompletableFuture<String> calculate(int counter) throws InterruptedException{
Thread.sleep(1000L);
System.out.println("------Util------counter->" + counter);
return CompletableFuture.completedFuture("Async -" + counter);
}
}
When i keep the async method calculate() in the same contrller class, code is not executing in asynchronus way. it is running as a synchronus method.
But when i move the async method calculate() to a different component. It is working fine.
I didnt understand why there is behavour change.
if both methods (calling, caller) are in same class, code is running synchronusly.
if both methods (calling, caller) are in different classes, code is running in asynchronusly.
Could you any one explain me is there any reason behind.

For the same reason that the #Transactional Annotation only works in a Subclass and not the same Class.
Spring creates Proxies around your Services. Some Annotations only can be used when the proxy intercepts the call and the execute the code. Does Spring #Transactional attribute work on a private method?
Static code analysis (e.g. Sonar) should warn you about such Annotations that not used.

Related

Spring-boot AOP advice with CompletableFuture

I try to log with AOP for a CompletableFuture controller. #Before advice is working OK. But with #AfterReturning it is not working correctly in the exception case. I also tried with #AfterThrowing, which is not working either. When an exception occurs, my #AfterReturning advice also is not triggered and #AfterThrowing is never reached.
How can I use an AOP advice with exceptions in this case?
JController:
#RestController
public class JController extends BaseExceptionHandler {
#GetMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public CompletableFuture<BaseResponse> search() {
final CompletableFuture result = asyncProcessor.process(request);
return result;
}
}
BaseExceptionHandler:
public class BaseExceptionHandler {
#ExceptionHandler(Exception.class)
public ResponseEntity handleException(final Exception exception) {
return new ResponseEntity<>(new ErrorResponse(Message.INTERNAL_SERVER_ERROR, StatusCode.UNKNOWN_ERROR), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
AOP Class
#AfterReturning(value = "execution(* com.xxx.xxx.controller.*.*(..))", returning = "result")
public void outgoingSuccess(final JoinPoint joinPoint, final CompletableFuture result) {
LOGGER.debug("After Returning method: " + joinPoint.getTarget().getClass().getSimpleName());
}
#AfterThrowing("execution(* com.xxx.xxx.controller.*.*(..))")
public void outgoingError(final JoinPoint joinPoint) {
LOGGER.debug("After Throwing method: " + joinPoint.getTarget().getClass().getSimpleName());
}

Spring Boot Controller Test Failing

I have a RestController that has just one method for http GET and is taking no input arguments. It is calling the service method which takes some arguments. Below is the controller snippet.
#RequestMapping(value = "/leagueResults", method = RequestMethod.GET)
public List<LeagueTableEntry> getResults(){
List<LeagueTableEntry> leagueTableEntryList = new ArrayList<>();
List<Match> listOfMatches = getListOfMatches();
leagueTableEntryList = leagueService.getResults(listOfMatches);
return leagueTableEntryList;
}
Below is my ControllerTest class snippet
#RunWith(SpringRunner.class)
#WebMvcTest(LeagueController.class)
#WebAppConfiguration
public class LeagueControllerTest {
#Autowired
private MockMvc mvc;
#MockBean
private LeagueService leagueService ;
private List<LeagueTableEntry> leagueEntryList;
private List<Match> matchList;
#Before
public void setUp() throws Exception
{
MockitoAnnotations.initMocks(this);
createSampleInputData();//This method populates the instance variable matchList
getLeagueEntryOutput();//This method populates the instance variable leagueEntryList
}
#Test
public void checkNoOfRecordsReturned()throws Exception {
try{
Mockito.when(leagueService.getResults(matchList)).thenReturn(leagueEntryList);
mvc.perform(get("/leagueResults").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(4)));
}
catch(Exception e){
throw new Exception();
}
}
private void getLeagueEntryOutput(){
leagueEntryList = new ArrayList<>();
leagueEntryList.add(new LeagueTableEntry());
leagueEntryList.add(new LeagueTableEntry());
leagueEntryList.add(new LeagueTableEntry());
leagueEntryList.add(new LeagueTableEntry());
}
So, here I am expecting the count of objects in the returned list as 4, but it is coming as 0.So, my test is failing. Can you please let me know what am i missing.
I think you can instead of writing
Mockito.when(leagueService.getResults(matchList)).thenReturn(leagueEntryList);
write
Mockito.when(leagueService.getResults(Mockito.anyList())).thenReturn(leagueEntryList);
Also if this didn't work I would need to get the implementation of
List<Match> listOfMatches = getListOfMatches();

Spring Aspect on Converter

I created simple Aspect an annotation for measuring time of execution of annotated method.
When I annotate method of a simple Spring Bean, inject bean, and run it like bean.annotatedMethod(), everything works fine.
However, when I annotate convert() method on Spring Converter, annotation is ignored. I'm guessing the reason is that convert() is called internally by Spring's ConversionService, and somehow Aspects are not respected. Is there any way to get it to work?
Annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface LogExecTime {
}
Aspect, which I register in Spring:
#Aspect
#Component
public class LogTimeAspect {
#Around(value = "#annotation(annotation)")
public Object LogExecutionTime(final ProceedingJoinPoint joinPoint, final LogExecTime annotation) throws Throwable {
final long startMillis = System.currentTimeMillis();
try {
System.out.println("Starting timed operation");
final Object retVal = joinPoint.proceed();
return retVal;
} finally {
final long duration = System.currentTimeMillis() - startMillis;
System.out.println("Call to " + joinPoint.getSignature() + " took " + duration + " ms");
}
}
}
This works fine:
#Component
public class Operator {
#LogExecTime
public void operate() throws InterruptedException {
System.out.println("Performing operation");
Thread.sleep(1000);
}
}
#Bean
protected Void test(Operator o) {
o.operate();
return null;
}
But here, annotation is ignored:
public class SampleConverter implements Converter<SourceType, ResultType> {
#Override
#LogExecTime
public ImmutableData convert(#Nonnull ClassifiedEvent result) {
...
}
}
ConversionService conversionService;
...
conversionService.convert(source, ResultType.class));
Solved by comment of #EssexBoy, my converter was not a spring managed bean.

Spring Boot Aspect #Around

I am using Spring Boot & try to log response time of every request.
For that purpose, I am trying to #Around Aspect.
Code :
#Aspect
#Component
public class XYZ {
#Around("execution(* org.springframework.web.servlet.DispatcherServlet.service(..))")
public void doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
long startTime = System.currentTimeMillis();
System.out.println("Before");
pjp.proceed();
long endTime = System.currentTimeMillis();
// stop stopwatch
System.out.println("Me here");
}
}
The code gets compiled, but the issue is that when I am executing any controller method, nothing happens. I mean my SOP should get printed but they aren't.
What am i missing?
How about this
#Pointcut("#annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void requestMapping() {}
#Pointcut("within(path.to your.controller.package.*)")
public void myController() {}
#Around("requestMapping() || myController()")
public void logAround(ProceedingJoinPoint joinPoint) throws Throwable {
...............
joinPoint.proceed();
...............
}

Spring MVC how to get progress of running async task

I would like to start an asynchronous task from within controller like in following code sniplet from Spring docs.
import org.springframework.core.task.TaskExecutor;
public class TaskExecutorExample {
private class MessagePrinterTask implements Runnable {
private int cn;
public MessagePrinterTask() {
}
public void run() {
//dummy code
for (int i = 0; i < 10; i++) {
cn = i;
}
}
}
private TaskExecutor taskExecutor;
public TaskExecutorExample(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
public void printMessages() {
taskExecutor.execute(new MessagePrinterTask());
}
}
afterwards in annother request (in the case that task is running) I need to check the progress of the task. Basicaly get the value of cn.
What would be the best aproach in Spring MVC a how to avoid syncronisation issues.
Thanks
Pepa Procházka
Have you looked at the #Async annotation in the Spring reference doc?
First, create a bean for your asynchronous task:
#Service
public class AsyncServiceBean implements ServiceBean {
private AtomicInteger cn;
#Async
public void doSomething() {
// triggers the async task, which updates the cn status accordingly
}
public Integer getCn() {
return cn.get();
}
}
Next, call it from the controller:
#Controller
public class YourController {
private final ServiceBean bean;
#Autowired
YourController(ServiceBean bean) {
this.bean = bean;
}
#RequestMapping(value = "/trigger")
void triggerAsyncJob() {
bean.doSomething();
}
#RequestMapping(value = "/status")
#ResponseBody
Map<String, Integer> fetchStatus() {
return Collections.singletonMap("cn", bean.getCn());
}
}
Remember to configure an executor accordingly, e.g.
<task:annotation-driven executor="myExecutor"/>
<task:executor id="myExecutor" pool-size="5"/>
One solution could be: in your async thread, write to a DB, and have your checking code check the DB table for progress. You get the additional benefit of persisting performance data for later evaluation.
Also, just use the #Async annotation to kick off the asynchronous thread - makes life easier and is a Spring Way To Do It.
Check this github source, it gives pretty simple way of catching status of the background job using #Async of Spring mvc.
https://github.com/frenos/spring-mvc-async-progress/tree/master/src/main/java/de/codepotion/examples/asyncExample
Ignoring synchronization issues you could do something like this:
private class MessagePrinterTask implements Runnable {
private int cn;
public int getCN() {
return cn;
}
...
}
public class TaskExecutorExample {
MessagePrinterTask printerTask;
public void printMessages() {
printerTask = new MessagePrinterTask();
taskExecutor.execute(printerTask);
}
...
}

Resources