diff --git a/spring-boot-modules/spring-boot-3/pom.xml b/spring-boot-modules/spring-boot-3/pom.xml index 84cfd8ee7850..0932501ad64a 100644 --- a/spring-boot-modules/spring-boot-3/pom.xml +++ b/spring-boot-modules/spring-boot-3/pom.xml @@ -83,6 +83,11 @@ org.springframework.boot spring-boot-starter-test + + + org.apache.httpcomponents.client5 + httpclient5 + diff --git a/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/restclient/RestTemplateConfiguration.java b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/restclient/RestTemplateConfiguration.java new file mode 100644 index 000000000000..33ff21ffcc90 --- /dev/null +++ b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/restclient/RestTemplateConfiguration.java @@ -0,0 +1,55 @@ +package com.baeldung.restclient; + +import org.apache.hc.client5.http.config.ConnectionConfig; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; +import org.apache.hc.core5.http.io.SocketConfig; +import org.apache.hc.core5.util.Timeout; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfiguration { + + @Bean + public RestTemplate restTemplate() { + try { + // Timeout configurations + SocketConfig socketConfig = SocketConfig.custom() + .setSoTimeout(Timeout.ofSeconds(30)) // Read timeout + .build(); + + ConnectionConfig connectionConfig = ConnectionConfig.custom() + .setConnectTimeout(Timeout.ofSeconds(30)) // Connect timeout + .build(); + + RequestConfig requestConfig = RequestConfig.custom() + .setConnectionRequestTimeout(Timeout.ofSeconds(30)) // Pool wait timeout + .build(); + + // Connection pool configuration + PoolingHttpClientConnectionManager connectionManager = + PoolingHttpClientConnectionManagerBuilder.create() + .setMaxConnPerRoute(20) + .setMaxConnTotal(100) + .setDefaultSocketConfig(socketConfig) + .setDefaultConnectionConfig(connectionConfig) + .build(); + + CloseableHttpClient httpClient = HttpClients.custom() + .setConnectionManager(connectionManager) + .setDefaultRequestConfig(requestConfig) + .build(); + + return new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient)); + + } catch (Exception e) { + throw new IllegalStateException("Failed to configure RestTemplate", e); + } + } +} diff --git a/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/restclient/RestTemplateConfigurationUnitTest.java b/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/restclient/RestTemplateConfigurationUnitTest.java new file mode 100644 index 000000000000..efcbd1210aa5 --- /dev/null +++ b/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/restclient/RestTemplateConfigurationUnitTest.java @@ -0,0 +1,40 @@ +package com.baeldung.restclient; + +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.web.client.RestTemplate; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +@ContextConfiguration(classes = RestTemplateConfiguration.class) +public class RestTemplateConfigurationUnitTest { + + @Autowired + private ApplicationContext context; + + @Test + public void givenSpringContext_whenRestTemplateBeanRetrieved_thenReturnsProperlyConfiguredInstance() { + // When - We retrieve the RestTemplate bean + RestTemplate restTemplate = context.getBean(RestTemplate.class); + + // Then - Verify it's properly configured + assertThat(restTemplate) + .isNotNull() + .extracting(RestTemplate::getRequestFactory) + .isInstanceOf(HttpComponentsClientHttpRequestFactory.class); + + HttpComponentsClientHttpRequestFactory factory = + (HttpComponentsClientHttpRequestFactory) restTemplate.getRequestFactory(); + + assertThat(factory.getHttpClient()) + .isInstanceOfSatisfying(CloseableHttpClient.class, client -> { + assertThat(client).isNotNull(); + }); + } +}