2

I have following code:

LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();

        FileSystemResource fileSystemResource = new FileSystemResource(fileNameWithPath);
 map.add("file", fileSystemResource);
        map.add("type", fileType);
        map.add("org_id", systemSettingsService.getSystemSettings().getOrganizationId());
        map.add("stone_id", stoneId);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map, headers);

restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class)

If I provide all parameters correct - enpoint returns 200 http stsatus code as expected as a Postman application.

But if I provide wrong url - I see exception:

 org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://external_server/api/v1/push_file1": Broken pipe (Write failed); nested exception is java.net.SocketException: Broken pipe (Write failed)
 at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:666)
 at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
 at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:531)
 at com.pack.GenericDataServiceImpl.pushAttachment(GenericDataServiceImpl.java:311)
 at com.pack.FailedAttachmentPushReSender.retryFailedPushes(FailedAttachmentPushReSender.java:24)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:498)
 at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65)
 at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
 at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
 at java.util.concurrent.Executors$RunnableAdapter.call$$$capture(Executors.java:511)
 at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java)
 at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
 at java.util.concurrent.FutureTask.run(FutureTask.java)
 at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
 at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
 at java.lang.Thread.run(Thread.java:748) Caused by: java.net.SocketException: Broken pipe (Write failed)
 at java.net.SocketOutputStream.socketWrite0(Native Method)
 at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:111)
 at java.net.SocketOutputStream.write(SocketOutputStream.java:155)
 at sun.security.ssl.OutputRecord.writeBuffer(OutputRecord.java:431)
 at sun.security.ssl.OutputRecord.write(OutputRecord.java:417)
 at sun.security.ssl.SSLSocketImpl.writeRecordInternal(SSLSocketImpl.java:886)
 at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:857)
 at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:123)
 at org.apache.http.impl.io.SessionOutputBufferImpl.streamWrite(SessionOutputBufferImpl.java:124)
 at org.apache.http.impl.io.SessionOutputBufferImpl.write(SessionOutputBufferImpl.java:160)
 at org.apache.http.impl.io.ContentLengthOutputStream.write(ContentLengthOutputStream.java:113)
 at org.apache.http.entity.ByteArrayEntity.writeTo(ByteArrayEntity.java:114)
 at org.apache.http.impl.DefaultBHttpClientConnection.sendRequestEntity(DefaultBHttpClientConnection.java:156)
 at org.apache.http.impl.conn.CPoolProxy.sendRequestEntity(CPoolProxy.java:160)
 at org.apache.http.protocol.HttpRequestExecutor.doSendRequest(HttpRequestExecutor.java:238)
 at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:123)
 at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:272)
 at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
 at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
 at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:111)
 at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
 at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
 at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
 at org.springframework.http.client.HttpComponentsClientHttpRequest.executeInternal(HttpComponentsClientHttpRequest.java:89)
 at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
 at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
 at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:652) ... 20 more 

But when I modify url in postman at the same manner - I see 404 error:

enter image description here

I want to have response like in postman and response as in postman.

In case if I provide File instead of FileSystemResource I get the correct HttpStatusException where I can extract data I want:

catch (HttpStatusCodeException e) {          
            String response = e.getResponseBodyAsString();
            logger.error("Error during attachment push for file: {}, responseText: {}", fileNameWithPath, response, e);
}

How can I write universal code which will extract response and httpStatusCode ?

P.S.1.

RestTemplate initialization:

CloseableHttpClient httpClient
                = HttpClients.custom()
                .setSSLHostnameVerifier(new NoopHostnameVerifier())
                .build();
        HttpComponentsClientHttpRequestFactory requestFactory
                = new HttpComponentsClientHttpRequestFactory();
        requestFactory.setHttpClient(httpClient);
        restTemplate = new RestTemplate(requestFactory);

P.S.2

I've read RestTemplate ClientHttpResponse.getBody() throws I/O Error but looks like already use suggested factory

