From 473bbb4879ad9148bae714c663045b47656e75fb Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Thu, 30 Jan 2020 11:24:42 -0500 Subject: [PATCH 1/5] GH-3154: Support `UriBuilderFactory.EncodingMode` Fixes https://github.com/spring-projects/spring-integration/issues/3154 Spring Framework now provides a `DefaultUriBuilderFactory.EncodingMode` for encoding URIs in the `RestTemplate` before and after uri template enrichment with uri variables. Therefore `encodeUri` and manual uri variables substitution is not necessary in Spring Integration HTTP components * Deprecate `AbstractHttpRequestExecutingMessageHandler.encodeUri` in favor of `DefaultUriBuilderFactory.EncodingMode` and respective configuration on the `RestTemplate` in HTTP module and `WebClient` in WebFlux module --- .../HttpOutboundChannelAdapterParser.java | 5 +- .../config/HttpOutboundGatewayParser.java | 5 +- .../http/dsl/BaseHttpMessageHandlerSpec.java | 16 +++- ...actHttpRequestExecutingMessageHandler.java | 59 +++++++------ .../HttpRequestExecutingMessageHandler.java | 58 +++++++++--- .../http/config/spring-integration-http.xsd | 24 ++++- .../http/HttpProxyScenarioTests.java | 28 +++--- .../HttpOutboundWithinChainTests-context.xml | 10 +-- ...tpRequestExecutingMessageHandlerTests.java | 88 +++++++++++-------- 9 files changed, 188 insertions(+), 105 deletions(-) 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 79e8e64f2d8..a97aee5ae4e 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,7 +46,6 @@ protected AbstractBeanDefinition parseConsumer(Element element, ParserContext pa builder.addPropertyValue("expectReply", false); HttpAdapterParsingUtils.configureUrlConstructorArg(element, parserContext, builder); - IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri"); HttpAdapterParsingUtils.setHttpMethodOrExpression(element, parserContext, builder); String headerMapper = element.getAttribute("header-mapper"); @@ -90,6 +89,8 @@ protected BeanDefinitionBuilder getBuilder(Element element, ParserContext parser for (String referenceAttributeName : HttpAdapterParsingUtils.SYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES) { IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName); } + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encoding-mode"); } 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 2350b66605c..ffefef279ed 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,7 +47,6 @@ protected BeanDefinitionBuilder parseHandler(Element element, ParserContext pars BeanDefinitionBuilder builder = getBuilder(element, parserContext); HttpAdapterParsingUtils.configureUrlConstructorArg(element, parserContext, builder); - IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri"); HttpAdapterParsingUtils.setHttpMethodOrExpression(element, parserContext, builder); String headerMapper = element.getAttribute("header-mapper"); @@ -103,6 +102,8 @@ protected BeanDefinitionBuilder getBuilder(Element element, ParserContext parser for (String referenceAttributeName : HttpAdapterParsingUtils.SYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES) { IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName); } + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encoding-mode"); } return builder; } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/dsl/BaseHttpMessageHandlerSpec.java b/spring-integration-http/src/main/java/org/springframework/integration/http/dsl/BaseHttpMessageHandlerSpec.java index a45a5e2b8e1..aa4c9f9d110 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/dsl/BaseHttpMessageHandlerSpec.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/dsl/BaseHttpMessageHandlerSpec.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2019 the original author or authors. + * Copyright 2017-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ import org.springframework.integration.mapping.HeaderMapper; import org.springframework.messaging.Message; import org.springframework.util.Assert; +import org.springframework.web.util.DefaultUriBuilderFactory; /** * The base {@link MessageHandlerSpec} for {@link AbstractHttpRequestExecutingMessageHandler}s. @@ -71,12 +72,25 @@ protected S expectReply(boolean expectReply) { * expanding and before send request via underlying implementation. The default value is true. * @param encodeUri true if the URI should be encoded. * @return the spec + * @deprecated since 5.3 in favor of {@link #encodingMode} */ + @Deprecated public S encodeUri(boolean encodeUri) { this.target.setEncodeUri(encodeUri); return _this(); } + /** + * Specify a {@link DefaultUriBuilderFactory.EncodingMode} for uri construction. + * @param encodingMode to use for uri construction. + * @return the spec + * @since 5.3 + */ + public S encodingMode(DefaultUriBuilderFactory.EncodingMode encodingMode) { + this.target.setEncodingMode(encodingMode); + return _this(); + } + /** * Specify the SpEL {@link Expression} to determine {@link HttpMethod} at runtime. * @param httpMethodExpression The method expression. diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java index 2bc81894fd2..e05b00c3da5 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2019 the original author or authors. + * Copyright 2017-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ package org.springframework.integration.http.outbound; import java.net.URI; -import java.net.URISyntaxException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -27,7 +26,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.function.Supplier; import javax.xml.transform.Source; @@ -56,14 +54,13 @@ import org.springframework.integration.support.MessageBuilderFactory; import org.springframework.lang.Nullable; import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHandlingException; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; -import org.springframework.web.util.UriComponents; +import org.springframework.web.util.DefaultUriBuilderFactory; import org.springframework.web.util.UriComponentsBuilder; /** @@ -85,6 +82,8 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac private static final List NO_BODY_HTTP_METHODS = Arrays.asList(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.TRACE); + DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory(); + private final Map uriVariableExpressions = new HashMap<>(); private final Expression uriExpression; @@ -95,8 +94,6 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac private boolean trustedSpel; - private boolean encodeUri = true; - private Expression httpMethodExpression = new ValueExpression<>(HttpMethod.POST); private boolean expectReply = true; @@ -118,6 +115,7 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac public AbstractHttpRequestExecutingMessageHandler(Expression uriExpression) { Assert.notNull(uriExpression, "URI Expression is required"); this.uriExpression = uriExpression; + this.uriFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.TEMPLATE_AND_VALUES); } /** @@ -127,9 +125,25 @@ public AbstractHttpRequestExecutingMessageHandler(Expression uriExpression) { * true. * @param encodeUri true if the URI should be encoded. * @see UriComponentsBuilder + * @deprecated since 5.3 in favor of {@link #setEncodingMode} */ + @Deprecated public void setEncodeUri(boolean encodeUri) { - this.encodeUri = encodeUri; + setEncodingMode( + encodeUri + ? DefaultUriBuilderFactory.EncodingMode.TEMPLATE_AND_VALUES + : DefaultUriBuilderFactory.EncodingMode.NONE); + } + + /** + * Set the encoding mode to use. + * By default this is set to {@link DefaultUriBuilderFactory.EncodingMode#TEMPLATE_AND_VALUES}. + * @param encodingMode the mode to use for uri encoding + * @since 5.3 + */ + public void setEncodingMode(DefaultUriBuilderFactory.EncodingMode encodingMode) { + Assert.notNull(encodingMode, "'encodingMode' must not be null"); + this.uriFactory.setEncodingMode(encodingMode); } /** @@ -291,32 +305,23 @@ protected Object handleRequestMessage(Message requestMessage) { Object expectedResponseType = determineExpectedResponseType(requestMessage); HttpEntity httpRequest = generateHttpRequest(requestMessage, httpMethod); - return exchange(() -> generateUri(requestMessage), httpMethod, httpRequest, expectedResponseType, - requestMessage); - } - - protected abstract Object exchange(Supplier uriSupplier, HttpMethod httpMethod, HttpEntity httpRequest, - Object expectedResponseType, Message requestMessage); - - private URI generateUri(Message requestMessage) { Object uri = this.uriExpression.getValue(this.evaluationContext, requestMessage); Assert.state(uri instanceof String || uri instanceof URI, () -> "'uriExpression' evaluation must result in a 'String' or 'URI' instance, not: " + (uri == null ? "null" : uri.getClass())); - Map uriVariables = determineUriVariables(requestMessage); - UriComponentsBuilder uriComponentsBuilder = - uri instanceof String - ? UriComponentsBuilder.fromUriString((String) uri) - : UriComponentsBuilder.fromUri((URI) uri); - UriComponents uriComponents = uriComponentsBuilder.buildAndExpand(uriVariables); - try { - return this.encodeUri ? uriComponents.encode().toUri() : new URI(uriComponents.toUriString()); - } - catch (URISyntaxException e) { - throw new MessageHandlingException(requestMessage, "Invalid URI [" + uri + "] in the [" + this + ']', e); + + Map uriVariables = null; + + if (uri instanceof String) { + uriVariables = determineUriVariables(requestMessage); } + + return exchange(uri, httpMethod, httpRequest, expectedResponseType, requestMessage, uriVariables); } + protected abstract Object exchange(Object uri, HttpMethod httpMethod, HttpEntity httpRequest, + Object expectedResponseType, Message requestMessage, Map uriVariables); + protected Object getReply(ResponseEntity httpResponse) { if (this.expectReply) { HttpHeaders httpHeaders = httpResponse.getHeaders(); 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 1704fba7ee9..cc89868a060 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ import java.net.URI; import java.util.List; -import java.util.function.Supplier; +import java.util.Map; import org.springframework.core.ParameterizedTypeReference; import org.springframework.expression.Expression; @@ -29,12 +29,14 @@ import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.integration.expression.ValueExpression; +import org.springframework.lang.Nullable; 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.RestClientException; import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.DefaultUriBuilderFactory; /** * A {@link org.springframework.messaging.MessageHandler} @@ -64,6 +66,8 @@ public class HttpRequestExecutingMessageHandler extends AbstractHttpRequestExecu private final RestTemplate restTemplate; + private final boolean restTemplateExplicitlySet; + /** * Create a handler that will send requests to the provided URI. * @param uri The URI. @@ -109,14 +113,21 @@ public HttpRequestExecutingMessageHandler(String uri, RestTemplate restTemplate) * {@link org.springframework.beans.factory.BeanFactory}. * @param restTemplate The rest template. */ - public HttpRequestExecutingMessageHandler(Expression uriExpression, RestTemplate restTemplate) { + public HttpRequestExecutingMessageHandler(Expression uriExpression, @Nullable RestTemplate restTemplate) { super(uriExpression); - this.restTemplate = (restTemplate == null ? new RestTemplate() : restTemplate); + this.restTemplateExplicitlySet = restTemplate != null; + this.restTemplate = (this.restTemplateExplicitlySet ? restTemplate : new RestTemplate()); } @Override public String getComponentType() { - return (this.isExpectReply() ? "http:outbound-gateway" : "http:outbound-channel-adapter"); + return (isExpectReply() ? "http:outbound-gateway" : "http:outbound-channel-adapter"); + } + + private void assertLocalRestTemplate(String option) { + Assert.isTrue(!this.restTemplateExplicitlySet, + () -> "The option '" + option + "' must be provided on the externally configured RestTemplate: " + + this.restTemplate); } /** @@ -125,6 +136,7 @@ public String getComponentType() { * @see RestTemplate#setErrorHandler(ResponseErrorHandler) */ public void setErrorHandler(ResponseErrorHandler errorHandler) { + assertLocalRestTemplate("errorHandler"); this.restTemplate.setErrorHandler(errorHandler); } @@ -135,6 +147,7 @@ public void setErrorHandler(ResponseErrorHandler errorHandler) { * @see RestTemplate#setMessageConverters(java.util.List) */ public void setMessageConverters(List> messageConverters) { + assertLocalRestTemplate("messageConverters"); this.restTemplate.setMessageConverters(messageConverters); } @@ -144,24 +157,43 @@ public void setMessageConverters(List> messageConverters * @see RestTemplate#setRequestFactory(ClientHttpRequestFactory) */ public void setRequestFactory(ClientHttpRequestFactory requestFactory) { + assertLocalRestTemplate("requestFactory"); this.restTemplate.setRequestFactory(requestFactory); } @Override - protected Object exchange(Supplier uriSupplier, HttpMethod httpMethod, HttpEntity httpRequest, - Object expectedResponseType, Message requestMessage) { + public void setEncodingMode(DefaultUriBuilderFactory.EncodingMode encodingMode) { + assertLocalRestTemplate("encodingMode on UriTemplateHandler"); + super.setEncodingMode(encodingMode); + } + + @Override + protected Object exchange(Object uri, HttpMethod httpMethod, HttpEntity httpRequest, + Object expectedResponseType, Message requestMessage, Map uriVariables) { - URI uri = uriSupplier.get(); ResponseEntity httpResponse; try { - if (expectedResponseType instanceof ParameterizedTypeReference) { - httpResponse = this.restTemplate.exchange(uri, httpMethod, httpRequest, - (ParameterizedTypeReference) expectedResponseType); + if (uri instanceof URI) { + if (expectedResponseType instanceof ParameterizedTypeReference) { + httpResponse = this.restTemplate.exchange((URI) uri, httpMethod, httpRequest, + (ParameterizedTypeReference) expectedResponseType); + } + else { + httpResponse = this.restTemplate.exchange((URI) uri, httpMethod, httpRequest, + (Class) expectedResponseType); + } } else { - httpResponse = this.restTemplate.exchange(uri, httpMethod, httpRequest, - (Class) expectedResponseType); + if (expectedResponseType instanceof ParameterizedTypeReference) { + httpResponse = this.restTemplate.exchange((String) uri, httpMethod, httpRequest, + (ParameterizedTypeReference) expectedResponseType, uriVariables); + } + else { + httpResponse = this.restTemplate.exchange((String) uri, httpMethod, httpRequest, + (Class) expectedResponseType, uriVariables); + } } + return getReply(httpResponse); } catch (RestClientException e) { 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 67f8e525889..ba2cdf218a7 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 @@ -844,12 +844,23 @@ - When set to "false", the real URI won't be encoded before the request is sent. This may be useful - in some scenarios as it allows user control over the encoding, if needed, + [DEPRECATED] When set to "false", the real URI won't be encoded before the request is sent. + This may be useful in some scenarios as it allows user control over the encoding, if needed, for example by using the "url-expression". Default is "true". + Deprecated since 5.3 in favor of 'encoding-mode'. + + + + Set the encoding mode during URI building. + + + + + + @@ -943,4 +954,13 @@ + + + + + + + + + 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 f109da491b2..8126251035b 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 @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 the original author or authors. + * Copyright 2013-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.isNull; -import java.net.URI; import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -27,8 +26,7 @@ import java.util.Calendar; import java.util.Locale; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.beans.DirectFieldAccessor; @@ -47,7 +45,7 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; @@ -64,7 +62,7 @@ * * @since 3.0 */ -@RunWith(SpringRunner.class) +@SpringJUnitConfig @DirtiesContext public class HttpProxyScenarioTests { @@ -122,8 +120,8 @@ public void testHttpProxyScenario() throws Exception { final String contentDispositionValue = "attachment; filename=\"test.txt\""; Mockito.doAnswer(invocation -> { - URI uri = invocation.getArgument(0); - assertThat(uri).isEqualTo(new URI("http://testServer/test?foo=bar&FOO=BAR")); + 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); @@ -134,8 +132,9 @@ public void testHttpProxyScenario() throws Exception { responseHeaders.set("Connection", "close"); responseHeaders.set("Content-Disposition", contentDispositionValue); return new ResponseEntity<>(responseHeaders, HttpStatus.OK); - }).when(template).exchange(Mockito.any(URI.class), Mockito.any(HttpMethod.class), - Mockito.any(HttpEntity.class), (Class) isNull()); + }).when(template) + .exchange(Mockito.anyString(), Mockito.any(HttpMethod.class), + Mockito.any(HttpEntity.class), (Class) isNull(), Mockito.anyMap()); PropertyAccessor dfa = new DirectFieldAccessor(this.handler); dfa.setPropertyValue("restTemplate", template); @@ -175,8 +174,8 @@ public void testHttpMultipartProxyScenario() throws Exception { RestTemplate template = Mockito.spy(new RestTemplate()); Mockito.doAnswer(invocation -> { - URI uri = invocation.getArgument(0); - assertThat(uri).isEqualTo(new URI("http://testServer/testmp")); + 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"); @@ -191,8 +190,9 @@ public void testHttpMultipartProxyScenario() throws Exception { responseHeaders.set("Connection", "close"); responseHeaders.set("Content-Type", "text/plain"); return new ResponseEntity<>(responseHeaders, HttpStatus.OK); - }).when(template).exchange(Mockito.any(URI.class), Mockito.any(HttpMethod.class), - Mockito.any(HttpEntity.class), (Class) isNull()); + }).when(template) + .exchange(Mockito.anyString(), Mockito.any(HttpMethod.class), + Mockito.any(HttpEntity.class), (Class) isNull(), Mockito.anyMap()); PropertyAccessor dfa = new DirectFieldAccessor(this.handlermp); dfa.setPropertyValue("restTemplate", template); 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 3d6a12113ce..01911c1df41 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,20 +9,16 @@ 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 9877aac52ab..526945227cf 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,6 @@ import java.io.IOException; import java.io.Serializable; import java.net.URI; -import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; @@ -36,8 +35,7 @@ import javax.xml.transform.Source; -import org.junit.Test; -import org.mockito.Mockito; +import org.junit.jupiter.api.Test; import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.BeanFactory; @@ -59,13 +57,19 @@ import org.springframework.integration.http.converter.SerializingHttpMessageConverter; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.TestUtils; +import org.springframework.lang.Nullable; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.PollableChannel; import org.springframework.messaging.support.GenericMessage; +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.RestClientException; import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.DefaultUriBuilderFactory; /** * @author Mark Fisher @@ -695,38 +699,38 @@ public void contentTypeIsNotSetForGetAndHeadRequest() { } } - @Test // INT-2275 - public void testOutboundChannelAdapterWithinChain() throws URISyntaxException { + @Test + public void testOutboundChannelAdapterWithinChain() { ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext( "HttpOutboundWithinChainTests-context.xml", this.getClass()); MessageChannel channel = ctx.getBean("httpOutboundChannelAdapterWithinChain", MessageChannel.class); - RestTemplate restTemplate = ctx.getBean("restTemplate", RestTemplate.class); + MockRestTemplate2 restTemplate = ctx.getBean("restTemplate", MockRestTemplate2.class); channel.send(MessageBuilder.withPayload("test").build()); - Mockito.verify(restTemplate).exchange(Mockito.eq(new URI("http://localhost/test1/%2f")), - Mockito.eq(HttpMethod.POST), Mockito.any(HttpEntity.class), Mockito.>eq(null)); + + assertThat(restTemplate.actualUrl.get()).isEqualTo("http://localhost/test1/%2f"); + HttpRequestExecutingMessageHandler handler = ctx.getBean("chain$child.adapter.handler", HttpRequestExecutingMessageHandler.class); + assertThat(TestUtils.getPropertyValue(handler, "trustedSpel")).isEqualTo(Boolean.TRUE); ctx.close(); } - @Test // INT-1029 - public void testHttpOutboundGatewayWithinChain() throws URISyntaxException { + @Test + public void testHttpOutboundGatewayWithinChain() { ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext( "HttpOutboundWithinChainTests-context.xml", this.getClass()); MessageChannel channel = ctx.getBean("httpOutboundGatewayWithinChain", MessageChannel.class); - RestTemplate restTemplate = ctx.getBean("restTemplate", RestTemplate.class); + MockRestTemplate2 restTemplate = ctx.getBean("restTemplate", MockRestTemplate2.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); - Mockito.verify(restTemplate).exchange( - Mockito.eq(new URI("http://localhost:51235/%2f/testApps?param=http+Outbound+Gateway+Within+Chain")), - Mockito.eq(HttpMethod.POST), Mockito.any(HttpEntity.class), - Mockito.eq(new ParameterizedTypeReference>() { - })); + assertThat(restTemplate.actualUrl.get()) + .isEqualTo("http://localhost:51235/%2f/testApps?param=http+Outbound+Gateway+Within+Chain"); + ctx.close(); } @@ -757,9 +761,6 @@ public void testUriEncoded() { restTemplate ); - // This flag is set by default to true, but for sake of clarity for the reader we explicitly set it here again - handler.setEncodeUri(true); - handler.setUriVariableExpressions(Collections.singletonMap("query", parser.parseExpression("payload"))); setBeanFactory(handler); handler.afterPropertiesSet(); @@ -776,13 +777,15 @@ public void testUriEncoded() { public void testUriEncodedDisabled() { SpelExpressionParser parser = new SpelExpressionParser(); MockRestTemplate restTemplate = new MockRestTemplate(); + DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(); + uriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE); + restTemplate.setUriTemplateHandler(uriBuilderFactory); HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler( "https://example.com?query={query}", restTemplate ); - handler.setEncodeUri(false); handler.setUriVariableExpressions(Collections.singletonMap("query", parser.parseExpression("payload"))); setBeanFactory(handler); handler.afterPropertiesSet(); @@ -798,9 +801,12 @@ public void testUriEncodedDisabled() { @Test public void testInt2455UriNotEncoded() { MockRestTemplate restTemplate = new MockRestTemplate(); + DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(); + uriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE); + restTemplate.setUriTemplateHandler(uriBuilderFactory); + HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler( new SpelExpressionParser().parseExpression("'https://my.RabbitMQ.com/api/' + payload"), restTemplate); - handler.setEncodeUri(false); setBeanFactory(handler); handler.afterPropertiesSet(); Message message = MessageBuilder.withPayload("queues/%2f/si.test.queue?foo#bar").build(); @@ -933,12 +939,11 @@ private static class MockRestTemplate extends RestTemplate { private final AtomicReference actualUrl = new AtomicReference<>(); - @Override - public ResponseEntity exchange(URI uri, HttpMethod method, HttpEntity requestEntity, - Class responseType) throws RestClientException { - - this.actualUrl.set(uri.toString()); - this.lastRequestEntity.set(requestEntity); + @Nullable + protected T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, + @Nullable ResponseExtractor responseExtractor) throws RestClientException { + this.actualUrl.set(url.toString()); + this.lastRequestEntity.set(TestUtils.getPropertyValue(requestCallback, "requestEntity", HttpEntity.class)); throw new RuntimeException("intentional"); } @@ -947,16 +952,25 @@ public ResponseEntity exchange(URI uri, HttpMethod method, HttpEntity @SuppressWarnings("unused") private static class MockRestTemplate2 extends RestTemplate { - @Override - public ResponseEntity exchange(URI uri, HttpMethod method, HttpEntity requestEntity, - Class responseType) throws RestClientException { - return new ResponseEntity(HttpStatus.OK); - } + private final AtomicReference actualUrl = new AtomicReference<>(); - @Override - public ResponseEntity exchange(URI url, HttpMethod method, HttpEntity requestEntity, - ParameterizedTypeReference responseType) throws RestClientException { - return new ResponseEntity(HttpStatus.OK); + MockRestTemplate2() { + DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(); + uriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE); + setUriTemplateHandler(uriBuilderFactory); + } + + @Nullable + protected T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, + @Nullable 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); + } } } From 4002501289ec0bb7877b85e6b40f92d5011a618f Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Thu, 30 Jan 2020 12:36:01 -0500 Subject: [PATCH 2/5] * Really populate `uriFactory` into an internal `RestTemplate` * Ensure in tests that `encoding-mode` is populated properly into an internal `RestTemplate` * Clean up affected HTTP tests for AssertJ and JUnit 5 --- ...actHttpRequestExecutingMessageHandler.java | 5 +- .../HttpRequestExecutingMessageHandler.java | 3 + ...boundChannelAdapterParserTests-context.xml | 1 + ...HttpOutboundChannelAdapterParserTests.java | 66 ++-- ...tpRequestExecutingMessageHandlerTests.java | 292 +++++++----------- 5 files changed, 158 insertions(+), 209 deletions(-) diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java index e05b00c3da5..229e3830398 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java @@ -82,7 +82,7 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac private static final List NO_BODY_HTTP_METHODS = Arrays.asList(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.TRACE); - DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory(); + protected final DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory(); // NOSONAR - final private final Map uriVariableExpressions = new HashMap<>(); @@ -115,7 +115,6 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac public AbstractHttpRequestExecutingMessageHandler(Expression uriExpression) { Assert.notNull(uriExpression, "URI Expression is required"); this.uriExpression = uriExpression; - this.uriFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.TEMPLATE_AND_VALUES); } /** @@ -138,6 +137,8 @@ public void setEncodeUri(boolean encodeUri) { /** * Set the encoding mode to use. * By default this is set to {@link DefaultUriBuilderFactory.EncodingMode#TEMPLATE_AND_VALUES}. + * For more complicated scenarios consider to configure an {@link org.springframework.web.util.UriTemplateHandler} + * on an externally provided {@link org.springframework.web.client.RestTemplate}. * @param encodingMode the mode to use for uri encoding * @since 5.3 */ 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 cc89868a060..d6f7fa40a0c 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 @@ -117,6 +117,9 @@ public HttpRequestExecutingMessageHandler(Expression uriExpression, @Nullable Re super(uriExpression); this.restTemplateExplicitlySet = restTemplate != null; this.restTemplate = (this.restTemplateExplicitlySet ? restTemplate : new RestTemplate()); + if (!this.restTemplateExplicitlySet) { + this.restTemplate.setUriTemplateHandler(this.uriFactory); + } } @Override 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 ce05d539525..7ccd067db6e 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 @@ -31,6 +31,7 @@ request-factory="testRequestFactory" error-handler="testErrorHandler" order="77" + encoding-mode="VALUES_ONLY" auto-startup="false"> 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 f36b615e39c..d90700b7764 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 @@ -17,13 +17,12 @@ package org.springframework.integration.http.config; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import java.io.IOException; import java.nio.charset.Charset; import java.util.Map; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; @@ -46,11 +45,11 @@ import org.springframework.messaging.MessageHandler; import org.springframework.messaging.support.GenericMessage; import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.springframework.util.ObjectUtils; import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.DefaultUriBuilderFactory; /** * @author Mark Fisher @@ -60,8 +59,7 @@ * @author Biju Kunjummen * @author Shiliang Li */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration +@SpringJUnitConfig @DirtiesContext public class HttpOutboundChannelAdapterParserTests { @@ -112,13 +110,15 @@ public void minimalConfig() { RestTemplate restTemplate = TestUtils.getPropertyValue(this.minimalConfig, "handler.restTemplate", RestTemplate.class); assertThat(restTemplate).isNotSameAs(customRestTemplate); - HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler"); + HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor + .getPropertyValue("handler"); DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler); assertThat(handlerAccessor.getPropertyValue("expectReply")).isEqualTo(false); assertThat(endpointAccessor.getPropertyValue("inputChannel")) .isEqualTo(this.applicationContext.getBean("requests")); assertThat(handlerAccessor.getPropertyValue("outputChannel")).isNull(); - DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate")); + DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate" + )); ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory) templateAccessor.getPropertyValue("requestFactory"); assertThat(requestFactory instanceof SimpleClientHttpRequestFactory).isTrue(); @@ -134,7 +134,8 @@ public void minimalConfig() { @SuppressWarnings("unchecked") public void fullConfig() { DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.fullConfig); - HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler"); + HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor + .getPropertyValue("handler"); DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler); assertThat(handlerAccessor.getPropertyValue("expectReply")).isEqualTo(false); assertThat(endpointAccessor.getPropertyValue("inputChannel")) @@ -142,7 +143,8 @@ public void fullConfig() { assertThat(handlerAccessor.getPropertyValue("outputChannel")).isNull(); assertThat(handlerAccessor.getPropertyValue("order")).isEqualTo(77); assertThat(endpointAccessor.getPropertyValue("autoStartup")).isEqualTo(Boolean.FALSE); - DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate")); + DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate" + )); ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory) templateAccessor.getPropertyValue("requestFactory"); assertThat(TestUtils.getPropertyValue(handler, "expectedResponseTypeExpression", Expression.class).getValue()) @@ -171,6 +173,10 @@ public void fullConfig() { assertThat(mappedResponseHeaders.length).isEqualTo(0); assertThat(ObjectUtils.containsElement(mappedRequestHeaders, "requestHeader1")).isTrue(); assertThat(ObjectUtils.containsElement(mappedRequestHeaders, "requestHeader2")).isTrue(); + assertThat( + TestUtils.getPropertyValue(handler, + "restTemplate.uriTemplateHandler.encodingMode", DefaultUriBuilderFactory.EncodingMode.class)) + .isEqualTo(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY); } @Test @@ -180,10 +186,12 @@ public void restTemplateConfig() { assertThat(restTemplate).isEqualTo(customRestTemplate); } - @Test(expected = BeanDefinitionParsingException.class) + @Test public void failWithRestTemplateAndRestAttributes() { - new ClassPathXmlApplicationContext("HttpOutboundChannelAdapterParserTests-fail-context.xml", this.getClass()) - .close(); + assertThatExceptionOfType(BeanDefinitionParsingException.class) + .isThrownBy(() -> + new ClassPathXmlApplicationContext("HttpOutboundChannelAdapterParserTests-fail-context.xml", + getClass())); } @Test @@ -192,13 +200,15 @@ public void withUrlAndTemplate() { RestTemplate restTemplate = TestUtils.getPropertyValue(this.withUrlAndTemplate, "handler.restTemplate", RestTemplate.class); assertThat(restTemplate).isSameAs(customRestTemplate); - HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler"); + HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor + .getPropertyValue("handler"); DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler); assertThat(handlerAccessor.getPropertyValue("expectReply")).isEqualTo(false); assertThat(endpointAccessor.getPropertyValue("inputChannel")) .isEqualTo(this.applicationContext.getBean("requests")); assertThat(handlerAccessor.getPropertyValue("outputChannel")).isNull(); - DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate")); + DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate" + )); ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory) templateAccessor.getPropertyValue("requestFactory"); assertThat(requestFactory instanceof SimpleClientHttpRequestFactory).isTrue(); @@ -224,13 +234,15 @@ public void withUrlExpression() { RestTemplate restTemplate = TestUtils.getPropertyValue(this.withUrlExpression, "handler.restTemplate", RestTemplate.class); assertThat(restTemplate).isNotSameAs(customRestTemplate); - HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler"); + HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor + .getPropertyValue("handler"); DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler); assertThat(handlerAccessor.getPropertyValue("expectReply")).isEqualTo(false); assertThat(endpointAccessor.getPropertyValue("inputChannel")) .isEqualTo(this.applicationContext.getBean("requests")); assertThat(handlerAccessor.getPropertyValue("outputChannel")).isNull(); - DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate")); + DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate" + )); ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory) templateAccessor.getPropertyValue("requestFactory"); assertThat(requestFactory instanceof SimpleClientHttpRequestFactory).isTrue(); @@ -257,13 +269,15 @@ public void withUrlExpressionAndTemplate() { TestUtils.getPropertyValue(this.withUrlExpressionAndTemplate, "handler.restTemplate", RestTemplate.class); assertThat(restTemplate).isSameAs(customRestTemplate); - HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler"); + HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor + .getPropertyValue("handler"); DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler); assertThat(handlerAccessor.getPropertyValue("expectReply")).isEqualTo(false); assertThat(endpointAccessor.getPropertyValue("inputChannel")) .isEqualTo(this.applicationContext.getBean("requests")); assertThat(handlerAccessor.getPropertyValue("outputChannel")).isNull(); - DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate")); + DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate" + )); ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory) templateAccessor.getPropertyValue("requestFactory"); assertThat(requestFactory instanceof SimpleClientHttpRequestFactory).isTrue(); @@ -281,21 +295,23 @@ public void withPoller() { assertThat(this.withPoller1).isInstanceOf(PollingConsumer.class); } - @Test(expected = BeanDefinitionParsingException.class) + @Test public void failWithUrlAndExpression() { - new ClassPathXmlApplicationContext("HttpOutboundChannelAdapterParserTests-url-fail-context.xml", - this.getClass()).close(); + assertThatExceptionOfType(BeanDefinitionParsingException.class) + .isThrownBy(() -> + new ClassPathXmlApplicationContext("HttpOutboundChannelAdapterParserTests-url-fail-context.xml", + getClass())); } public static class StubErrorHandler implements ResponseErrorHandler { @Override - public boolean hasError(ClientHttpResponse response) throws IOException { + public boolean hasError(ClientHttpResponse response) { return false; } @Override - public void handleError(ClientHttpResponse response) throws IOException { + public void handleError(ClientHttpResponse response) { } } 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 526945227cf..aa351624a12 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 @@ -17,7 +17,7 @@ package org.springframework.integration.http.outbound; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -103,14 +103,11 @@ public void simpleStringKeyStringValueFormData() { Message message = MessageBuilder.withPayload(form).build(); QueueChannel replyChannel = new QueueChannel(); handler.setOutputChannel(replyChannel); - Exception exception = null; - try { - handler.handleMessage(message); - } - catch (Exception e) { - exception = e; - } - assertThat(exception.getCause().getMessage()).isEqualTo("intentional"); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(message)) + .withStackTraceContaining("intentional"); + HttpEntity request = template.lastRequestEntity.get(); Object body = request.getBody(); assertThat(request.getHeaders().getContentType()).isNotNull(); @@ -138,14 +135,11 @@ public void simpleStringKeyObjectValueFormData() { Message message = MessageBuilder.withPayload(form).build(); QueueChannel replyChannel = new QueueChannel(); handler.setOutputChannel(replyChannel); - Exception exception = null; - try { - handler.handleMessage(message); - } - catch (Exception e) { - exception = e; - } - assertThat(exception.getCause().getMessage()).isEqualTo("intentional"); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(message)) + .withStackTraceContaining("intentional"); + HttpEntity request = template.lastRequestEntity.get(); Object body = request.getBody(); assertThat(body instanceof MultiValueMap).isTrue(); @@ -172,14 +166,11 @@ public void simpleObjectKeyObjectValueFormData() { Message message = MessageBuilder.withPayload(form).build(); QueueChannel replyChannel = new QueueChannel(); handler.setOutputChannel(replyChannel); - Exception exception = null; - try { - handler.handleMessage(message); - } - catch (Exception e) { - exception = e; - } - assertThat(exception.getCause().getMessage()).isEqualTo("intentional"); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(message)) + .withStackTraceContaining("intentional"); + HttpEntity request = template.lastRequestEntity.get(); Object body = request.getBody(); assertThat(body instanceof Map).isTrue(); @@ -206,14 +197,11 @@ public void stringKeyStringArrayValueFormData() { form.put("c", new String[] { "5" }); form.put("d", "6"); Message message = MessageBuilder.withPayload(form).build(); - Exception exception = null; - try { - handler.handleMessage(message); - } - catch (Exception e) { - exception = e; - } - assertThat(exception.getCause().getMessage()).isEqualTo("intentional"); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(message)) + .withStackTraceContaining("intentional"); + HttpEntity request = template.lastRequestEntity.get(); Object body = request.getBody(); assertThat(body instanceof MultiValueMap).isTrue(); @@ -254,14 +242,11 @@ public void stringKeyPrimitiveArrayValueMixedFormData() { form.put("c", new String[] { "5" }); form.put("d", "6"); Message message = MessageBuilder.withPayload(form).build(); - Exception exception = null; - try { - handler.handleMessage(message); - } - catch (Exception e) { - exception = e; - } - assertThat(exception.getCause().getMessage()).isEqualTo("intentional"); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(message)) + .withStackTraceContaining("intentional"); + HttpEntity request = template.lastRequestEntity.get(); Object body = request.getBody(); assertThat(body instanceof MultiValueMap).isTrue(); @@ -303,14 +288,11 @@ public void stringKeyNullArrayValueMixedFormData() { form.put("a", new Object[] { null, 4, null }); form.put("b", "4"); Message message = MessageBuilder.withPayload(form).build(); - Exception exception = null; - try { - handler.handleMessage(message); - } - catch (Exception e) { - exception = e; - } - assertThat(exception.getCause().getMessage()).isEqualTo("intentional"); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(message)) + .withStackTraceContaining("intentional"); + HttpEntity request = template.lastRequestEntity.get(); Object body = request.getBody(); assertThat(body instanceof MultiValueMap).isTrue(); @@ -351,14 +333,11 @@ public void stringKeyNullCollectionValueMixedFormDataString() { form.put("a", list); form.put("b", "4"); Message message = MessageBuilder.withPayload(form).build(); - Exception exception = null; - try { - handler.handleMessage(message); - } - catch (Exception e) { - exception = e; - } - assertThat(exception.getCause().getMessage()).isEqualTo("intentional"); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(message)) + .withStackTraceContaining("intentional"); + HttpEntity request = template.lastRequestEntity.get(); Object body = request.getBody(); assertThat(body instanceof MultiValueMap).isTrue(); @@ -398,14 +377,11 @@ public void stringKeyNullCollectionValueMixedFormDataObject() { form.put("a", list); form.put("b", "4"); Message message = MessageBuilder.withPayload(form).build(); - Exception exception = null; - try { - handler.handleMessage(message); - } - catch (Exception e) { - exception = e; - } - assertThat(exception.getCause().getMessage()).isEqualTo("intentional"); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(message)) + .withStackTraceContaining("intentional"); + HttpEntity request = template.lastRequestEntity.get(); Object body = request.getBody(); assertThat(body instanceof MultiValueMap).isTrue(); @@ -441,14 +417,11 @@ public void stringKeyStringCollectionValueFormData() { form.put("b", Collections.EMPTY_LIST); form.put("c", Collections.singletonList("3")); Message message = MessageBuilder.withPayload(form).build(); - Exception exception = null; - try { - handler.handleMessage(message); - } - catch (Exception e) { - exception = e; - } - assertThat(exception.getCause().getMessage()).isEqualTo("intentional"); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(message)) + .withStackTraceContaining("intentional"); + HttpEntity request = template.lastRequestEntity.get(); Object body = request.getBody(); assertThat(body instanceof MultiValueMap).isTrue(); @@ -486,14 +459,11 @@ public void stringKeyObjectCollectionValueFormData() { form.put("b", Collections.EMPTY_LIST); form.put("c", Collections.singletonList(new City("Mohnton"))); Message message = MessageBuilder.withPayload(form).build(); - Exception exception = null; - try { - handler.handleMessage(message); - } - catch (Exception e) { - exception = e; - } - assertThat(exception.getCause().getMessage()).isEqualTo("intentional"); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(message)) + .withStackTraceContaining("intentional"); + HttpEntity request = template.lastRequestEntity.get(); Object body = request.getBody(); assertThat(body instanceof MultiValueMap).isTrue(); @@ -528,14 +498,11 @@ public void nameOnlyWithNullValues() { form.put("b", "foo"); form.put("c", null); Message message = MessageBuilder.withPayload(form).build(); - Exception exception = null; - try { - handler.handleMessage(message); - } - catch (Exception e) { - exception = e; - } - assertThat(exception.getCause().getMessage()).isEqualTo("intentional"); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(message)) + .withStackTraceContaining("intentional"); + HttpEntity request = template.lastRequestEntity.get(); Object body = request.getBody(); assertThat(body instanceof MultiValueMap).isTrue(); @@ -564,14 +531,11 @@ public void contentAsByteArray() { byte[] bytes = "Hello World".getBytes(); Message message = MessageBuilder.withPayload(bytes).build(); - Exception exception = null; - try { - handler.handleMessage(message); - } - catch (Exception e) { - exception = e; - } - assertThat(exception.getCause().getMessage()).isEqualTo("intentional"); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(message)) + .withStackTraceContaining("intentional"); + HttpEntity request = template.lastRequestEntity.get(); Object body = request.getBody(); assertThat(body instanceof byte[]).isTrue(); @@ -590,14 +554,11 @@ public void contentAsXmlSource() { handler.afterPropertiesSet(); Message message = MessageBuilder.withPayload(mock(Source.class)).build(); - Exception exception = null; - try { - handler.handleMessage(message); - } - catch (Exception e) { - exception = e; - } - assertThat(exception.getCause().getMessage()).isEqualTo("intentional"); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(message)) + .withStackTraceContaining("intentional"); + HttpEntity request = template.lastRequestEntity.get(); Object body = request.getBody(); assertThat(body instanceof Source).isTrue(); @@ -648,55 +609,39 @@ public void contentTypeIsNotSetForGetAndHeadRequest() { setBeanFactory(handler); handler.afterPropertiesSet(); - Message message = MessageBuilder.withPayload(mock(Source.class)).build(); - try { - handler.handleMessage(message); - fail("An Exception expected"); - } - catch (Exception e) { - assertThat(e.getCause().getMessage()).isEqualTo("intentional"); - assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isNull(); - } + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(MessageBuilder.withPayload(mock(Source.class)).build())) + .withStackTraceContaining("intentional"); + + assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isNull(); //HEAD handler.setHttpMethod(HttpMethod.HEAD); - message = MessageBuilder.withPayload(mock(Source.class)).build(); - try { - handler.handleMessage(message); - fail("An Exception expected"); - } - catch (Exception e) { - assertThat(e.getCause().getMessage()).isEqualTo("intentional"); - assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isNull(); - } + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(MessageBuilder.withPayload(mock(Source.class)).build())) + .withStackTraceContaining("intentional"); + + assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isNull(); //DELETE handler.setHttpMethod(HttpMethod.DELETE); - message = MessageBuilder.withPayload(mock(Source.class)).build(); - try { - handler.handleMessage(message); - fail("An Exception expected"); - } - catch (Exception e) { - assertThat(e.getCause().getMessage()).isEqualTo("intentional"); - assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isEqualTo(MediaType.TEXT_XML); - } + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(MessageBuilder.withPayload(mock(Source.class)).build())) + .withStackTraceContaining("intentional"); + + assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isEqualTo(MediaType.TEXT_XML); //TRACE handler.setHttpMethod(HttpMethod.TRACE); - message = MessageBuilder.withPayload(mock(Source.class)).build(); - try { - handler.handleMessage(message); - fail("An Exception expected"); - } - catch (Exception e) { - assertThat(e.getCause().getMessage()).isEqualTo("intentional"); - assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isNull(); - } + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(MessageBuilder.withPayload(mock(Source.class)).build())) + .withStackTraceContaining("intentional"); + + assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isNull(); } @Test @@ -743,11 +688,10 @@ public void testUriExpression() { handler.afterPropertiesSet(); String theURL = "https://bar/baz?foo#bar"; Message message = MessageBuilder.withPayload("").setHeader("foo", theURL).build(); - try { - handler.handleRequestMessage(message); - } - catch (Exception e) { - } + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(message)); + assertThat(restTemplate.actualUrl.get()).isEqualTo(theURL); } @@ -764,12 +708,10 @@ public void testUriEncoded() { handler.setUriVariableExpressions(Collections.singletonMap("query", parser.parseExpression("payload"))); setBeanFactory(handler); handler.afterPropertiesSet(); - Message message = new GenericMessage<>("test-äöü&%"); - try { - handler.handleMessage(message); - } - catch (Exception ignored) { - } + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(new GenericMessage<>("test-äöü&%"))); + assertThat(restTemplate.actualUrl.get()).isEqualTo("https://example.com?query=test-%C3%A4%C3%B6%C3%BC%26%25"); } @@ -789,12 +731,10 @@ public void testUriEncodedDisabled() { handler.setUriVariableExpressions(Collections.singletonMap("query", parser.parseExpression("payload"))); setBeanFactory(handler); handler.afterPropertiesSet(); - Message message = new GenericMessage<>("test-äöü"); - try { - handler.handleMessage(message); - } - catch (Exception ignored) { - } + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(new GenericMessage<>("test-äöü"))); + assertThat(restTemplate.actualUrl.get()).isEqualTo("https://example.com?query=test-äöü"); } @@ -809,12 +749,10 @@ public void testInt2455UriNotEncoded() { new SpelExpressionParser().parseExpression("'https://my.RabbitMQ.com/api/' + payload"), restTemplate); setBeanFactory(handler); handler.afterPropertiesSet(); - Message message = MessageBuilder.withPayload("queues/%2f/si.test.queue?foo#bar").build(); - try { - handler.handleRequestMessage(message); - } - catch (Exception e) { - } + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(new GenericMessage<>("queues/%2f/si.test.queue?foo#bar"))); + assertThat(restTemplate.actualUrl.get()) .isEqualTo("https://my.RabbitMQ.com/api/queues/%2f/si.test.queue?foo#bar"); } @@ -836,17 +774,12 @@ public void acceptHeaderForSerializableResponse() throws IOException { HttpHeaders requestHeaders = setUpMocksToCaptureSentHeaders(restTemplate); - Message message = MessageBuilder.withPayload("foo").build(); - Exception exception = null; - try { - handler.handleMessage(message); - } - catch (Exception e) { - exception = e; - } + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(new GenericMessage<>("foo"))) + .withStackTraceContaining("404 Not Found"); + assertThat(requestHeaders.getAccept()).isNotNull(); assertThat(requestHeaders.getAccept().size() > 0).isTrue(); - assertThat(exception.getCause().getMessage()).contains("404 Not Found"); List accept = requestHeaders.getAccept(); assertThat(accept.size() > 0).isTrue(); assertThat(accept.get(0).getType()).isEqualTo("application"); @@ -872,17 +805,12 @@ public void acceptHeaderForSerializableResponseMessageExchange() throws IOExcept HttpHeaders requestHeaders = setUpMocksToCaptureSentHeaders(restTemplate); - Message message = MessageBuilder.withPayload("foo").build(); - Exception exception = null; - try { - handler.handleMessage(message); - } - catch (Exception e) { - exception = e; - } + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(new GenericMessage<>("foo"))) + .withStackTraceContaining("404 Not Found"); + assertThat(requestHeaders.getAccept()).isNotNull(); assertThat(requestHeaders.getAccept().size() > 0).isTrue(); - assertThat(exception.getCause().getMessage()).contains("404 Not Found"); List accept = requestHeaders.getAccept(); assertThat(accept.size() > 0).isTrue(); assertThat(accept.get(0).getType()).isEqualTo("application"); From 615c36d6cc9b01cf774d14ce3999fd9dbcfabb9a Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Thu, 30 Jan 2020 12:45:47 -0500 Subject: [PATCH 3/5] * Clean up formatting --- ...HttpOutboundChannelAdapterParserTests.java | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) 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 d90700b7764..92c6b4304b4 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 @@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Map; import org.junit.jupiter.api.Test; @@ -117,8 +118,8 @@ public void minimalConfig() { assertThat(endpointAccessor.getPropertyValue("inputChannel")) .isEqualTo(this.applicationContext.getBean("requests")); assertThat(handlerAccessor.getPropertyValue("outputChannel")).isNull(); - DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate" - )); + DirectFieldAccessor templateAccessor = + new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate")); ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory) templateAccessor.getPropertyValue("requestFactory"); assertThat(requestFactory instanceof SimpleClientHttpRequestFactory).isTrue(); @@ -126,7 +127,7 @@ public void minimalConfig() { assertThat(uriExpression.getValue()).isEqualTo("http://localhost/test1"); assertThat(TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString()) .isEqualTo(HttpMethod.POST.name()); - assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(Charset.forName("UTF-8")); + assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(StandardCharsets.UTF_8); assertThat(handlerAccessor.getPropertyValue("extractPayload")).isEqualTo(true); } @@ -143,8 +144,8 @@ public void fullConfig() { assertThat(handlerAccessor.getPropertyValue("outputChannel")).isNull(); assertThat(handlerAccessor.getPropertyValue("order")).isEqualTo(77); assertThat(endpointAccessor.getPropertyValue("autoStartup")).isEqualTo(Boolean.FALSE); - DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate" - )); + DirectFieldAccessor templateAccessor = + new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate")); ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory) templateAccessor.getPropertyValue("requestFactory"); assertThat(TestUtils.getPropertyValue(handler, "expectedResponseTypeExpression", Expression.class).getValue()) @@ -160,7 +161,7 @@ public void fullConfig() { assertThat(uriExpression.getValue()).isEqualTo("http://localhost/test2/{foo}"); assertThat(TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString()) .isEqualTo(HttpMethod.GET.name()); - assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(Charset.forName("UTF-8")); + assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(StandardCharsets.UTF_8); assertThat(handlerAccessor.getPropertyValue("extractPayload")).isEqualTo(false); Map uriVariableExpressions = (Map) handlerAccessor.getPropertyValue("uriVariableExpressions"); @@ -207,8 +208,8 @@ public void withUrlAndTemplate() { assertThat(endpointAccessor.getPropertyValue("inputChannel")) .isEqualTo(this.applicationContext.getBean("requests")); assertThat(handlerAccessor.getPropertyValue("outputChannel")).isNull(); - DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate" - )); + DirectFieldAccessor templateAccessor = + new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate")); ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory) templateAccessor.getPropertyValue("requestFactory"); assertThat(requestFactory instanceof SimpleClientHttpRequestFactory).isTrue(); @@ -216,7 +217,7 @@ public void withUrlAndTemplate() { assertThat(uriExpression.getValue()).isEqualTo("http://localhost/test1"); assertThat(TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString()) .isEqualTo(HttpMethod.POST.name()); - assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(Charset.forName("UTF-8")); + assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(StandardCharsets.UTF_8); assertThat(handlerAccessor.getPropertyValue("extractPayload")).isEqualTo(true); //INT-3055 @@ -241,8 +242,8 @@ public void withUrlExpression() { assertThat(endpointAccessor.getPropertyValue("inputChannel")) .isEqualTo(this.applicationContext.getBean("requests")); assertThat(handlerAccessor.getPropertyValue("outputChannel")).isNull(); - DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate" - )); + DirectFieldAccessor templateAccessor = + new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate")); ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory) templateAccessor.getPropertyValue("requestFactory"); assertThat(requestFactory instanceof SimpleClientHttpRequestFactory).isTrue(); @@ -251,7 +252,7 @@ public void withUrlExpression() { assertThat(expression.getExpressionString()).isEqualTo("'http://localhost/test1'"); assertThat(TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString()) .isEqualTo(HttpMethod.POST.name()); - assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(Charset.forName("UTF-8")); + assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(StandardCharsets.UTF_8); assertThat(handlerAccessor.getPropertyValue("extractPayload")).isEqualTo(true); } @@ -276,8 +277,8 @@ public void withUrlExpressionAndTemplate() { assertThat(endpointAccessor.getPropertyValue("inputChannel")) .isEqualTo(this.applicationContext.getBean("requests")); assertThat(handlerAccessor.getPropertyValue("outputChannel")).isNull(); - DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate" - )); + DirectFieldAccessor templateAccessor = + new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate")); ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory) templateAccessor.getPropertyValue("requestFactory"); assertThat(requestFactory instanceof SimpleClientHttpRequestFactory).isTrue(); @@ -286,7 +287,7 @@ public void withUrlExpressionAndTemplate() { assertThat(expression.getExpressionString()).isEqualTo("'http://localhost/test1'"); assertThat(TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString()) .isEqualTo(HttpMethod.POST.name()); - assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(Charset.forName("UTF-8")); + assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(StandardCharsets.UTF_8); assertThat(handlerAccessor.getPropertyValue("extractPayload")).isEqualTo(true); } From 6a30f1cde80316a323d7fcea165d1f927972d951 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Thu, 30 Jan 2020 14:10:35 -0500 Subject: [PATCH 4/5] * Apply fix for WebFlux module * Add docs for new `encoding-mode` option --- .../HttpOutboundChannelAdapterParser.java | 5 ++- .../WebFluxOutboundChannelAdapterParser.java | 6 ++- ...WebFluxRequestExecutingMessageHandler.java | 44 +++++++++++++++---- ...boundChannelAdapterParserTests-context.xml | 3 +- ...FluxOutboundChannelAdapterParserTests.java | 18 +++++--- src/reference/asciidoc/http.adoc | 9 +++- src/reference/asciidoc/whats-new.adoc | 7 +++ 7 files changed, 70 insertions(+), 22 deletions(-) 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 a97aee5ae4e..a35f0ceefc6 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 @@ -25,6 +25,7 @@ import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler; +import org.springframework.integration.http.support.DefaultHttpHeaderMapper; import org.springframework.util.StringUtils; /** @@ -59,8 +60,8 @@ protected AbstractBeanDefinition parseConsumer(Element element, ParserContext pa builder.addPropertyReference("headerMapper", headerMapper); } else if (StringUtils.hasText(mappedRequestHeaders)) { - BeanDefinitionBuilder headerMapperBuilder = BeanDefinitionBuilder.genericBeanDefinition( - "org.springframework.integration.http.support.DefaultHttpHeaderMapper"); + BeanDefinitionBuilder headerMapperBuilder = + BeanDefinitionBuilder.genericBeanDefinition(DefaultHttpHeaderMapper.class); IntegrationNamespaceUtils.setValueIfAttributeDefined(headerMapperBuilder, element, "mapped-request-headers", "outboundHeaderNames"); builder.addPropertyValue("headerMapper", headerMapperBuilder.getBeanDefinition()); diff --git a/spring-integration-webflux/src/main/java/org/springframework/integration/webflux/config/WebFluxOutboundChannelAdapterParser.java b/spring-integration-webflux/src/main/java/org/springframework/integration/webflux/config/WebFluxOutboundChannelAdapterParser.java index 8b0905bf8d7..0072cf69af8 100644 --- a/spring-integration-webflux/src/main/java/org/springframework/integration/webflux/config/WebFluxOutboundChannelAdapterParser.java +++ b/spring-integration-webflux/src/main/java/org/springframework/integration/webflux/config/WebFluxOutboundChannelAdapterParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.ExpressionFactoryBean; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; import org.springframework.integration.http.config.HttpOutboundChannelAdapterParser; import org.springframework.integration.webflux.outbound.WebFluxRequestExecutingMessageHandler; import org.springframework.util.StringUtils; @@ -52,6 +53,9 @@ static BeanDefinitionBuilder buildWebFluxRequestExecutingMessageHandler(Element .getConstructorArgumentValues() .addIndexedArgumentValue(1, new RuntimeBeanReference(webClientRef)); } + else { + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encoding-mode"); + } String type = element.getAttribute("publisher-element-type"); String typeExpression = element.getAttribute("publisher-element-type-expression"); diff --git a/spring-integration-webflux/src/main/java/org/springframework/integration/webflux/outbound/WebFluxRequestExecutingMessageHandler.java b/spring-integration-webflux/src/main/java/org/springframework/integration/webflux/outbound/WebFluxRequestExecutingMessageHandler.java index 4cab9fe3165..8fcd0402a63 100644 --- a/spring-integration-webflux/src/main/java/org/springframework/integration/webflux/outbound/WebFluxRequestExecutingMessageHandler.java +++ b/spring-integration-webflux/src/main/java/org/springframework/integration/webflux/outbound/WebFluxRequestExecutingMessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2019 the original author or authors. + * Copyright 2017-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ import java.net.URI; import java.nio.charset.StandardCharsets; -import java.util.function.Supplier; +import java.util.Map; import org.reactivestreams.Publisher; @@ -50,6 +50,7 @@ import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClientResponseException; +import org.springframework.web.util.DefaultUriBuilderFactory; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -71,6 +72,8 @@ public class WebFluxRequestExecutingMessageHandler extends AbstractHttpRequestEx private final WebClient webClient; + private final boolean webClientExplicitlySet; + private boolean replyPayloadToFlux; private BodyExtractor bodyExtractor; @@ -124,10 +127,26 @@ public WebFluxRequestExecutingMessageHandler(String uri, @Nullable WebClient web */ public WebFluxRequestExecutingMessageHandler(Expression uriExpression, @Nullable WebClient webClient) { super(uriExpression); - this.webClient = (webClient == null ? WebClient.create() : webClient); + this.webClientExplicitlySet = webClient != null; + this.webClient = + !this.webClientExplicitlySet + ? WebClient.builder().uriBuilderFactory(this.uriFactory).build() + : webClient; this.setAsync(true); } + private void assertLocalWebClient(String option) { + Assert.isTrue(!this.webClientExplicitlySet, + () -> "The option '" + option + "' must be provided on the externally configured WebClient: " + + this.webClient); + } + + @Override + public void setEncodingMode(DefaultUriBuilderFactory.EncodingMode encodingMode) { + assertLocalWebClient("encodingMode on UriBuilderFactory"); + super.setEncodingMode(encodingMode); + } + /** * The boolean flag to identify if the reply payload should be as a {@link Flux} from the response body * or as resolved value from the {@link Mono} of the response body. @@ -185,13 +204,20 @@ public String getComponentType() { } @Override - protected Object exchange(Supplier uriSupplier, HttpMethod httpMethod, HttpEntity httpRequest, - Object expectedResponseType, Message requestMessage) { + protected Object exchange(Object uri, HttpMethod httpMethod, HttpEntity httpRequest, + Object expectedResponseType, Message requestMessage, Map uriVariables) { + + WebClient.RequestBodyUriSpec requestBodyUriSpec = this.webClient.method(httpMethod); + WebClient.RequestBodySpec requestSpec; + + if (uri instanceof URI) { + requestSpec = requestBodyUriSpec.uri((URI) uri); + } + else { + requestSpec = requestBodyUriSpec.uri((String) uri, uriVariables); + } - WebClient.RequestBodySpec requestSpec = - this.webClient.method(httpMethod) - .uri(b -> uriSupplier.get()) - .headers(headers -> headers.putAll(httpRequest.getHeaders())); + requestSpec = requestSpec.headers(headers -> headers.putAll(httpRequest.getHeaders())); BodyInserter inserter = buildBodyInserterForRequest(requestMessage, httpRequest); if (inserter != null) { requestSpec.body(inserter); diff --git a/spring-integration-webflux/src/test/java/org/springframework/integration/webflux/config/WebFluxOutboundChannelAdapterParserTests-context.xml b/spring-integration-webflux/src/test/java/org/springframework/integration/webflux/config/WebFluxOutboundChannelAdapterParserTests-context.xml index d16a911a62f..7accf9b6de8 100644 --- a/spring-integration-webflux/src/test/java/org/springframework/integration/webflux/config/WebFluxOutboundChannelAdapterParserTests-context.xml +++ b/spring-integration-webflux/src/test/java/org/springframework/integration/webflux/config/WebFluxOutboundChannelAdapterParserTests-context.xml @@ -10,7 +10,8 @@ - + nameValuePairs = ---- ==== +[[http-uri-encoding]] ==== Controlling URI Encoding By default, the URL string is encoded (see https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/util/UriComponentsBuilder.html[`UriComponentsBuilder`]) to the URI object before sending the request. In some scenarios with a non-standard URI (such as the RabbitMQ REST API), it is undesirable to perform the encoding. -The `` and `` provide an `encode-uri` attribute. -To disable encoding the URL, set this attribute to `false` (by default, it is `true`). +The `` and `` provide an `encoding-mode` attribute. +To disable encoding the URL, set this attribute to `NONE` (by default, it is `TEMPLATE_AND_VALUES`). If you wish to partially encode some of the URL, use an `expression` within a ``, as the following example shows: ==== @@ -722,6 +723,10 @@ If you wish to partially encode some of the URL, use an `expression` within a `< ---- ==== +With Java DSL this option can be controlled by the `BaseHttpMessageHandlerSpec.encodingMode()` option. +Same configuration applies for similar outbound components in the <<./webflux.adoc/[webflux,WebFlux module>>. +For much sophisticated scenarios it is recommended to configure an `UriTemplateHandler` on the externally provided `RestTemplate`; or in case of WebFlux - `WebClient` with it `UriBuilderFactory`. + [[http-java-config]] === Configuring HTTP Endpoints with Java diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc index 9ff06ec7174..eae817cffcd 100644 --- a/src/reference/asciidoc/whats-new.adoc +++ b/src/reference/asciidoc/whats-new.adoc @@ -41,3 +41,10 @@ See <<./gateway.adoc/gateway-calling-default-methods,Invoking `default` Methods> Internal components (such as `_org.springframework.integration.errorLogger`) now have a shortened name when they are represented in the integration graph. See <<./graph.adoc#integration-graph,Integration Graph>> for more information. + +[[x5.3-http]] +=== HTTP Changes + +The `encodeUri` property on the `AbstractHttpRequestExecutingMessageHandler` has been deprecated in favor of newly introduced `encodingMode`. +See `DefaultUriBuilderFactory.EncodingMode` JavaDocs and <<./http.adoc/http-uri-encoding,Controlling URI Encoding>> for more information. +This also effects `WebFluxRequestExecutingMessageHandler`, respective Java DSL and XML configuration. From a884203f4f0274a81ac9ba47a29535447aa7b23f Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Thu, 30 Jan 2020 14:22:11 -0500 Subject: [PATCH 5/5] * Remove unused import in the test --- .../http/config/HttpOutboundChannelAdapterParserTests.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 92c6b4304b4..88ada166538 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Map;