Why #Async Annotation Clauses AopInvocationException? - spring

Here is a code snippet:
#GetMapping("/account")
#SuppressWarnings("unchecked")
public UserDTO getAccount(Principal principal) {
...
janitorService.cleanUp((AbstractAuthenticationToken) principal);
...
}
#Component
public class JanitorService {
...
#Async
public boolean cleanUp(AbstractAuthenticationToken authToken){
...
return true;
}
}
and there is an async configuration class.
org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type for: public boolean com.mycompany.myteam.JanitorService.cleanUp(org.springframework.security.authentication.AbstractAuthenticationToken)
The exception won't be thrown after #Async is removed. The reason I use #Async is to kick off a thread. Why does it cause the exception?

For #Async methods with return, the return should be a Future<T> not a T.
Try returning Future<Boolean>
Source: https://www.baeldung.com/spring-async

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());
}

Response received from service to controller class is null after aspect gets executed on service class method

I have a controller class which further calls service class method. An AOP #Around aspect is applied on the service class method.
package com.hetal.example;
#RestController
public class CustomerController {
#Autowired
CustomerService customerService;
#RequestMapping(value = "/getDetails", method = RequestMethod.GET)
public String getCustomerDetails() {
System.out.println("Inside controller class");
String details = customerService.getDetails(custName);
System.out.println("Customer details is = " + details); // prints null
}
}
package com.hetal.example;
#Service
public class CustomerServiceImpl implements CustomerService {
#Override
public String getDetails(String custName) {
//some code
returns "Customer details";
}
}
An aspect is written to be executed #Around the method getDetails() of CustomerServiceImpl
package com.hetal.config;
public class JoinPointConfig {
#Pointcut(value="execution(* com.hetal.example.CustomerService.getDetails(..) && args(custName)"))
public void handleCustomerDetails(String custName) {}
}
package com.hetal.config;
#Aspect
#Component
public class CustomerAspect {
#Around("com.hetal.config.JoinPointConfig.handleCustomerDetails(custName)")
public Object aroundCustomerAdvice(ProceedingJoinPoint joinpoint, String custName) {
System.out.println("Start aspect");
Object result= null;
try {
result = joinpoint.proceed();
System.out.println("End aspect");
}
catch(Exception e) {}
return result;
}
}
Execution goes as below,
Controller calls CustomerServiceImpl.getDetails method.
CustomerAspect is called, prints "Start aspect". //before advice
joinpoint.proceed() calls actual CustomerServiceImpl.getDetails method.
CustomerServiceImpl.getDetails returns a string "Customer details" and control comes back to the aspect, prints "End aspect" //after returning advice
Control goes back to controller class but the response received is null.
I want the response returned from the service class into the controller class after the completion of the aspect.
Thank you in advance !!
Yeah you some compilation issue in your applications make those changes and with the belwo return type issue in Aspect class,
but the main issue is with your Aspect class, its void return type hence that coming as null you should return the result as object , below is the code
package com.hetal.config;
#Aspect
#Component
public class CustomerAspect {
#Around("com.hetal.config.JoinPointConfig.handleCustomerDetails(custName)")
public Object aroundCustomerAdvice(ProceedingJoinPoint joinpoint, String custName) {
System.out.println("Start aspect");
Object result= null;
try {
result = joinpoint.proceed();
System.out.println("End aspect");
}
catch(Exception e) {}
return result;
}
}

Annotation and Pointcut on same class method

I have two aspects but only TryCatchLog is working even when method annotated with CatchRedBanner.
One on all methods returning AssertionData in package pageActions
package com.abc.acceptance.b2b.aspects;
#Aspect
#Component
public class TryCatchLogAspect {
#Pointcut(
"execution(com.abc.acceptance.b2b.annotations.AssertionData com.abc.acceptance.b2b.pageActions..*(..))")
private void pageActionsTryCatchLog() {
}
#Around("pageActionsTryCatchLog()")
public Object tryCatchLog(ProceedingJoinPoint joinPoint) throws Throwable { ...
This one for methods with my annotation
import java.lang.annotation.*;
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface CheckRedBanner {
}
-----
package com.abc.acceptance.b2b.annotations;
#Aspect
#Component
#Slf4j
public class CheckRedBannerAspect {
#Before("#annotation(CheckRedBanner)")
public void myAdviceForMethodAnnotation(JoinPoint joinPoint) {
handleBeforeExecution(joinPoint);
}
protected void handleBeforeExecution(JoinPoint joinPoint) {
System.out.println("Made iitttt !!! ");
}
}
My code is calling only TryCatchLog but not CheckRedBanner even when I annotate the method
package com.abc.acceptance.b2b.pageActions;
#Component
#Slf4j
#Scope(SCOPE_CUCUMBER_GLUE)
public class BroadbandPanelActions extends PageActions {
#CheckRedBanner
public AssertionData clickFindAddress() {
broadbandPanel.getFindAddressButton().click();
return new AssertionData();
}
...
From the reference docs regarding Advice Ordering
When two pieces of advice defined in different aspects both need to
run at the same join point, unless you specify otherwise, the order of
execution is undefined. You can control the order of execution by
specifying precedence.
For me the behaviour is reproducible when I order the Aspects as follows . I have ordered so that Around advice is executed before Before advice . Also note that I have commented the joinPoint.proceed(); call.
#Component
#Aspect
#Order(0)
public class TryCatchLogAspect {
#Pointcut("execution(sec2.aop.bean.AssertionData sec2.aop.bean..*(..))")
private void pageActionsTryCatchLog() {
}
#Around("pageActionsTryCatchLog()")
public Object tryCatchLog(ProceedingJoinPoint joinPoint) throws Throwable {
// Object result = joinPoint.proceed();
System.out.println("pageActionsTryCatchLog");
return new AssertionData();
}
}
and
#Component
#Aspect
#Order(1)
public class CheckRedBannerAspect {
#Before("#annotation(CheckRedBanner)")
public void myAdviceForMethodAnnotation(JoinPoint joinPoint) {
handleBeforeExecution(joinPoint);
}
protected void handleBeforeExecution(JoinPoint joinPoint) {
System.out.println("Made iitttt !!! ");
}
}
when I uncomment the proceed() call both the aspects work.
#Around("pageActionsTryCatchLog()")
public Object tryCatchLog(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();
System.out.println("pageActionsTryCatchLog");
return new AssertionData();
}
When we are not explicitly calling joinPoint.proceed(); spring does that for us. But in the first case we are returning without calling the underlying method and the #Before advice fails to execute.

Spring boot 1.5.12 Aspect not called

I know that there are a lot of similar questions in Stackoverflow but none of them helped me.
I have a controller like this:
com.mypkg.controller;
#RestController
public class MyController {
#RequestMapping(method = RequestMethod.POST,
......
public ResponseEntity<?> MyEndpoint(myParams) {
return this.myMethod(myParams, "myString");
}
public ResponseEntity<?> myMethod(myParams, String myString){
//do something
return myReponseEntity
}
}
I defined my aspect in this way:
com.mypkg.controller;
#Aspect
#Component
#Slf4j
public class MyAspect {
#Around("execution(* com.mypkg.controller.MyController.MyEndpoint(..)) && args(..,aParam)")
public ResponseEntity<?> endpointAround(ProceedingJoinPoint joinPoint, String aParam) throws Throwable {
// I am working fine
// do something
return
}
#Around("execution(* com.mypkg.controller.MyController.myMethod(..)) && args(..,myString)")
public ResponseEntity<?> myMethodAround(ProceedingJoinPoint joinPoint, String myString) throws Throwable {
// **** I AM NOT CALLED****
// do something
// return ...
}
}
I configured the AutoProxy
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class AopConfig {}
The function endpointAround is called every time that I call MyEndpoint (throw the REST api).
The problem is the second #Around. it is not called. I need to call a method everytime MyEndpoint is exectued and another one eveytime that MyEndpoint call myMethod.
The problem is that your method myMethod is call from within your other method directly, and not as a someSpringBean.myMethod.
The way spring works is by wrapping any of your beans, and then on the 'wrapping' it can execute all the aspect or other spring related stuff. When you call one method from another one inside the same class, you don't go through the wrapping, thus the aspect related stuff can't happen
You just missed some code. Let us use below code snippet it will work.
Use this com.mypkg.controller.MyController.myMethod instead of
com.mypkg.controller.myMethod it will work
Controller
com.mypkg.controller;
#RestController
public class MyController {
#RequestMapping(method = RequestMethod.POST,
......
public ResponseEntity<?> MyEndpoint(myParams) {
return this.myMethod(myParams, "myString");
}
public ResponseEntity<?> myMethod(myParams, String myString){
//do something
return myReponseEntity
}
}
and in Aspect
com.mypkg.controller;
#Aspect
#Component
#Slf4j
public class MyAspect {
#Around("execution(* com.mypkg.controller.MyController.MyEndpoint(..)) && args(..,aParam)")
public ResponseEntity<?> endpointAround(ProceedingJoinPoint joinPoint, String aParam) throws Throwable {
// I am working fine
// do something
return
}
#Around("execution(* com.mypkg.controller.MyController.myMethod(..)) && args(..,myString)")
public ResponseEntity<?> myMethodAround(ProceedingJoinPoint joinPoint, String myString) throws Throwable {
// **** I AM NOT CALLED****
// do something
// return ...
}
}
You just missed the package path. Your method path should be like this...springbean.method

Make Spring's #RequestBody annotation return a custom response on-fail

I have a controller as follows:
public void createEntity(#Valid #RequestBody final MyEntity myEntity) {}
However, when the object transformation fails, the API automatically returns a 400 with the Java stack trace. How can I modify this on-failure response? (I wish to change or remove the response message).
Here is an example how to do this with an #ExceptionHandler annotation
#RestController
public class Controller {
#RequestMapping(value = "/", method = RequestMethod.POST)
public void createEntity(#Valid #RequestBody final MyEntity myEntity) {
//
}
#ControllerAdvice
public class RestEndpointExceptionHandler {
#ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Object> handleNotValidExceptionException(HttpServletRequest req, MethodArgumentNotValidException ex) {
Object customException = "Validation failed";
return new ResponseEntity<Object>(customException, HttpStatus.BAD_REQUEST);
}
}
}
I pushed the code in here
You can use #ExceptionHandler with #ResponseStatus and leave handler empty so that only Status Code is returned back.
#ExceptionHandler(EmptyResultDataAccessException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public void notFoundException() {
}

Resources