Webflux: catch any unhandled exception from mono - spring-boot

I'm using this code in order to save an entity:
public Mono<QdCFPresenter> store(QdCF qdcf) {
return this.qdcfRepository.save(qdcf);
}
According to documentation, ReactiveCrudRepository.save method throws two exceptions:
IllegalArgumentException
OptimisticLockingFailureException
I want to wrap them into my business exception service:
Mono.error(
GitException.builder()
.reason(GitReason.QDCF_NOT_STORED)
.build()
)
);
However, I don't quite figure out how to catch case any exception is raised.

Related

Sonar complaining logging or rethrowing the exception

I have the following below piece of code when I am running SonarQube for code quality check on it after integrating it with Maven.
However, Sonar is complaining that I should Either log or rethrow this exception.
What am I missing here? Can some one help me please.
Code
public ShippingResponse processShipping(ShippingRequest request) {
log.debug("Processing Reservation Request ....");
try{
return helper.processShippingMethod(request);
} catch (ServiceException serviceException) {
log.error(RESERVATION_EXCE, ExceptionUtils.getStackTrace(serviceException));
throw serviceException;
} catch (Exception e) {
throw new ServiceException(ErrorMessages.EPO_SM_ERR_03, e.getMessage());
}
}
The point that Sonar is trying to make is that you ideally print or keep the root cause of your exception, so basically the stack. You keep it by passing the exception object because if you only keep the message you lose all that information. To make sonar happy you either print the stack trace (log.error(ErrorMessages.EPO_SM_ERR_03, e)), or re-throw a new exception passing the Throwable object to the constructor.
So the ideal solution would be to use the ServiceException like this;
public class ServiceException extends Exception {
public ServiceException(String message, Throwable cause) {
super(message, cause);
}
}
throw new ServiceException(ErrorMessages.EPO_SM_ERR_03, e);

Throw new RuntimeException() from a Mono, in AOP

