14

I have this piece of code working fine on a project that uses RestTemplateBuilder 1.5.14

this.restTemplate = restTemplateBuilder
                .setConnectTimeout(connectTimeout)
                .setReadTimeout(readTimeout)
                .requestFactory(new MyHttpComponentFactoryBuilder()
                        .build())
                .build();

After updating to RestTemplateBuilder 2.1.5 I have this piece of code:

this.restTemplate = restTemplateBuilder
                .setConnectTimeout(Duration.ofMillis(connectTimeout))
                .setReadTimeout(Duration.ofMillis(readTimeout))
                .requestFactory(new MyHttpComponentFactoryBuilder().build().getClass())
                .build();

but when running the code I have a InvocationTargetException / NullPointerException that dissapears when deleting the line .requestFactory(new MyHttpComponentFactoryBuilder().build().getClass()) , but debugging new MyHttpComponentFactoryBuilder().build().getClass() is not null

I also tried with the solution proposed:

... 
.requestFactory(new MyRequestFactorySupplier())
...

class MyRequestFactorySupplier implements Supplier<ClientHttpRequestFactory> {

        @Override
        public ClientHttpRequestFactory get() {

            // Using Apache HTTP client.
            HttpClientBuilder clientBuilder = HttpClientBuilder.create();
            HttpClient httpClient = clientBuilder.build();
            HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
            requestFactory.setBufferRequestBody(false); // When sending large amounts of data via POST or PUT, it is recommended to change this property to false, so as not to run out of memory.
            return requestFactory;
        }

    }

but I have also a InvocationTargetException / NullPointerException

