All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.box.sdk.BoxAPIResponse Maven / Gradle / Ivy

There is a newer version: 4.11.1
Show newest version
package com.box.sdk;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;

/**
 * Used to read HTTP responses from the Box API.
 *
 * 

All responses from the REST API are read using this class or one of its subclasses. This class wraps {@link * HttpURLConnection} in order to provide a simpler interface that can automatically handle various conditions specific * to Box's API. When a response is contructed, it will throw a {@link BoxAPIException} if the response from the API * was an error. Therefore every BoxAPIResponse instance is guaranteed to represent a successful response.

* *

This class usually isn't instantiated directly, but is instead returned after calling {@link BoxAPIRequest#send}. *

*/ public class BoxAPIResponse { private static final Logger LOGGER = Logger.getLogger(BoxAPIResponse.class.getName()); private static final int BUFFER_SIZE = 8192; private final HttpURLConnection connection; //Batch API Response will have headers in response body private final Map headers; private int responseCode; private String bodyString; /** * The raw InputStream is the stream returned directly from HttpURLConnection.getInputStream(). We need to keep * track of this stream in case we need to access it after wrapping it inside another stream. */ private InputStream rawInputStream; /** * The regular InputStream is the stream that will be returned by getBody(). This stream might be a GZIPInputStream * or a ProgressInputStream (or both) that wrap the raw InputStream. */ private InputStream inputStream; /** * Constructs an empty BoxAPIResponse without an associated HttpURLConnection. */ public BoxAPIResponse() { this.connection = null; this.headers = null; } /** * Constructs a BoxAPIResponse with a http response code and response headers. * @param responseCode http response code * @param headers map of headers */ public BoxAPIResponse(int responseCode, Map headers) { this.connection = null; this.responseCode = responseCode; this.headers = headers; } /** * Constructs a BoxAPIResponse using an HttpURLConnection. * @param connection a connection that has already sent a request to the API. */ public BoxAPIResponse(HttpURLConnection connection) { this.connection = connection; this.headers = null; this.inputStream = null; try { this.responseCode = this.connection.getResponseCode(); } catch (IOException e) { throw new BoxAPIException("Couldn't connect to the Box API due to a network error.", e); } if (!isSuccess(this.responseCode)) { this.logResponse(); throw new BoxAPIException("The API returned an error code: " + this.responseCode, this.responseCode, this.bodyToString(), this.connection.getHeaderFields()); } this.logResponse(); } /** * Gets the response code returned by the API. * @return the response code returned by the API. */ public int getResponseCode() { return this.responseCode; } /** * Gets the length of this response's body as indicated by the "Content-Length" header. * @return the length of the response's body. */ public long getContentLength() { return this.connection.getContentLength(); } /** * Gets the value of the given header field. * @param fieldName name of the header field. * @return value of the header. */ public String getHeaderField(String fieldName) { // headers map is null for all regular response calls except when made as a batch request if (this.headers == null) { if (this.connection != null) { return this.connection.getHeaderField(fieldName); } else { return null; } } else { return this.headers.get(fieldName); } } /** * Gets an InputStream for reading this response's body. * @return an InputStream for reading the response's body. */ public InputStream getBody() { return this.getBody(null); } /** * Gets an InputStream for reading this response's body which will report its read progress to a ProgressListener. * @param listener a listener for monitoring the read progress of the body. * @return an InputStream for reading the response's body. */ public InputStream getBody(ProgressListener listener) { if (this.inputStream == null) { String contentEncoding = this.connection.getContentEncoding(); try { if (this.rawInputStream == null) { this.rawInputStream = this.connection.getInputStream(); } if (listener == null) { this.inputStream = this.rawInputStream; } else { this.inputStream = new ProgressInputStream(this.rawInputStream, listener, this.getContentLength()); } if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) { this.inputStream = new GZIPInputStream(this.inputStream); } } catch (IOException e) { throw new BoxAPIException("Couldn't connect to the Box API due to a network error.", e); } } return this.inputStream; } /** * Disconnects this response from the server and frees up any network resources. The body of this response can no * longer be read after it has been disconnected. */ public void disconnect() { if (this.connection == null) { return; } try { if (this.rawInputStream == null) { this.rawInputStream = this.connection.getInputStream(); } // We need to manually read from the raw input stream in case there are any remaining bytes. There's a bug // where a wrapping GZIPInputStream may not read to the end of a chunked response, causing Java to not // return the connection to the connection pool. byte[] buffer = new byte[BUFFER_SIZE]; int n = this.rawInputStream.read(buffer); while (n != -1) { n = this.rawInputStream.read(buffer); } this.rawInputStream.close(); if (this.inputStream != null) { this.inputStream.close(); } } catch (IOException e) { throw new BoxAPIException("Couldn't finish closing the connection to the Box API due to a network error or " + "because the stream was already closed.", e); } } @Override public String toString() { String lineSeparator = System.getProperty("line.separator"); Map> headers = this.connection.getHeaderFields(); StringBuilder builder = new StringBuilder(); builder.append("Response"); builder.append(lineSeparator); builder.append(this.connection.getRequestMethod()); builder.append(' '); builder.append(this.connection.getURL().toString()); builder.append(lineSeparator); builder.append(headers.get(null).get(0)); builder.append(lineSeparator); for (Map.Entry> entry : headers.entrySet()) { String key = entry.getKey(); if (key == null) { continue; } List nonEmptyValues = new ArrayList(); for (String value : entry.getValue()) { if (value != null && value.trim().length() != 0) { nonEmptyValues.add(value); } } if (nonEmptyValues.size() == 0) { continue; } builder.append(key); builder.append(": "); for (String value : nonEmptyValues) { builder.append(value); builder.append(", "); } builder.delete(builder.length() - 2, builder.length()); builder.append(lineSeparator); } String bodyString = this.bodyToString(); if (bodyString != null && bodyString != "") { builder.append(lineSeparator); builder.append(bodyString); } return builder.toString().trim(); } /** * Returns a string representation of this response's body. This method is used when logging this response's body. * By default, it returns an empty string (to avoid accidentally logging binary data) unless the response contained * an error message. * @return a string representation of this response's body. */ protected String bodyToString() { if (this.bodyString == null && !isSuccess(this.responseCode)) { this.bodyString = readErrorStream(this.getErrorStream()); } return this.bodyString; } /** * Returns the response error stream, handling the case when it contains gzipped data. * @return gzip decoded (if needed) error stream or null */ private InputStream getErrorStream() { InputStream errorStream = this.connection.getErrorStream(); if (errorStream != null) { final String contentEncoding = this.connection.getContentEncoding(); if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) { try { errorStream = new GZIPInputStream(errorStream); } catch (IOException e) { // just return the error stream as is } } } return errorStream; } private void logResponse() { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, this.toString()); } } private static boolean isSuccess(int responseCode) { return responseCode >= 200 && responseCode < 300; } private static String readErrorStream(InputStream stream) { if (stream == null) { return null; } InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8); StringBuilder builder = new StringBuilder(); char[] buffer = new char[BUFFER_SIZE]; try { int read = reader.read(buffer, 0, BUFFER_SIZE); while (read != -1) { builder.append(buffer, 0, read); read = reader.read(buffer, 0, BUFFER_SIZE); } reader.close(); } catch (IOException e) { return null; } return builder.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy