[JAVA-27763] Upgrade spring-reactive-client module to Spring Boot 3 (#16187)

This commit is contained in:
panos-kakos 2024-04-11 09:24:07 +03:00 committed by GitHub
parent c4a14c6931
commit c53d0f44f4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 57 additions and 46 deletions

View File

@ -9,9 +9,10 @@
<description>spring boot sample project about new features</description> <description>spring boot sample project about new features</description>
<parent> <parent>
<groupId>com.baeldung.spring.reactive</groupId> <groupId>com.baeldung</groupId>
<artifactId>spring-reactive-modules</artifactId> <artifactId>parent-boot-3</artifactId>
<version>1.0.0-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-boot-3</relativePath>
</parent> </parent>
<dependencies> <dependencies>
@ -42,8 +43,13 @@
<version>${reactor-spring.version}</version> <version>${reactor-spring.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>javax.json.bind</groupId> <groupId>jakarta.json.bind</groupId>
<artifactId>javax.json.bind-api</artifactId> <artifactId>jakarta.json.bind-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>${jakarta.json-api.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.geronimo.specs</groupId> <groupId>org.apache.geronimo.specs</groupId>
@ -53,6 +59,7 @@
<dependency> <dependency>
<groupId>org.apache.johnzon</groupId> <groupId>org.apache.johnzon</groupId>
<artifactId>johnzon-jsonb</artifactId> <artifactId>johnzon-jsonb</artifactId>
<version>${johnzon-jsonb.version}</version>
</dependency> </dependency>
<!-- utils --> <!-- utils -->
<dependency> <dependency>
@ -92,7 +99,7 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.tomakehurst</groupId> <groupId>org.wiremock</groupId>
<artifactId>wiremock-standalone</artifactId> <artifactId>wiremock-standalone</artifactId>
<version>${wiremock-standalone.version}</version> <version>${wiremock-standalone.version}</version>
<scope>test</scope> <scope>test</scope>
@ -183,12 +190,14 @@
<properties> <properties>
<reactor-spring.version>1.0.1.RELEASE</reactor-spring.version> <reactor-spring.version>1.0.1.RELEASE</reactor-spring.version>
<geronimo-json_1.1_spec.version>1.0</geronimo-json_1.1_spec.version> <geronimo-json_1.1_spec.version>1.0</geronimo-json_1.1_spec.version>
<jetty-reactive-httpclient.version>1.1.6</jetty-reactive-httpclient.version> <jetty-reactive-httpclient.version>4.0.3</jetty-reactive-httpclient.version>
<okhttp.version>5.0.0-alpha.12</okhttp.version> <okhttp.version>5.0.0-alpha.12</okhttp.version>
<reactor-test.version>3.5.3</reactor-test.version> <reactor-test.version>3.5.3</reactor-test.version>
<wiremock-standalone.version>2.26.0</wiremock-standalone.version> <wiremock-standalone.version>3.4.2</wiremock-standalone.version>
<spring-cloud-starter-openfeign.version>3.1.4</spring-cloud-starter-openfeign.version> <spring-cloud-starter-openfeign.version>4.0.3</spring-cloud-starter-openfeign.version>
<kotlin-stdlib.version>2.0.0-Beta4</kotlin-stdlib.version> <kotlin-stdlib.version>2.0.0-Beta4</kotlin-stdlib.version>
<johnzon-jsonb.version>2.0.0</johnzon-jsonb.version>
<jakarta.json-api.version>2.1.3</jakarta.json-api.version>
</properties> </properties>
</project> </project>

View File

@ -2,7 +2,7 @@ package com.baeldung.reactive.controller;
import com.baeldung.reactive.service.ReactiveUploadService; import com.baeldung.reactive.service.ReactiveUploadService;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatusCode;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -18,13 +18,13 @@ public class UploadController {
@PostMapping(path = "/upload") @PostMapping(path = "/upload")
@ResponseBody @ResponseBody
public Mono<HttpStatus> uploadPdf(@RequestParam("file") final MultipartFile multipartFile) { public Mono<HttpStatusCode> uploadPdf(@RequestParam("file") final MultipartFile multipartFile) {
return uploadService.uploadPdf(multipartFile.getResource()); return uploadService.uploadPdf(multipartFile.getResource());
} }
@PostMapping(path = "/upload/multipart") @PostMapping(path = "/upload/multipart")
@ResponseBody @ResponseBody
public Mono<HttpStatus> uploadMultipart(@RequestParam("file") final MultipartFile multipartFile) { public Mono<HttpStatusCode> uploadMultipart(@RequestParam("file") final MultipartFile multipartFile) {
return uploadService.uploadMultipart(multipartFile); return uploadService.uploadMultipart(multipartFile);
} }
} }

View File

@ -1,4 +1,5 @@
package com.baeldung.reactive.service; package com.baeldung.reactive.service;
import com.baeldung.reactive.model.Employee; import com.baeldung.reactive.model.Employee;
import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;

View File

@ -4,6 +4,7 @@ package com.baeldung.reactive.service;
import com.baeldung.reactive.exception.ServiceException; import com.baeldung.reactive.exception.ServiceException;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.client.MultipartBodyBuilder; import org.springframework.http.client.MultipartBodyBuilder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -26,10 +27,10 @@ public class ReactiveUploadService {
} }
public Mono<HttpStatus> uploadPdf(final Resource resource) { public Mono<HttpStatusCode> uploadPdf(final Resource resource) {
final URI url = UriComponentsBuilder.fromHttpUrl(EXTERNAL_UPLOAD_URL).build().toUri(); final URI url = UriComponentsBuilder.fromHttpUrl(EXTERNAL_UPLOAD_URL).build().toUri();
Mono<HttpStatus> httpStatusMono = webClient.post() Mono<HttpStatusCode> httpStatusMono = webClient.post()
.uri(url) .uri(url)
.contentType(MediaType.APPLICATION_PDF) .contentType(MediaType.APPLICATION_PDF)
.body(BodyInserters.fromResource(resource)) .body(BodyInserters.fromResource(resource))
@ -44,13 +45,13 @@ public class ReactiveUploadService {
} }
public Mono<HttpStatus> uploadMultipart(final MultipartFile multipartFile) { public Mono<HttpStatusCode> uploadMultipart(final MultipartFile multipartFile) {
final URI url = UriComponentsBuilder.fromHttpUrl(EXTERNAL_UPLOAD_URL).build().toUri(); final URI url = UriComponentsBuilder.fromHttpUrl(EXTERNAL_UPLOAD_URL).build().toUri();
final MultipartBodyBuilder builder = new MultipartBodyBuilder(); final MultipartBodyBuilder builder = new MultipartBodyBuilder();
builder.part("file", multipartFile.getResource()); builder.part("file", multipartFile.getResource());
Mono<HttpStatus> httpStatusMono = webClient.post() Mono<HttpStatusCode> httpStatusMono = webClient.post()
.uri(url) .uri(url)
.contentType(MediaType.MULTIPART_FORM_DATA) .contentType(MediaType.MULTIPART_FORM_DATA)
.body(BodyInserters.fromMultipartData(builder.build())) .body(BodyInserters.fromMultipartData(builder.build()))

View File

@ -3,6 +3,7 @@ package com.baeldung.webclient.status;
import com.baeldung.webclient.status.exception.CustomBadRequestException; import com.baeldung.webclient.status.exception.CustomBadRequestException;
import com.baeldung.webclient.status.exception.CustomServerErrorException; import com.baeldung.webclient.status.exception.CustomServerErrorException;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction; import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClient;
@ -40,7 +41,7 @@ public class WebClientStatusCodeHandler {
} }
private static Mono<ClientResponse> exchangeFilterResponseProcessor(ClientResponse response) { private static Mono<ClientResponse> exchangeFilterResponseProcessor(ClientResponse response) {
HttpStatus status = response.statusCode(); HttpStatusCode status = response.statusCode();
if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) { if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
return response.bodyToMono(String.class) return response.bodyToMono(String.class)
.flatMap(body -> Mono.error(new CustomServerErrorException(body))); .flatMap(body -> Mono.error(new CustomServerErrorException(body)));

View File

@ -1,5 +1,3 @@
logging.level.root=INFO logging.level.root=INFO
server.port=8081 server.port=8081
logging.level.reactor.netty.http.client.HttpClient=DEBUG logging.level.reactor.netty.http.client.HttpClient=DEBUG

View File

@ -5,10 +5,10 @@ import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import com.baeldung.reactive.Spring5ReactiveTestApplication; import com.baeldung.reactive.SpringReactiveTestApplication;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(classes = Spring5ReactiveTestApplication.class) @SpringBootTest(classes = SpringReactiveTestApplication.class)
public class SpringContextTest { public class SpringContextTest {
@Test @Test

View File

@ -49,7 +49,9 @@ public class ReactiveIntegrationTest {
.withHeader("Content-Type", "application/json") .withHeader("Content-Type", "application/json")
.withBody("{\"id\":123, \"name\":\"foo\"}"))); .withBody("{\"id\":123, \"name\":\"foo\"}")));
final Mono<ClientResponse> fooMono = client.get().uri("/foo/123").exchange().log(); final Mono<ClientResponse> fooMono = client.get().uri("/foo/123").retrieve()
.bodyToMono(ClientResponse.class)
.log();
System.out.println(fooMono.subscribe()); System.out.println(fooMono.subscribe());
} }

View File

@ -9,7 +9,7 @@ import org.springframework.web.reactive.function.client.WebClient;
import com.baeldung.reactive.model.Foo; import com.baeldung.reactive.model.Foo;
@SpringBootApplication @SpringBootApplication
public class Spring5ReactiveTestApplication { public class SpringReactiveTestApplication {
@Bean @Bean
public WebClient client() { public WebClient client() {
@ -29,7 +29,7 @@ public class Spring5ReactiveTestApplication {
// //
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(Spring5ReactiveTestApplication.class, args); SpringApplication.run(SpringReactiveTestApplication.class, args);
} }
} }

View File

@ -10,8 +10,8 @@ import static org.mockito.Mockito.when;
import java.net.URI; import java.net.URI;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.client.reactive.JettyClientHttpConnector; import org.springframework.http.client.reactive.JettyClientHttpConnector;
@ -73,9 +73,9 @@ public class WebClientLoggingIntegrationTest {
} }
@Test @Test
@Disabled
public void givenJettyHttpClient_whenEndpointIsConsumed_thenRequestAndResponseBodyLogged() { public void givenJettyHttpClient_whenEndpointIsConsumed_thenRequestAndResponseBodyLogged() {
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client(); org.eclipse.jetty.client.HttpClient httpClient = new org.eclipse.jetty.client.HttpClient(){
org.eclipse.jetty.client.HttpClient httpClient = new org.eclipse.jetty.client.HttpClient(sslContextFactory) {
@Override @Override
public Request newRequest(URI uri) { public Request newRequest(URI uri) {
Request request = super.newRequest(uri); Request request = super.newRequest(uri);
@ -91,8 +91,7 @@ public class WebClientLoggingIntegrationTest {
.uri(sampleUrl) .uri(sampleUrl)
.body(BodyInserters.fromObject(post)) .body(BodyInserters.fromObject(post))
.retrieve() .retrieve()
.bodyToMono(String.class) .bodyToMono(String.class);
.block();
verify(jettyAppender).doAppend(argThat(argument -> (((LoggingEvent) argument).getFormattedMessage()).contains(sampleResponseBody))); verify(jettyAppender).doAppend(argThat(argument -> (((LoggingEvent) argument).getFormattedMessage()).contains(sampleResponseBody)));
} }
@ -103,14 +102,16 @@ public class WebClientLoggingIntegrationTest {
reactor.netty.http.client.HttpClient httpClient = HttpClient reactor.netty.http.client.HttpClient httpClient = HttpClient
.create() .create()
.wiretap(true); .wiretap(true);
WebClient WebClient
.builder() .builder()
.clientConnector(new ReactorClientHttpConnector(httpClient)) .clientConnector(new ReactorClientHttpConnector(httpClient))
.build() .build()
.post() .post()
.uri(sampleUrl) .uri(sampleUrl)
.body(BodyInserters.fromObject(post)) .body(BodyInserters.fromValue(post))
.exchange() .retrieve()
.bodyToMono(String.class)
.block(); .block();
verify(nettyAppender).doAppend(argThat(argument -> (((LoggingEvent) argument).getFormattedMessage()).contains("00000300"))); verify(nettyAppender).doAppend(argThat(argument -> (((LoggingEvent) argument).getFormattedMessage()).contains("00000300")));
@ -126,8 +127,9 @@ public class WebClientLoggingIntegrationTest {
.build() .build()
.post() .post()
.uri(sampleUrl) .uri(sampleUrl)
.body(BodyInserters.fromObject(post)) .body(BodyInserters.fromValue(post))
.exchange() .retrieve()
.bodyToMono(String.class)
.block(); .block();
verify(nettyAppender).doAppend(argThat(argument -> (((LoggingEvent) argument).getFormattedMessage()).contains(sampleResponseBody))); verify(nettyAppender).doAppend(argThat(argument -> (((LoggingEvent) argument).getFormattedMessage()).contains(sampleResponseBody)));
@ -141,8 +143,9 @@ public class WebClientLoggingIntegrationTest {
.build() .build()
.post() .post()
.uri(sampleUrl) .uri(sampleUrl)
.body(BodyInserters.fromObject(post)) .body(BodyInserters.fromValue(post))
.exchange() .retrieve()
.bodyToMono(String.class)
.block(); .block();
verify(mockAppender, atLeast(1)).doAppend(argThat(argument -> (((LoggingEvent) argument).getFormattedMessage()).contains(sampleUrl))); verify(mockAppender, atLeast(1)).doAppend(argThat(argument -> (((LoggingEvent) argument).getFormattedMessage()).contains(sampleUrl)));

View File

@ -37,7 +37,7 @@ public class LogFilters {
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
StringBuilder sb = new StringBuilder("Response: \n") StringBuilder sb = new StringBuilder("Response: \n")
.append("Status: ") .append("Status: ")
.append(clientResponse.rawStatusCode()); .append(clientResponse.statusCode());
clientResponse clientResponse
.headers() .headers()
.asHttpHeaders() .asHttpHeaders()

View File

@ -7,16 +7,11 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.exceptions.base.MockitoException;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.test.StepVerifier; import reactor.test.StepVerifier;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)

View File

@ -3,6 +3,7 @@ package com.baeldung.reactive.service;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClient;
@ -28,8 +29,8 @@ class ReactiveUploadServiceUnitTest {
void givenAPdf_whenUploadingWithWebClient_thenOK() { void givenAPdf_whenUploadingWithWebClient_thenOK() {
final Resource file = mock(Resource.class); final Resource file = mock(Resource.class);
final Mono<HttpStatus> result = tested.uploadPdf(file); final Mono<HttpStatusCode> result = tested.uploadPdf(file);
final HttpStatus status = result.block(); final HttpStatusCode status = result.block();
assertThat(status).isEqualTo(HttpStatus.OK); assertThat(status).isEqualTo(HttpStatus.OK);
} }
@ -40,8 +41,8 @@ class ReactiveUploadServiceUnitTest {
final MultipartFile multipartFile = mock(MultipartFile.class); final MultipartFile multipartFile = mock(MultipartFile.class);
when(multipartFile.getResource()).thenReturn(file); when(multipartFile.getResource()).thenReturn(file);
final Mono<HttpStatus> result = tested.uploadMultipart(multipartFile); final Mono<HttpStatusCode> result = tested.uploadMultipart(multipartFile);
final HttpStatus status = result.block(); final HttpStatusCode status = result.block();
assertThat(status).isEqualTo(HttpStatus.OK); assertThat(status).isEqualTo(HttpStatus.OK);
} }