DeferredResult, exceptions handler
authorGustavo Martin Morcuende <gu.martinm@gmail.com>
Sat, 10 Dec 2016 03:22:35 +0000 (04:22 +0100)
committerGustavo Martin Morcuende <gu.martinm@gmail.com>
Sat, 10 Dec 2016 03:22:35 +0000 (04:22 +0100)
12 files changed:
SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/AwesomeBusinessLogic.java
SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/CompletableFutureBusinessLogic.java
SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/RxJavaBusinessLogic.java
SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/impl/AwesomeBusinessLogicImpl.java
SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/impl/CompletablefutureBusinessLogicImpl.java
SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/impl/RxJavaBusinessLogicImpl.java
SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/DeferrableCarController.java
SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/RxJavaCarController.java
SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/adapters/CompletableFutureAdapter.java
SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/adapters/RxJavaAdapter.java
SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/handler/ControllerExceptionHandler.java [new file with mode: 0644]
SpringJava/RxJava/web-services-spring-rxjava-server/src/main/resources/spring-configuration/mvc/rest/rest-config.xml

index 261d4d0..1ba2b29 100644 (file)
@@ -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;
        
 }
index 7d49402..01a6189 100644 (file)
@@ -13,6 +13,8 @@ public interface RxJavaBusinessLogic {
        
        public Observable<Car> findById(long id);
        
-       public Observable<Car> create(Car resource);
+       public Observable<Car> create(Car car);
+       
+       public Observable<Car> createThrowable(Car car);
        
 }
index 248caf5..f41af5d 100644 (file)
@@ -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");
+       }
 }
index 001d3a7..e9c248f 100644 (file)
@@ -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<Car> create(Car car) {
                return CompletableFuture.supplyAsync(() -> awesomeBusinessLogic.create(car));
        }
+
+       @Override
+       public CompletableFuture<Car> 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);
+                       }
+               });
+       }
 }
index 5eec938..db032a0 100644 (file)
@@ -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<Car> create(Car car) {        
                return Observable.create(observer -> observer.onNext(awesomeBusinessLogic.create(car)));
        }
+       
+       @Override
+       public Observable<Car> 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);
+                               }
+       
+               });
+       }
 }
index 4ecc0c1..caf35b4 100644 (file)
@@ -90,7 +90,7 @@ public class DeferrableCarController {
     private CompletableFuture<ResponseEntity<Car>> createAsync(Car car) {
        
        return completableFutureBusinessLogic
-                       .create(car)
+                       .createThrowable(car)
                        .thenComposeAsync(newCar -> 
                                CompletableFuture.supplyAsync(() -> createResponseCar(newCar))
                
index 1426d0d..3aa72d2 100644 (file)
@@ -90,11 +90,11 @@ public class RxJavaCarController {
     private Observable<ResponseEntity<Car>> createAsync(Car car) {
        
        return rxJavaBusinessLogic
-                       .create(car)
+                       .createThrowable(car)
                        .map(this::createResponseCar);          
     }
     
-    private ResponseEntity<Car> createResponseCar(Car car) {           
+    private ResponseEntity<Car> createResponseCar(Car car) {
                HttpHeaders headers = new HttpHeaders();
            headers.add(HttpHeaders.LOCATION, "/api/cars/" + car.getId());
            return new ResponseEntity<>(car, headers, HttpStatus.CREATED);
index f2ddd5c..a9b4337 100644 (file)
@@ -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 <T> DeferredResult<T> deferredAdapter(CompletableFuture<T> completableFuture) {
-               
+
        DeferredResult<T> 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;
+       }
 }
index ca52b11..8d7446f 100644 (file)
@@ -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 <T> DeferredResult<T> deferredAdapter(Observable<T> observable) {
-               
+
        DeferredResult<T> 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 (file)
index 0000000..7a995d4
--- /dev/null
@@ -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<Object> handleBadRequest(final Exception ex, final WebRequest request) {
+        return handleExceptionInternal(ex, null, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request);
+    }
+
+}
index 54b8540..ce5b5a7 100644 (file)
@@ -16,7 +16,8 @@
                See: org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser
                <mvc:annotation-driven/>
         -->
-        
+    <!-- Required for making work @ControllerAdvice -->
+    <mvc:annotation-driven/> 
    
        <context:annotation-config />