diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpAdapterParsingUtils.java b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpAdapterParsingUtils.java index b4bb1b6cf0..61758ef493 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpAdapterParsingUtils.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpAdapterParsingUtils.java @@ -26,6 +26,7 @@ import org.springframework.beans.factory.xml.ParserContext; import org.springframework.expression.common.LiteralExpression; import org.springframework.integration.config.ExpressionFactoryBean; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; @@ -37,6 +38,7 @@ * @author Artem Bilan * @author Shiliang Li * @author Arun Sethumadhavan + * @author Burak Kalayci * * @since 2.0.2 */ @@ -52,6 +54,19 @@ final class HttpAdapterParsingUtils { private static final LocalClientKind REST_CLIENT_KIND = new LocalClientKind("rest-client", "the provided client", "RestClient.Builder.uriBuilderFactory"); + static void configureLocalClientAttributes(BeanDefinitionBuilder builder, Element element) { + for (String referenceAttributeName : LOCAL_CLIENT_REFERENCE_ATTRIBUTES) { + if ("error-handler".equals(referenceAttributeName)) { + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName, + "defaultStatusHandler"); + } + else { + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName); + } + } + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encoding-mode"); + } + static void verifyNoRestTemplateAttributes(Element element, ParserContext parserContext) { verifyNoLocalClientAttributes(element, parserContext, REST_TEMPLATE_KIND); } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParser.java b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParser.java index affaf7ca7f..b1633fceea 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParser.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParser.java @@ -37,6 +37,7 @@ * @author Artem Bilan * @author Shiliang Li * @author Arun Sethumadhavan + * @author Burak Kalayci * * @since 2.0 */ @@ -100,10 +101,7 @@ else if (StringUtils.hasText(restClientRef)) { .addIndexedArgumentValue(1, new RuntimeBeanReference(restClientRef)); } else { - for (String referenceAttributeName : HttpAdapterParsingUtils.LOCAL_CLIENT_REFERENCE_ATTRIBUTES) { - IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName); - } - IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encoding-mode"); + HttpAdapterParsingUtils.configureLocalClientAttributes(builder, element); } return builder; } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundGatewayParser.java b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundGatewayParser.java index 8913f57f68..872c5cfbbb 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundGatewayParser.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundGatewayParser.java @@ -35,6 +35,7 @@ * @author Artem Bilan * @author Shiliang Li * @author Arun Sethumadhavan + * @author Burak Kalayci */ public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser { @@ -113,10 +114,7 @@ else if (StringUtils.hasText(restClientRef)) { .addIndexedArgumentValue(1, new RuntimeBeanReference(restClientRef)); } else { - for (String referenceAttributeName : HttpAdapterParsingUtils.LOCAL_CLIENT_REFERENCE_ATTRIBUTES) { - IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName); - } - IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encoding-mode"); + HttpAdapterParsingUtils.configureLocalClientAttributes(builder, element); } return builder; } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/dsl/Http.java b/spring-integration-http/src/main/java/org/springframework/integration/http/dsl/Http.java index bac05d8e12..96e47412aa 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/dsl/Http.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/dsl/Http.java @@ -29,7 +29,6 @@ import org.springframework.messaging.Message; import org.springframework.util.Assert; import org.springframework.web.client.RestClient; -import org.springframework.web.client.RestTemplate; /** * The HTTP components Factory. @@ -37,6 +36,7 @@ * @author Artem Bilan * @author Shiliang Li * @author Arun Sethumadhavan + * @author Burak Kalayci * * @since 5.0 */ @@ -83,14 +83,16 @@ public static HttpMessageHandlerSpec outboundChannelAdapter(Expression uriExpres /** * Create an {@link HttpMessageHandlerSpec} builder for one-way adapter - * based on provided {@link URI} and {@link RestTemplate}. + * based on provided {@link URI} and {@link org.springframework.web.client.RestTemplate}. * @param uri the {@link URI} to send requests. - * @param restTemplate {@link RestTemplate} to use. + * @param restTemplate {@link org.springframework.web.client.RestTemplate} to use. * @return the HttpMessageHandlerSpec instance * @deprecated Since 7.1 in favor of {@link RestClient}-based configuration. */ @Deprecated(since = "7.1", forRemoval = true) - public static HttpMessageHandlerSpec outboundChannelAdapter(URI uri, @Nullable RestTemplate restTemplate) { + @SuppressWarnings("removal") + public static HttpMessageHandlerSpec outboundChannelAdapter(URI uri, + org.springframework.web.client.@Nullable RestTemplate restTemplate) { return outboundChannelAdapter(uri, toRestClient(restTemplate)); } @@ -108,14 +110,16 @@ public static HttpMessageHandlerSpec outboundChannelAdapter(URI uri, @Nullable R /** * Create an {@link HttpMessageHandlerSpec} builder for one-way adapter - * based on provided {@code uri} and {@link RestTemplate}. + * based on provided {@code uri} and {@link org.springframework.web.client.RestTemplate}. * @param uri the {@code uri} to send requests. - * @param restTemplate {@link RestTemplate} to use. + * @param restTemplate {@link org.springframework.web.client.RestTemplate} to use. * @return the HttpMessageHandlerSpec instance * @deprecated Since 7.1 in favor of {@link RestClient}-based configuration. */ @Deprecated(since = "7.1", forRemoval = true) - public static HttpMessageHandlerSpec outboundChannelAdapter(String uri, @Nullable RestTemplate restTemplate) { + @SuppressWarnings("removal") + public static HttpMessageHandlerSpec outboundChannelAdapter(String uri, + org.springframework.web.client.@Nullable RestTemplate restTemplate) { return outboundChannelAdapter(uri, toRestClient(restTemplate)); } @@ -134,16 +138,17 @@ public static HttpMessageHandlerSpec outboundChannelAdapter(String uri, @Nullabl /** * Create an {@link HttpMessageHandlerSpec} builder for one-way adapter * based on provided {@code Function} to evaluate target {@code uri} against request message - * and {@link RestTemplate} for HTTP exchanges. + * and {@link org.springframework.web.client.RestTemplate} for HTTP exchanges. * @param uriFunction the {@code Function} to evaluate {@code uri} at runtime. - * @param restTemplate {@link RestTemplate} to use. + * @param restTemplate {@link org.springframework.web.client.RestTemplate} to use. * @param

the expected payload type. * @return the HttpMessageHandlerSpec instance * @deprecated Since 7.1 in favor of {@link RestClient}-based configuration. */ @Deprecated(since = "7.1", forRemoval = true) + @SuppressWarnings("removal") public static

HttpMessageHandlerSpec outboundChannelAdapter(Function, ?> uriFunction, - RestTemplate restTemplate) { + org.springframework.web.client.RestTemplate restTemplate) { return outboundChannelAdapter(new FunctionExpression<>(uriFunction), toRestClient(restTemplate)); } @@ -167,15 +172,16 @@ public static

HttpMessageHandlerSpec outboundChannelAdapter(Function the expected payload type. * @return the HttpMessageHandlerSpec instance * @deprecated Since 7.1 in favor of {@link RestClient}-based configuration. */ @Deprecated(since = "7.1", forRemoval = true) + @SuppressWarnings("removal") public static

HttpMessageHandlerSpec outboundGateway(Function, ?> uriFunction, - RestTemplate restTemplate) { + org.springframework.web.client.RestTemplate restTemplate) { return outboundGateway(new FunctionExpression<>(uriFunction), toRestClient(restTemplate)); } @@ -320,15 +331,16 @@ public static

HttpMessageHandlerSpec outboundGateway(Function, ?> /** * Create an {@link HttpMessageHandlerSpec} builder for request-reply gateway * based on provided SpEL {@link Expression} to evaluate target {@code uri} - * against request message and {@link RestTemplate} for HTTP exchanges. + * against request message and {@link org.springframework.web.client.RestTemplate} for HTTP exchanges. * @param uriExpression the SpEL {@link Expression} to evaluate {@code uri} at runtime. - * @param restTemplate {@link RestTemplate} to use. + * @param restTemplate {@link org.springframework.web.client.RestTemplate} to use. * @return the HttpMessageHandlerSpec instance * @deprecated Since 7.1 in favor of {@link RestClient}-based configuration. */ @Deprecated(since = "7.1", forRemoval = true) + @SuppressWarnings("removal") public static HttpMessageHandlerSpec outboundGateway(Expression uriExpression, - @Nullable RestTemplate restTemplate) { + org.springframework.web.client.@Nullable RestTemplate restTemplate) { return outboundGateway(uriExpression, toRestClient(restTemplate)); } @@ -360,7 +372,9 @@ private static HttpMessageHandlerSpec outboundGatewaySpec(Expression uriExpressi return new HttpMessageHandlerSpec(uriExpression, restClient); } - private static @Nullable RestClient toRestClient(@Nullable RestTemplate restTemplate) { + @SuppressWarnings("removal") + private static @Nullable RestClient toRestClient( + org.springframework.web.client.@Nullable RestTemplate restTemplate) { return restTemplate != null ? RestClient.create(restTemplate) : null; } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/dsl/HttpMessageHandlerSpec.java b/spring-integration-http/src/main/java/org/springframework/integration/http/dsl/HttpMessageHandlerSpec.java index 66280cc792..a8d652bed4 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/dsl/HttpMessageHandlerSpec.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/dsl/HttpMessageHandlerSpec.java @@ -30,9 +30,7 @@ import org.springframework.integration.expression.ValueExpression; import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler; import org.springframework.util.Assert; -import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestClient; -import org.springframework.web.client.RestTemplate; /** * The {@link BaseHttpMessageHandlerSpec} implementation for the {@link HttpRequestExecutingMessageHandler}. @@ -42,6 +40,7 @@ * @author Oleksii Komlyk * @author Arun Sethumadhavan * @author Glenn Renfro + * @author Burak Kalayci * * @since 5.0 * @@ -56,7 +55,8 @@ public class HttpMessageHandlerSpec * @deprecated Since 7.1 in favor of {@link RestClient}-based configuration. */ @Deprecated(since = "7.1", forRemoval = true) - protected HttpMessageHandlerSpec(URI uri, @Nullable RestTemplate restTemplate) { + @SuppressWarnings("removal") + protected HttpMessageHandlerSpec(URI uri, org.springframework.web.client.@Nullable RestTemplate restTemplate) { this(new ValueExpression<>(uri), restTemplate != null ? RestClient.create(restTemplate) : null); } @@ -64,7 +64,8 @@ protected HttpMessageHandlerSpec(URI uri, @Nullable RestTemplate restTemplate) { * @deprecated Since 7.1 in favor of {@link RestClient}-based configuration. */ @Deprecated(since = "7.1", forRemoval = true) - protected HttpMessageHandlerSpec(String uri, @Nullable RestTemplate restTemplate) { + @SuppressWarnings("removal") + protected HttpMessageHandlerSpec(String uri, org.springframework.web.client.@Nullable RestTemplate restTemplate) { this(new LiteralExpression(uri), restTemplate != null ? RestClient.create(restTemplate) : null); } @@ -72,7 +73,9 @@ protected HttpMessageHandlerSpec(String uri, @Nullable RestTemplate restTemplate * @deprecated Since 7.1 in favor of {@link RestClient}-based configuration. */ @Deprecated(since = "7.1", forRemoval = true) - protected HttpMessageHandlerSpec(Expression uriExpression, @Nullable RestTemplate restTemplate) { + @SuppressWarnings("removal") + protected HttpMessageHandlerSpec(Expression uriExpression, + org.springframework.web.client.@Nullable RestTemplate restTemplate) { this(uriExpression, restTemplate != null ? RestClient.create(restTemplate) : null); } @@ -90,7 +93,8 @@ protected HttpMessageHandlerSpec(Expression uriExpression, @Nullable RestClient } /** - * Set the {@link ClientHttpRequestFactory} for the underlying {@link RestTemplate}. + * Set the {@link ClientHttpRequestFactory} for the underlying + * {@link org.springframework.web.client.RestTemplate}. * @param requestFactory The request factory. * @return the spec * @deprecated Since 7.2 in favor of {@link RestClient}-based configuration. @@ -103,13 +107,16 @@ public HttpMessageHandlerSpec requestFactory(ClientHttpRequestFactory requestFac } /** - * Set the {@link ResponseErrorHandler} for the underlying {@link RestTemplate}. + * Set the {@link org.springframework.web.client.ResponseErrorHandler} for the underlying + * {@link org.springframework.web.client.RestTemplate}. * @param errorHandler The error handler. * @return the spec * @deprecated Since 7.2 in favor of {@link RestClient.ResponseSpec.ErrorHandler}. */ @Deprecated(since = "7.2", forRemoval = true) - public HttpMessageHandlerSpec errorHandler(ResponseErrorHandler errorHandler) { + @SuppressWarnings("removal") + public HttpMessageHandlerSpec errorHandler( + org.springframework.web.client.ResponseErrorHandler errorHandler) { Assert.isTrue(!isClientSet(), "the 'errorHandler' must be specified on the provided client"); this.target.setErrorHandler(errorHandler); return _this(); diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java index daf3536887..bc4dfac761 100755 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java @@ -37,16 +37,14 @@ import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandlingException; import org.springframework.util.Assert; -import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; import org.springframework.web.util.DefaultUriBuilderFactory; /** * A {@link org.springframework.messaging.MessageHandler} * implementation that executes HTTP requests by delegating - * to a {@link RestClient} or {@link RestTemplate} instance. + * to a {@link RestClient} or {@link org.springframework.web.client.RestTemplate} instance. * If the 'expectReply' flag is set to true (the default) * then a reply Message will be generated from the HTTP response. If that response contains * a body, it will be used as the reply Message's payload. Otherwise the reply Message's @@ -67,12 +65,14 @@ * @author Shiliang Li * @author Arun Sethumadhavan * @author Glenn Renfro + * @author Burak Kalayci * * @since 2.0 */ public class HttpRequestExecutingMessageHandler extends AbstractHttpRequestExecutingMessageHandler { - private final @Nullable RestTemplate restTemplate; + @SuppressWarnings("removal") + private final org.springframework.web.client.@Nullable RestTemplate restTemplate; private volatile @Nullable RestClient restClient; @@ -113,7 +113,9 @@ public HttpRequestExecutingMessageHandler(Expression uriExpression) { * @deprecated Since 7.1 in favor of {@link RestClient}-based configuration. */ @Deprecated(since = "7.1", forRemoval = true) - public HttpRequestExecutingMessageHandler(String uri, @Nullable RestTemplate restTemplate) { + @SuppressWarnings("removal") + public HttpRequestExecutingMessageHandler(String uri, + org.springframework.web.client.@Nullable RestTemplate restTemplate) { this(new LiteralExpression(uri), restTemplate); /* * We'd prefer to do this assertion first, but the compiler doesn't allow it. However, @@ -131,7 +133,9 @@ public HttpRequestExecutingMessageHandler(String uri, @Nullable RestTemplate res * @deprecated Since 7.1 in favor of {@link RestClient}-based configuration. */ @Deprecated(since = "7.1", forRemoval = true) - public HttpRequestExecutingMessageHandler(Expression uriExpression, @Nullable RestTemplate restTemplate) { + @SuppressWarnings("removal") + public HttpRequestExecutingMessageHandler(Expression uriExpression, + org.springframework.web.client.@Nullable RestTemplate restTemplate) { this(uriExpression, restTemplate, null); } @@ -162,8 +166,9 @@ public HttpRequestExecutingMessageHandler(Expression uriExpression, @Nullable Re this(uriExpression, null, restClient); } + @SuppressWarnings("removal") private HttpRequestExecutingMessageHandler(Expression uriExpression, - @Nullable RestTemplate restTemplate, @Nullable RestClient restClient) { + org.springframework.web.client.@Nullable RestTemplate restTemplate, @Nullable RestClient restClient) { super(uriExpression); Assert.isTrue(restTemplate == null || restClient == null, @@ -219,14 +224,16 @@ protected void doInit() { } /** - * Set the {@link ResponseErrorHandler} for the underlying {@link RestTemplate}. + * Set the {@link org.springframework.web.client.ResponseErrorHandler} for the underlying + * {@link org.springframework.web.client.RestTemplate}. * @param errorHandler The error handler. * @deprecated Use {@link #setDefaultStatusHandler(RestClient.ResponseSpec.ErrorHandler)} * or {@link #defaultStatusHandler(Predicate, RestClient.ResponseSpec.ErrorHandler)} - * @see RestTemplate#setErrorHandler(ResponseErrorHandler) + * @see org.springframework.web.client.RestTemplate#setErrorHandler(org.springframework.web.client.ResponseErrorHandler) */ @Deprecated(since = "7.2", forRemoval = true) - public void setErrorHandler(ResponseErrorHandler errorHandler) { + @SuppressWarnings("removal") + public void setErrorHandler(org.springframework.web.client.ResponseErrorHandler errorHandler) { assertLocalClient("errorHandler"); RestClient.Builder localRestClientBuilder = this.localRestClientBuilder; Assert.state(localRestClientBuilder != null, "'localRestClientBuilder' must not be null"); @@ -325,10 +332,11 @@ public void setEncodingMode(DefaultUriBuilderFactory.EncodingMode encodingMode) } } + @SuppressWarnings("removal") private ResponseEntity exchangeWithRestTemplate(Object uri, HttpMethod httpMethod, HttpEntity httpRequest, Object expectedResponseType, Map uriVariables) { - RestTemplate restTemplate = this.restTemplate; + org.springframework.web.client.RestTemplate restTemplate = this.restTemplate; Assert.state(restTemplate != null, "'restTemplate' must not be null"); if (uri instanceof URI uriToUse) { diff --git a/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http.xsd b/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http.xsd index 5c75987c32..43616dc97a 100644 --- a/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http.xsd +++ b/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http.xsd @@ -832,11 +832,11 @@ - Reference to a ResponseErrorHandler to be used by the underlying RestTemplate. + Reference to a RestClient.ResponseSpec.ErrorHandler to be used by the underlying RestClient. - + diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/HttpProxyScenarioTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/HttpProxyScenarioTests.java index a6f95a7a72..359c9e259b 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/HttpProxyScenarioTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/HttpProxyScenarioTests.java @@ -16,23 +16,24 @@ package org.springframework.integration.http; +import java.net.URI; import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Calendar; import java.util.Locale; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.function.Function; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; import org.springframework.beans.DirectFieldAccessor; -import org.springframework.beans.PropertyAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler; @@ -45,7 +46,7 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -54,11 +55,17 @@ import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * @author Artem Bilan * @author Gary Russell * @author Arun Sethumadhavan + * @author Burak Kalayci * * @since 3.0 */ @@ -115,31 +122,19 @@ public void testHttpProxyScenario() throws Exception { MockHttpServletResponse response = new MockHttpServletResponse(); - RestTemplate template = Mockito.spy(new RestTemplate()); - final String contentDispositionValue = "attachment; filename=\"test.txt\""; - Mockito.doAnswer(invocation -> { - String uri = invocation.getArgument(0); - assertThat(uri).isEqualTo("http://testServer/test?foo=bar&FOO=BAR"); - HttpEntity httpEntity = (HttpEntity) invocation.getArguments()[2]; - HttpHeaders httpHeaders = httpEntity.getHeaders(); - assertThat(httpHeaders.getIfModifiedSince()).isEqualTo(ifModifiedSince); - assertThat(httpHeaders.getFirst("If-Unmodified-Since")).isEqualTo(ifUnmodifiedSinceValue); - assertThat(httpHeaders.getFirst("Connection")).isEqualTo("Keep-Alive"); - - HttpHeaders responseHeaders = HttpHeaders.copyOf(httpHeaders); - responseHeaders.set("Connection", "close"); - responseHeaders.set("Content-Disposition", contentDispositionValue); - return new ResponseEntity<>(responseHeaders, HttpStatus.OK); - }).when(template) - .exchange(Mockito.anyString(), Mockito.any(HttpMethod.class), - Mockito.any(HttpEntity.class), Mockito.>any(), Mockito.anyMap()); - - PropertyAccessor dfa = new DirectFieldAccessor(this.handler); - dfa.setPropertyValue("localRestClientBuilder", null); - dfa.setPropertyValue("restClient", null); - dfa.setPropertyValue("restTemplate", template); + injectRestClient(this.handler, httpEntity -> { + HttpHeaders httpHeaders = httpEntity.getHeaders(); + assertThat(httpHeaders.getIfModifiedSince()).isEqualTo(ifModifiedSince); + assertThat(httpHeaders.getFirst("If-Unmodified-Since")).isEqualTo(ifUnmodifiedSinceValue); + assertThat(httpHeaders.getFirst("Connection")).isEqualTo("Keep-Alive"); + + HttpHeaders responseHeaders = HttpHeaders.copyOf(httpHeaders); + responseHeaders.set("Connection", "close"); + responseHeaders.set("Content-Disposition", contentDispositionValue); + return new ResponseEntity<>(responseHeaders, HttpStatus.OK); + }, uri -> assertThat(uri).isEqualTo("http://testServer/test?foo=bar&FOO=BAR")); RequestAttributes attributes = new ServletRequestAttributes(request); RequestContextHolder.setRequestAttributes(attributes); @@ -176,33 +171,21 @@ public void testHttpMultipartProxyScenario() throws Exception { MockHttpServletResponse response = new MockHttpServletResponse(); - RestTemplate template = Mockito.spy(new RestTemplate()); - Mockito.doAnswer(invocation -> { - String uri = invocation.getArgument(0); - assertThat(uri).isEqualTo("http://testServer/testmp"); - HttpEntity httpEntity = (HttpEntity) invocation.getArguments()[2]; - HttpHeaders httpHeaders = httpEntity.getHeaders(); - assertThat(httpHeaders.getFirst("Connection")).isEqualTo("Keep-Alive"); - assertThat(httpHeaders.getContentType().toString()) - .isEqualTo("multipart/form-data;boundary=----WebKitFormBoundarywABD2xqC1FLBijlQ"); - - HttpEntity entity = (HttpEntity) invocation.getArguments()[2]; - assertThat(entity.getBody()).isInstanceOf(MultiValueMap.class); - assertThat(((MultiValueMap) entity.getBody()).getFirst("foo")) - .isEqualTo("foo".getBytes()); - - HttpHeaders responseHeaders = HttpHeaders.copyOf(httpHeaders); - responseHeaders.set("Connection", "close"); - responseHeaders.set("Content-Type", "text/plain"); - return new ResponseEntity<>(responseHeaders, HttpStatus.OK); - }).when(template) - .exchange(Mockito.anyString(), Mockito.any(HttpMethod.class), - Mockito.any(HttpEntity.class), Mockito.>any(), Mockito.anyMap()); - - PropertyAccessor dfa = new DirectFieldAccessor(this.handlermp); - dfa.setPropertyValue("localRestClientBuilder", null); - dfa.setPropertyValue("restClient", null); - dfa.setPropertyValue("restTemplate", template); + injectRestClient(this.handlermp, httpEntity -> { + HttpHeaders httpHeaders = httpEntity.getHeaders(); + assertThat(httpHeaders.getFirst("Connection")).isEqualTo("Keep-Alive"); + assertThat(httpHeaders.getContentType().toString()) + .isEqualTo("multipart/form-data;boundary=----WebKitFormBoundarywABD2xqC1FLBijlQ"); + + assertThat(httpEntity.getBody()).isInstanceOf(MultiValueMap.class); + assertThat(((MultiValueMap) httpEntity.getBody()).getFirst("foo")) + .isEqualTo("foo".getBytes()); + + HttpHeaders responseHeaders = HttpHeaders.copyOf(httpHeaders); + responseHeaders.set("Connection", "close"); + responseHeaders.set("Content-Type", "text/plain"); + return new ResponseEntity<>(responseHeaders, HttpStatus.OK); + }, uri -> assertThat(uri).isEqualTo("http://testServer/testmp")); RequestAttributes attributes = new ServletRequestAttributes(request); RequestContextHolder.setRequestAttributes(attributes); @@ -215,4 +198,44 @@ public void testHttpMultipartProxyScenario() throws Exception { RequestContextHolder.resetRequestAttributes(); } + @SuppressWarnings("unchecked") + private static void injectRestClient(HttpRequestExecutingMessageHandler handler, + Function, ResponseEntity> exchange, + Consumer uriAsserter) { + + RestClient restClient = mock(); + RestClient.RequestBodyUriSpec spec = mock(); + RestClient.ResponseSpec responseSpec = mock(); + HttpHeaders headers = new HttpHeaders(); + AtomicReference body = new AtomicReference<>(); + AtomicReference> response = new AtomicReference<>(); + + when(restClient.method(any())).thenReturn(spec); + when(spec.uri(any(URI.class))).thenAnswer(invocation -> { + uriAsserter.accept(invocation.getArgument(0).toString()); + return spec; + }); + when(spec.uri(anyString(), anyMap())).thenAnswer(invocation -> { + uriAsserter.accept(invocation.getArgument(0)); + return spec; + }); + when(spec.headers(any())).thenAnswer(invocation -> { + Consumer headerConsumer = invocation.getArgument(0); + headerConsumer.accept(headers); + return spec; + }); + when(spec.body(any(Object.class))).thenAnswer(invocation -> { + body.set(invocation.getArgument(0)); + return spec; + }); + when(spec.retrieve()).thenAnswer(invocation -> { + response.set(exchange.apply(new HttpEntity<>(body.get(), headers))); + return responseSpec; + }); + when(responseSpec.toEntity(any(Class.class))).thenAnswer(invocation -> response.get()); + when(responseSpec.toBodilessEntity()).thenAnswer(invocation -> response.get()); + + new DirectFieldAccessor(handler).setPropertyValue("restClient", restClient); + } + } diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests-context.xml b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests-context.xml index 4556bfe5dd..1b4a57101c 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests-context.xml +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests-context.xml @@ -14,11 +14,6 @@ - - - - @@ -50,7 +45,7 @@ + rest-client="customRestClient"/> diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests.java index bdbe0c80fc..21221c2fc0 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests.java @@ -16,7 +16,6 @@ package org.springframework.integration.http.config; -import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; @@ -32,9 +31,9 @@ import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpression; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpResponse; -import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.integration.endpoint.AbstractEndpoint; import org.springframework.integration.endpoint.PollingConsumer; import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; @@ -46,9 +45,7 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.springframework.util.ObjectUtils; -import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestClient; -import org.springframework.web.client.RestTemplate; import org.springframework.web.util.DefaultUriBuilderFactory; import static org.assertj.core.api.Assertions.assertThat; @@ -63,6 +60,7 @@ * @author Shiliang Li * @author Glenn Renfro * @author Arun Sethumadhavan + * @author Burak Kalayci */ @SpringJUnitConfig @DirtiesContext @@ -76,14 +74,6 @@ public class HttpOutboundChannelAdapterParserTests { @Qualifier("fullConfig") private AbstractEndpoint fullConfig; - @Autowired - @Qualifier("restTemplateConfig") - private AbstractEndpoint restTemplateConfig; - - @Autowired - @Qualifier("customRestTemplate") - private RestTemplate customRestTemplate; - @Autowired @Qualifier("restClientConfig") private AbstractEndpoint restClientConfig; @@ -182,13 +172,6 @@ public void fullConfig() { .isEqualTo(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY); } - @Test - public void restTemplateConfig() { - RestTemplate restTemplate = - TestUtils.getPropertyValue(this.restTemplateConfig, "handler.restTemplate"); - assertThat(restTemplate).isEqualTo(customRestTemplate); - } - @Test public void restClientConfig() { RestClient restClient = TestUtils.getPropertyValue(this.restClientConfig, "handler.restClient"); @@ -215,8 +198,8 @@ public void failWithRestTemplateAndRestClientAttributes() { @Test public void withUrlAndTemplate() { DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.withUrlAndTemplate); - RestTemplate restTemplate = TestUtils.getPropertyValue(this.withUrlAndTemplate, "handler.restTemplate"); - assertThat(restTemplate).isSameAs(customRestTemplate); + RestClient restClient = TestUtils.getPropertyValue(this.withUrlAndTemplate, "handler.restClient"); + assertThat(restClient).isSameAs(this.customRestClient); HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor .getPropertyValue("handler"); DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler); @@ -224,11 +207,6 @@ public void withUrlAndTemplate() { assertThat(endpointAccessor.getPropertyValue("inputChannel")) .isEqualTo(this.applicationContext.getBean("requests")); assertThat(handlerAccessor.getPropertyValue("outputChannel")).isNull(); - DirectFieldAccessor templateAccessor = - new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate")); - ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory) - templateAccessor.getPropertyValue("requestFactory"); - assertThat(requestFactory instanceof SimpleClientHttpRequestFactory).isTrue(); Expression uriExpression = (Expression) handlerAccessor.getPropertyValue("uriExpression"); assertThat(uriExpression.getValue()).isEqualTo("http://localhost/test1"); assertThat(TestUtils.getPropertyValue(handler, "httpMethodExpression").getExpressionString()) @@ -288,15 +266,10 @@ public void failWithUrlAndExpression() { getClass())); } - public static class StubErrorHandler implements ResponseErrorHandler { - - @Override - public boolean hasError(ClientHttpResponse response) { - return false; - } + public static class StubErrorHandler implements RestClient.ResponseSpec.ErrorHandler { @Override - public void handleError(URI url, HttpMethod method, ClientHttpResponse response) { + public void handle(HttpRequest request, ClientHttpResponse response) { } } diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests.java index 1c517c2b9a..653276ed0c 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests.java @@ -45,7 +45,6 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.springframework.util.ObjectUtils; -import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestClient; import static org.assertj.core.api.Assertions.assertThat; @@ -65,7 +64,7 @@ @DirtiesContext public class HttpOutboundGatewayParserTests { - public static final ResponseErrorHandler mockResponseErrorHandler = mock(); + public static final RestClient.ResponseSpec.ErrorHandler mockResponseErrorHandler = mock(); @Autowired @Qualifier("minimalConfig") diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpOutboundWithinChainTests-context.xml b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpOutboundWithinChainTests-context.xml index 01911c1df4..a42a7eedf4 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpOutboundWithinChainTests-context.xml +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpOutboundWithinChainTests-context.xml @@ -9,16 +9,17 @@ http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - + - + diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java index f281acdeba..acaf5146cd 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import javax.xml.transform.Source; @@ -37,7 +38,6 @@ import reactor.core.publisher.Sinks; import reactor.test.StepVerifier; -import org.springframework.beans.DirectFieldAccessor; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.ParameterizedTypeReference; @@ -68,12 +68,8 @@ import org.springframework.mock.http.client.MockClientHttpRequest; import org.springframework.mock.http.client.MockClientHttpResponse; import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RequestCallback; -import org.springframework.web.client.ResourceAccessException; -import org.springframework.web.client.ResponseExtractor; import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; import org.springframework.web.util.DefaultUriBuilderFactory; import static org.assertj.core.api.Assertions.assertThat; @@ -81,6 +77,8 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -93,6 +91,7 @@ * @author Florian Schöffl * @author Glenn Renfro * @author Arun Sethumadhavan + * @author Burak Kalayci */ public class HttpRequestExecutingMessageHandlerTests implements TestApplicationContextAware { @@ -113,10 +112,9 @@ void setUp() { @Test public void simpleStringKeyStringValueFormData() { + CapturingRestClient capturing = new CapturingRestClient(); HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler( - "https://www.springsource.org/spring-integration"); - MockRestTemplate template = new MockRestTemplate(); - setRestTemplateForTesting(handler, template); + "https://www.springsource.org/spring-integration", capturing.client()); handler.setHttpMethod(HttpMethod.POST); setBeanFactory(handler); handler.afterPropertiesSet(); @@ -136,7 +134,7 @@ public void simpleStringKeyStringValueFormData() { .isThrownBy(() -> handler.handleMessage(message)) .withStackTraceContaining("intentional"); - HttpEntity request = template.lastRequestEntity.get(); + HttpEntity request = capturing.lastRequestEntity(); Object body = request.getBody(); assertThat(request.getHeaders().getContentType()).isNotNull(); assertThat(body).isInstanceOf(MultiValueMap.class); @@ -151,10 +149,9 @@ public void simpleStringKeyStringValueFormData() { @Test public void simpleStringKeyObjectValueFormData() { + CapturingRestClient capturing = new CapturingRestClient(); HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler( - "https://www.springsource.org/spring-integration"); - MockRestTemplate template = new MockRestTemplate(); - setRestTemplateForTesting(handler, template); + "https://www.springsource.org/spring-integration", capturing.client()); handler.setHttpMethod(HttpMethod.POST); setBeanFactory(handler); handler.afterPropertiesSet(); @@ -170,7 +167,7 @@ public void simpleStringKeyObjectValueFormData() { .isThrownBy(() -> handler.handleMessage(message)) .withStackTraceContaining("intentional"); - HttpEntity request = template.lastRequestEntity.get(); + HttpEntity request = capturing.lastRequestEntity(); Object body = request.getBody(); assertThat(body instanceof MultiValueMap).isTrue(); MultiValueMap map = (MultiValueMap) body; @@ -182,10 +179,9 @@ public void simpleStringKeyObjectValueFormData() { @Test public void simpleObjectKeyObjectValueFormData() { + CapturingRestClient capturing = new CapturingRestClient(); HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler( - "https://www.springsource.org/spring-integration"); - MockRestTemplate template = new MockRestTemplate(); - setRestTemplateForTesting(handler, template); + "https://www.springsource.org/spring-integration", capturing.client()); handler.setHttpMethod(HttpMethod.POST); setBeanFactory(handler); handler.afterPropertiesSet(); @@ -201,7 +197,7 @@ public void simpleObjectKeyObjectValueFormData() { .isThrownBy(() -> handler.handleMessage(message)) .withStackTraceContaining("intentional"); - HttpEntity request = template.lastRequestEntity.get(); + HttpEntity request = capturing.lastRequestEntity(); Object body = request.getBody(); assertThat(body instanceof Map).isTrue(); Map map = (Map) body; @@ -213,10 +209,9 @@ public void simpleObjectKeyObjectValueFormData() { @Test public void stringKeyStringArrayValueFormData() { + CapturingRestClient capturing = new CapturingRestClient(); HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler( - "https://www.springsource.org/spring-integration"); - MockRestTemplate template = new MockRestTemplate(); - setRestTemplateForTesting(handler, template); + "https://www.springsource.org/spring-integration", capturing.client()); handler.setHttpMethod(HttpMethod.POST); setBeanFactory(handler); handler.afterPropertiesSet(); @@ -231,7 +226,7 @@ public void stringKeyStringArrayValueFormData() { .isThrownBy(() -> handler.handleMessage(message)) .withStackTraceContaining("intentional"); - HttpEntity request = template.lastRequestEntity.get(); + HttpEntity request = capturing.lastRequestEntity(); Object body = request.getBody(); assertThat(body instanceof MultiValueMap).isTrue(); MultiValueMap map = (MultiValueMap) body; @@ -258,10 +253,9 @@ public void stringKeyStringArrayValueFormData() { @Test public void stringKeyPrimitiveArrayValueMixedFormData() { + CapturingRestClient capturing = new CapturingRestClient(); HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler( - "https://www.springsource.org/spring-integration"); - MockRestTemplate template = new MockRestTemplate(); - setRestTemplateForTesting(handler, template); + "https://www.springsource.org/spring-integration", capturing.client()); handler.setHttpMethod(HttpMethod.POST); setBeanFactory(handler); handler.afterPropertiesSet(); @@ -276,7 +270,7 @@ public void stringKeyPrimitiveArrayValueMixedFormData() { .isThrownBy(() -> handler.handleMessage(message)) .withStackTraceContaining("intentional"); - HttpEntity request = template.lastRequestEntity.get(); + HttpEntity request = capturing.lastRequestEntity(); Object body = request.getBody(); assertThat(body instanceof MultiValueMap).isTrue(); MultiValueMap map = (MultiValueMap) body; @@ -306,10 +300,9 @@ public void stringKeyPrimitiveArrayValueMixedFormData() { @Test public void stringKeyNullArrayValueMixedFormData() { + CapturingRestClient capturing = new CapturingRestClient(); HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler( - "https://www.springsource.org/spring-integration"); - MockRestTemplate template = new MockRestTemplate(); - setRestTemplateForTesting(handler, template); + "https://www.springsource.org/spring-integration", capturing.client()); handler.setHttpMethod(HttpMethod.POST); setBeanFactory(handler); handler.afterPropertiesSet(); @@ -322,7 +315,7 @@ public void stringKeyNullArrayValueMixedFormData() { .isThrownBy(() -> handler.handleMessage(message)) .withStackTraceContaining("intentional"); - HttpEntity request = template.lastRequestEntity.get(); + HttpEntity request = capturing.lastRequestEntity(); Object body = request.getBody(); assertThat(body instanceof MultiValueMap).isTrue(); MultiValueMap map = (MultiValueMap) body; @@ -347,10 +340,9 @@ public void stringKeyNullArrayValueMixedFormData() { */ @Test public void stringKeyNullCollectionValueMixedFormDataString() { + CapturingRestClient capturing = new CapturingRestClient(); HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler( - "https://www.springsource.org/spring-integration"); - MockRestTemplate template = new MockRestTemplate(); - setRestTemplateForTesting(handler, template); + "https://www.springsource.org/spring-integration", capturing.client()); handler.setHttpMethod(HttpMethod.POST); setBeanFactory(handler); handler.afterPropertiesSet(); @@ -367,7 +359,7 @@ public void stringKeyNullCollectionValueMixedFormDataString() { .isThrownBy(() -> handler.handleMessage(message)) .withStackTraceContaining("intentional"); - HttpEntity request = template.lastRequestEntity.get(); + HttpEntity request = capturing.lastRequestEntity(); Object body = request.getBody(); assertThat(body instanceof MultiValueMap).isTrue(); MultiValueMap map = (MultiValueMap) body; @@ -391,10 +383,9 @@ public void stringKeyNullCollectionValueMixedFormDataString() { */ @Test public void stringKeyNullCollectionValueMixedFormDataObject() { + CapturingRestClient capturing = new CapturingRestClient(); HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler( - "https://www.springsource.org/spring-integration"); - MockRestTemplate template = new MockRestTemplate(); - setRestTemplateForTesting(handler, template); + "https://www.springsource.org/spring-integration", capturing.client()); handler.setHttpMethod(HttpMethod.POST); setBeanFactory(handler); handler.afterPropertiesSet(); @@ -411,7 +402,7 @@ public void stringKeyNullCollectionValueMixedFormDataObject() { .isThrownBy(() -> handler.handleMessage(message)) .withStackTraceContaining("intentional"); - HttpEntity request = template.lastRequestEntity.get(); + HttpEntity request = capturing.lastRequestEntity(); Object body = request.getBody(); assertThat(body instanceof MultiValueMap).isTrue(); MultiValueMap map = (MultiValueMap) body; @@ -431,10 +422,9 @@ public void stringKeyNullCollectionValueMixedFormDataObject() { @Test public void stringKeyStringCollectionValueFormData() { + CapturingRestClient capturing = new CapturingRestClient(); HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler( - "https://www.springsource.org/spring-integration"); - MockRestTemplate template = new MockRestTemplate(); - setRestTemplateForTesting(handler, template); + "https://www.springsource.org/spring-integration", capturing.client()); handler.setHttpMethod(HttpMethod.POST); setBeanFactory(handler); handler.afterPropertiesSet(); @@ -451,7 +441,7 @@ public void stringKeyStringCollectionValueFormData() { .isThrownBy(() -> handler.handleMessage(message)) .withStackTraceContaining("intentional"); - HttpEntity request = template.lastRequestEntity.get(); + HttpEntity request = capturing.lastRequestEntity(); Object body = request.getBody(); assertThat(body instanceof MultiValueMap).isTrue(); MultiValueMap map = (MultiValueMap) body; @@ -473,10 +463,9 @@ public void stringKeyStringCollectionValueFormData() { @Test public void stringKeyObjectCollectionValueFormData() { + CapturingRestClient capturing = new CapturingRestClient(); HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler( - "https://www.springsource.org/spring-integration"); - MockRestTemplate template = new MockRestTemplate(); - setRestTemplateForTesting(handler, template); + "https://www.springsource.org/spring-integration", capturing.client()); handler.setHttpMethod(HttpMethod.POST); setBeanFactory(handler); handler.afterPropertiesSet(); @@ -493,7 +482,7 @@ public void stringKeyObjectCollectionValueFormData() { .isThrownBy(() -> handler.handleMessage(message)) .withStackTraceContaining("intentional"); - HttpEntity request = template.lastRequestEntity.get(); + HttpEntity request = capturing.lastRequestEntity(); Object body = request.getBody(); assertThat(body instanceof MultiValueMap).isTrue(); MultiValueMap map = (MultiValueMap) body; @@ -515,10 +504,9 @@ public void stringKeyObjectCollectionValueFormData() { @Test public void nameOnlyWithNullValues() { + CapturingRestClient capturing = new CapturingRestClient(); HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler( - "https://www.springsource.org/spring-integration"); - MockRestTemplate template = new MockRestTemplate(); - setRestTemplateForTesting(handler, template); + "https://www.springsource.org/spring-integration", capturing.client()); handler.setHttpMethod(HttpMethod.POST); setBeanFactory(handler); handler.afterPropertiesSet(); @@ -532,7 +520,7 @@ public void nameOnlyWithNullValues() { .isThrownBy(() -> handler.handleMessage(message)) .withStackTraceContaining("intentional"); - HttpEntity request = template.lastRequestEntity.get(); + HttpEntity request = capturing.lastRequestEntity(); Object body = request.getBody(); assertThat(body instanceof MultiValueMap).isTrue(); MultiValueMap map = (MultiValueMap) body; @@ -549,10 +537,9 @@ public void nameOnlyWithNullValues() { @Test public void contentAsByteArray() { + CapturingRestClient capturing = new CapturingRestClient(); HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler( - "https://www.springsource.org/spring-integration"); - MockRestTemplate template = new MockRestTemplate(); - setRestTemplateForTesting(handler, template); + "https://www.springsource.org/spring-integration", capturing.client()); handler.setHttpMethod(HttpMethod.POST); setBeanFactory(handler); handler.afterPropertiesSet(); @@ -564,7 +551,7 @@ public void contentAsByteArray() { .isThrownBy(() -> handler.handleMessage(message)) .withStackTraceContaining("intentional"); - HttpEntity request = template.lastRequestEntity.get(); + HttpEntity request = capturing.lastRequestEntity(); Object body = request.getBody(); assertThat(body).asInstanceOf(InstanceOfAssertFactories.BYTE_ARRAY).isEqualTo(bytes); assertThat(request.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_OCTET_STREAM); @@ -572,10 +559,9 @@ public void contentAsByteArray() { @Test public void contentAsXmlSource() { + CapturingRestClient capturing = new CapturingRestClient(); HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler( - "https://www.springsource.org/spring-integration"); - MockRestTemplate template = new MockRestTemplate(); - setRestTemplateForTesting(handler, template); + "https://www.springsource.org/spring-integration", capturing.client()); handler.setHttpMethod(HttpMethod.POST); setBeanFactory(handler); handler.afterPropertiesSet(); @@ -586,7 +572,7 @@ public void contentAsXmlSource() { .isThrownBy(() -> handler.handleMessage(message)) .withStackTraceContaining("intentional"); - HttpEntity request = template.lastRequestEntity.get(); + HttpEntity request = capturing.lastRequestEntity(); Object body = request.getBody(); assertThat(body).isInstanceOf(Source.class); assertThat(request.getHeaders().getContentType()).isEqualTo(MediaType.TEXT_XML); @@ -598,8 +584,6 @@ public void testWarnMessageForNonPostPutAndExtractPayload() { HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler( "https://www.springsource.org/spring-integration"); - MockRestTemplate template = new MockRestTemplate(); - setRestTemplateForTesting(handler, template); handler.setHttpMethod(HttpMethod.GET); handler.setExtractPayload(true); setBeanFactory(handler); @@ -608,8 +592,6 @@ public void testWarnMessageForNonPostPutAndExtractPayload() { // should not see a warn message since 'setExtractPayload' is not set explicitly handler = new HttpRequestExecutingMessageHandler("https://www.springsource.org/spring-integration"); - template = new MockRestTemplate(); - setRestTemplateForTesting(handler, template); handler.setHttpMethod(HttpMethod.GET); setBeanFactory(handler); handler.afterPropertiesSet(); @@ -617,8 +599,6 @@ public void testWarnMessageForNonPostPutAndExtractPayload() { // should not see a warn message since HTTP method is not GET handler = new HttpRequestExecutingMessageHandler("https://www.springsource.org/spring-integration"); - template = new MockRestTemplate(); - setRestTemplateForTesting(handler, template); handler.setHttpMethod(HttpMethod.POST); handler.setExtractPayload(true); setBeanFactory(handler); @@ -628,10 +608,10 @@ public void testWarnMessageForNonPostPutAndExtractPayload() { @Test public void contentTypeIsNotSetForGetAndHeadRequest() { // GET + CapturingRestClient capturing = new CapturingRestClient(); HttpRequestExecutingMessageHandler handler = - new HttpRequestExecutingMessageHandler("https://www.springsource.org/spring-integration"); - MockRestTemplate template = new MockRestTemplate(); - setRestTemplateForTesting(handler, template); + new HttpRequestExecutingMessageHandler("https://www.springsource.org/spring-integration", + capturing.client()); handler.setHttpMethod(HttpMethod.GET); setBeanFactory(handler); handler.afterPropertiesSet(); @@ -640,7 +620,7 @@ public void contentTypeIsNotSetForGetAndHeadRequest() { .isThrownBy(() -> handler.handleMessage(MessageBuilder.withPayload(mock(Source.class)).build())) .withStackTraceContaining("intentional"); - assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isNull(); + assertThat(capturing.lastRequestEntity().getHeaders().getContentType()).isNull(); //HEAD handler.setHttpMethod(HttpMethod.HEAD); @@ -649,7 +629,7 @@ public void contentTypeIsNotSetForGetAndHeadRequest() { .isThrownBy(() -> handler.handleMessage(MessageBuilder.withPayload(mock(Source.class)).build())) .withStackTraceContaining("intentional"); - assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isNull(); + assertThat(capturing.lastRequestEntity().getHeaders().getContentType()).isNull(); //DELETE handler.setHttpMethod(HttpMethod.DELETE); @@ -658,7 +638,7 @@ public void contentTypeIsNotSetForGetAndHeadRequest() { .isThrownBy(() -> handler.handleMessage(MessageBuilder.withPayload(mock(Source.class)).build())) .withStackTraceContaining("intentional"); - assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isEqualTo(MediaType.TEXT_XML); + assertThat(capturing.lastRequestEntity().getHeaders().getContentType()).isEqualTo(MediaType.TEXT_XML); //TRACE handler.setHttpMethod(HttpMethod.TRACE); @@ -667,7 +647,7 @@ public void contentTypeIsNotSetForGetAndHeadRequest() { .isThrownBy(() -> handler.handleMessage(MessageBuilder.withPayload(mock(Source.class)).build())) .withStackTraceContaining("intentional"); - assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isNull(); + assertThat(capturing.lastRequestEntity().getHeaders().getContentType()).isNull(); } @Test @@ -762,10 +742,10 @@ public void testOutboundChannelAdapterWithinChain() { ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext( "HttpOutboundWithinChainTests-context.xml", this.getClass()); MessageChannel channel = ctx.getBean("httpOutboundChannelAdapterWithinChain", MessageChannel.class); - MockRestTemplate2 restTemplate = ctx.getBean("restTemplate", MockRestTemplate2.class); + CapturingRequestFactory requestFactory = ctx.getBean("requestFactory", CapturingRequestFactory.class); channel.send(MessageBuilder.withPayload("test").build()); - assertThat(restTemplate.actualUrl.get()).isEqualTo("http://localhost/test1/%2f"); + assertThat(requestFactory.actualUrl.get()).hasToString("http://localhost/test1/%2f"); HttpRequestExecutingMessageHandler handler = ctx.getBean("chain$child.adapter.handler", HttpRequestExecutingMessageHandler.class); @@ -779,25 +759,28 @@ public void testHttpOutboundGatewayWithinChain() { ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext( "HttpOutboundWithinChainTests-context.xml", this.getClass()); MessageChannel channel = ctx.getBean("httpOutboundGatewayWithinChain", MessageChannel.class); - MockRestTemplate2 restTemplate = ctx.getBean("restTemplate", MockRestTemplate2.class); + CapturingRequestFactory requestFactory = ctx.getBean("requestFactory", CapturingRequestFactory.class); channel.send(MessageBuilder.withPayload("test").build()); PollableChannel output = ctx.getBean("replyChannel", PollableChannel.class); Message receive = output.receive(); assertThat(((ResponseEntity) receive.getPayload()).getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(restTemplate.actualUrl.get()) - .isEqualTo("http://localhost:51235/%2f/testApps?param=http+Outbound+Gateway+Within+Chain"); + assertThat(requestFactory.actualUrl.get()) + .hasToString("http://localhost:51235/%2f/testApps?param=http+Outbound+Gateway+Within+Chain"); ctx.close(); } @Test - @SuppressWarnings("removal") public void testUriExpression() { - MockRestTemplate restTemplate = new MockRestTemplate(); + AtomicReference actualUri = new AtomicReference<>(); HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler( - new SpelExpressionParser().parseExpression("headers['foo']"), restTemplate); + new SpelExpressionParser().parseExpression("headers['foo']")); + handler.setRequestFactory((uri, httpMethod) -> { + actualUri.set(uri); + throw new RuntimeException("intentional"); + }); setBeanFactory(handler); handler.afterPropertiesSet(); String theURL = "https://bar/baz?foo#bar"; @@ -806,7 +789,7 @@ public void testUriExpression() { assertThatException() .isThrownBy(() -> handler.handleMessage(message)); - assertThat(restTemplate.actualUrl.get()).isEqualTo(theURL); + assertThat(actualUri.get()).hasToString(theURL); } @Test @@ -923,31 +906,21 @@ public void acceptHeaderForSerializableResponseMessageExchange() throws IOExcept } @Test - @SuppressWarnings("removal") public void testNoContentTypeAndSmartConverter() { Sinks.One httpHeadersSink = Sinks.one(); - RestTemplate testRestTemplate = new RestTemplate() { - - protected T doExecute(URI url, String uriTemplate, HttpMethod method, - RequestCallback requestCallback, ResponseExtractor responseExtractor) - throws RestClientException { - - try { - ClientHttpRequest request = createRequest(url, method); - requestCallback.doWithRequest(request); - httpHeadersSink.tryEmitValue(request.getHeaders()); - } - catch (IOException e) { - throw new RestClientException("Not possible", e); - } - throw new RuntimeException("intentional"); - } - - }; HttpRequestExecutingMessageHandler handler = - new HttpRequestExecutingMessageHandler("https://www.springsource.org/spring-integration", - testRestTemplate); + new HttpRequestExecutingMessageHandler("https://www.springsource.org/spring-integration"); + handler.setRequestFactory((uri, httpMethod) -> + new MockClientHttpRequest(httpMethod, uri) { + + @Override + protected ClientHttpResponse executeInternal() { + httpHeadersSink.tryEmitValue(getHeaders()); + throw new RuntimeException("intentional"); + } + + }); setBeanFactory(handler); handler.afterPropertiesSet(); @@ -966,15 +939,6 @@ private static void setBeanFactory(HttpRequestExecutingMessageHandler handler) { handler.setBeanFactory(TEST_INTEGRATION_CONTEXT); } - private static void setRestTemplateForTesting(HttpRequestExecutingMessageHandler handler, - RestTemplate restTemplate) { - - DirectFieldAccessor accessor = new DirectFieldAccessor(handler); - accessor.setPropertyValue("localRestClientBuilder", null); - accessor.setPropertyValue("restClient", null); - accessor.setPropertyValue("restTemplate", restTemplate); - } - private static HttpHeaders setUpMocksToCaptureSentHeaders(HttpRequestExecutingMessageHandler handler) throws IOException { @@ -1010,46 +974,50 @@ public String toString() { } - private static class MockRestTemplate extends RestTemplate { - - private final AtomicReference> lastRequestEntity = new AtomicReference<>(); - - private final AtomicReference actualUrl = new AtomicReference<>(); - - protected T doExecute(URI url, String uriTemplate, HttpMethod method, - RequestCallback requestCallback, ResponseExtractor responseExtractor) - throws RestClientException { + private static final class CapturingRestClient { + + private final HttpHeaders headers = new HttpHeaders(); + + private final AtomicReference body = new AtomicReference<>(); + + private RestClient client() { + RestClient restClient = mock(RestClient.class); + RestClient.RequestBodyUriSpec spec = mock(RestClient.RequestBodyUriSpec.class); + + when(restClient.method(any())).thenReturn(spec); + when(spec.uri(any(URI.class))).thenReturn(spec); + when(spec.uri(anyString(), anyMap())).thenReturn(spec); + when(spec.headers(any())).thenAnswer(invocation -> { + this.headers.clear(); + this.body.set(null); + Consumer headerConsumer = invocation.getArgument(0); + headerConsumer.accept(this.headers); + return spec; + }); + when(spec.body(any(Object.class))).thenAnswer(invocation -> { + this.body.set(invocation.getArgument(0)); + return spec; + }); + when(spec.retrieve()).thenThrow(new RuntimeException("intentional")); + return restClient; + } - this.actualUrl.set(url.toString()); - this.lastRequestEntity.set(TestUtils.getPropertyValue(requestCallback, "requestEntity")); - throw new RuntimeException("intentional"); + private HttpEntity lastRequestEntity() { + return new HttpEntity<>(this.body.get(), this.headers); } } - @SuppressWarnings("unused") - private static class MockRestTemplate2 extends RestTemplate { - - private final AtomicReference actualUrl = new AtomicReference<>(); + public static final class CapturingRequestFactory implements ClientHttpRequestFactory { - MockRestTemplate2() { - DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(); - uriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE); - setUriTemplateHandler(uriBuilderFactory); - } + public final AtomicReference actualUrl = new AtomicReference<>(); - protected T doExecute(URI url, String uriTemplate, HttpMethod method, - RequestCallback requestCallback, ResponseExtractor responseExtractor) - throws RestClientException { - - this.actualUrl.set(url.toString()); - try { - return responseExtractor.extractData(new MockClientHttpResponse(new byte[0], HttpStatus.OK)); - } - catch (IOException ex) { - throw new ResourceAccessException("I/O error on " + method.name() + - " request for \"" + url + "\": " + ex.getMessage(), ex); - } + @Override + public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) { + this.actualUrl.set(uri); + MockClientHttpRequest request = new MockClientHttpRequest(httpMethod, uri); + request.setResponse(new MockClientHttpResponse(new byte[0], HttpStatus.OK)); + return request; } }