Sandro Rey
  • 2,429
  • 13
  • 36
  • 80
  • 3
    does this help? https://stackoverflow.com/questions/53511471/custom-resttemplate-using-requestfactory-of-resttemplatebuilder-in-springboot-2 – Majid Roustaei May 23 '20 at 17:59
  • No thanks, I have the same error – Sandro Rey May 24 '20 at 17:23
  • Please update the question with complete error log when you use `.requestFactory(new MyRequestFactorySupplier())`. (This works fine for me) – Smile May 26 '20 at 07:10
  • 1
    Post a reproducible code. I can not reproduce this on my end. Chances are that you would end up solving it on your own while creating a reproduceable code. You can also use github to post it. – Aniket Sahrawat May 26 '20 at 09:25
  • 1
    Check that - `new MyHttpComponentFactoryBuilder().build().getClass()` is not null (but you already did) - the class returned by `new MyHttpComponentFactoryBuilder().build().getClass()` has a no args constructor Also, if you include the full stack trace it will be easy to diagnose, and possibly the `MyHttpComponentFactoryBuilder` code. – Francesco Pitzalis May 26 '20 at 11:52
  • The only stacktrace I got is a nullPointer on the requestFactory method from the restTemplate builder – Sandro Rey May 26 '20 at 13:56
  • 1
    If it works while debugging, but does not when running normally, there might be some [race condition](https://en.wikipedia.org/wiki/Race_condition#Software). Can you post the `MyHttpComponentFactoryBuilder` code? – Šimon Kocúrek May 26 '20 at 17:14
  • please add code base for testing – Anish B. May 26 '20 at 17:25
  • Perhaps theres somethin in the constructor or build method of MyHttpComponentFactoryBuilder which is causing the error? – Tarmo May 26 '20 at 18:35
  • 1
    Did you upgrade all your Spring dependencies accordingly? You updated a major version, so chances are that you need to update other Spring dependencies to avoid compatibility problems. – Gustavo Passini May 26 '20 at 19:52
  • Please update de question with your maven or gradlle config, I think you have some conflcit with the dependencies – fmodos May 26 '20 at 23:19
  • 1
    NPE's can't be trusted, they don't pin point the exact location and it is a bit of a pain to debug such code. Java 14 has a preview feature to pin point it but don't use it for production. You should enable it just for debugging, know the cause and solve it. Then back to the java version you want. See [JEP](https://openjdk.java.net/jeps/358) – Aniket Sahrawat May 27 '20 at 04:11
  • This can't be answered without the sources of ```MyHttpComponentFactory``` and the maven configuration file. – chriopp May 29 '20 at 06:17

5 Answers5

4

The below code shows how you need to create the template for simple cases.

RestTemplate tmpl = new RestTemplateBuilder().setConnectTimeout(Duration.ofMillis(200))
                                             .setReadTimeout(Duration.ofMillis(100))
                                             .requestFactory(org.springframework.http.client.SimpleClientHttpRequestFactory.class)
                                             .build();

It would be better for you to provide the source code of MyHttpComponentFactoryBuilder class. But my suggestion is that to create a class MyHttpComponentFactory which extends SimpleClientHttpRequestFactory class migrate your codes from MyHttpComponentFactoryBuilder to it.

Serhii Povísenko
  • 3,352
  • 1
  • 30
  • 48
Mehmet Sunkur
  • 2,373
  • 16
  • 22
2

I have wrote detail notes on my github wiki page , please check sure will be helpful

Here is example :

        public String retrieveData(String id, String name) {

            HttpHeaders headers =createHeader();
            String requestJson = "{\"name\":\"" + name + "\"}";
            HttpEntity<String> request = new HttpEntity<String>(requestJson, headers);
            // external call time
            long startTime = System.currentTimeMillis();
            ResponseEntity<String> response = customRestTemplate().exchange(externalUrl, HttpMethod.POST, request,   
                    String.class);
            long endTime = System.currentTimeMillis();
            long duration = (endTime - startTime); // divide by 1000000 to get milliseconds.
            log.info("{\"RestTemplateDemo\":{\"id\":\"" + id + "\",\"external call duration\":" + duration + "}}");
            ObjectMapper mapper = new ObjectMapper();
            return response.getBody();
        }
vaquar khan
  • 10,864
  • 5
  • 72
  • 96
1

You don't need a builder here anymore as the supplier builds the ClientHttpRequestFactory on demand. Define the RequestFactorySupplier and the RestTemplateBuilder as beans.

    @Bean
    Supplier<ClientHttpRequestFactory> myRequestFactorySupplier() {
        return () -> {
            HttpClientBuilder clientBuilder = HttpClientBuilder.create();
            HttpClient httpClient = clientBuilder.build();
            HttpComponentsClientHttpRequestFactory requestFactory = 
                    new HttpComponentsClientHttpRequestFactory(httpClient);
            requestFactory.setConnectTimeout(5000);
            requestFactory.setReadTimeout(5000);
            requestFactory.setBufferRequestBody(false);
            return requestFactory;
        };
    }

    @Bean
    public RestTemplateBuilder restTemplateBuilder() {
        return new RestTemplateBuilder();
    }

Use the factory in the controller like this.

@RestController
public class TestController {

    private final RestTemplate restTemplate;

    public TestController(
            RestTemplateBuilder restTemplateBuilder,
            Supplier<ClientHttpRequestFactory> myRequestFactorySupplier
    ) {
            this.restTemplate = restTemplateBuilder                
                .requestFactory(myRequestFactorySupplier)
                .build();
    }

Of course you could also use a specialized class.

public class MyRequestFactorySupplier implements Supplier<ClientHttpRequestFactory> {

        @Override
        public ClientHttpRequestFactory get() {
            // Using Apache HTTP client.
            HttpClientBuilder clientBuilder = HttpClientBuilder.create();
            HttpClient httpClient = clientBuilder.build();
            HttpComponentsClientHttpRequestFactory requestFactory = 
                    new HttpComponentsClientHttpRequestFactory(httpClient);
            requestFactory.setBufferRequestBody(false); // When sending large amounts of data via POST or PUT, it is recommended to change this property to false, so as not to run out of memory.
            return requestFactory;
        }

}

using it in the controller as you proposed:

 public TestController(
     RestTemplateBuilder restTemplateBuilder,
     Supplier<ClientHttpRequestFactory> myRequestFactorySupplier
 ) {
     this.restTemplate = restTemplateBuilder
                .setConnectTimeout(Duration.ofMillis(5000))
                .setReadTimeout(Duration.ofMillis(5000))
                .requestFactory(new MyRequestFactorySupplier())
                .build();
 }

chriopp
  • 947
  • 7
  • 12
0

Try removing .getClass() call and if .requestFactory call points to requestFactory(Class<? extends ClientHttpRequestFactory> requestFactory) method of class RestTemplateBuilder then plz share code snippet of your MyHttpComponentFactoryBuilder class

abhinav kumar
  • 1,487
  • 1
  • 12
  • 20
  • The signature of the method is `public RestTemplateBuilder requestFactory(Class extends ClientHttpRequestFactory> requestFactory)`. How can removing the `.getClass()` help? – Francesco Pitzalis May 26 '20 at 11:54
  • we have 2 signatures or we can say overloaded methods `requestFactory(Class extends ClientHttpRequestFactory> requestFactory) requestFactory(Supplier requestFactory)`....thats why i asked the code snippet of your MyHttpComponentFactoryBuilder class as i am unaware of your implementation – abhinav kumar May 26 '20 at 12:32
  • Correct, but a `build()` call in a Builder pattern returns the actual instance, not a Supplier – Francesco Pitzalis May 26 '20 at 13:36
0

This should work (with lambda used)

.requestFactory(() -> new MyHttpComponentFactoryBuilder().build())
Nikolai Shevchenko
  • 7,083
  • 8
  • 33
  • 42