I'm working on a microservice app, in service layout I want to invoke with CompletableFuture.runAsync(). The problem is when I want to throw exception, I have my own Handler Exception, but I can't capture error when it is produced in my catch block inside CompletedFuture shown below:
Controller:
#PostMapping(path="/offers/offer")
public CompletableFuture<Oferta> infoPropiedad(#Valid #RequestBody OfertaRequest inDTO) throws
WebServiceBadResponseException, SOAPException, IOException, InterruptedException, ExecutionException {
System.out.println("THREAD: "+Thread.currentThread().getName());
CompletableFuture<Oferta> outTO = new CompletableFuture<Oferta>();
return CompletableFuture.supplyAsync(()->{
try {
return ofertasService.ofertasService(inDTO);
} catch (Exception e) {
System.out.println("Error inesperado en la capa del controlador");
}
return null;
});
}
Service:
CompletableFuture<OfertaCrm> completableFutureCRM =
CompletableFuture.supplyAsync(()-> {
try {
return clientOferta.llamadaWebServiceOfertas(inDTOCrm);
} catch (Exception e1) {
//throw Exception and capture it with my handler class
}
});
ClientWs:
public OfertaCrm llamadaWebServiceOfertas(OfertaRequestCRM inDtoCrm)
throws SOAPException, IOException {
CompletableFuture<OfertaCrm> completableFuture = new CompletableFuture<OfertaCrm>();
logger.info("Iniciamos la llamada al WS");
//Error produces here and I want to controle it and capture with my handler class
Error handler:
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler({
WebServiceBadResponseException.class,
SOAPException.class,
IOException.class
})
#ResponseBody
public ErrorMessage internalError(Exception exception) {
return new ErrorMessage(exception,exception.getMessage());
}
I could not be applying the correct form. Any idea how to throw the exception inside the supplyAsync block?
CompletableFuture will wrap the exception thrown within the execution inside a CompletionException. You can handle it by intercepting the root cause exception directly. Below is a simplified example.
Controller:
#RestController
public class SimpleController {
#Autowired
SimpleService simpleService;
#GetMapping("/testing")
public CompletableFuture<Integer> testing(){
return simpleService.doStuff();
}
}
Service:
#Service
public class SimpleService {
public CompletableFuture<Integer> doStuff(){
// 1 / 0 will throw ArithmeticException
return CompletableFuture.supplyAsync(() -> 1 / 0);
}
}
Controller Advice:
#RestControllerAdvice
public class SimpleControllerAdvice {
#ExceptionHandler(ArithmeticException.class)
public String handleCompletionException(ArithmeticException ex){
return "hello world";
}
}
GET /testing
hello world
Related
The transactional roll back is not working when the exception is caught on the catch block, and another method is called for throw the exception. The pseudo code for the above is:
#Transactional(rollBackFor = Exception.class)
public void method1() {
// Calling another method
method2();
}
private void method2() {
try {
dbOperation1();
} catch (Exception e) {
handleFault()
}
}
handleFault() {
// Calling another method and throwing an exception
throwException()
}
throwException() {
//....
throw new Exception();
}
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());
}
}
This is the class to save:
#Service
public class DataService {
#Transactional(readOnly = true)
public String fetchData() { //no exception signature
try {
//some operations
checkData();
}
catch(Exception e) {
throw new CanerRuntimeException("an error occurred in fetchdata: " + e.getMessage(), e);//it cant come here with exception from child
}
}
private void checkData() throws SystemException { //intellj made me put that exception
try {
//some operations
if (!isCanerNotMade) {
String errorMessage = "It is not caner made by";
throw new CanerBusinessException(errorMessage);
}
}
} catch(CanerBusinessException e) {
logger.error("CheckForFksLimitations CanerBusinessExceptionerror {}", e.getMessage());
throw e;
}
} catch(Exception e) {
logger.error("CheckForFksLimitations Exception error {}", e.getMessage());
throw e;
} finally {
if (fksLog != null) {
saveLog(fksLog);
}
logger.info("CheckForFksLimitations ended for identityNumber: {}", identityNumber);//3
}
}
#Transactional
private void saveLog(FksLog fksLog) {
try {
logger.info("CheckForFksLimitations saving fksControlLog: {}", mobilityUtil.getObjectAsJson(fksControlLog));//1
FksControlLog savedfksControlLog = fksControlLogRepository.saveAndFlush(fksControlLog);
logger.info("CheckForFksLimitations saved fksControlLog: {}", mobilityUtil.getObjectAsJson(savedfksControlLog));//2
} catch(CanerBusinessException e) {
logger.info("CheckForFksLimitations error: {}", e.getMessage(), e);
}
}
and that exceptions are:
public class CanerBusinessException extends RuntimeException {}
public class CanerRuntimeException extends RuntimeException {}
I send data for both cases. One for not to throw exception and it can save without any rollback. I made saveAndFlush because it cant save inside a readonly=False parent method. That is how it can save as child.
But when i send the case to throw exception, it throws exception. It goes to finally block then save method. But after that, it rolls back
I see those logs:
CheckForFksLimitations saving fksControlLog: {"id":null,
CheckForFksLimitations saved fksControlLog: {"id":91,
CheckForFksLimitations ended for identityNumber: ARJUNA016129: Could not end XA resource com.ibm.db2.jcc.t4.a4#2a5410b8 com.ibm.db2.jcc.am.XaException: [jcc][t4][10401][12066][4.24.92] Xa exception: XA_RBROLLBACK ERRORCODE=-4228, SQLSTATE=null
It is oracle db.
I did not put any rollback class for exception. It is because of this?
I also put exception to parent signatures but did not work. This service called by a controller.
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);
}
I have some Ten API which talks to redis for storing the data.
Currently it is throwing nested exception, so I have done like below to handle the nested exception.
#Override
public boolean delMyStatus(String key) {
try{
return redisTemplate.delete(key);
}
catch (Exception e){
if(e.getCause() != null && e.getCause().getCause() instanceof RedisException) {
RedisException ex = (RedisException)e.getCause().getCause();
log.error("RedisException " + ex.getMessage());
/* Do some action*/
} else {
throw new IllegalStateException("...");
}
}
return false;
}
But I dont want to do this for all the APIS of redis dao, Is there any better way to handle exception.
You can use #RestControllerAdvice. Make a custom exception class CustomRedisException throw CustomRedisException Exception from every controller and handle this in separate class annotated with #RestControllerAdvice.
#Override
public boolean delMyStatus(String key) {
try{
return redisTemplate.delete(key);
}
catch (Exception e){
if(e.getCause() != null && e.getCause().getCause() instanceof RedisException) { RedisException ex = (RedisException)e.getCause().getCause();
throw new CustomRedisException(ex);
} else {
throw new IllegalStateException("...");
}
}
return false;
}
Make GlobalExceptionHandler like below.
#RestControllerAdvice(basePackages = "your base package here", basePackageClasses = RepositoryRestExceptionHandler.class)
public class GlobalRestExceptionHandler {
#ExceptionHandler
public ResponseEntity<ErrorResponse> handleCustomException(final CustomRedisExceptionex) {
// code for exception handling here.
return new ResponseEntity<>(
new ErrorResponse(HttpStatus.PRECONDITION_FAILED.value(), ex.getMessage()),
HttpStatus.PRECONDITION_FAILED);
}
}
You can achieve it with aspects and #AfterThrowing annotation.
First make sure you allowed Spring to use aspects using #EnableAspectJAutoProxy annotation on any of your configuration classes.
Then define an #Aspect class with method annotated with #AfterThrowing like this:
#Aspect
public class GenericExceptionHandler {
// you can use more specific path here
#AfterThrowing ("execution(* *.*(..))", throwing = "ex")
public void handleException(Exception ex) throws Exception {
// handle the exception here
}
}