From 221002b388c5e40317780f2f4256b25c248a7fe0 Mon Sep 17 00:00:00 2001 From: Gustavo Martin Morcuende Date: Sat, 10 Dec 2016 00:50:14 +0100 Subject: [PATCH] No repeat yourself :) Adapter for DeferredResult <-> CompletableFuture --- .../rest/controller/DeferrableCarController.java | 88 +++++----------------- .../adapters/CompletableFutureAdapter.java | 49 ++++++++++++ 2 files changed, 66 insertions(+), 71 deletions(-) create mode 100644 SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/adapters/CompletableFutureAdapter.java 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 6788427..ad92446 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 @@ -25,6 +25,7 @@ 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.controller.adapters.CompletableFutureAdapter; @RestController @RequestMapping("/api/deferrable/cars/") @@ -32,18 +33,7 @@ public class DeferrableCarController { private static final Logger LOGGER = LoggerFactory.getLogger(DeferrableCarController.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; @Inject @@ -54,23 +44,9 @@ public class DeferrableCarController { @RequestMapping(produces = { MediaType.APPLICATION_JSON_UTF8_VALUE }, method = RequestMethod.GET) @ResponseStatus(HttpStatus.OK) public DeferredResult> cars() { - - // THIS CODE (I GUESS) SHOULD BE LOCATED IN Service layer. Anyhow this is just an example. - DeferredResult> 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 CompletableFutureAdapter.callAdapter(() -> + awesomeBusinessLogic.findAll(new PageRequest(PAGE, PAGE_SIZE))); } @RequestMapping(value = "{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.GET) @@ -97,54 +73,24 @@ public class DeferrableCarController { LOGGER.info(wheel); } } - - // THIS CODE (I GUESS) SHOULD BE LOCATED IN Service layer. Anyhow this is just an example. - DeferredResult 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 CompletableFutureAdapter.callAdapter(() -> awesomeBusinessLogic.findById(id)); } @RequestMapping(consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.POST) @ResponseStatus(HttpStatus.CREATED) public DeferredResult> create(@RequestBody Car car) { - - // THIS CODE (I GUESS) SHOULD BE LOCATED IN Service layer. Anyhow this is just an example. - DeferredResult> 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 CompletableFutureAdapter.callAdapter(() -> createResponseCar(car)); } + + private ResponseEntity createResponseCar(Car car) { + Car createdCar = awesomeBusinessLogic.create(car); + + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.LOCATION, "/api/cars/" + createdCar.getId()); + return new ResponseEntity<>(createdCar, 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 new file mode 100644 index 0000000..094d5f0 --- /dev/null +++ b/SpringJava/RxJava/web-services-spring-rxjava-server/src/main/java/de/spring/webservices/rest/controller/adapters/CompletableFutureAdapter.java @@ -0,0 +1,49 @@ +package de.spring.webservices.rest.controller.adapters; + +import java.util.concurrent.CompletableFuture; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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 */ + + + @FunctionalInterface + public interface DeferredCall { + + public T doCall(); + } + + + public static final DeferredResult callAdapter(DeferredCall deferredCall) { + + DeferredResult deferredResult = new DeferredResult<>(ASYNC_TIMEOUT); + CompletableFuture + .supplyAsync(deferredCall::doCall) + .thenAcceptAsync(deferredResult::setResult) + .exceptionally(exception -> { + + LOGGER.error("findById error: ", exception); + + deferredResult.setErrorResult(exception); + + return null; + }); + + return deferredResult; + } + +} -- 2.1.4