From d0fe5535a9823f4670fc4dfddd46f02d0446058c Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Fri, 1 Nov 2024 10:28:36 +0000 Subject: [PATCH 1/3] feat : upgrade to springboot 3.4 --- httpClients/boot-restclient/pom.xml | 4 +- .../ClientLoggerRequestInterceptor.java | 105 ++++++++++++++++++ .../config/RestClientConfiguration.java | 74 +----------- .../src/main/resources/application.properties | 4 + 4 files changed, 114 insertions(+), 73 deletions(-) create mode 100644 httpClients/boot-restclient/src/main/java/com/example/restclient/bootrestclient/config/ClientLoggerRequestInterceptor.java diff --git a/httpClients/boot-restclient/pom.xml b/httpClients/boot-restclient/pom.xml index 4bc6075be..b6edf1940 100644 --- a/httpClients/boot-restclient/pom.xml +++ b/httpClients/boot-restclient/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.3.5 + 3.4.0-RC1 com.example.restclient @@ -72,7 +72,7 @@ - 1.22.0 + 1.24.0 diff --git a/httpClients/boot-restclient/src/main/java/com/example/restclient/bootrestclient/config/ClientLoggerRequestInterceptor.java b/httpClients/boot-restclient/src/main/java/com/example/restclient/bootrestclient/config/ClientLoggerRequestInterceptor.java new file mode 100644 index 000000000..54190e115 --- /dev/null +++ b/httpClients/boot-restclient/src/main/java/com/example/restclient/bootrestclient/config/ClientLoggerRequestInterceptor.java @@ -0,0 +1,105 @@ +package com.example.restclient.bootrestclient.config; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpRequest; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.stereotype.Component; + +@Component +public class ClientLoggerRequestInterceptor implements ClientHttpRequestInterceptor { + + private static final Logger log = LoggerFactory.getLogger(ClientLoggerRequestInterceptor.class); + + @Override + public ClientHttpResponse intercept( + HttpRequest request, byte[] body, ClientHttpRequestExecution execution) + throws IOException { + logRequest(request, body); + ClientHttpResponse response = execution.execute(request, body); + return logResponse(response); + } + + private BufferingClientHttpResponseWrapper logResponse(ClientHttpResponse response) + throws IOException { + log.info( + "============================response begin=========================================="); + log.info("Status code : {}", response.getStatusCode()); + log.info("Status text : {}", response.getStatusText()); + logHeaders(response.getHeaders()); + byte[] responseBody = response.getBody().readAllBytes(); + if (responseBody.length > 0) { + log.info("Response body: {}", new String(responseBody, StandardCharsets.UTF_8)); + } + log.info( + "=======================response end================================================="); + return new BufferingClientHttpResponseWrapper(response, responseBody); + } + + private void logRequest(HttpRequest request, byte[] body) { + + log.info( + "===========================request begin================================================"); + log.info("URI : {}", request.getURI()); + log.info("Method : {}", request.getMethod()); + logHeaders(request.getHeaders()); + if (body.length > 0) { + log.info("Request body: {}", new String(body, StandardCharsets.UTF_8)); + } + log.info( + "==========================request end================================================"); + } + + private void logHeaders(HttpHeaders headers) { + log.info("Headers : "); + headers.forEach((name, values) -> values.forEach(value -> log.info("{}={}", name, value))); + } + + private static class BufferingClientHttpResponseWrapper implements ClientHttpResponse { + private final ClientHttpResponse response; + private final byte[] body; + + public BufferingClientHttpResponseWrapper(ClientHttpResponse response, byte[] body) { + this.response = response; + this.body = body; + } + + @Override + public InputStream getBody() throws IOException { + return new ByteArrayInputStream(body); + } + + @Override + public HttpHeaders getHeaders() { + return response.getHeaders(); + } + + @Override + public HttpStatusCode getStatusCode() throws IOException { + return response.getStatusCode(); + } + + @Override + public int getRawStatusCode() throws IOException { + return response.getRawStatusCode(); + } + + @Override + public String getStatusText() throws IOException { + return response.getStatusText(); + } + + @Override + public void close() { + response.close(); + } + } +} diff --git a/httpClients/boot-restclient/src/main/java/com/example/restclient/bootrestclient/config/RestClientConfiguration.java b/httpClients/boot-restclient/src/main/java/com/example/restclient/bootrestclient/config/RestClientConfiguration.java index 1b20b257b..3dea72f21 100644 --- a/httpClients/boot-restclient/src/main/java/com/example/restclient/bootrestclient/config/RestClientConfiguration.java +++ b/httpClients/boot-restclient/src/main/java/com/example/restclient/bootrestclient/config/RestClientConfiguration.java @@ -1,23 +1,12 @@ package com.example.restclient.bootrestclient.config; -import java.io.IOException; -import java.net.http.HttpClient; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.time.Duration; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.web.client.RestClientCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpRequest; import org.springframework.http.MediaType; -import org.springframework.http.client.BufferingClientHttpRequestFactory; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.http.client.JdkClientHttpRequestFactory; -import org.springframework.lang.NonNull; import org.springframework.retry.annotation.EnableRetry; -import org.springframework.util.StreamUtils; import org.springframework.web.client.RestClient; import org.springframework.web.util.DefaultUriBuilderFactory; @@ -34,7 +23,7 @@ RestClient restClient(RestClient.Builder restClientBuilder) { @Bean RestClientCustomizer restClientCustomizer( ApplicationProperties applicationProperties, - @NonNull BufferingClientHttpRequestFactory bufferingClientHttpRequestFactory) { + ClientLoggerRequestInterceptor clientLoggerRequestInterceptor) { DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(applicationProperties.getExternalCallUrl()); factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.URI_COMPONENT); @@ -45,64 +34,7 @@ RestClientCustomizer restClientCustomizer( httpHeaders -> { httpHeaders.setContentType(MediaType.APPLICATION_JSON); httpHeaders.setAccept(List.of(MediaType.APPLICATION_JSON)); - }); - // .requestFactory(bufferingClientHttpRequestFactory) - // .requestInterceptor( - // (request, body, execution) -> { - // logRequest(request, body); - // ClientHttpResponse response = execution.execute(request, body); - // logResponse(response); - // return response; - // }); - } - - private void logResponse(ClientHttpResponse response) throws IOException { - log.info( - "============================response begin=========================================="); - log.info("Status code : {}", response.getStatusCode()); - log.info("Status text : {}", response.getStatusText()); - log.info("Headers : {}", response.getHeaders()); - log.info( - "Response body: {}", - StreamUtils.copyToString(response.getBody(), Charset.defaultCharset())); - log.info( - "=======================response end================================================="); - } - - private void logRequest(HttpRequest request, byte[] body) { - - log.info( - "===========================request begin================================================"); - log.info("URI : {}", request.getURI()); - log.info("Method : {}", request.getMethod()); - log.info("Headers : {}", request.getHeaders()); - log.info("Request body: {}", new String(body, StandardCharsets.UTF_8)); - log.info( - "==========================request end================================================"); - } - - @Bean - HttpClient jdkClient() { - return HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_2) - .connectTimeout(Duration.ofSeconds(30)) - .followRedirects(HttpClient.Redirect.NORMAL) - .build(); - } - - @Bean - JdkClientHttpRequestFactory jdkClientHttpRequestFactory(@NonNull HttpClient jdkClient) { - JdkClientHttpRequestFactory jdkClientHttpRequestFactory = - new JdkClientHttpRequestFactory(jdkClient); - jdkClientHttpRequestFactory.setReadTimeout(Duration.ofSeconds(60)); - return jdkClientHttpRequestFactory; - } - - // BufferingClientHttpRequestFactory allows us to read the response body multiple times for a - // single request. - @Bean - BufferingClientHttpRequestFactory bufferingClientHttpRequestFactory( - @NonNull JdkClientHttpRequestFactory jdkClientHttpRequestFactory) { - return new BufferingClientHttpRequestFactory(jdkClientHttpRequestFactory); + }) + .requestInterceptor(clientLoggerRequestInterceptor); } } diff --git a/httpClients/boot-restclient/src/main/resources/application.properties b/httpClients/boot-restclient/src/main/resources/application.properties index 1cecb1aad..e2aae6aaf 100644 --- a/httpClients/boot-restclient/src/main/resources/application.properties +++ b/httpClients/boot-restclient/src/main/resources/application.properties @@ -4,3 +4,7 @@ spring.mvc.problemdetails.enabled=true spring.threads.virtual.enabled=true application.external-call-url=https://jsonplaceholder.typicode.com + +spring.http.client.factory=jdk +spring.http.client.connect-timeout=PT30S +spring.http.client.read-timeout=PT1M From f90d163c98d74f825f64cfedeac2e6ea80235adf Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Fri, 1 Nov 2024 11:00:45 +0000 Subject: [PATCH 2/3] fix : issues --- .../config/ClientLoggerRequestInterceptor.java | 18 ++++++++++-------- .../services/PostServiceTest.java | 12 ++++++++++-- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/httpClients/boot-restclient/src/main/java/com/example/restclient/bootrestclient/config/ClientLoggerRequestInterceptor.java b/httpClients/boot-restclient/src/main/java/com/example/restclient/bootrestclient/config/ClientLoggerRequestInterceptor.java index 54190e115..b1e6404fe 100644 --- a/httpClients/boot-restclient/src/main/java/com/example/restclient/bootrestclient/config/ClientLoggerRequestInterceptor.java +++ b/httpClients/boot-restclient/src/main/java/com/example/restclient/bootrestclient/config/ClientLoggerRequestInterceptor.java @@ -3,6 +3,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,6 +14,7 @@ import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.stereotype.Component; +import org.springframework.util.StreamUtils; @Component public class ClientLoggerRequestInterceptor implements ClientHttpRequestInterceptor { @@ -35,10 +37,10 @@ private BufferingClientHttpResponseWrapper logResponse(ClientHttpResponse respon log.info("Status code : {}", response.getStatusCode()); log.info("Status text : {}", response.getStatusText()); logHeaders(response.getHeaders()); - byte[] responseBody = response.getBody().readAllBytes(); - if (responseBody.length > 0) { - log.info("Response body: {}", new String(responseBody, StandardCharsets.UTF_8)); - } + + String responseBody = + StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()); + log.info("Response body: {}", responseBody); log.info( "=======================response end================================================="); return new BufferingClientHttpResponseWrapper(response, responseBody); @@ -65,16 +67,16 @@ private void logHeaders(HttpHeaders headers) { private static class BufferingClientHttpResponseWrapper implements ClientHttpResponse { private final ClientHttpResponse response; - private final byte[] body; + private final String body; - public BufferingClientHttpResponseWrapper(ClientHttpResponse response, byte[] body) { + public BufferingClientHttpResponseWrapper(ClientHttpResponse response, String body) { this.response = response; this.body = body; } @Override - public InputStream getBody() throws IOException { - return new ByteArrayInputStream(body); + public InputStream getBody() { + return new ByteArrayInputStream(body.getBytes()); } @Override diff --git a/httpClients/boot-restclient/src/test/java/com/example/restclient/bootrestclient/services/PostServiceTest.java b/httpClients/boot-restclient/src/test/java/com/example/restclient/bootrestclient/services/PostServiceTest.java index f6dbecd17..a6c6a7cfd 100644 --- a/httpClients/boot-restclient/src/test/java/com/example/restclient/bootrestclient/services/PostServiceTest.java +++ b/httpClients/boot-restclient/src/test/java/com/example/restclient/bootrestclient/services/PostServiceTest.java @@ -1,9 +1,12 @@ package com.example.restclient.bootrestclient.services; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.header; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; +import com.example.restclient.bootrestclient.config.ClientLoggerRequestInterceptor; import com.example.restclient.bootrestclient.config.RestClientConfiguration; import com.example.restclient.bootrestclient.model.response.PostDto; import com.fasterxml.jackson.core.JsonProcessingException; @@ -18,7 +21,12 @@ import org.springframework.test.web.client.MockRestServiceServer; @RestClientTest( - components = {PostService.class, HttpClientService.class, RestClientConfiguration.class}) + components = { + PostService.class, + HttpClientService.class, + RestClientConfiguration.class, + ClientLoggerRequestInterceptor.class + }) class PostServiceTest { @Autowired private MockRestServiceServer mockRestServiceServer; From 16569f7f42338413a33b85dbf82c1244115695c2 Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Fri, 1 Nov 2024 11:01:59 +0000 Subject: [PATCH 3/3] adds metadata --- .../additional-spring-configuration-metadata.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 httpClients/boot-restclient/src/main/resources/META-INF/additional-spring-configuration-metadata.json diff --git a/httpClients/boot-restclient/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/httpClients/boot-restclient/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 000000000..4429d9dcd --- /dev/null +++ b/httpClients/boot-restclient/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,9 @@ +{ + "properties": [ + { + "name": "application.external-call-url", + "type": "java.lang.String", + "description": "URL to which call should be made." + } + ] +} \ No newline at end of file