/* * Copyright (c) 2010 Google Inc. * * 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 com.google.api.client.http; import com.google.api.client.util.Preconditions; import com.google.api.client.util.StringUtils; import java.io.IOException; /** * Exception thrown when an error status code is detected in an HTTP response. * * <p> * Implementation is not thread safe. * </p> * * @since 1.0 * @author Yaniv Inbar */ public class HttpResponseException extends IOException { private static final long serialVersionUID = -1875819453475890043L; /** HTTP status code. */ private final int statusCode; /** Status message or {@code null}. */ private final String statusMessage; /** HTTP headers. */ private final transient HttpHeaders headers; /** HTTP response content or {@code null} for none. */ private final String content; /** * Constructor that constructs a detail message from the given HTTP response that includes the * status code, status message and HTTP response content. * * <p> * Callers of this constructor should call {@link HttpResponse#disconnect} after * {@link HttpResponseException} is instantiated. Example usage: * </p> * * <pre> try { throw new HttpResponseException(response); } finally { response.disconnect(); } * </pre> * * @param response HTTP response */ public HttpResponseException(HttpResponse response) { this(new Builder(response)); } /** * @param builder builder * * @since 1.14 */ protected HttpResponseException(Builder builder) { super(builder.message); statusCode = builder.statusCode; statusMessage = builder.statusMessage; headers = builder.headers; content = builder.content; } /** * Returns whether received a successful HTTP status code {@code >= 200 && < 300} (see * {@link #getStatusCode()}). * * @since 1.7 */ public final boolean isSuccessStatusCode() { return HttpStatusCodes.isSuccess(statusCode); } /** * Returns the HTTP status code or {@code 0} for none. * * @since 1.7 */ public final int getStatusCode() { return statusCode; } /** * Returns the HTTP status message or {@code null} for none. * * @since 1.9 */ public final String getStatusMessage() { return statusMessage; } /** * Returns the HTTP response headers. * * @since 1.7 */ public HttpHeaders getHeaders() { return headers; } /** * Returns the HTTP response content or {@code null} for none. * * @since 1.14 */ public final String getContent() { return content; } /** * Builder. * * <p> * Implementation is not thread safe. * </p> * * * @since 1.14 */ public static class Builder { /** HTTP status code. */ int statusCode; /** Status message or {@code null}. */ String statusMessage; /** HTTP headers. */ HttpHeaders headers; /** Response content or {@code null} for none. */ String content; /** Detail message to use or {@code null} for none. */ String message; /** * @param statusCode HTTP status code * @param statusMessage status message or {@code null} * @param headers HTTP headers */ public Builder(int statusCode, String statusMessage, HttpHeaders headers) { setStatusCode(statusCode); setStatusMessage(statusMessage); setHeaders(headers); } /** * @param response HTTP response */ public Builder(HttpResponse response) { this(response.getStatusCode(), response.getStatusMessage(), response.getHeaders()); // content try { content = response.parseAsString(); if (content.length() == 0) { content = null; } } catch (IOException exception) { // it would be bad to throw an exception while throwing an exception exception.printStackTrace(); } // message StringBuilder builder = computeMessageBuffer(response); if (content != null) { builder.append(StringUtils.LINE_SEPARATOR).append(content); } message = builder.toString(); } /** Returns the detail message to use or {@code null} for none. */ public final String getMessage() { return message; } /** * Sets the detail message to use or {@code null} for none. * * <p> * Overriding is only supported for the purpose of calling the super implementation and changing * the return type, but nothing else. * </p> */ public Builder setMessage(String message) { this.message = message; return this; } /** Returns the HTTP status code or {@code 0} for none. */ public final int getStatusCode() { return statusCode; } /** * Sets the HTTP status code or {@code 0} for none. * * <p> * Overriding is only supported for the purpose of calling the super implementation and changing * the return type, but nothing else. * </p> */ public Builder setStatusCode(int statusCode) { Preconditions.checkArgument(statusCode >= 0); this.statusCode = statusCode; return this; } /** Returns the HTTP status message or {@code null} for none. */ public final String getStatusMessage() { return statusMessage; } /** * Sets the HTTP status message or {@code null} for none. * * <p> * Overriding is only supported for the purpose of calling the super implementation and changing * the return type, but nothing else. * </p> */ public Builder setStatusMessage(String statusMessage) { this.statusMessage = statusMessage; return this; } /** Returns the HTTP response headers. */ public HttpHeaders getHeaders() { return headers; } /** * Sets the HTTP response headers. * * <p> * Overriding is only supported for the purpose of calling the super implementation and changing * the return type, but nothing else. * </p> */ public Builder setHeaders(HttpHeaders headers) { this.headers = Preconditions.checkNotNull(headers); return this; } /** Returns the HTTP response content or {@code null} for none. */ public final String getContent() { return content; } /** * Sets the HTTP response content or {@code null} for none. * * <p> * Overriding is only supported for the purpose of calling the super implementation and changing * the return type, but nothing else. * </p> */ public Builder setContent(String content) { this.content = content; return this; } /** Returns a new instance of {@link HttpResponseException} based on this builder. */ public HttpResponseException build() { return new HttpResponseException(this); } } /** * Returns an exception message string builder to use for the given HTTP response. * * @since 1.7 */ public static StringBuilder computeMessageBuffer(HttpResponse response) { StringBuilder builder = new StringBuilder(); int statusCode = response.getStatusCode(); if (statusCode != 0) { builder.append(statusCode); } String statusMessage = response.getStatusMessage(); if (statusMessage != null) { if (statusCode != 0) { builder.append(' '); } builder.append(statusMessage); } return builder; } }