P.S.3

I tried this:

CloseableHttpClient client = HttpClients.createDefault();
            HttpPost httpPost = new HttpPost(buildAttachmentPushUrl());

            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            builder.addBinaryBody("file", fileSystemResource.getFile(), ContentType.APPLICATION_OCTET_STREAM, fileSystemResource.getFilename());
            builder.addTextBody("type", fileType);
            builder.addTextBody("org_id", systemSettingsService.getSystemSettings().getOrganizationId());
            builder.addTextBody("stone_id", stoneId);

            HttpEntity multipart = builder.build();

            httpPost.setEntity(multipart);

            CloseableHttpResponse response = client.execute(httpPost);
            logger.info("response code {}", response.getStatusLine().getStatusCode());

            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

But result the same - socket write error

gstackoverflow
  • 36,709
  • 117
  • 359
  • 710
  • "In case if I provide File instead of FileSystemResource I get the correct HttpStatusException where I can extract data I want" so why not use that? you can call .getFile() on it to get underlying File object. – eis Jan 18 '18 at 21:24
  • @eis, I use it but I in case if I provide wrong url I get another exception - **org.springframework.web.client.ResourceAccessException** – gstackoverflow Jan 18 '18 at 21:30
  • now you're contradicting. You just wrote that you get correct HttpStatusException if you use a File? – eis Jan 18 '18 at 21:31
  • @eis, you are correct. Also I get another exception in case of bad URL – gstackoverflow Jan 18 '18 at 21:37
  • I'm not following. If you use File, what is the problem, if you get the correct exception? – eis Jan 18 '18 at 21:40
  • @eis I want get raw responses in all cases - not only for incorect *file* parameter – gstackoverflow Jan 18 '18 at 22:28
  • so you don't get raw responses with File parameter? you should get them with using ResponseEntity return type. have you tried your code outside camel (which you seem to be using), do you see the same issue? sorry for insisting on questions, just trying to get this clarified :) – eis Jan 19 '18 at 09:11
  • @eis, please read the topic from the begining. looks like you don't understand my issue. I get raw response in case if I provide wrong parameter(for example File(incorrect) instead of FileSystemResource(correct)). But if I provide wrong URL I expect to get 404 http status code anf thus catch HttpStatusCodeException. HttpStatusCodeException allows me to retrieve http status code and response body. But (!important) in case of wrong URL actually I get *ResourceAccessException* which doesn't allow me to get http status code and response body. – gstackoverflow Jan 19 '18 at 09:22
  • I fully understand the exception issue. I guess the confusion is that you wrote that with File type you get the things that you want, *not* that File wouldn't work for correct url. If it doesn't, that's a key information that was left out. – eis Jan 19 '18 at 09:46
  • @eis, I want to get response body and http status code in any case. Is it wrong? For case when I use FileSystemResource(correct) anf incorrect url - I expect to get 404 response code – gstackoverflow Jan 19 '18 at 09:53
  • No, there's nothing wrong about it, and that's how it should work. It might be that there is a bug somewhere, *or* there is something wrong with your FileSystemResource parameter, so HTTP request never happens. Maybe if you could add to your question what kind of path you actually have in `fileNameWithPath` variable, if it would hint to some problem with that. – eis Jan 19 '18 at 09:56
  • You could also test with ClassPathResource, pointing to resource inside your .jar (if you have one), if it would work better than FileSystemResource. – eis Jan 19 '18 at 09:57
  • @eis in case if leave everything as is - I get 200 http status with correct response body thus I am sure that FileSystemResource is correct, but if I change only URL I get ResourceAccesException – gstackoverflow Jan 19 '18 at 09:59
  • yes, I understand this, but you could still try the suggestion(s) I had to help understanding the issue. It might be that there is a problem with error handling only for example, related to specific parameters. – eis Jan 19 '18 at 10:01
  • @eis What can I do with this suggestion? – gstackoverflow Jan 19 '18 at 10:03
  • ? like I wrote, "You could also test with ClassPathResource, pointing to resource inside your .jar (if you have one), if it would work better than FileSystemResource. " and " if you could add to your question what kind of path you actually have in fileNameWithPath variable". – eis Jan 19 '18 at 10:46
  • @eis, hmm, **ClassPathResource** is working but this file is not on classpath – gstackoverflow Jan 19 '18 at 12:41
  • @eis I've found out that this exception happens from time to time independently on the resource(FileSystemResource or ClassPathResource). I see that the same code behave differently on different PCs and on single PC from time to time. It is weird – gstackoverflow Jan 19 '18 at 15:19
  • Possible duplicate of [Official reasons for "Software caused connection abort: socket write error"](https://stackoverflow.com/questions/2126607/official-reasons-for-software-caused-connection-abort-socket-write-error) – OrangeDog Jan 19 '18 at 15:25
  • I think it is less weird than the original description. It would have been more strange if it would consistently have worked with postman but failed with code. Since it occurs only from time to time, it sounds like a network, possibly network appliance issue which is not related to Spring. – eis Jan 20 '18 at 10:01
  • I also now run your latter code (the one without RestTemplate) against a file upload site and it worked out just fine, I'm able to get response http headers also for 404s. – eis Jan 20 '18 at 11:02
  • @eis, It is phantom behaviour, unfortunately – gstackoverflow Jan 21 '18 at 06:52

2 Answers2

2

How can I get real http code and response body using restTemplate?

The short answer is you cannot, because there isn't one.

org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://external_server.ventures/api/v1/push_file1": Software caused connection abort: socket write error; nested exception is java.net.SocketException: Software caused connection abort: socket write error

Read the exception message. The cause (for which you have omitted the stacktrace) is a java.net.SocketException with an underlying socket write error.

You never received a response because you probably never finished sending a request. See the linked question for some reasons why.


RestClientResponseException is what you want to catch. It covers all errors related to responses that you may receive, and provides access to the status, headers and body. What you've got being thrown is ResourceAccessException, which covers all the I/O errors that can happen and don't have any response.

UML diagram of RestClientException and subclasses

OrangeDog
  • 36,653
  • 12
  • 122
  • 207
  • **As you've not provided the actual exception details, there's nothing to be said.** - which details are are you expecting? – gstackoverflow Jan 19 '18 at 15:48
  • @gstackoverflow The stacktrace for the SocketException. – OrangeDog Jan 19 '18 at 15:49
  • added full stacktrace into the topic – gstackoverflow Jan 19 '18 at 20:28
  • 1
    @gstackoverflow that's a different one. It says "Broken pipe", not "Software caused connection abort". – OrangeDog Jan 19 '18 at 20:39
  • this answer should possibly say something like "you can get the headers if a http response exists, the way you're already doing - however, you're experiencing a case where there isn't one, since connection breaks before such response is provided". It's the same thing you're essentially saying already, but to make it more clear for future readers. Upvoted for pointing out the actual problem here. – eis Jan 20 '18 at 11:04
0

I normally use this pattern, may be it works for you

try{
    ResponseEntity<String> responseEntity = restTemplate.exchange(url, httpMethod, httpEntity, String.class);
    HttpStatus.Series responseSeries = responseEntity.getStatusCode().series();
    if(!HttpStatus.Series.SERVER_ERROR.equals(responseSeries)){
        return responseEntity;
    }
}
catch(HttpClientErrorException e){
    //handle exception here
    //Response received from server: {} ", e.getResponseBodyAsString());
}
catch (RestClientException e) {
    //handle exception here
}
catch (Exception e) {
    //handle exception here
}
gargkshitiz
  • 2,130
  • 17
  • 19
  • this won't help with the problem described in the question. The problem is not catching the exception, the problem is that there is an exception thrown that doesn't contain the HTTP response. – eis Jan 20 '18 at 10:45