RxJava REST example
authorGustavo Martin Morcuende <gu.martinm@gmail.com>
Sat, 10 Dec 2016 01:37:10 +0000 (02:37 +0100)
committerGustavo Martin Morcuende <gu.martinm@gmail.com>
Sat, 10 Dec 2016 01:37:10 +0000 (02:37 +0100)
SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/business/service/RxJavaBusinessLogic.java [new file with mode: 0644]
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 [new file with mode: 0644]
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/RxJavaAdapter.java [new file with mode: 0644]
SpringJava/RxJava/web-services-spring-rxjava-server/src/main/webapp/WEB-INF/web.xml

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
new file mode 100644 (file)
index 0000000..7d49402
--- /dev/null
@@ -0,0 +1,18 @@
+package de.spring.webservices.rest.business.service;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+import de.spring.webservices.domain.Car;
+import rx.Observable;
+
+
+public interface RxJavaBusinessLogic {
+
+       public Observable<Page<Car>> findAll(Pageable pageRequest);
+       
+       public Observable<Car> findById(long id);
+       
+       public Observable<Car> create(Car resource);
+       
+}
index 0b5af2f..001d3a7 100644 (file)
@@ -12,6 +12,15 @@ 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 final AwesomeBusinessLogic awesomeBusinessLogic;
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
new file mode 100644 (file)
index 0000000..5eec938
--- /dev/null
@@ -0,0 +1,47 @@
+package de.spring.webservices.rest.business.service.impl;
+
+import javax.inject.Inject;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+
+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.
+ *
+ */
+
+
+@Service("rxJavaBusinessLogic")
+public class RxJavaBusinessLogicImpl implements RxJavaBusinessLogic {
+    private final AwesomeBusinessLogic awesomeBusinessLogic;
+    
+    @Inject
+       public RxJavaBusinessLogicImpl(AwesomeBusinessLogic awesomeBusinessLogic) {
+               this.awesomeBusinessLogic = awesomeBusinessLogic;
+       }
+
+       @Override
+       public Observable<Page<Car>> findAll(Pageable pageRequest) {
+       return Observable.create(observer -> observer.onNext( awesomeBusinessLogic.findAll(pageRequest)));
+
+       }
+
+       @Override
+       public Observable<Car> findById(long id) {
+       return Observable.create(observer -> observer.onNext( awesomeBusinessLogic.findById(id)));
+       }
+
+       @Override
+       public Observable<Car> create(Car car) {        
+               return Observable.create(observer -> observer.onNext(awesomeBusinessLogic.create(car)));
+       }
+}
index a6d3b08..1426d0d 100644 (file)
@@ -1,7 +1,8 @@
 package de.spring.webservices.rest.controller;
 
+import static de.spring.webservices.rest.controller.adapters.RxJavaAdapter.deferredAdapter;
+
 import java.util.Map;
-import java.util.concurrent.CompletableFuture;
 
 import javax.inject.Inject;
 
@@ -24,7 +25,8 @@ import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.context.request.async.DeferredResult;
 
 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;
 
 @RestController
 @RequestMapping("/api/rxjava/cars/")
