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

src.org.jets3t.service.impl.rest.httpclient.HttpMethodReleaseInputStream Maven / Gradle / Ivy

/*
 * jets3t : Java Extra-Tasty S3 Toolkit (for Amazon S3 online storage service)
 * This is a java.net project, see https://jets3t.dev.java.net/
 * 
 * Copyright 2006 James Murty
 * 
 * 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.jets3t.service.impl.rest.httpclient;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jets3t.service.io.InputStreamWrapper;
import org.jets3t.service.io.InterruptableInputStream;

/**
 * Utility class to wrap InputStreams obtained from an HttpClient library's HttpMethod object, and
 * ensure the stream and HTTP connection is cleaned up properly. 
 * 

* This input stream wrapper is used to ensure that input streams obtained through HttpClient * connections are cleaned up correctly once the caller has read all the contents of the * connection's input stream, or closed that input stream. *

*

* Important! This input stream must be completely consumed or closed to ensure the necessary * cleanup operations can be performed. *

* * @author James Murty * */ public class HttpMethodReleaseInputStream extends InputStream implements InputStreamWrapper { private static final Log log = LogFactory.getLog(HttpMethodReleaseInputStream.class); private InputStream inputStream = null; private HttpMethod httpMethod = null; private boolean alreadyReleased = false; private boolean underlyingStreamConsumed = false; /** * Constructs an input stream based on an {@link HttpMethod} object representing an HTTP connection. * If a connection input stream is available, this constructor wraps the underlying input stream * in an {@link InterruptableInputStream} and makes that stream available. If no underlying connection * is available, an empty {@link ByteArrayInputStream} is made available. * * @param httpMethod */ public HttpMethodReleaseInputStream(HttpMethod httpMethod) { this.httpMethod = httpMethod; try { this.inputStream = new InterruptableInputStream(httpMethod.getResponseBodyAsStream()); } catch (IOException e) { if (log.isWarnEnabled()) { log.warn("Unable to obtain HttpMethod's response data stream", e); } httpMethod.releaseConnection(); this.inputStream = new ByteArrayInputStream(new byte[] {}); // Empty input stream; } } /** * Returns the underlying HttpMethod object that contains/manages the actual HTTP connection. * * @return * the HTTPMethod object that provides the data input stream. */ public HttpMethod getHttpMethod() { return httpMethod; } /** * Forces the release of an HttpMethod's connection in a way that will perform all the necessary * cleanup through the correct use of HttpClient methods. * * @throws IOException */ protected void releaseConnection() throws IOException { if (!alreadyReleased) { if (!underlyingStreamConsumed) { // Underlying input stream has not been consumed, abort method // to force connection to be closed and cleaned-up. httpMethod.abort(); } httpMethod.releaseConnection(); alreadyReleased = true; } } /** * Standard input stream read method, except it calls {@link #releaseConnection} when the underlying * input stream is consumed. */ public int read() throws IOException { try { int read = inputStream.read(); if (read == -1) { underlyingStreamConsumed = true; if (!alreadyReleased) { releaseConnection(); if (log.isDebugEnabled()) { log.debug("Released HttpMethod as its response data stream is fully consumed"); } } } return read; } catch (IOException e) { releaseConnection(); if (log.isDebugEnabled()) { log.debug("Released HttpMethod as its response data stream threw an exception", e); } throw e; } } /** * Standard input stream read method, except it calls {@link #releaseConnection} when the underlying * input stream is consumed. */ public int read(byte[] b, int off, int len) throws IOException { try { int read = inputStream.read(b, off, len); if (read == -1) { underlyingStreamConsumed = true; if (!alreadyReleased) { releaseConnection(); if (log.isDebugEnabled()) { log.debug("Released HttpMethod as its response data stream is fully consumed"); } } } return read; } catch (IOException e) { releaseConnection(); if (log.isDebugEnabled()) { log.debug("Released HttpMethod as its response data stream threw an exception", e); } throw e; } } public int available() throws IOException { try { return inputStream.available(); } catch (IOException e) { releaseConnection(); if (log.isDebugEnabled()) { log.debug("Released HttpMethod as its response data stream threw an exception", e); } throw e; } } /** * Standard input stream close method, except it ensures that {@link #releaseConnection()} is called * before the input stream is closed. */ public void close() throws IOException { if (!alreadyReleased) { releaseConnection(); if (log.isDebugEnabled()) { log.debug("Released HttpMethod as its response data stream is closed"); } } inputStream.close(); } /** * Tries to ensure a connection is always cleaned-up correctly by calling {@link #releaseConnection()} * on class destruction if the cleanup hasn't already been done. *

* This desperate cleanup act will only be necessary if the user of this class does not completely * consume or close this input stream prior to object destruction. This method will log Warning * messages if a forced cleanup is required, hopefully reminding the user to close their streams * properly. */ protected void finalize() throws Throwable { if (!alreadyReleased) { if (log.isWarnEnabled()) { log.warn("Attempting to release HttpMethod in finalize() as its response data stream has gone out of scope. " + "This attempt will not always succeed and cannot be relied upon! Please ensure S3 response data streams are " + "always fully consumed or closed to avoid HTTP connection starvation."); } releaseConnection(); if (log.isWarnEnabled()) { log.warn("Successfully released HttpMethod in finalize(). You were lucky this time... " + "Please ensure S3 response data streams are always fully consumed or closed."); } } super.finalize(); } /** * @return * the underlying input stream wrapped by this class. */ public InputStream getWrappedInputStream() { return inputStream; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy