Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add native-image support for RestTemplateBuilder #31888

Closed
mhalbritter opened this issue Jul 27, 2022 · 5 comments
Closed

Add native-image support for RestTemplateBuilder #31888

mhalbritter opened this issue Jul 27, 2022 · 5 comments
Assignees
Labels
theme: aot An issue related to Ahead-of-time processing type: enhancement A general enhancement
Milestone

Comments

@mhalbritter
Copy link
Contributor

When using RestTemplateBuilder in a native-image, for example in this code:

private final RestTemplateBuilder restTemplateBuilder;

// ...

RestTemplate restTemplate = this.restTemplateBuilder
  .setConnectTimeout(Duration.ofSeconds(5))
  .setReadTimeout(Duration.ofSeconds(5))
  .build();

then this fails in a native image with this stacktrace:

java.lang.IllegalStateException: Request factory class org.springframework.http.client.SimpleClientHttpRequestFactory does not have a suitable setConnectTimeout method
	at org.springframework.boot.web.client.RestTemplateBuilder$RequestFactoryCustomizer.findMethod(RestTemplateBuilder.java:782)
	at org.springframework.boot.web.client.RestTemplateBuilder$RequestFactoryCustomizer.setConnectTimeout(RestTemplateBuilder.java:761)
	at org.springframework.boot.web.client.RestTemplateBuilder$RequestFactoryCustomizer.accept(RestTemplateBuilder.java:736)
	at org.springframework.boot.web.client.RestTemplateBuilder.buildRequestFactory(RestTemplateBuilder.java:656)
	at org.springframework.boot.web.client.RestTemplateBuilder.configure(RestTemplateBuilder.java:614)
	at org.springframework.boot.web.client.RestTemplateBuilder.build(RestTemplateBuilder.java:589)
	at com.example.resttemplate.CLR.http(CLR.java:33)
	at com.example.resttemplate.CLR.run(CLR.java:26)
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:768)
	at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
	at com.example.resttemplate.RestTemplateApplication.main(RestTemplateApplication.java:13)

This can be observed in https://ci.spring.io/teams/spring-aot-smoke-tests/pipelines/spring-aot-smoke-tests-1.0.x/jobs/rest-template/builds/1

@mhalbritter mhalbritter added type: enhancement A general enhancement theme: aot An issue related to Ahead-of-time processing labels Jul 27, 2022
@mhalbritter mhalbritter added this to the 3.0.x milestone Jul 27, 2022
@mhalbritter
Copy link
Contributor Author

mhalbritter commented Jul 27, 2022

The interface org.springframework.http.client.ClientHttpRequestFactory in Spring Framework doesn't have the methods

  • setConnectTimeout
  • setReadTimeout
  • setBufferRequestBody

which the RestTemplateBuilder needs to set. Some of the implementing classes have these methods, for example:

  • org.springframework.http.client.HttpComponentsClientHttpRequestFactory
  • org.springframework.http.client.OkHttp3ClientHttpRequestFactory - this one doesn't have setBufferRequestBody
  • org.springframework.http.client.SimpleClientHttpRequestFactory
  • org.springframework.http.client.Netty4ClientHttpRequestFactory - this one doesn't have setBufferRequestBody

I wonder if we should introduce a new interface in Spring Framework for configurable ClientHttpRequestFactorys, which would remove this reflection call.

If this isn't possible, we have to include the methods on the implementing classes in the reflection metadata.

@mhalbritter
Copy link
Contributor Author

We could add an interface in Boot with multiple implementations for every HttpRequestFactory type to get rid of the reflective call, too.

@mhalbritter mhalbritter self-assigned this Aug 1, 2022
@mhalbritter
Copy link
Contributor Author

I decided to not refactor the reflection away and went for the RuntimeHints way instead. I made ClientHttpRequestFactorySupplier more native-friendly by introducting _PRESENT constants.

@mhalbritter mhalbritter modified the milestones: 3.0.x, 3.0.0-M5 Aug 1, 2022
@akefirad
Copy link

@mhalbritter does it make sense to refactor the following to add hints only if the method exists:

private void registerReflectionHints(ReflectionHints hints,
	Class<? extends ClientHttpRequestFactory> requestFactoryType, Consumer<TypeHint.Builder> hintCustomizer) {
  hints.registerType(requestFactoryType, (typeHint) -> {
	typeHint.withMethod("setConnectTimeout", TypeReference.listOf(int.class), ExecutableMode.INVOKE);
	typeHint.withMethod("setReadTimeout", TypeReference.listOf(int.class), ExecutableMode.INVOKE);
	typeHint.withMethod("setBufferRequestBody", TypeReference.listOf(boolean.class), ExecutableMode.INVOKE);
	hintCustomizer.accept(typeHint);
  });
}

Since it generates warning while building the native image if for example setBufferRequestBody for OkHttp3ClientHttpRequestFactory:

Warning: Method org.springframework.http.client.OkHttp3ClientHttpRequestFactory.setBufferRequestBody(boolean) not found.

If not, Is there any workaround on the application side to fix/clean-up the hint so that we don't see the warning?

@snicoll
Copy link
Member

snicoll commented Nov 15, 2022

Good catch @akefirad, I've created #33203

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
theme: aot An issue related to Ahead-of-time processing type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

3 participants