@@ -32,45 +34,19 @@ public class RxJavaCarController {
        private static final Logger LOGGER = LoggerFactory.getLogger(RxJavaCarController.class);
        private static final int PAGE = 2;
        private static final int PAGE_SIZE = 10;
-
-       // 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 */
-    
-       /**
-        * 
-        * 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)
-        * 
-        */
-       
-       private final AwesomeBusinessLogic awesomeBusinessLogic;
+       
+       private final RxJavaBusinessLogic rxJavaBusinessLogic;
 
        @Inject
-    public RxJavaCarController(AwesomeBusinessLogic awesomeBusinessLogic) {
-               this.awesomeBusinessLogic = awesomeBusinessLogic;
+    public RxJavaCarController(RxJavaBusinessLogic completableFutureBusinessLogic) {
+               this.rxJavaBusinessLogic = completableFutureBusinessLogic;
        }
 
        @RequestMapping(produces = { MediaType.APPLICATION_JSON_UTF8_VALUE }, method = RequestMethod.GET)
     @ResponseStatus(HttpStatus.OK)
     public DeferredResult<Page<Car>> cars() {
-               
-       // THIS CODE (I GUESS) SHOULD BE LOCATED IN Service layer. Anyhow this is just an example.
-       DeferredResult<Page<Car>> deferredResult = new DeferredResult<>(ASYNC_TIMEOUT);
-       CompletableFuture
-               .supplyAsync(() -> awesomeBusinessLogic.findAll(new PageRequest(PAGE, PAGE_SIZE)))
-               .thenAcceptAsync(car -> deferredResult.setResult(car))
-               .exceptionally(exception -> {
-                       LOGGER.error("findAll error: ", exception);
-                       
-                       // DO NOT FORGET THE EXCEPTIONS.
-                       // It will trigger the Spring Exception Handler as you know it :)
-                       deferredResult.setErrorResult(exception);
-                       
-                       return null;
-               });
-       
-        return deferredResult;
+                                       
+               return deferredAdapter(rxJavaBusinessLogic.findAll(new PageRequest(PAGE, PAGE_SIZE)));
     }
 
     @RequestMapping(value = "{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.GET)
@@ -97,24 +73,9 @@ public class RxJavaCarController {
                        LOGGER.info(wheel);
                }
        }
-       
-       // THIS CODE (I GUESS) SHOULD BE LOCATED IN Service layer. Anyhow this is just an example.
-       DeferredResult<Car> deferredResult = new DeferredResult<>(ASYNC_TIMEOUT);
-       CompletableFuture
-               .supplyAsync(() -> awesomeBusinessLogic.findById(id))
-               .thenAcceptAsync(car -> deferredResult.setResult(car))
-               .exceptionally(exception -> {
-                       
-                       LOGGER.error("findById error: ", exception);
-                       
-                       // DO NOT FORGET THE EXCEPTIONS.
-                       // It will trigger the Spring Exception Handler as you know it :)
-                       deferredResult.setErrorResult(exception);
-                       
-                       return null;
-               });
-       
-        return deferredResult;
+               
+               return deferredAdapter(rxJavaBusinessLogic.findById(id));
+
     }
     
     @RequestMapping(consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
@@ -122,29 +83,20 @@ public class RxJavaCarController {
        @ResponseStatus(HttpStatus.CREATED)
     public DeferredResult<ResponseEntity<Car>> create(@RequestBody Car car) {
        
-       // THIS CODE (I GUESS) SHOULD BE LOCATED IN Service layer. Anyhow this is just an example.
-       DeferredResult<ResponseEntity<Car>> deferredResult = new DeferredResult<>(ASYNC_TIMEOUT);
-       CompletableFuture
-               .supplyAsync(() -> {
-                       Car createdCar = awesomeBusinessLogic.create(car);
-                       
-                       HttpHeaders headers = new HttpHeaders();
-                   headers.add(HttpHeaders.LOCATION, "/api/cars/" + createdCar.getId());
-                   return new ResponseEntity<>(createdCar, headers, HttpStatus.CREATED);
-               })
-               .thenAcceptAsync(response -> deferredResult.setResult(response))
-               .exceptionally(exception -> {
-                       
-                       LOGGER.error("create error: ", exception);
-                       
-                       // DO NOT FORGET THE EXCEPTIONS.
-                       // It will trigger the Spring Exception Handler as you know it :)
-                       deferredResult.setErrorResult(exception);
-                       
-                       return null;
-               });
-       
-               return deferredResult;
+       return deferredAdapter(createAsync(car));
     }
 
+    
+    private Observable<ResponseEntity<Car>> createAsync(Car car) {
+       
+       return rxJavaBusinessLogic
+                       .create(car)
+                       .map(this::createResponseCar);          
+    }
+    
+    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);
+    }
 }
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
new file mode 100644 (file)
index 0000000..ca52b11
--- /dev/null
@@ -0,0 +1,42 @@
+package de.spring.webservices.rest.controller.adapters;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.request.async.DeferredResult;
+
+import rx.Observable;
+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.
+       private static final long ASYNC_TIMEOUT = 60000;  /* milliseconds */
+
+       
+       @FunctionalInterface
+       public interface DeferredCall<T> {
+               
+               public T doCall();
+       }
+       
+       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));
+       
+        return deferredResult; 
+       }
+       
+}
index 6c9c1d0..a1eda07 100644 (file)
@@ -3,7 +3,7 @@
          xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
          version="2.4">
 
-    <display-name>Spring REST Services: example</display-name>
+    <display-name>Spring Reactive REST Services: example</display-name>
 
     <listener>
         <listener-class>