From e938c5669d4f922e0b4df001bad14749b69101ac Mon Sep 17 00:00:00 2001 From: Gustavo Martin Morcuende Date: Sat, 10 Dec 2016 04:22:35 +0100 Subject: [PATCH] DeferredResult, exceptions handler --- .../business/service/AwesomeBusinessLogic.java | 6 +++- .../service/CompletableFutureBusinessLogic.java | 2 ++ .../rest/business/service/RxJavaBusinessLogic.java | 4 ++- .../service/impl/AwesomeBusinessLogicImpl.java | 15 +++++++--- .../impl/CompletablefutureBusinessLogicImpl.java | 27 ++++++++++++----- .../service/impl/RxJavaBusinessLogicImpl.java | 34 +++++++++++++++++----- .../rest/controller/DeferrableCarController.java | 2 +- .../rest/controller/RxJavaCarController.java | 4 +-- .../adapters/CompletableFutureAdapter.java | 24 +++++++-------- .../rest/controller/adapters/RxJavaAdapter.java | 24 ++++++++------- .../handler/ControllerExceptionHandler.java | 23 +++++++++++++++ .../spring-configuration/mvc/rest/rest-config.xml | 3 +- 12 files changed, 119 insertions(+), 49 deletions(-) create mode 100644 SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/handler/ControllerExceptionHandler.java diff --git a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/AwesomeBusinessLogic.java b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/AwesomeBusinessLogic.java index 261d4d0..1ba2b29 100644 --- a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/AwesomeBusinessLogic.java +++ b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/AwesomeBusinessLogic.java @@ -1,5 +1,7 @@ package de.spring.webservices.rest.business.service; +import java.io.IOException; + import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -12,6 +14,8 @@ public interface AwesomeBusinessLogic { public Car findById(long id); - public Car create(Car resource); + public Car create(Car car); + + public Car createThrowable(Car car) throws IOException; } diff --git a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/CompletableFutureBusinessLogic.java b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/CompletableFutureBusinessLogic.java index dec0011..ac7e01c 100644 --- a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/CompletableFutureBusinessLogic.java +++ b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/CompletableFutureBusinessLogic.java @@ -16,4 +16,6 @@ public interface CompletableFutureBusinessLogic { public CompletableFuture create(Car resource); + public CompletableFuture createThrowable(Car car); + } diff --git a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/RxJavaBusinessLogic.java b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/RxJavaBusinessLogic.java index 7d49402..01a6189 100644 --- a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/RxJavaBusinessLogic.java +++ b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/RxJavaBusinessLogic.java @@ -13,6 +13,8 @@ public interface RxJavaBusinessLogic { public Observable findById(long id); - public Observable create(Car resource); + public Observable create(Car car); + + public Observable createThrowable(Car car); } diff --git a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/impl/AwesomeBusinessLogicImpl.java b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/impl/AwesomeBusinessLogicImpl.java index 248caf5..f41af5d 100644 --- a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/impl/AwesomeBusinessLogicImpl.java +++ b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/impl/AwesomeBusinessLogicImpl.java @@ -1,5 +1,6 @@ package de.spring.webservices.rest.business.service.impl; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; @@ -26,7 +27,7 @@ public class AwesomeBusinessLogicImpl implements AwesomeBusinessLogic { cars.add(new Car(counter.incrementAndGet(), String.format(TEMPLATE, 3))); try { - Thread.sleep(300000); + Thread.sleep(10000); } catch(InterruptedException ex) { Thread.currentThread().interrupt(); } @@ -38,7 +39,7 @@ public class AwesomeBusinessLogicImpl implements AwesomeBusinessLogic { public Car findById(long id) { try { - Thread.sleep(300000); + Thread.sleep(10000); } catch(InterruptedException ex) { Thread.currentThread().interrupt(); } @@ -48,15 +49,21 @@ public class AwesomeBusinessLogicImpl implements AwesomeBusinessLogic { } @Override - public Car create(Car resource) { + public Car create(Car car) { long count = counter.incrementAndGet(); try { - Thread.sleep(300000); + Thread.sleep(10000); } catch(InterruptedException ex) { Thread.currentThread().interrupt(); } return new Car(count, String.format(TEMPLATE, count)); } + + @Override + public Car createThrowable(Car car) throws IOException { + + throw new IOException("createThrowable FATAL ERROR"); + } } diff --git a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/impl/CompletablefutureBusinessLogicImpl.java b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/impl/CompletablefutureBusinessLogicImpl.java index 001d3a7..e9c248f 100644 --- a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/impl/CompletablefutureBusinessLogicImpl.java +++ b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/impl/CompletablefutureBusinessLogicImpl.java @@ -1,9 +1,12 @@ package de.spring.webservices.rest.business.service.impl; +import java.io.IOException; import java.util.concurrent.CompletableFuture; import javax.inject.Inject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -12,17 +15,11 @@ import de.spring.webservices.domain.Car; import de.spring.webservices.rest.business.service.AwesomeBusinessLogic; import de.spring.webservices.rest.business.service.CompletableFutureBusinessLogic; -/** - * - * - * TODO: WHAT ABOUT EXCEPTIONS FROM awesomeBusinessLogic? RuntimeExceptions for example - * I guess they will be caught by my adapter in controller layer but I must try it. - * - */ - @Service("completableFutureBusinessLogic") public class CompletablefutureBusinessLogicImpl implements CompletableFutureBusinessLogic { + private static final Logger LOGGER = LoggerFactory.getLogger(CompletablefutureBusinessLogicImpl.class); + private final AwesomeBusinessLogic awesomeBusinessLogic; @Inject @@ -44,4 +41,18 @@ public class CompletablefutureBusinessLogicImpl implements CompletableFutureBusi public CompletableFuture create(Car car) { return CompletableFuture.supplyAsync(() -> awesomeBusinessLogic.create(car)); } + + @Override + public CompletableFuture createThrowable(Car car) { + return CompletableFuture.supplyAsync(() -> { + try { + return awesomeBusinessLogic.createThrowable(car); + } catch (IOException ex) { + + LOGGER.error("createThrowable error: ", ex); + + throw new RuntimeException("Nested exception", ex); + } + }); + } } diff --git a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/impl/RxJavaBusinessLogicImpl.java b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/impl/RxJavaBusinessLogicImpl.java index 5eec938..db032a0 100644 --- a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/impl/RxJavaBusinessLogicImpl.java +++ b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/impl/RxJavaBusinessLogicImpl.java @@ -1,7 +1,11 @@ package de.spring.webservices.rest.business.service.impl; +import java.io.IOException; + import javax.inject.Inject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -10,18 +14,13 @@ import de.spring.webservices.domain.Car; import de.spring.webservices.rest.business.service.AwesomeBusinessLogic; import de.spring.webservices.rest.business.service.RxJavaBusinessLogic; import rx.Observable; - -/** - * - * - * TODO: WHAT ABOUT EXCEPTIONS FROM awesomeBusinessLogic? RuntimeExceptions for example - * I guess they will be caught by my adapter in controller layer but I must try it. - * - */ +import rx.exceptions.Exceptions; @Service("rxJavaBusinessLogic") public class RxJavaBusinessLogicImpl implements RxJavaBusinessLogic { + private static final Logger LOGGER = LoggerFactory.getLogger(RxJavaBusinessLogicImpl.class); + private final AwesomeBusinessLogic awesomeBusinessLogic; @Inject @@ -44,4 +43,23 @@ public class RxJavaBusinessLogicImpl implements RxJavaBusinessLogic { public Observable create(Car car) { return Observable.create(observer -> observer.onNext(awesomeBusinessLogic.create(car))); } + + @Override + public Observable createThrowable(Car car) { + return Observable.create(observer -> { + + try { + observer.onNext(awesomeBusinessLogic.createThrowable(car)); + } catch (IOException ex) { + // I could use this implementation. Instead, I will wrap my exception because + // that is what you would be doing if you were using any other method from RxJava (like map() for example) + // observer.onError(ex); + + LOGGER.error("createThrowable error: ", ex); + + Exceptions.propagate(ex); + } + + }); + } } diff --git a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/DeferrableCarController.java b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/DeferrableCarController.java index 4ecc0c1..caf35b4 100644 --- a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/DeferrableCarController.java +++ b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/DeferrableCarController.java @@ -90,7 +90,7 @@ public class DeferrableCarController { private CompletableFuture> createAsync(Car car) { return completableFutureBusinessLogic - .create(car) + .createThrowable(car) .thenComposeAsync(newCar -> CompletableFuture.supplyAsync(() -> createResponseCar(newCar)) diff --git a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/RxJavaCarController.java b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/RxJavaCarController.java index 1426d0d..3aa72d2 100644 --- a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/RxJavaCarController.java +++ b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/RxJavaCarController.java @@ -90,11 +90,11 @@ public class RxJavaCarController { private Observable> createAsync(Car car) { return rxJavaBusinessLogic - .create(car) + .createThrowable(car) .map(this::createResponseCar); } - private ResponseEntity createResponseCar(Car car) { + private ResponseEntity createResponseCar(Car car) { HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.LOCATION, "/api/cars/" + car.getId()); return new ResponseEntity<>(car, headers, HttpStatus.CREATED); diff --git a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/adapters/CompletableFutureAdapter.java b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/adapters/CompletableFutureAdapter.java index f2ddd5c..a9b4337 100644 --- a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/adapters/CompletableFutureAdapter.java +++ b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/adapters/CompletableFutureAdapter.java @@ -9,13 +9,6 @@ import org.springframework.web.context.request.async.DeferredResult; public class CompletableFutureAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(CompletableFutureAdapter.class); - /** - * - * WHEN EXCEPTION IN setErrorResult, Spring WILL TRIGGER THE Spring Exception Handler AS YOU KNOW IT (I HOPE) - * SO, YOU COULD HOOK UP THE HANDLER AND RETURN YOUR CUSTOM MESSAGESS (as usual) - * - */ - // With no value, we depend on the Tomcat/Jboss/Jetty/etc timeout value for asynchronous requests. // Spring will answer after 60 secs with an empty response (by default) and HTTP 503 status (by default) when timeout. private static final long ASYNC_TIMEOUT = 60000; /* milliseconds */ @@ -28,21 +21,26 @@ public class CompletableFutureAdapter { } public static final DeferredResult deferredAdapter(CompletableFuture completableFuture) { - + DeferredResult deferredResult = new DeferredResult<>(ASYNC_TIMEOUT); completableFuture .thenAcceptAsync(deferredResult::setResult) .exceptionally(exception -> { - - LOGGER.error("error: ", exception); - + Throwable realException = launderException(exception); + + LOGGER.error("error: ", realException); + deferredResult.setErrorResult(exception); - return null; }); - + return deferredResult; } + private static final Throwable launderException(Throwable exception) { + return exception.getCause() != null + ? exception.getCause() + : exception; + } } diff --git a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/adapters/RxJavaAdapter.java b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/adapters/RxJavaAdapter.java index ca52b11..8d7446f 100644 --- a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/adapters/RxJavaAdapter.java +++ b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/adapters/RxJavaAdapter.java @@ -9,13 +9,6 @@ import rx.schedulers.Schedulers; public class RxJavaAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(RxJavaAdapter.class); - - /** - * - * WHEN EXCEPTION IN setErrorResult, Spring WILL TRIGGER THE Spring Exception Handler AS YOU KNOW IT (I HOPE) - * SO, YOU COULD HOOK UP THE HANDLER AND RETURN YOUR CUSTOM MESSAGESS (as usual) - * - */ // With no value, we depend on the Tomcat/Jboss/Jetty/etc timeout value for asynchronous requests. // Spring will answer after 60 secs with an empty response (by default) and HTTP 503 status (by default) when timeout. @@ -29,14 +22,25 @@ public class RxJavaAdapter { } public static final DeferredResult deferredAdapter(Observable observable) { - + DeferredResult deferredResult = new DeferredResult<>(ASYNC_TIMEOUT); observable .subscribeOn(Schedulers.io()) - .subscribe(deferredResult::setResult, exception -> LOGGER.error("error: ", exception)); - + .subscribe(deferredResult::setResult, exception -> { + Throwable realException = launderException(exception); + + LOGGER.error("error: ", realException); + + deferredResult.setErrorResult(realException); + }); + return deferredResult; } + private static final Throwable launderException(Throwable exception) { + return exception.getCause() != null + ? exception.getCause() + : exception; + } } diff --git a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/handler/ControllerExceptionHandler.java b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/handler/ControllerExceptionHandler.java new file mode 100644 index 0000000..7a995d4 --- /dev/null +++ b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/handler/ControllerExceptionHandler.java @@ -0,0 +1,23 @@ +package de.spring.webservices.rest.controller.handler; + +import java.io.IOException; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +// using XML configuration @EnableWebMvc +@ControllerAdvice +public class ControllerExceptionHandler extends ResponseEntityExceptionHandler { + + // 500 + @ExceptionHandler({ IOException.class }) + public ResponseEntity handleBadRequest(final Exception ex, final WebRequest request) { + return handleExceptionInternal(ex, null, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request); + } + +} diff --git a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/resources/spring-configuration/mvc/rest/rest-config.xml b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/resources/spring-configuration/mvc/rest/rest-config.xml index 54b8540..ce5b5a7 100644 --- a/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/resources/spring-configuration/mvc/rest/rest-config.xml +++ b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/resources/spring-configuration/mvc/rest/rest-config.xml @@ -16,7 +16,8 @@ See: org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser --> - + + -- 2.1.4