Skip to content

Commit e9ff3bb

Browse files
committed
DefaultResponseErrorHandler delegate methods declared as protected
Also revises copyToByteArray/String in FileCopyUtils/StreamUtils for lenient null handling. Issue: SPR-15329 (cherry picked from commit ab7db41)
1 parent 57c8c75 commit e9ff3bb

File tree

4 files changed

+107
-67
lines changed

4 files changed

+107
-67
lines changed

spring-core/src/main/java/org/springframework/util/FileCopyUtils.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -60,8 +60,9 @@ public abstract class FileCopyUtils {
6060
public static int copy(File in, File out) throws IOException {
6161
Assert.notNull(in, "No input File specified");
6262
Assert.notNull(out, "No output File specified");
63+
6364
return copy(new BufferedInputStream(new FileInputStream(in)),
64-
new BufferedOutputStream(new FileOutputStream(out)));
65+
new BufferedOutputStream(new FileOutputStream(out)));
6566
}
6667

6768
/**
@@ -73,6 +74,7 @@ public static int copy(File in, File out) throws IOException {
7374
public static void copy(byte[] in, File out) throws IOException {
7475
Assert.notNull(in, "No input byte array specified");
7576
Assert.notNull(out, "No output File specified");
77+
7678
ByteArrayInputStream inStream = new ByteArrayInputStream(in);
7779
OutputStream outStream = new BufferedOutputStream(new FileOutputStream(out));
7880
copy(inStream, outStream);
@@ -86,6 +88,7 @@ public static void copy(byte[] in, File out) throws IOException {
8688
*/
8789
public static byte[] copyToByteArray(File in) throws IOException {
8890
Assert.notNull(in, "No input File specified");
91+
8992
return copyToByteArray(new BufferedInputStream(new FileInputStream(in)));
9093
}
9194

