#ExceptionHandler with VndErrors does not handle errors - spring-boot

The problem: in the following example the handlers (if commented out) with void return type work without problems. I get HttpStatus 422.
If I switch to use the version with VndErrors the handlers do not work anymore and I get HttpStatus 500, and I can't find the reason for this behavior.
#ControllerAdvice
#RequestMapping(produces = "application/vnd.error")
#ResponseBody
public class CurrencyCalculatorControllerAdvice {
// this works
#ExceptionHandler
#ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
private void handleCurrencyDetailsNotFoundException(CurrencyDetailsNotFoundException e) {}
#ExceptionHandler
#ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
private void handleCurrencyRateNotFoundException(CurrencyRateNotFoundException e) {}
// this does not work
#ExceptionHandler
#ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
VndErrors handleCurrencyDetailsNotFoundException(CurrencyDetailsNotFoundException e) {
return new VndErrors(e.getLogRef().assemble(), e.getMessage());
}
#ExceptionHandler
#ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
VndErrors handleCurrencyDetailsNotFoundException(CurrencyRateNotFoundException e) {
return new VndErrors(e.getLogRef().assemble(), e.getMessage());
}
}

Related

ControllerAdvice can not handle CompletionException

While experimenting with asynchronous processing, I discovered an unusual phenomenon.
#Slf4j
#ControllerAdvice
public class ErrorAdvice {
#ExceptionHandler(Exception.class)
public void ex(Exception e) {
log.info("error e = ", e);
}
#ExceptionHandler(CompletionException.class)
public void ex2(CompletionException e) {
log.info("error e = ", e);
}
}
#Service
#RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
#Transactional
public void save(String name) {
memberRepository.save(new Member(name));
throw new IllegalStateException();
}
}
public class Client {
#Transactional
public void save(String name) {
CompletableFuture.runAsync(() -> memberService.save(name));
}
}
Client starts a transaction and CompletableFuture starts another thread, thus starting a newly bound transaction scope.
But the problem is that I can't catch the error in ControllerAdvice. Now I think it's very risky in real application operation. What is the cause and how to fix it?
In my opinion, ControllerAdvice wraps the controller as a proxy, so even if Memberservice does asynchronous processing, it doesn't make sense that the exception is not handled because it is inside the proxy. It doesn't even leave any logs. Why is the exception being ignored??
why are you using CompletableFuture.runAsync(() -> memberService.save(name));?
just try memberService.save(name));

ControllerAdvice is not triggered in my springboot application?

Actually I am working in a Kafka streams application using Spring Boot.
So here I am trying to handle exceptions globally using #ControllerAdvice but it is not working for me.
Is it possible to use #ControllerAdvice in my application.
Is this controller advice is only works when the error is coming from controller.
Note: I am not having any controller / rest controller endpoints in my application.
Can we achieve the same in some other ways?
Please share your valuable thoughts!
Main Stream:
#Autowired
private XyzTransformer xyztransformer;
#Bean
public KStream<String, ABC> processMember(#Qualifier("defaultKafkaStreamsBuilder") StreamsBuilder streamsBuilder) {
try {
LOGGER.info("streaming started...");
KStream<String, Xyz> xyStream = streamsBuilder.stream(appProperty.kafkaStreamsTopicInput)
.transformValues(() -> xyztransformer)
xyStream.to(appProperty.kafkaStreamsTopicOutput);
return memberStream;
} catch (Exception ex) {
LOGGER.error("Exception occurred in Streams " + Arrays.toString(ex.getStackTrace()));
throw ex;
}
}
TransformerClass:
#Component
public class XyzTransformer implements ValueTransformer<Xyz, Abc> {
#Override
public void close() {
}
#Override
public void init(ProcessorContext processorContext) {
}
#SneakyThrows
#Override
public Abc transform(Xyz data) {
String[] dataSourceTables = new String[]{"abc"};
try {
return Abc.builder()
.name(data.getName())
.build();
} catch (Exception ex) {
System.out.println("catched and throwing");
throw new CustomTesException("test 1");
}
}
}
ControllerAdvice:
#ControllerAdvice
public class Advice extends ResponseEntityExceptionHandler {
#ExceptionHandler(NullPointerException.class)
public final void handleAllExceptions(NullPointerException ex) {
System.out.println("it is in the handler");
System.out.println(ex.getMessage());
}
#ExceptionHandler(Exception.class)
public final void handleAllException(Exception ex) {
System.out.println("it is in the exception handler");
System.out.println(ex.getMessage());
}
#ExceptionHandler(CustomTesException.class)
public final void handleAllExceptio(CustomTesException ex) {
System.out.println("it is in the exception handler");
System.out.println(ex.getMessage());
}
}

Spring Boot ControllerAdvice TimeoutException Not Caught

I have multiple ExceptionHandler in controllerAdvice. one for TimeoutException and other for Exception classes.
When i throw new TimeoutException, it gets caught in Exception Handler not in TimeoutException Handler
below is the code:
#ControllerAdvice
public class CoreControllerAdvice {
#ExceptionHandler(value = { Exception.class })
#ResponseBody
public ResponseEntity<ErrorResponse> handleException(Exception ex) {
log.info("handleException", ex);
}
#ExceptionHandler(value = { TimeoutException.class })
#ResponseBody
public ResponseEntity<ErrorResponse> handleTimeoutException(Exception ex) {
log.info("handleTimeoutException", ex);
}
}
I throw Exception as
throw new TimeoutException("test");
Can some one help, why it is not caught by TimeoutException Handler
Thanks,
The parameter in the method handleTimeoutException seems incorrect to me, instead of Exception it should be TimeoutException.
#ExceptionHandler(value = { TimeoutException.class })
#ResponseBody
public ResponseEntity<ErrorResponse> handleTimeoutException(TimeoutException ex) {
log.info("handleTimeoutException", ex);
}

Spring batch update db status after rollback due to exception

In Spring batch Writer I'm updating the db row status from 0 to 1. If any exception occurs update to 2.
However due to #transaction rollback I'm unable to update the status to 2.
(I'm throwing exception to trigger the rollback)
#Override
#Transactional
public void write(List<? extends TestEntity> enityList) throws Exception {
for(TestEntity testEntity : enityList) {
try {
testEntity.setStatus(2);
testRepository.save(testEntity);
testRepository.flush();
testMethod(testEntity); (which throws exception)
}catch (Exception exception) {
testEntity.setStatus(2);
testRepository.save(testEntity);
}
}
}
#Transactional
public void testMethod(TestEntity testEntity) throws Exception {
try{
//Some service call
//...
} catch(Exception e) {
log.error("error", e);
throw new Exception("exp");
}
}
Methods that have the #Transactional will rollback the transaction when they throw an exception. So if an exception is an expected and okay-ish flow of your code, you shouldn't throw an exception and return some kind of result object or status code instead.
#Transactional
public void testMethodThatIsAllowedToFail(TestEntity testEntity) {
try{
//Some service call
} catch(Exception e) {
return Status.FAILURE; // enum you have to create
}
return Status.SUCCESS;
}
// spring batch writer
public void write(List<? extends TestEntity> enityList) throws Exception {
[...]
Status result = testMethod(testEntity); (which throws exception);
if (result != Status.SUCCESS) {
// do something with it
}
[...]
}
you could also play around with #Transactional(propagation = Propagation.REQUIRES_NEW) but you would have to think hard whether having an extra transaction is desireable.

Is decorated function returned by Retry threadsafe?

I have a class that sends a message to a remote service as shown below.
I'm using resilience4j-retry to retry the network call. As the retry instance is thread safe according to the documentation, I'm creating it in the class level and reusing it.
public class RemoteMessageService {
Retry retry = Retry.of("RemoteMessageService", RetryConfig.custom()
.maxAttempts(5)
.retryExceptions(ProcessingException.class)
.intervalFunction(IntervalFunction.ofExponentialBackoff())
.build());
public void postMessageWithRetry(final String message){
Function<Integer, Void> postMessageFunction = Retry.decorateFunction(retry, this::postMessage);
try {
postMessageFunction.apply(message)
} catch (final ProcessingException e) {
LOG.warn("Got processing exception: {}", e.getMessage());
} catch (final Exception e) {
LOG.error("Got unknown exception: {}", e.getMessage());
}
}
private Void postMessage(final String message){
// Do a network call to send the message to a rest service
// throw ProcessingException in case of timeout
return null;
}
}
My question is if the decorated function returned by Retry.decorateFunction(retry, this::postMessage); is also thread safe?
In that case I could move this to class level instead of repeating it every time the postMessageWithRetry function is called.
After looking into the resilience4j-retry code, I found that the decorated function is in fact thread safe; as long as the function that we decorate in the first place is thread safe.
So I can rewrite the code as below since the postMessage function is thread safe, and therefor the decorated postMessageFunction function is also thread safe.
public class RemoteMessageService {
private final Retry retry = Retry.of("RemoteMessageService", RetryConfig.custom()
.maxAttempts(5)
.retryExceptions(ProcessingException.class)
.intervalFunction(IntervalFunction.ofExponentialBackoff())
.build());
private final Function<Integer, Void> postMessageFunction = Retry.decorateFunction(retry, this::postMessage);
public void postMessageWithRetry(final String message) {
try {
postMessageFunction.apply(message)
} catch (final ProcessingException e) {
LOG.warn("Got processing exception: {}", e.getMessage());
} catch (final Exception e) {
LOG.error("Got unknown exception: {}", e.getMessage());
}
}
private Void postMessage(final String message) {
// Do a network call to send the message to a rest service
// throw ProcessingException in case of timeout
return null;
}
}

Resources