I have a reactive springboot application, in it an aspect
#Before("approveRejectPointcut()")
public void logAfterReturning(JoinPoint joinPoint) {
Status newStatus = AspectUtils.returnFirstParameterOfTypeOrFail(allParameters, Status.class, "approveRejectPointcut");
String comments = AspectUtils.returnFirstParameterOfTypeOrFail(allParameters, String.class, "approveRejectPointcut");
Mono<Object> someObjectOrException = someService.updateApplicationStatus(appId, newStatus, comments);
someObjectOrException
.subscribe(i->log.info(i),
error->{
log.info("we have some ex: "+error.getMessage());
throw new RuntimeException("PLEASE LORD: "+error.getMessage());
});
}
What happens here, the updateApplicationStatus() from some service throws at some point a Business exception(which is good), in this aspect I print the exception message, which is also good. However, when i try to throw a business exception (a RuntimeException at it's base) .. i can see this exception in the console, however.. it does not reach the client.
I suspect it is being thrown on a different thread. Any clues ?
In the end, changed the aspect to #Around
#Around("approveRejectPointcut()")
public Mono<Object> logAfterReturning(ProceedingJoinPoint pjp) {
...
Mono<Object> someObjectOrException = someService.updateApplicationStatus(appId, newStatus, comments);
someObjectOrException
.map(unused->pjp.proceed(pjp.getArgs())
.onErrorMap(RuntimeException.class, (s)->{
return new GenericBusinessException(s.getMessage());
});
}
this way in case all goes well in the service -> we carry on with the execution, otherwise, we throw our business exception with the exception message from the service. Perhaps that map() should be replaced with some other operator accepting a funtion.. :)

Spring ExceptionHandler for Caused By / Wrapped Exception

I couldn't find a good solution: In my Spring Boot app, as an #ExceptionHandler method, I need to define a handler not for a specific exception, but for any exception caused by a specific exception (i.e. a wrapped exception).
Example: Sometimes I get this:
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:541) ~[spring-orm-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:746) ~[spring-tx-5.1.4.RELEASE.jar:5.1.4.RELEASE]
... 121 common frames omitted
Caused by: custom.TechRoleException: My custom TechRoleException
at myapp.method1[..]
at myapp.methodOuter[..]
My custom TechRoleException is an exception I throw inside some Hibernate EventListener's pre-update method, and the direct exception is that Persistence couldn't occur.
However, the following method that tries to use my custom exception is never reached:
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(TechRoleException.class)
public String techRoleException(HttpServletRequest request, Exception ex) {
System.out.println("Got here");
return "home";
}
}
Here's a similar thread where the answer is wrong and didn't answer this question:
#ExceptionHandler for Wrapped Exception / getCause() in Spring
Maybe something like that?
#ExceptionHandler(Exception.class)
public String techRoleException(HttpServletRequest request, Exception ex) {
if(ex instanceof TechRoleException) {
System.out.println("Got here");
return "home";
} else {
throw ex; //or something else
}
}
My final working answer is to handle a General Exception, and then use Apache ExceptionUtils.getRootCause() to detect the specific Caused-By I'm looking for within this general handler.
(Other specific Exceptions won't come to this method if they have their dedicated Handlers. But if there's no dedicated Handler, the exception will come here.) This is the only way to detect some target Caused-By.
#ExceptionHandler(Exception.class)
public String handleGeneralException(HttpServletRequest request, Exception ex) {
Throwable rootCause = ExceptionUtils.getRootCause(ex);
if (rootCause != null && "com.myapp.TechRoleException".equals(rootCause.getClass().getName())
{
//... handle this Caused-By Exception here
// ...
}
// All other exceptions that don't have dedicated handlers can also be handled below...
// ...
}

Spring #Async with Future and Callable

I am trying to implement a generic class that executes a callable asynchronously, but I am not sure about the semantics.
#Component
public class MyCallerImpl implements MyCaller {
#Async
#Override
public <T> Future<T> runAsync(Callable<T> callable) throws Exception {
return new AsyncResult<T>(callable.call());
}
}
Basically, this component executes arbitrary actions from any callable asynchronously using the #Async annotation.
I am unsure about the Exception in the throws clause of the method signature.
A Junit test:
#ContextConfiguration("classpath:test-config.xml")
#RunWith(SpringJUnit4ClassRunner.class)
public class RunnerTest{
#Resource(name="myCallerImpl")
private MyCaller myCaller;
#Test
public void testException(){
final Callable<String> callable = new Callable<String>(){
#Override
public String call() throws Exception{
throw new MyException("foobar");
}
};
try
{
final Future<String> future = myCaller.runAsync(callable); // this can throw Exception due to Callable.call()
future.get(); // this can throw InterruptedException and ExecutionException
}
catch (final InterruptedException ie)
{
// do someting
}
catch (final ExecutionException ee)
{
// we want to check the cause
final Throwable cause = ee.getCause();
assertTrue(cause instanceof MyException);
}
catch (final Exception e)
{
// Not sure what to do here.
// Must be caught as it is declared to
// be thrown from the MyCaller.runAsync() method
// but nothing will really ever get here
// since the method is #Async and any exception will be
// wrapped by an ExecutionException and thrown during Future.get()
fail("this is unexpected);
}
My question is what to do about the Exception declared in the throws clause of MyCallerImpl.runAsync()?
The only reason I have declared it is because of the way I am calling the callable. Originally I had the following in the async method:
FutureTask<T> futureTask = new FutureTask<T>(callable);
futureTask.run();
return futureTask;
But when an exception is thrown from the callable in that instance, it gets wrapped twice in an ExecutionException, the first time when FutureTask.run() is called eventually FutureTask.Sync.innerRun() catches the exception and calls innnerSetException() and a second time when the AsyncExecutionIntercepter gets the result from the Future via Future.get(), which eventually again checks if there is an exception and throws a new ExecutionException wrapping the ExecutionException caught in innerRun()
I also tried to do the following in the method:
FutureTask<T> futureTask = new FutureTask<T>(callable);
return futureTask;
I had figured that since the AsyncExecutionInterceptor calls Future.get(), that the callable would be called immediately, but that was not the case. It just hangs on FutureTask.acquireSharedInterruptibly() and never returns.
Maybe I'm in over my head here. It works how I have it set-up with the callable now, but I rather not have the method signature declare a throws Exception.
Any advice? Should I forget about this generic way of doing async calls with a callable?
There are 2 layers of exception here.
one:
the exception leading to the calling of the Callable
if (string.equals("X")){ callable.call();}
two:
the exception caused when calling the callable.call() method (your "throw new MyException("foobar");")
since you do not have any other code prior to "callable.call();", it would be safe to remove the checked exception.
Change
public <T> Future<T> runAsync(Callable<T> callable) throws Exception
to
public <T> Future<T> runAsync(Callable<T> callable)
additionally, you can code it this way
final Future<String> future = myCaller.runAsync(callable);
try
{
future.get(); // this can throw InterruptedException and ExecutionException
}
catch (final InterruptedException ie)
{
// do someting
}
catch (final ExecutionException ee)
{
// we want to check the cause
final Throwable cause = ee.getCause();
assertTrue(cause instanceof MyException);
}

Using Spring AOP, How can I intercept a exception just when it occurred?

Some code like this:
public class A {
#Autoware
private B b;
public void a() {
//AAA: some logic process that maybe throw exception
b.b();
}
}
public class B {
public void b() {
//BBB: some logic process maybe also throw exception
}
}
Both exceptions in A.a() and B.b() need to be intercept, so i use #AfterThrowing annotation do it. but the question is, when i call A.a() in other code and exception has occurred in B.b(), the Advice will execute twice! because exception that occurred in B.b() was propagating to its caller A.a().
I can't swallow the exception silently, because i use spring-amqp, above codes is on Consumer side, i need some message processing that based on the exceptions that occurred in Consumer.
#Around does not work too since i can't swallow the throwed exception.
So, How can i intercept a exception just when it occurred? ignore propagation of it.
Any reply is greatly appreciated.

Resources