GH-3154: Support UriBuilderFactory.EncodingMode (#3162)
* 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 * * 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 * * Clean up formatting * * Apply fix for WebFlux module * Add docs for new `encoding-mode` option * * Remove unused import in the test
This commit is contained in:
@@ -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.
|
||||
@@ -25,6 +25,7 @@ import org.springframework.beans.factory.xml.ParserContext;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -46,7 +47,6 @@ public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAda
|
||||
|
||||
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");
|
||||
@@ -60,8 +60,8 @@ public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAda
|
||||
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());
|
||||
@@ -90,6 +90,8 @@ public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAda
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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 @@ public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser {
|
||||
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 @@ public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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.http.support.DefaultHttpHeaderMapper;
|
||||
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 @@ public abstract class BaseHttpMessageHandlerSpec<S extends BaseHttpMessageHandle
|
||||
* expanding and before send request via underlying implementation. The default value is <code>true</code>.
|
||||
* @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.
|
||||
|
||||
@@ -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.HashMap;
|
||||
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.AbstractIntegrationMessageBuilder
|
||||
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<HttpMethod> NO_BODY_HTTP_METHODS =
|
||||
Arrays.asList(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.TRACE);
|
||||
|
||||
protected final DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory(); // NOSONAR - final
|
||||
|
||||
private final Map<String, Expression> 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;
|
||||
@@ -127,9 +124,27 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac
|
||||
* <code>true</code>.
|
||||
* @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}.
|
||||
* 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
|
||||
*/
|
||||
public void setEncodingMode(DefaultUriBuilderFactory.EncodingMode encodingMode) {
|
||||
Assert.notNull(encodingMode, "'encodingMode' must not be null");
|
||||
this.uriFactory.setEncodingMode(encodingMode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,32 +306,23 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac
|
||||
Object expectedResponseType = determineExpectedResponseType(requestMessage);
|
||||
|
||||
HttpEntity<?> httpRequest = generateHttpRequest(requestMessage, httpMethod);
|
||||
return exchange(() -> generateUri(requestMessage), httpMethod, httpRequest, expectedResponseType,
|
||||
requestMessage);
|
||||
}
|
||||
|
||||
protected abstract Object exchange(Supplier<URI> 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<String, ?> 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<String, ?> 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<String, ?> uriVariables);
|
||||
|
||||
protected Object getReply(ResponseEntity<?> httpResponse) {
|
||||
if (this.expectReply) {
|
||||
HttpHeaders httpHeaders = httpResponse.getHeaders();
|
||||
|
||||
@@ -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 @@ package org.springframework.integration.http.outbound;
|
||||
|
||||
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.ResponseEntity;
|
||||
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,24 @@ public class HttpRequestExecutingMessageHandler extends AbstractHttpRequestExecu
|
||||
* {@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());
|
||||
if (!this.restTemplateExplicitlySet) {
|
||||
this.restTemplate.setUriTemplateHandler(this.uriFactory);
|
||||
}
|
||||
}
|
||||
|
||||
@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 +139,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractHttpRequestExecu
|
||||
* @see RestTemplate#setErrorHandler(ResponseErrorHandler)
|
||||
*/
|
||||
public void setErrorHandler(ResponseErrorHandler errorHandler) {
|
||||
assertLocalRestTemplate("errorHandler");
|
||||
this.restTemplate.setErrorHandler(errorHandler);
|
||||
}
|
||||
|
||||
@@ -135,6 +150,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractHttpRequestExecu
|
||||
* @see RestTemplate#setMessageConverters(java.util.List)
|
||||
*/
|
||||
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
|
||||
assertLocalRestTemplate("messageConverters");
|
||||
this.restTemplate.setMessageConverters(messageConverters);
|
||||
}
|
||||
|
||||
@@ -144,24 +160,43 @@ public class HttpRequestExecutingMessageHandler extends AbstractHttpRequestExecu
|
||||
* @see RestTemplate#setRequestFactory(ClientHttpRequestFactory)
|
||||
*/
|
||||
public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
|
||||
assertLocalRestTemplate("requestFactory");
|
||||
this.restTemplate.setRequestFactory(requestFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object exchange(Supplier<URI> 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<String, ?> 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) {
|
||||
|
||||
@@ -844,12 +844,23 @@
|
||||
<xsd:attribute name="encode-uri" type="xsd:string" default="true">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
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'.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="encoding-mode" default="TEMPLATE_AND_VALUES">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Set the encoding mode during URI building.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:union memberTypes="encodingModeEnumeration xsd:string"/>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="http-method">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
@@ -943,4 +954,13 @@
|
||||
</xsd:attribute>
|
||||
</xsd:attributeGroup>
|
||||
|
||||
<xsd:simpleType name="encodingModeEnumeration">
|
||||
<xsd:restriction base="xsd:token">
|
||||
<xsd:enumeration value="TEMPLATE_AND_VALUES"/>
|
||||
<xsd:enumeration value="VALUES_ONLY"/>
|
||||
<xsd:enumeration value="URI_COMPONENT"/>
|
||||
<xsd:enumeration value="NONE"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
</xsd:schema>
|
||||
|
||||
@@ -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 @@ package org.springframework.integration.http;
|
||||
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.time.format.DateTimeFormatter;
|
||||
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.messaging.PollableChannel;
|
||||
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 @@ import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringJUnitConfig
|
||||
@DirtiesContext
|
||||
public class HttpProxyScenarioTests {
|
||||
|
||||
@@ -122,8 +120,8 @@ public class HttpProxyScenarioTests {
|
||||
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 class HttpProxyScenarioTests {
|
||||
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 class HttpProxyScenarioTests {
|
||||
|
||||
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 class HttpProxyScenarioTests {
|
||||
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);
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
request-factory="testRequestFactory"
|
||||
error-handler="testErrorHandler"
|
||||
order="77"
|
||||
encoding-mode="VALUES_ONLY"
|
||||
auto-startup="false">
|
||||
<uri-variable name="foo" expression="headers.bar"/>
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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.nio.charset.StandardCharsets;
|
||||
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.Message;
|
||||
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 @@ import org.springframework.web.client.RestTemplate;
|
||||
* @author Biju Kunjummen
|
||||
* @author Shiliang Li
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@SpringJUnitConfig
|
||||
@DirtiesContext
|
||||
public class HttpOutboundChannelAdapterParserTests {
|
||||
|
||||
@@ -112,13 +110,15 @@ public class HttpOutboundChannelAdapterParserTests {
|
||||
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();
|
||||
@@ -126,7 +126,7 @@ public class HttpOutboundChannelAdapterParserTests {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -134,7 +134,8 @@ public class HttpOutboundChannelAdapterParserTests {
|
||||
@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 class HttpOutboundChannelAdapterParserTests {
|
||||
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())
|
||||
@@ -158,7 +160,7 @@ public class HttpOutboundChannelAdapterParserTests {
|
||||
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<String, Expression> uriVariableExpressions =
|
||||
(Map<String, Expression>) handlerAccessor.getPropertyValue("uriVariableExpressions");
|
||||
@@ -171,6 +173,10 @@ public class HttpOutboundChannelAdapterParserTests {
|
||||
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 class HttpOutboundChannelAdapterParserTests {
|
||||
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 class HttpOutboundChannelAdapterParserTests {
|
||||
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();
|
||||
@@ -206,7 +216,7 @@ public class HttpOutboundChannelAdapterParserTests {
|
||||
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
|
||||
@@ -224,13 +234,15 @@ public class HttpOutboundChannelAdapterParserTests {
|
||||
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();
|
||||
@@ -239,7 +251,7 @@ public class HttpOutboundChannelAdapterParserTests {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -257,13 +269,15 @@ public class HttpOutboundChannelAdapterParserTests {
|
||||
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();
|
||||
@@ -272,7 +286,7 @@ public class HttpOutboundChannelAdapterParserTests {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -281,21 +295,23 @@ public class HttpOutboundChannelAdapterParserTests {
|
||||
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) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,20 +9,16 @@
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<si:chain id="chain" input-channel="httpOutboundChannelAdapterWithinChain">
|
||||
<outbound-channel-adapter id="adapter" url="http://localhost/test1/%2f" encode-uri="false" rest-template="restTemplate"
|
||||
<outbound-channel-adapter id="adapter" url="http://localhost/test1/%2f" rest-template="restTemplate"
|
||||
trusted-spel="true" />
|
||||
</si:chain>
|
||||
|
||||
<beans:bean id="restTemplate" class="org.mockito.Mockito" factory-method="spy">
|
||||
<beans:constructor-arg>
|
||||
<beans:bean class="org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandlerTests$MockRestTemplate2"/>
|
||||
</beans:constructor-arg>
|
||||
</beans:bean>
|
||||
<beans:bean id="restTemplate"
|
||||
class="org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandlerTests$MockRestTemplate2"/>
|
||||
|
||||
<si:chain input-channel="httpOutboundGatewayWithinChain" output-channel="replyChannel">
|
||||
<outbound-gateway url="http://localhost:51235/%2f/testApps?param={param}"
|
||||
rest-template="restTemplate"
|
||||
encode-uri="false"
|
||||
trusted-spel="true"
|
||||
expected-response-type-expression="T (org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandlerTests).testParameterizedTypeReference()">
|
||||
<uri-variable name="param" expression="T(java.net.URLEncoder).encode('http Outbound Gateway Within Chain', 'UTF-8')"/>
|
||||
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
@@ -26,7 +26,6 @@ import java.io.ByteArrayInputStream;
|
||||
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 java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
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.channel.QueueChannel;
|
||||
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
|
||||
@@ -99,14 +103,11 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
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();
|
||||
@@ -134,14 +135,11 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
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();
|
||||
@@ -168,14 +166,11 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
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();
|
||||
@@ -202,14 +197,11 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
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();
|
||||
@@ -250,14 +242,11 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
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();
|
||||
@@ -299,14 +288,11 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
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();
|
||||
@@ -347,14 +333,11 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
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();
|
||||
@@ -394,14 +377,11 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
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();
|
||||
@@ -437,14 +417,11 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
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();
|
||||
@@ -482,14 +459,11 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
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();
|
||||
@@ -524,14 +498,11 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
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();
|
||||
@@ -560,14 +531,11 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
|
||||
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();
|
||||
@@ -586,14 +554,11 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
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();
|
||||
@@ -644,89 +609,73 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
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 // 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.<Class<Object>>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<List<String>>() {
|
||||
|
||||
}));
|
||||
assertThat(restTemplate.actualUrl.get())
|
||||
.isEqualTo("http://localhost:51235/%2f/testApps?param=http+Outbound+Gateway+Within+Chain");
|
||||
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@@ -739,11 +688,10 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -757,18 +705,13 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
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();
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -776,39 +719,40 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
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();
|
||||
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-äöü");
|
||||
}
|
||||
|
||||
@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();
|
||||
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");
|
||||
}
|
||||
@@ -830,17 +774,12 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
|
||||
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<MediaType> accept = requestHeaders.getAccept();
|
||||
assertThat(accept.size() > 0).isTrue();
|
||||
assertThat(accept.get(0).getType()).isEqualTo("application");
|
||||
@@ -866,17 +805,12 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
|
||||
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<MediaType> accept = requestHeaders.getAccept();
|
||||
assertThat(accept.size() > 0).isTrue();
|
||||
assertThat(accept.get(0).getType()).isEqualTo("application");
|
||||
@@ -933,12 +867,11 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
|
||||
private final AtomicReference<String> actualUrl = new AtomicReference<>();
|
||||
|
||||
@Override
|
||||
public <T> ResponseEntity<T> exchange(URI uri, HttpMethod method, HttpEntity<?> requestEntity,
|
||||
Class<T> responseType) throws RestClientException {
|
||||
|
||||
this.actualUrl.set(uri.toString());
|
||||
this.lastRequestEntity.set(requestEntity);
|
||||
@Nullable
|
||||
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
|
||||
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
|
||||
this.actualUrl.set(url.toString());
|
||||
this.lastRequestEntity.set(TestUtils.getPropertyValue(requestCallback, "requestEntity", HttpEntity.class));
|
||||
throw new RuntimeException("intentional");
|
||||
}
|
||||
|
||||
@@ -947,16 +880,25 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
@SuppressWarnings("unused")
|
||||
private static class MockRestTemplate2 extends RestTemplate {
|
||||
|
||||
@Override
|
||||
public <T> ResponseEntity<T> exchange(URI uri, HttpMethod method, HttpEntity<?> requestEntity,
|
||||
Class<T> responseType) throws RestClientException {
|
||||
return new ResponseEntity<T>(HttpStatus.OK);
|
||||
private final AtomicReference<String> actualUrl = new AtomicReference<>();
|
||||
|
||||
MockRestTemplate2() {
|
||||
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory();
|
||||
uriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE);
|
||||
setUriTemplateHandler(uriBuilderFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity,
|
||||
ParameterizedTypeReference<T> responseType) throws RestClientException {
|
||||
return new ResponseEntity<T>(HttpStatus.OK);
|
||||
@Nullable
|
||||
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
|
||||
@Nullable ResponseExtractor<T> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.config.RuntimeBeanReference;
|
||||
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 @@ public class WebFluxOutboundChannelAdapterParser extends HttpOutboundChannelAdap
|
||||
.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");
|
||||
|
||||
@@ -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 @@ package org.springframework.integration.webflux.outbound;
|
||||
|
||||
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.BodyInserters;
|
||||
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<?, ClientHttpResponse> bodyExtractor;
|
||||
@@ -124,10 +127,26 @@ public class WebFluxRequestExecutingMessageHandler extends AbstractHttpRequestEx
|
||||
*/
|
||||
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 class WebFluxRequestExecutingMessageHandler extends AbstractHttpRequestEx
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object exchange(Supplier<URI> uriSupplier, HttpMethod httpMethod, HttpEntity<?> httpRequest,
|
||||
Object expectedResponseType, Message<?> requestMessage) {
|
||||
protected Object exchange(Object uri, HttpMethod httpMethod, HttpEntity<?> httpRequest,
|
||||
Object expectedResponseType, Message<?> requestMessage, Map<String, ?> uriVariables) {
|
||||
|
||||
WebClient.RequestBodySpec requestSpec =
|
||||
this.webClient.method(httpMethod)
|
||||
.uri(b -> uriSupplier.get())
|
||||
.headers(headers -> headers.putAll(httpRequest.getHeaders()));
|
||||
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);
|
||||
}
|
||||
|
||||
requestSpec = requestSpec.headers(headers -> headers.putAll(httpRequest.getHeaders()));
|
||||
BodyInserter<?, ? super ClientHttpRequest> inserter = buildBodyInserterForRequest(requestMessage, httpRequest);
|
||||
if (inserter != null) {
|
||||
requestSpec.body(inserter);
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
|
||||
<si:channel id="requests"/>
|
||||
|
||||
<outbound-channel-adapter id="reactiveMinimalConfig" url="http://localhost/test1" channel="requests"/>
|
||||
<outbound-channel-adapter id="reactiveMinimalConfig" url="http://localhost/test1" channel="requests"
|
||||
encoding-mode="VALUES_ONLY"/>
|
||||
|
||||
<outbound-channel-adapter id="reactiveWebClientConfig" url="http://localhost/test1" channel="requests"
|
||||
web-client="webClient"
|
||||
|
||||
@@ -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,11 +18,10 @@ package org.springframework.integration.webflux.config;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
|
||||
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;
|
||||
@@ -33,15 +32,16 @@ import org.springframework.http.HttpMethod;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.util.DefaultUriBuilderFactory;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringJUnitConfig
|
||||
@DirtiesContext
|
||||
public class WebFluxOutboundChannelAdapterParserTests {
|
||||
|
||||
@@ -75,8 +75,12 @@ public class WebFluxOutboundChannelAdapterParserTests {
|
||||
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);
|
||||
assertThat(
|
||||
TestUtils.getPropertyValue(handler, "webClient.uriBuilderFactory.encodingMode",
|
||||
DefaultUriBuilderFactory.EncodingMode.class))
|
||||
.isEqualTo(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -703,12 +703,13 @@ List<NameValuePair> 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 `<http:outbound-gateway/>` and `<http:outbound-channel-adapter/>` provide an `encode-uri` attribute.
|
||||
To disable encoding the URL, set this attribute to `false` (by default, it is `true`).
|
||||
The `<http:outbound-gateway/>` and `<http:outbound-channel-adapter/>` 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 `<uri-variable/>`, 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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user