@@ -105,6 +108,7 @@ public static byte[] copyToByteArray(File in) throws IOException {
105108
public static int copy(InputStream in, OutputStream out) throws IOException {
106109
Assert.notNull(in, "No InputStream specified");
107110
Assert.notNull(out, "No OutputStream specified");
111+
108112
try {
109113
return StreamUtils.copy(in, out);
110114
}
@@ -132,6 +136,7 @@ public static int copy(InputStream in, OutputStream out) throws IOException {
132136
public static void copy(byte[] in, OutputStream out) throws IOException {
133137
Assert.notNull(in, "No input byte array specified");
134138
Assert.notNull(out, "No OutputStream specified");
139+
135140
try {
136141
out.write(in);
137142
}
@@ -147,11 +152,15 @@ public static void copy(byte[] in, OutputStream out) throws IOException {
147152
/**
148153
* Copy the contents of the given InputStream into a new byte array.
149154
* Closes the stream when done.
150-
* @param in the stream to copy from
151-
* @return the new byte array that has been copied to
155+
* @param in the stream to copy from (may be {@code null} or empty)
156+
* @return the new byte array that has been copied to (possibly empty)
152157
* @throws IOException in case of I/O errors
153158
*/
154159
public static byte[] copyToByteArray(InputStream in) throws IOException {
160+
if (in == null) {
161+
return new byte[0];
162+
}
163+
155164
ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE);
156165
copy(in, out);
157166
return out.toByteArray();
@@ -173,6 +182,7 @@ public static byte[] copyToByteArray(InputStream in) throws IOException {
173182
public static int copy(Reader in, Writer out) throws IOException {
174183
Assert.notNull(in, "No Reader specified");
175184
Assert.notNull(out, "No Writer specified");
185+
176186
try {
177187
int byteCount = 0;
178188
char[] buffer = new char[BUFFER_SIZE];
@@ -208,6 +218,7 @@ public static int copy(Reader in, Writer out) throws IOException {
208218
public static void copy(String in, Writer out) throws IOException {
209219
Assert.notNull(in, "No input String specified");
210220
Assert.notNull(out, "No Writer specified");
221+
211222
try {
212223
out.write(in);
213224
}
@@ -223,11 +234,15 @@ public static void copy(String in, Writer out) throws IOException {
223234
/**
224235
* Copy the contents of the given Reader into a String.
225236
* Closes the reader when done.
226-
* @param in the reader to copy from
227-
* @return the String that has been copied to
237+
* @param in the reader to copy from (may be {@code null} or empty)
238+
* @return the String that has been copied to (possibly empty)
228239
* @throws IOException in case of I/O errors
229240
*/
230241
public static String copyToString(Reader in) throws IOException {
242+
if (in == null) {
243+
return "";
244+
}
245+
231246
StringWriter out = new StringWriter();
232247
copy(in, out);
233248
return out.toString();

spring-core/src/main/java/org/springframework/util/StreamUtils.java

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -51,11 +51,15 @@ public abstract class StreamUtils {
5151
/**
5252
* Copy the contents of the given InputStream into a new byte array.
5353
* Leaves the stream open when done.
54-
* @param in the stream to copy from
55-
* @return the new byte array that has been copied to
54+
* @param in the stream to copy from (may be {@code null} or empty)
55+
* @return the new byte array that has been copied to (possibly empty)
5656
* @throws IOException in case of I/O errors
5757
*/
5858
public static byte[] copyToByteArray(InputStream in) throws IOException {
59+
if (in == null) {
60+
return new byte[0];
61+
}
62+
5963
ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE);
6064
copy(in, out);
6165
return out.toByteArray();
@@ -64,12 +68,15 @@ public static byte[] copyToByteArray(InputStream in) throws IOException {
6468
/**
6569
* Copy the contents of the given InputStream into a String.
6670
* Leaves the stream open when done.
67-
* @param in the InputStream to copy from
68-
* @return the String that has been copied to
71+
* @param in the InputStream to copy from (may be {@code null} or empty)
72+
* @return the String that has been copied to (possibly empty)
6973
* @throws IOException in case of I/O errors
7074
*/
7175
public static String copyToString(InputStream in, Charset charset) throws IOException {
72-
Assert.notNull(in, "No InputStream specified");
76+
if (in == null) {
77+
return "";
78+
}
79+
7380
StringBuilder out = new StringBuilder();
7481
InputStreamReader reader = new InputStreamReader(in, charset);
7582
char[] buffer = new char[BUFFER_SIZE];
@@ -90,6 +97,7 @@ public static String copyToString(InputStream in, Charset charset) throws IOExce
9097
public static void copy(byte[] in, OutputStream out) throws IOException {
9198
Assert.notNull(in, "No input byte array specified");
9299
Assert.notNull(out, "No OutputStream specified");
100+
93101
out.write(in);
94102
}
95103

@@ -105,6 +113,7 @@ public static void copy(String in, Charset charset, OutputStream out) throws IOE
105113
Assert.notNull(in, "No input String specified");
106114
Assert.notNull(charset, "No charset specified");
107115
Assert.notNull(out, "No OutputStream specified");
116+
108117
Writer writer = new OutputStreamWriter(out, charset);
109118
writer.write(in);
110119
writer.flush();
@@ -121,6 +130,7 @@ public static void copy(String in, Charset charset, OutputStream out) throws IOE
121130
public static int copy(InputStream in, OutputStream out) throws IOException {
122131
Assert.notNull(in, "No InputStream specified");
123132
Assert.notNull(out, "No OutputStream specified");
133+
124134
int byteCount = 0;
125135
byte[] buffer = new byte[BUFFER_SIZE];
126136
int bytesRead = -1;
@@ -146,10 +156,14 @@ public static int copy(InputStream in, OutputStream out) throws IOException {
146156
* @since 4.3
147157
*/
148158
public static long copyRange(InputStream in, OutputStream out, long start, long end) throws IOException {
159+
Assert.notNull(in, "No InputStream specified");
160+
Assert.notNull(out, "No OutputStream specified");
161+
149162
long skipped = in.skip(start);
150163
if (skipped < start) {
151-
throw new IOException("Skipped only " + skipped + " bytes out of " + start + " required.");
164+
throw new IOException("Skipped only " + skipped + " bytes out of " + start + " required");
152165
}
166+
153167
long bytesToCopy = end - start + 1;
154168
byte buffer[] = new byte[StreamUtils.BUFFER_SIZE];
155169
while (bytesToCopy > 0) {
@@ -166,7 +180,7 @@ else if (bytesRead <= bytesToCopy) {
166180
bytesToCopy = 0;
167181
}
168182
}
169-
return end - start + 1 - bytesToCopy;
183+
return (end - start + 1 - bytesToCopy);
170184
}
171185

172186
/**
@@ -248,4 +262,5 @@ public void write(byte[] b, int off, int let) throws IOException {
248262
public void close() throws IOException {
249263
}
250264
}
265+
251266
}
Lines changed: 58 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@
1717
package org.springframework.web.client;
1818

1919
import java.io.IOException;
20-
import java.io.InputStream;
2120
import java.nio.charset.Charset;
2221

2322
import org.springframework.http.HttpHeaders;
@@ -27,13 +26,12 @@
2726
import org.springframework.util.FileCopyUtils;
2827

2928
/**
30-
* Default implementation of the {@link ResponseErrorHandler} interface.
29+
* Spring's default implementation of the {@link ResponseErrorHandler} interface.
3130
*
32-
* <p>This error handler checks for the status code on the {@link ClientHttpResponse}: any
33-
* code with series {@link org.springframework.http.HttpStatus.Series#CLIENT_ERROR} or
31+
* <p>This error handler checks for the status code on the {@link ClientHttpResponse}:
32+
* Any code with series {@link org.springframework.http.HttpStatus.Series#CLIENT_ERROR} or
3433
* {@link org.springframework.http.HttpStatus.Series#SERVER_ERROR} is considered to be an
35-
* error. This behavior can be changed by overriding the {@link #hasError(HttpStatus)}
36-
* method.
34+
* error. This behavior can be changed by overriding the {@link #hasError(HttpStatus)} method.
3735
*
3836
* @author Arjen Poutsma
3937
* @author Rossen Stoyanchev
@@ -50,16 +48,45 @@ public boolean hasError(ClientHttpResponse response) throws IOException {
5048
return hasError(getHttpStatusCode(response));
5149
}
5250

53-
private HttpStatus getHttpStatusCode(ClientHttpResponse response) throws IOException {
54-
HttpStatus statusCode;
51+
/**
52+
* This default implementation throws a {@link HttpClientErrorException} if the response status code
53+
* is {@link org.springframework.http.HttpStatus.Series#CLIENT_ERROR}, a {@link HttpServerErrorException}
54+
* if it is {@link org.springframework.http.HttpStatus.Series#SERVER_ERROR},
55+
* and a {@link RestClientException} in other cases.
56+
*/
57+
@Override
58+
public void handleError(ClientHttpResponse response) throws IOException {
59+
HttpStatus statusCode = getHttpStatusCode(response);
60+
switch (statusCode.series()) {
61+
case CLIENT_ERROR:
62+
throw new HttpClientErrorException(statusCode, response.getStatusText(),
63+
response.getHeaders(), getResponseBody(response), getCharset(response));
64+
case SERVER_ERROR:
65+
throw new HttpServerErrorException(statusCode, response.getStatusText(),
66+
response.getHeaders(), getResponseBody(response), getCharset(response));
67+
default:
68+
throw new RestClientException("Unknown status code [" + statusCode + "]");
69+
}
70+
}
71+
72+
73+
/**
74+
* Determine the HTTP status of the given response.
75+
* @param response the response to inspect
76+
* @return the associated HTTP status
77+
* @throws IOException in case of I/O errors
78+
* @throws UnknownHttpStatusCodeException in case of an unknown status code
79+
* that cannot be represented with the {@link HttpStatus} enum
80+
* @since 4.3.8
81+
*/
82+
protected HttpStatus getHttpStatusCode(ClientHttpResponse response) throws IOException {
5583
try {
56-
statusCode = response.getStatusCode();
84+
return response.getStatusCode();
5785
}
5886
catch (IllegalArgumentException ex) {
59-
throw new UnknownHttpStatusCodeException(response.getRawStatusCode(),
60-
response.getStatusText(), response.getHeaders(), getResponseBody(response), getCharset(response));
87+
throw new UnknownHttpStatusCodeException(response.getRawStatusCode(), response.getStatusText(),
88+
response.getHeaders(), getResponseBody(response), getCharset(response));
6189
}
62-
return statusCode;
6390
}
6491

6592
/**
@@ -70,50 +97,40 @@ private HttpStatus getHttpStatusCode(ClientHttpResponse response) throws IOExcep
7097
* Can be overridden in subclasses.
7198
* @param statusCode the HTTP status code
7299
* @return {@code true} if the response has an error; {@code false} otherwise
100+
* @see #getHttpStatusCode(ClientHttpResponse)
73101
*/
74102
protected boolean hasError(HttpStatus statusCode) {
75103
return (statusCode.series() == HttpStatus.Series.CLIENT_ERROR ||
76104
statusCode.series() == HttpStatus.Series.SERVER_ERROR);
77105
}
78106

79107
/**
80-
* This default implementation throws a {@link HttpClientErrorException} if the response status code
81-
* is {@link org.springframework.http.HttpStatus.Series#CLIENT_ERROR}, a {@link HttpServerErrorException}
82-
* if it is {@link org.springframework.http.HttpStatus.Series#SERVER_ERROR},
83-
* and a {@link RestClientException} in other cases.
108+
* Determine the charset of the response (for inclusion in a status exception).
109+
* @param response the response to inspect
110+
* @return the associated charset, or {@code null} if none
111+
* @since 4.3.8
84112
*/
85-
@Override
86-
public void handleError(ClientHttpResponse response) throws IOException {
87-
HttpStatus statusCode = getHttpStatusCode(response);
88-
switch (statusCode.series()) {
89-
case CLIENT_ERROR:
90-
throw new HttpClientErrorException(statusCode, response.getStatusText(),
91-
response.getHeaders(), getResponseBody(response), getCharset(response));
92-
case SERVER_ERROR:
93-
throw new HttpServerErrorException(statusCode, response.getStatusText(),
94-
response.getHeaders(), getResponseBody(response), getCharset(response));
95-
default:
96-
throw new RestClientException("Unknown status code [" + statusCode + "]");
97-
}
113+
protected Charset getCharset(ClientHttpResponse response) {
114+
HttpHeaders headers = response.getHeaders();
115+
MediaType contentType = headers.getContentType();
116+
return (contentType != null ? contentType.getCharset() : null);
98117
}
99118

100-
private byte[] getResponseBody(ClientHttpResponse response) {
119+
/**
120+
* Read the body of the given response (for inclusion in a status exception).
121+
* @param response the response to inspect
122+
* @return the response body as a byte array,
123+
* or an empty byte array if the body could not be read
124+
* @since 4.3.8
125+
*/
126+
protected byte[] getResponseBody(ClientHttpResponse response) {
101127
try {
102-
InputStream responseBody = response.getBody();
103-
if (responseBody != null) {
104-
return FileCopyUtils.copyToByteArray(responseBody);
105-
}
128+
return FileCopyUtils.copyToByteArray(response.getBody());
106129
}
107130
catch (IOException ex) {
108131
// ignore
109132
}
110133
return new byte[0];
111134
}
112135

113-
private Charset getCharset(ClientHttpResponse response) {
114-
HttpHeaders headers = response.getHeaders();
115-
MediaType contentType = headers.getContentType();
116-
return contentType != null ? contentType.getCharset() : null;
117-
}
118-
119136
}

0 commit comments

Comments
 (0)