Skip to content

Commit 1cfd8a5

Browse files
mailarunsartembilan
authored andcommitted
GH-10830: Add RestClient support for HTTP outbound endpoints
Fixes: Fixes: #10830 * add `rest-client` XSD attribute for HTTP outbound gateway/channel adapter * extend XML parsers to wire `RestClient` and reject mixed `rest-template` + `rest-client` * add `HttpRequestExecutingMessageHandler` constructors and execution path for `RestClient` * add DSL overloads in `Http` and `HttpMessageHandlerSpec` for preconfigured `RestClient` * guard local client options (`request-factory`, `error-handler`, `message-converters`, `encoding-mode`) when external client is used * add parser/DSL/handler tests, including invalid dual-client configuration cases * update HTTP reference docs (`outbound`, `timeout`, `java-config`) for `RestClient` usage GH-10830: Deprecate RestTemplate outbound configuration and align with RestClient-first APIs * deprecate RestTemplate-based constructors and mutators in `HttpRequestExecutingMessageHandler` (`since = "7.1", forRemoval = true`) * keep current runtime behavior stable in the handler (RestTemplate local path + RestClient external path) to avoid backward-compatibility regressions * deprecate RestTemplate-based DSL factory methods in `Http` and align `RestClient` methods to `7.1` documentation * rework `HttpMessageHandlerSpec` to use `RestClient.create(restTemplate)` for RestTemplate-based construction and simplify client state tracking * deprecate RestTemplate-focused spec mutators (`requestFactory`, `errorHandler`, `messageConverters`) in favor of configuring `RestClient` * update HTTP reference docs (`java-config`, `outbound`, `timeout`) with 7.1 RestClient guidance and RestTemplate deprecation notes * adjust related tests/imports to match deprecation and client-selection changes GH-10830: Align HTTP outbound local client configuration with RestClient.Builder * keep no-client `HttpRequestExecutingMessageHandler` constructors non-deprecated and use `@SuppressWarnings("removal")` where they delegate to deprecated RestTemplate constructors * switch local (no external client) setup to `RestClient.Builder` and build the local `RestClient` in `doInit()` * keep local mutator APIs (`setRequestFactory`, `setErrorHandler`, `setMessageConverters`) and apply them to the local `RestClient.Builder` * rebuild the local `RestClient` after local mutator updates so post-init changes are visible at runtime * retain guards that reject local-only options when an external RestTemplate/RestClient is provided * handle `Void` response type in RestClient exchange path via `toBodilessEntity()` restoring prior behavior for bodiless replies. * make `restClient` `volatile` because it is rebuilt and read across lifecycle/request threads * remove deprecations from matching local mutators in `HttpMessageHandlerSpec` * update outbound/parser/DSL tests to align with local RestClient-backed behavior * adjust proxy and cookie test fixtures for RestClient path (including non-null request attributes in cookie request stub) GH-10830: Refine HTTP RestClient delegation and local client lifecycle * refactor nullable `RestClient` HTTP DSL factory methods to delegate via RestClient-first paths and remove duplicate fallback branches * add shared `outboundGatewaySpec(...)` helpers in `Http` to centralize URI/String/Expression client selection * add non-deprecated local constructors in `HttpMessageHandlerSpec` for URI/String/Expression paths * keep deprecated RestTemplate DSL overloads and add `@SuppressWarnings("removal")` where they intentionally call deprecated constructors * build local `RestClient` once in `HttpRequestExecutingMessageHandler#doInit()` and stop rebuilding on each local mutator call * keep local mutators (`setRequestFactory`, `setErrorHandler`, `setMessageConverters`) applying to local `RestClient.Builder` * align HTTP proxy test flow with lifecycle semantics by re-initializing after mutator-based setup * revise HTTP proxy/cookie test fixtures and formatting updates for the current RestClient-backed local behavior * add `[[x7.1-http-changes]]` section to `whats-new.adoc` documenting HTTP 7.1 updates GH-10830: Refine HTTP RestClient delegation * route deprecated RestTemplate HTTP DSL/spec paths through RestClient.create(RestTemplate) * remove deprecated internal-call suppressions in Http factory methods and simplify nullable RestClient delegation * align HttpRequestExecutingMessageHandler default constructor flow with RestClient-first initialization * switch local message converter setup to non-deprecated RestClient.Builder.configureMessageConverters API * update HTTP parser/DSL tests and reduce deprecated-constructor warnings in outbound handler tests * What's New entry for HTTP RestClient/RestTemplate migration notes Signed-off-by: Arun Sethumadhavan <[email protected]>
1 parent b39f22f commit 1cfd8a5

23 files changed

+763
-231
lines changed

spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpAdapterParsingUtils.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
* @author Gary Russell
3737
* @author Artem Bilan
3838
* @author Shiliang Li
39+
* @author Arun Sethumadhavan
3940
*
4041
* @since 2.0.2
4142
*/
@@ -62,6 +63,23 @@ static void verifyNoRestTemplateAttributes(Element element, ParserContext parser
6263
}
6364
}
6465

66+
static void verifyNoRestClientAttributes(Element element, ParserContext parserContext) {
67+
for (String attributeName : SYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
68+
if (element.hasAttribute(attributeName)) {
69+
parserContext.getReaderContext().error("When providing a 'rest-client' reference, the '"
70+
+ attributeName + "' attribute is not allowed, " +
71+
"it must be set on the provided client instead",
72+
parserContext.extractSource(element));
73+
}
74+
}
75+
76+
if (element.hasAttribute("encoding-mode")) {
77+
parserContext.getReaderContext().error("When providing a 'rest-client' reference, " +
78+
"the 'encoding-mode' must be set on the 'RestClient.Builder.uriBuilderFactory' property.",
79+
parserContext.extractSource(element));
80+
}
81+
}
82+
6583
static void configureUriVariableExpressions(BeanDefinitionBuilder builder, ParserContext parserContext,
6684
Element element) {
6785

@@ -97,6 +115,7 @@ static void configureUriVariableExpressions(BeanDefinitionBuilder builder, Parse
97115

98116
static void configureUrlConstructorArg(Element element, ParserContext parserContext,
99117
BeanDefinitionBuilder builder) {
118+
100119
String urlAttribute = element.getAttribute("url");
101120
String urlExpressionAttribute = element.getAttribute("url-expression");
102121
boolean hasUrlAttribute = StringUtils.hasText(urlAttribute);
@@ -114,6 +133,7 @@ static void configureUrlConstructorArg(Element element, ParserContext parserCont
114133
expressionDef = new RootBeanDefinition(ExpressionFactoryBean.class);
115134
expressionDef.getConstructorArgumentValues().addGenericArgumentValue(urlExpressionAttribute);
116135
}
136+
117137
builder.addConstructorArgValue(expressionDef);
118138

119139
}
@@ -147,6 +167,7 @@ else if (hasHttpMethodExpression) {
147167

148168
static void setExpectedResponseOrExpression(Element element, ParserContext parserContext,
149169
BeanDefinitionBuilder builder) {
170+
150171
String expectedResponseType = element.getAttribute("expected-response-type");
151172
String expectedResponseTypeExpression = element.getAttribute("expected-response-type-expression");
152173

spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParser.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
* @author Gary Russell
3737
* @author Artem Bilan
3838
* @author Shiliang Li
39+
* @author Arun Sethumadhavan
3940
*
4041
* @since 2.0
4142
*/
@@ -78,13 +79,26 @@ protected BeanDefinitionBuilder getBuilder(Element element, ParserContext parser
7879
BeanDefinitionBuilder.genericBeanDefinition(HttpRequestExecutingMessageHandler.class);
7980

8081
String restTemplateRef = element.getAttribute("rest-template");
82+
String restClientRef = element.getAttribute("rest-client");
83+
84+
if (StringUtils.hasText(restTemplateRef) && StringUtils.hasText(restClientRef)) {
85+
parserContext.getReaderContext()
86+
.error("Only one of 'rest-template' and 'rest-client' references is allowed.",
87+
parserContext.extractSource(element));
88+
}
8189

8290
if (StringUtils.hasText(restTemplateRef)) {
8391
HttpAdapterParsingUtils.verifyNoRestTemplateAttributes(element, parserContext);
8492
builder.getBeanDefinition()
8593
.getConstructorArgumentValues()
8694
.addIndexedArgumentValue(1, new RuntimeBeanReference(restTemplateRef));
8795
}
96+
else if (StringUtils.hasText(restClientRef)) {
97+
HttpAdapterParsingUtils.verifyNoRestClientAttributes(element, parserContext);
98+
builder.getBeanDefinition()
99+
.getConstructorArgumentValues()
100+
.addIndexedArgumentValue(1, new RuntimeBeanReference(restClientRef));
101+
}
88102
else {
89103
for (String referenceAttributeName : HttpAdapterParsingUtils.SYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
90104
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName);

spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundGatewayParser.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
* @author Gary Russell
3535
* @author Artem Bilan
3636
* @author Shiliang Li
37+
* @author Arun Sethumadhavan
3738
*/
3839
public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser {
3940

@@ -91,13 +92,26 @@ protected BeanDefinitionBuilder getBuilder(Element element, ParserContext parser
9192
BeanDefinitionBuilder.genericBeanDefinition(HttpRequestExecutingMessageHandler.class);
9293

9394
String restTemplateRef = element.getAttribute("rest-template");
95+
String restClientRef = element.getAttribute("rest-client");
96+
97+
if (StringUtils.hasText(restTemplateRef) && StringUtils.hasText(restClientRef)) {
98+
parserContext.getReaderContext()
99+
.error("Only one of 'rest-template' and 'rest-client' references is allowed.",
100+
parserContext.extractSource(element));
101+
}
94102

95103
if (StringUtils.hasText(restTemplateRef)) {
96104
HttpAdapterParsingUtils.verifyNoRestTemplateAttributes(element, parserContext);
97105
builder.getBeanDefinition()
98106
.getConstructorArgumentValues()
99107
.addIndexedArgumentValue(1, new RuntimeBeanReference(restTemplateRef));
100108
}
109+
else if (StringUtils.hasText(restClientRef)) {
110+
HttpAdapterParsingUtils.verifyNoRestClientAttributes(element, parserContext);
111+
builder.getBeanDefinition()
112+
.getConstructorArgumentValues()
113+
.addIndexedArgumentValue(1, new RuntimeBeanReference(restClientRef));
114+
}
101115
else {
102116
for (String referenceAttributeName : HttpAdapterParsingUtils.SYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
103117
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName);

0 commit comments

Comments
 (0)