From eea3ae3084226f8a6e379eb5f00747704ea5489c Mon Sep 17 00:00:00 2001 From: Jordan Simpson Date: Fri, 12 Mar 2021 01:04:20 -0600 Subject: [PATCH] BAEL-4685 (#10418) * Add code examples for article BAEL-4685 * Add tests for the endpoints. * Rename RequestTimeoutTests.java to RequestTimeoutUnitTest.java --- spring-web-modules/spring-rest-http-2/pom.xml | 17 ++++++ .../RequestTimeoutRestController.java | 61 +++++++++++++++++++ .../configuration/WebClientConfiguration.java | 21 +++++++ .../baeldung/requesttimeout/domain/Book.java | 28 +++++++++ .../requesttimeout/domain/BookRepository.java | 14 +++++ .../src/main/resources/application.properties | 1 + .../RequestTimeoutUnitTest.java | 46 ++++++++++++++ .../src/test/resources/application.properties | 1 + 8 files changed, 189 insertions(+) create mode 100644 spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/RequestTimeoutRestController.java create mode 100644 spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/configuration/WebClientConfiguration.java create mode 100644 spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/domain/Book.java create mode 100644 spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/domain/BookRepository.java create mode 100644 spring-web-modules/spring-rest-http-2/src/main/resources/application.properties create mode 100644 spring-web-modules/spring-rest-http-2/src/test/com/baeldung/requesttimeout/RequestTimeoutUnitTest.java create mode 100644 spring-web-modules/spring-rest-http-2/src/test/resources/application.properties diff --git a/spring-web-modules/spring-rest-http-2/pom.xml b/spring-web-modules/spring-rest-http-2/pom.xml index 6aa8be365c..0ea3fd6840 100644 --- a/spring-web-modules/spring-rest-http-2/pom.xml +++ b/spring-web-modules/spring-rest-http-2/pom.xml @@ -19,6 +19,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-webflux + io.springfox springfox-swagger2 @@ -29,6 +33,19 @@ springfox-swagger-ui ${swagger2.version} + + com.h2database + h2 + + + org.springframework.boot + spring-boot-starter-data-jpa + + + io.github.resilience4j + resilience4j-timelimiter + 1.6.1 + diff --git a/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/RequestTimeoutRestController.java b/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/RequestTimeoutRestController.java new file mode 100644 index 0000000000..d425737bab --- /dev/null +++ b/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/RequestTimeoutRestController.java @@ -0,0 +1,61 @@ +package com.baeldung.requesttimeout; + +import com.baeldung.requesttimeout.domain.Book; +import com.baeldung.requesttimeout.domain.BookRepository; +import io.github.resilience4j.timelimiter.TimeLimiter; +import io.github.resilience4j.timelimiter.TimeLimiterConfig; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.reactive.function.client.WebClient; + +import java.time.Duration; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; + +@RestController +public class RequestTimeoutRestController { + + private final BookRepository bookRepository; + private final WebClient webClient; + + public RequestTimeoutRestController(BookRepository bookRepository, WebClient webClient) { + this.bookRepository = bookRepository; + this.webClient = webClient; + } + + @GetMapping("/author/transactional") + @Transactional(timeout = 1) + public String getWithTransactionTimeout(@RequestParam String title) { + return getAuthor(title); + } + + private final TimeLimiter ourTimeLimiter = TimeLimiter.of(TimeLimiterConfig.custom().timeoutDuration(Duration.ofMillis(500)).build()); + @GetMapping("/author/resilience4j") + public Callable getWithResilience4jTimeLimiter(@RequestParam String title) { + return TimeLimiter.decorateFutureSupplier(ourTimeLimiter, () -> CompletableFuture.supplyAsync(() -> getAuthor(title))); + } + + @GetMapping("/author/mvc-request-timeout") + public Callable getWithMvcRequestTimeout(@RequestParam String title) { + return () -> getAuthor(title); + } + + @GetMapping("/author/webclient") + public String getWithWebClient(@RequestParam String title) { + return webClient.get() + .uri(uriBuilder -> uriBuilder + .path("/author/transactional") + .queryParam("title", title) + .build()) + .retrieve() + .bodyToMono(String.class) + .block(); + } + + private String getAuthor(String title) { + bookRepository.wasteTime(); + return bookRepository.findById(title).map(Book::getAuthor).orElse("No book found for this title."); + } +} diff --git a/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/configuration/WebClientConfiguration.java b/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/configuration/WebClientConfiguration.java new file mode 100644 index 0000000000..52b3573411 --- /dev/null +++ b/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/configuration/WebClientConfiguration.java @@ -0,0 +1,21 @@ +package com.baeldung.requesttimeout.configuration; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.reactive.ReactorClientHttpConnector; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.netty.http.client.HttpClient; + +import java.time.Duration; + +@Configuration +public class WebClientConfiguration { + + @Bean + public WebClient webClient() { + return WebClient.builder() + .baseUrl("http://localhost:8080") + .clientConnector(new ReactorClientHttpConnector(HttpClient.create().responseTimeout(Duration.ofMillis(250)))) + .build(); + } +} diff --git a/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/domain/Book.java b/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/domain/Book.java new file mode 100644 index 0000000000..846bfb2cec --- /dev/null +++ b/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/domain/Book.java @@ -0,0 +1,28 @@ +package com.baeldung.requesttimeout.domain; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class Book { + + @Id + private String title; + private String author; + + public void setTitle(String title) { + this.title = title; + } + + public String getTitle() { + return title; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } +} diff --git a/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/domain/BookRepository.java b/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/domain/BookRepository.java new file mode 100644 index 0000000000..8ecab0f1d2 --- /dev/null +++ b/spring-web-modules/spring-rest-http-2/src/main/java/com/baeldung/requesttimeout/domain/BookRepository.java @@ -0,0 +1,14 @@ +package com.baeldung.requesttimeout.domain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface BookRepository extends JpaRepository { + + default int wasteTime() { + int i = Integer.MIN_VALUE; + while(i < Integer.MAX_VALUE) { + i++; + } + return i; + } +} diff --git a/spring-web-modules/spring-rest-http-2/src/main/resources/application.properties b/spring-web-modules/spring-rest-http-2/src/main/resources/application.properties new file mode 100644 index 0000000000..ff4af943ec --- /dev/null +++ b/spring-web-modules/spring-rest-http-2/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.mvc.async.request-timeout=750 \ No newline at end of file diff --git a/spring-web-modules/spring-rest-http-2/src/test/com/baeldung/requesttimeout/RequestTimeoutUnitTest.java b/spring-web-modules/spring-rest-http-2/src/test/com/baeldung/requesttimeout/RequestTimeoutUnitTest.java new file mode 100644 index 0000000000..da7d40d53c --- /dev/null +++ b/spring-web-modules/spring-rest-http-2/src/test/com/baeldung/requesttimeout/RequestTimeoutUnitTest.java @@ -0,0 +1,46 @@ +package com.baeldung.requesttimeout; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientRequestException; + +@SpringBootTest +@RunWith(SpringRunner.class) +public class RequestTimeoutTests { + + private static final WebClient WEB_CLIENT = WebClient.builder().baseUrl("http://localhost:8080").build(); + + @Test(expected = WebClientRequestException.class) + public void givenTransactionTimeout_whenTimeExpires_thenReceiveException() { + getAuthor("transactional"); + } + + @Test(expected = WebClientRequestException.class) + public void givenResilience4jTimeLimiter_whenTimeExpires_thenReceiveException() { + getAuthor("resilience4j"); + } + + @Test(expected = WebClientRequestException.class) + public void givenMvcRequestTimeout_whenTimeExpires_thenReceiveException() { + getAuthor("mvc-request-timeout"); + } + + @Test(expected = WebClientRequestException.class) + public void givenWebClientTimeout_whenTimeExpires_thenReceiveException() { + getAuthor("webclient"); + } + + private void getAuthor(String authorPath) { + WEB_CLIENT.get() + .uri(uriBuilder -> uriBuilder + .path("/author/" + authorPath) + .queryParam("title", "title") + .build()) + .retrieve() + .bodyToMono(String.class) + .block(); + } +} diff --git a/spring-web-modules/spring-rest-http-2/src/test/resources/application.properties b/spring-web-modules/spring-rest-http-2/src/test/resources/application.properties new file mode 100644 index 0000000000..ff4af943ec --- /dev/null +++ b/spring-web-modules/spring-rest-http-2/src/test/resources/application.properties @@ -0,0 +1 @@ +spring.mvc.async.request-timeout=750 \ No newline at end of file