GH-10830: Add RestClient support for HTTP outbound endpoints#10835
GH-10830: Add RestClient support for HTTP outbound endpoints#10835mailaruns wants to merge 6 commits intospring-projects:mainfrom
Conversation
mailaruns
commented
Feb 24, 2026
artembilan
left a comment
There was a problem hiding this comment.
Very cool!
I left some review, but really nothing critical.
Thank you!
spring-integration-http/src/main/java/org/springframework/integration/http/dsl/Http.java
Outdated
Show resolved
Hide resolved
spring-integration-http/src/main/java/org/springframework/integration/http/dsl/Http.java
Outdated
Show resolved
Hide resolved
...tion-http/src/main/java/org/springframework/integration/http/dsl/HttpMessageHandlerSpec.java
Outdated
Show resolved
Hide resolved
...n/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java
Outdated
Show resolved
Hide resolved
artembilan
left a comment
There was a problem hiding this comment.
Please, run ./gradlew :spring-integration-http:check before pushing changes to the PR.
Thanks
artembilan
left a comment
There was a problem hiding this comment.
Please, rebase your branch to the latest main and run ./gradlew :spring-integration-http:check before pushing.
You have some Checkstyle violations:
Error: eckstyle] [ERROR] /home/runner/work/spring-integration/spring-integration/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java:207: Block tags have to appear in the order '[@param, @return, @throws, @since, @deprecated, @see]'. [AtclauseOrder]
Error: eckstyle] [ERROR] /home/runner/work/spring-integration/spring-integration/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java:222: Block tags have to appear in the order '[@param, @return, @throws, @since, @deprecated, @see]'. [AtclauseOrder]
Error: eckstyle] [ERROR] /home/runner/work/spring-integration/spring-integration/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java:236: Block tags have to appear in the order '[@param, @return, @throws, @since, @deprecated, @see]'. [AtclauseOrder]
|
Addressed in the latest commit. |
artembilan
left a comment
There was a problem hiding this comment.
Your change also deserves a [[x7.1-http-changes]] entry in the whats-new.adoc.
Thanks
...n/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java
Outdated
Show resolved
Hide resolved
...n/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java
Outdated
Show resolved
Hide resolved
artembilan
left a comment
There was a problem hiding this comment.
Still missed whats-new.adoc entry.
And ultimate goal is to not have deprecation warnings at all.
| public static HttpMessageHandlerSpec outboundChannelAdapter(URI uri, @Nullable RestClient restClient) { | ||
| return (restClient != null | ||
| ? new HttpMessageHandlerSpec(uri, restClient).expectReply(false) | ||
| : outboundChannelAdapter(uri, (RestTemplate) null)); |
There was a problem hiding this comment.
Why not RestClient-based delegation?
I believe that way we would avoid one more deprecation warning.
| public static HttpMessageHandlerSpec outboundChannelAdapter(String uri, @Nullable RestClient restClient) { | ||
| return (restClient != null | ||
| ? new HttpMessageHandlerSpec(uri, restClient).expectReply(false) | ||
| : outboundChannelAdapter(uri, (RestTemplate) null)); |
| public static HttpMessageHandlerSpec outboundChannelAdapter(Expression uriExpression, @Nullable RestClient restClient) { | ||
| return (restClient != null | ||
| ? new HttpMessageHandlerSpec(uriExpression, restClient).expectReply(false) | ||
| : outboundChannelAdapter(uriExpression, (RestTemplate) null)); |
| public static HttpMessageHandlerSpec outboundGateway(URI uri, @Nullable RestClient restClient) { | ||
| return (restClient != null | ||
| ? new HttpMessageHandlerSpec(uri, restClient) | ||
| : outboundGateway(uri, (RestTemplate) null)); |
| public static HttpMessageHandlerSpec outboundGateway(String uri, @Nullable RestClient restClient) { | ||
| return (restClient != null | ||
| ? new HttpMessageHandlerSpec(uri, restClient) | ||
| : outboundGateway(uri, (RestTemplate) null)); |
spring-integration-http/src/main/java/org/springframework/integration/http/dsl/Http.java
Outdated
Show resolved
Hide resolved
| RestClient.Builder localRestClientBuilder = this.localRestClientBuilder; | ||
| Assert.state(localRestClientBuilder != null, "'localRestClientBuilder' must not be null"); | ||
| localRestClientBuilder.defaultStatusHandler(errorHandler); | ||
| rebuildLocalRestClient(); |
There was a problem hiding this comment.
Why do we have to rebuild client every time?
The pattern in Spring Beans is:
- Create an instance
- Provide properties
- initialize
And that is exactly what happens with our doInit().
If some tests are failing, then they have to be fixed to call afterPropertiesSet().
| public HttpHeaders getHeaders() { | ||
| return headers; | ||
| } | ||
| private final Map<String, Object> attributes = new HashMap<>(); |
There was a problem hiding this comment.
Something is off with indents in this change.
However, I wonder if the change to this test class is legit for your current work.
Maybe this has to be fixed separately?
There was a problem hiding this comment.
The attributes map is now mandatory.
Previously, it was set to null, which caused the CookieTests to fail with a NullPointerException under the current RestClient-based execution path.
This behavior occurs because the RestClient implementation requires the request attributes to be non-null.
Please advise.
| PropertyAccessor dfa = new DirectFieldAccessor(this.handler); | ||
| dfa.setPropertyValue("localRestClientBuilder", null); | ||
| dfa.setPropertyValue("restClient", null); | ||
| dfa.setPropertyValue("restTemplate", template); |
There was a problem hiding this comment.
Please, revise your indents configuration.
We use tabs
artembilan
left a comment
There was a problem hiding this comment.
Please, rebase to the latest main.
Those failures in other modules should go away.
Also, whats-new.adoc might not merge clean, so keep a pulse on it when rebasing.
Thanks
| * @deprecated Since 7.1 in favor of {@link RestClient}-based configuration. | ||
| */ | ||
| @Deprecated(since = "7.1", forRemoval = true) | ||
| @SuppressWarnings("removal") |
There was a problem hiding this comment.
Yeah... That as my thinking, too.
So, can we instead of this @SuppressWarnings("removal") delegate to the RestClient API instead of calling new HttpMessageHandlerSpec(uri, restTemplate) directly?
| * @deprecated Since 7.1 in favor of {@link RestClient}-based configuration. | ||
| */ | ||
| @Deprecated(since = "7.1", forRemoval = true) | ||
| @SuppressWarnings("removal") |
| * @deprecated Since 7.1 in favor of {@link RestClient}-based configuration. | ||
| */ | ||
| @Deprecated(since = "7.1", forRemoval = true) | ||
| @SuppressWarnings("removal") |
There was a problem hiding this comment.
Right. S, revise all these @SuppressWarnings("removal"), please, in favor of RestClient.create(RestTemplate) and resepctive factory delegation.
What I mean specifically: let's try to not call deprecated API from our code as much as possible!
| this(new LiteralExpression(uri)); | ||
| } | ||
|
|
||
| protected HttpMessageHandlerSpec(Expression uriExpression) { |
There was a problem hiding this comment.
Why do we need to have these constructors if we could just follow same @Nullable pattern for new RestClient ctors?
And probably that way we would avoid some if..else in the Http factory.
| private final RestTemplate restTemplate; | ||
|
|
||
| @Nullable | ||
| private volatile RestClient restClient; |
There was a problem hiding this comment.
Please, consider to put @Nullable next to the type it is declared for.
It might work on the whole property yet, but our style and consistency dictates us that it has to just next to the type:
private volatile @Nullable RestClient restClient;
| @SuppressWarnings("removal") | ||
| public HttpRequestExecutingMessageHandler(String uri) { | ||
| this(uri, null); | ||
| this(uri, (RestTemplate) null); |
There was a problem hiding this comment.
OK.
I know you said that many of our tests rely on the RestTemplate as a default environment.
But looking into this, it turns out to be a default behavior for everyone.
We might migrate this to the RestClient, but whoever uses this class with such a default ctor, would continue blindly exchange with a RestTemplate.
I wonder if we really should put an effort and migrate those our tests to the RestClient and have this class relying on the RestClient by default.
If we are going to remove RestTemplate eventually anyway, it would be better to have tests based on the RestClient instead of just removing whatever could bring extra coverage benefit.
| public void restClientConfig() { | ||
| HttpRequestExecutingMessageHandler handler = | ||
| (HttpRequestExecutingMessageHandler) this.restClientConfig.getHandler(); | ||
| assertThat(TestUtils.<RestClient>getPropertyValue(handler, "restClient")).isEqualTo(this.customRestClient); |
There was a problem hiding this comment.
I think it is better to use isSameAs().
We also can use here assertThatObject() instead of that generic argument: doesn't look like we need here exact type of that property.
| public void restClientDslFactoryMethods() { | ||
| RestClient restClient = RestClient.create(); | ||
| assertThat(Http.outboundGateway("http://localhost/test", restClient)).isNotNull(); | ||
| assertThat(Http.outboundChannelAdapter("http://localhost/test", restClient)).isNotNull(); |
There was a problem hiding this comment.
I don't think this test makes sense at all.
Those factory methods really don't return null.
It is probably better to have a test which would check for exception thrown from those factories if they could.
Otherwise, please, remove this as redundant.
|
|
||
| The HTTP outbound Java DSL factory methods with nullable `RestClient` now delegate through RestClient-based configuration to avoid deprecated `RestTemplate` fallback paths. | ||
| Also, `HttpRequestExecutingMessageHandler` applies local `RestClient.Builder` options during bean initialization (`afterPropertiesSet()`/`doInit()`), following the standard Spring bean lifecycle pattern. | ||
| See xref:http/outbound.adoc[] and xref:http/java-config.adoc[] for more information. |
There was a problem hiding this comment.
That's too much info in the What's New.
Please, keep it brief like:
The `HttpRequestExecutingMessageHandler` (its XML and Java DSL) now can be used with a `RestClient`.
The `RestTemplate` configuration is deprecated.
See xref:http.adoc[] for more information.
And move this section to the end of this file.
artembilan
left a comment
There was a problem hiding this comment.
It is strange to have all my main changes in the PR.
Looks like you have done merge, but not rebase.
Please, try to fix your branch: https://stackoverflow.com/questions/804115/when-do-you-use-git-rebase-instead-of-git-merge
…points
Fixes: spring-projectsgh-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
Signed-off-by: Arun Sethumadhavan <mailaruns@gmail.com>
…n and align with RestClient-first APIs Fixes: spring-projectsgh-10830 * 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 Signed-off-by: Arun Sethumadhavan <mailaruns@gmail.com>
Signed-off-by: Arun Sethumadhavan <mailaruns@gmail.com>
…on with RestClient.Builder Fixes: spring-projectsgh-10830 * 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) Signed-off-by: Arun Sethumadhavan <mailaruns@gmail.com>
…client lifecycle Fixes spring-projectsgh-10830 * 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 Signed-off-by: Arun Sethumadhavan <mailaruns@gmail.com>
Fixes: spring-projectsgh-10830 * 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 <mailaruns@gmail.com>