diff --git a/spring-web/src/main/java/org/springframework/http/HttpStatus.java b/spring-web/src/main/java/org/springframework/http/HttpStatus.java index b733becaba..9dbe3ff70e 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpStatus.java +++ b/spring-web/src/main/java/org/springframework/http/HttpStatus.java @@ -445,8 +445,8 @@ public enum HttpStatus { return this.value; } - private static Series valueOf(HttpStatus status) { - int seriesCode = status.value() / 100; + public static Series valueOf(int status) { + int seriesCode = status / 100; for (Series series : values()) { if (series.value == seriesCode) { return series; @@ -455,6 +455,10 @@ public enum HttpStatus { throw new IllegalArgumentException("No matching constant for [" + status + "]"); } + public static Series valueOf(HttpStatus status) { + return valueOf(status.value); + } + } } diff --git a/spring-web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java b/spring-web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java index af0b676021..24a2db681f 100644 --- a/spring-web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java +++ b/spring-web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java @@ -36,6 +36,7 @@ import org.springframework.util.FileCopyUtils; * method. * * @author Arjen Poutsma + * @author Rossen Stoyanchev * @since 3.0 * @see RestTemplate#setErrorHandler */ @@ -45,16 +46,17 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler { * Delegates to {@link #hasError(HttpStatus)} with the response status code. */ public boolean hasError(ClientHttpResponse response) throws IOException { - return hasError(getStatusCode(response)); + return hasError(getHttpStatusCode(response)); } - private HttpStatus getStatusCode(ClientHttpResponse response) throws IOException { + private HttpStatus getHttpStatusCode(ClientHttpResponse response) throws IOException { HttpStatus statusCode; try { statusCode = response.getStatusCode(); } catch (IllegalArgumentException ex) { - throw new RestClientException("Unknown status code [" + response.getRawStatusCode() + "]"); + throw new UnknownHttpStatusCodeException(response.getRawStatusCode(), + response.getStatusText(), response.getHeaders(), getResponseBody(response), getCharset(response)); } return statusCode; } @@ -80,16 +82,14 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler { * and a {@link RestClientException} in other cases. */ public void handleError(ClientHttpResponse response) throws IOException { - HttpStatus statusCode = getStatusCode(response); - HttpHeaders headers = response.getHeaders(); - MediaType contentType = headers.getContentType(); - Charset charset = contentType != null ? contentType.getCharSet() : null; - byte[] body = getResponseBody(response); + HttpStatus statusCode = getHttpStatusCode(response); switch (statusCode.series()) { case CLIENT_ERROR: - throw new HttpClientErrorException(statusCode, response.getStatusText(), headers, body, charset); + throw new HttpClientErrorException(statusCode, response.getStatusText(), + response.getHeaders(), getResponseBody(response), getCharset(response)); case SERVER_ERROR: - throw new HttpServerErrorException(statusCode, response.getStatusText(), headers, body, charset); + throw new HttpServerErrorException(statusCode, response.getStatusText(), + response.getHeaders(), getResponseBody(response), getCharset(response)); default: throw new RestClientException("Unknown status code [" + statusCode + "]"); } @@ -108,4 +108,10 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler { return new byte[0]; } + private Charset getCharset(ClientHttpResponse response) { + HttpHeaders headers = response.getHeaders(); + MediaType contentType = headers.getContentType(); + return contentType != null ? contentType.getCharSet() : null; + } + } diff --git a/spring-web/src/main/java/org/springframework/web/client/UnknownHttpStatusCodeException.java b/spring-web/src/main/java/org/springframework/web/client/UnknownHttpStatusCodeException.java new file mode 100644 index 0000000000..648c82d79c --- /dev/null +++ b/spring-web/src/main/java/org/springframework/web/client/UnknownHttpStatusCodeException.java @@ -0,0 +1,108 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.web.client; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; + +/** + * Exception thrown when an unknown (or custom) HTTP status code is received. + * + * @author Rossen Stoyanchev + * @since 3.2 + */ +public class UnknownHttpStatusCodeException extends RestClientException { + + private static final long serialVersionUID = 4702443689088991600L; + + private static final String DEFAULT_CHARSET = "ISO-8859-1"; + + private final int rawStatusCode; + + private final String statusText; + + private final byte[] responseBody; + + private final HttpHeaders responseHeaders; + + private final String responseCharset; + + + /** + * Construct a new instance of {@code HttpStatusCodeException} based on an + * {@link HttpStatus}, status text, and response body content. + * @param rawStatusCode the raw status code value + * @param statusText the status text + * @param responseHeaders the response headers, may be {@code null} + * @param responseBody the response body content, may be {@code null} + * @param responseCharset the response body charset, may be {@code null} + */ + public UnknownHttpStatusCodeException(int rawStatusCode, String statusText, + HttpHeaders responseHeaders, byte[] responseBody, Charset responseCharset) { + + super("Unknown status code [" + String.valueOf(rawStatusCode) + "]" + " " + statusText); + this.rawStatusCode = rawStatusCode; + this.statusText = statusText; + this.responseHeaders = responseHeaders; + this.responseBody = responseBody != null ? responseBody : new byte[0]; + this.responseCharset = responseCharset != null ? responseCharset.name() : DEFAULT_CHARSET; + } + + /** + * Return the raw HTTP status code value. + */ + public int getRawStatusCode() { + return this.rawStatusCode; + } + + /** + * Return the HTTP status text. + */ + public String getStatusText() { + return this.statusText; + } + + /** + * Return the HTTP response headers. + */ + public HttpHeaders getResponseHeaders() { + return this.responseHeaders; + } + + /** + * Return the response body as a byte array. + */ + public byte[] getResponseBodyAsByteArray() { + return responseBody; + } + + /** + * Return the response body as a string. + */ + public String getResponseBodyAsString() { + try { + return new String(responseBody, responseCharset); + } + catch (UnsupportedEncodingException ex) { + // should not occur + throw new InternalError(ex.getMessage()); + } + } + +} diff --git a/spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerTests.java b/spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerTests.java index 36547e848f..7149dece12 100644 --- a/spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerTests.java @@ -75,7 +75,7 @@ public class DefaultResponseErrorHandlerTests { expect(response.getStatusCode()).andReturn(HttpStatus.NOT_FOUND); expect(response.getStatusText()).andReturn("Not Found"); - expect(response.getHeaders()).andReturn(headers); + expect(response.getHeaders()).andReturn(headers).atLeastOnce(); expect(response.getBody()).andReturn(new ByteArrayInputStream("Hello World".getBytes("UTF-8"))); replay(response); @@ -98,7 +98,7 @@ public class DefaultResponseErrorHandlerTests { expect(response.getStatusCode()).andReturn(HttpStatus.NOT_FOUND); expect(response.getStatusText()).andReturn("Not Found"); - expect(response.getHeaders()).andReturn(headers); + expect(response.getHeaders()).andReturn(headers).atLeastOnce(); expect(response.getBody()).andThrow(new IOException()); replay(response); @@ -115,7 +115,7 @@ public class DefaultResponseErrorHandlerTests { expect(response.getStatusCode()).andReturn(HttpStatus.NOT_FOUND); expect(response.getStatusText()).andReturn("Not Found"); - expect(response.getHeaders()).andReturn(headers); + expect(response.getHeaders()).andReturn(headers).atLeastOnce(); expect(response.getBody()).andReturn(null); replay(response); @@ -127,10 +127,16 @@ public class DefaultResponseErrorHandlerTests { // SPR-9406 - @Test(expected=RestClientException.class) + @Test(expected=UnknownHttpStatusCodeException.class) public void unknownStatusCode() throws Exception { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.TEXT_PLAIN); + expect(response.getStatusCode()).andThrow(new IllegalArgumentException("No matching constant for 999")); expect(response.getRawStatusCode()).andReturn(999); + expect(response.getStatusText()).andReturn("Custom status code"); + expect(response.getHeaders()).andReturn(headers).atLeastOnce(); + expect(response.getBody()).andReturn(null); replay(response); diff --git a/src/dist/changelog.txt b/src/dist/changelog.txt index 533b0dfd1f..1bcb38017b 100644 --- a/src/dist/changelog.txt +++ b/src/dist/changelog.txt @@ -58,7 +58,7 @@ Changes in version 3.2 RC1 (2012-11-02) * support inferred base package for @ComponentScan (SPR-9586) * sort candidate @AspectJ methods deterministically (SPR-9729) * improve SimpleStreamingClientHttpRequest performance (SPR-9530) - +* added UnknownHttpStatusCodeException (SPR-9406) Changes in version 3.2 M2 (2012-09-11) --------------------------------------