Index: trunk/http-core/src/contrib/org/apache/http/contrib/compress/GzipCompressingEntity.java =================================================================== --- trunk/http-core/src/contrib/org/apache/http/contrib/compress/GzipCompressingEntity.java (revision 374885) +++ trunk/http-core/src/contrib/org/apache/http/contrib/compress/GzipCompressingEntity.java (working copy) @@ -36,35 +36,28 @@ import org.apache.http.Header; import org.apache.http.HttpEntity; +import org.apache.http.entity.HttpEntityWrapper; import org.apache.http.protocol.HTTP; /** - *
- *
+ * Wrapping entity that compresses content when {@link #writeTo writing}. + * * @author Oleg Kalnichevski * * @version $Revision$ * * @since 4.0 */ -public class GzipCompressingEntity implements HttpEntity { +public class GzipCompressingEntity extends HttpEntityWrapper + implements HttpEntity { private static final String GZIP_CODEC = "gzip"; + - private final HttpEntity entity; - public GzipCompressingEntity(final HttpEntity entity) { - super(); - if (entity == null) { - throw new IllegalArgumentException("HTTP entity may not be null"); - } - this.entity = entity; + super(entity); } - public InputStream getContent() throws IOException { - return this.entity.getContent(); - } - public Header getContentEncoding() { return new Header(HTTP.CONTENT_ENCODING, GZIP_CODEC, true); } @@ -73,25 +66,17 @@ return -1; } - public Header getContentType() { - return this.entity.getContentType(); - } - public boolean isChunked() { // force content chunking return true; } - public boolean isRepeatable() { - return this.entity.isRepeatable(); - } - public boolean writeTo(final OutputStream outstream) throws IOException { if (outstream == null) { throw new IllegalArgumentException("Output stream may not be null"); } GZIPOutputStream gzip = new GZIPOutputStream(outstream); - InputStream in = this.entity.getContent(); + InputStream in = wrappedEntity.getContent(); byte[] tmp = new byte[2048]; int l; while ((l = in.read(tmp)) != -1) { @@ -102,4 +87,4 @@ return true; } -} +} // class GzipCompressingEntity Index: trunk/http-core/src/contrib/org/apache/http/contrib/compress/GzipDecompressingEntity.java =================================================================== --- trunk/http-core/src/contrib/org/apache/http/contrib/compress/GzipDecompressingEntity.java (revision 374885) +++ trunk/http-core/src/contrib/org/apache/http/contrib/compress/GzipDecompressingEntity.java (working copy) @@ -36,58 +36,40 @@ import org.apache.http.Header; import org.apache.http.HttpEntity; +import org.apache.http.entity.HttpEntityWrapper; /** - *- *
+ * Wrapping entity that decompresses {@link #getContent content}. + * * @author Oleg Kalnichevski * * @version $Revision$ * * @since 4.0 */ -public class GzipDecompressingEntity implements HttpEntity { - - private final HttpEntity entity; +public class GzipDecompressingEntity extends HttpEntityWrapper + implements HttpEntity { + private InputStream instream = null; public GzipDecompressingEntity(final HttpEntity entity) { - super(); - if (entity == null) { - throw new IllegalArgumentException("HTTP entity may not be null"); - } - this.entity = entity; + super(entity); } public InputStream getContent() throws IOException { if (this.instream == null) { - this.instream = new GZIPInputStream(this.entity.getContent()); + this.instream = new GZIPInputStream(wrappedEntity.getContent()); } return this.instream; } - public Header getContentEncoding() { - return this.entity.getContentEncoding(); - } - public long getContentLength() { return -1; } - public Header getContentType() { - return this.entity.getContentType(); - } - - public boolean isChunked() { - return this.entity.isChunked(); - } - public boolean isRepeatable() { - return this.entity.isRepeatable(); + // not repeatable, GZIPInputStream is created only once + return false; } - public boolean writeTo(final OutputStream outstream) throws IOException { - return this.entity.writeTo(outstream); - } - -} +} // class GzipDecompressingEntity Index: trunk/http-core/src/java/org/apache/http/HttpEntity.java =================================================================== --- trunk/http-core/src/java/org/apache/http/HttpEntity.java (revision 374885) +++ trunk/http-core/src/java/org/apache/http/HttpEntity.java (working copy) @@ -34,8 +34,34 @@ import java.io.OutputStream; /** + * An entity that can be sent or received with an HTTP message. + * Entities can be found in some + * {@link HttpEntityEnclosingRequest requests} and in + * {@link HttpResponse responses}, where they are optional. *- *
+ * In some places, the JavaDoc distinguishes three kinds of entities, + * depending on where their {@link #getContent content} originates: + *true if chunked encoding is preferred for this
+ * entity, or false if it is not
+ */
boolean isChunked();
+
+ /**
+ * Tells the length of the content, if known.
+ *
+ * @return the number of bytes of the content, or
+ * a negative number if unknown. If the content length is known
+ * but exceeds {@link java.lang.Long#MAX_VALUE Long.MAX_VALUE},
+ * a negative number is returned.
+ */
long getContentLength();
-
+
+
+ /**
+ * Obtains the Content-Type header, if known.
+ * This is the header that should be used when sending the entity,
+ * or the one that was received with the entity. It can include a
+ * charset attribute.
+ *
+ * @return the Content-Type header for this entity, or
+ * null if the content type is unknown
+ */
Header getContentType();
-
+
+
+ /**
+ * Obtains the Content-Encoding header, if known.
+ * This is the header that should be used when sending the entity,
+ * or the one that was received with the entity.
+ * Wrapping entities that modify the content encoding should
+ * adjust this header accordingly.
+ *
+ * @return the Content-Encoding header for this entity, or
+ * null if the content encoding is unknown
+ */
Header getContentEncoding();
-
+
+
/**
- * Creates a new InputStream object of the entity. It is a programming error
+ * Creates a new InputStream object of the entity.
+ * It is a programming error
* to return the same InputStream object more than once.
* @return a new input stream that returns the entity data.
* @throws IOException if the stream could not be created
*/
InputStream getContent() throws IOException;
+
/**
* Writes the entity content to the output stream either partially or entirely.
* This method may either write the entire content in one go, if it is feasible
* to do so, or store the output stream as a local variable and use it internally
- * to write the content in parts. If the former case this method MUST return
+ * to write the content in parts. In the former case this method MUST return
* true to indicate that the output stream can be closed. In the latter
* case the output stream MUST be closed once the last content part is written
* in order to ensure that content codings that emit a closing chunk are properly
@@ -84,5 +157,46 @@
* @throws IOException if an I/O error occurs
*/
boolean writeTo(OutputStream outstream) throws IOException;
-
-}
+
+
+ /**
+ * Tells whether this entity depends on an underlying stream.
+ * Streamed entities should return true until the
+ * content has been consumed, false afterwards.
+ * Self-contained entities should return false.
+ * Wrapping entities should delegate this call to the wrapped entity.
+ * true until
+ * {@link #consumeContent consumeContent} is called.
+ *
+ * @return true if the entity content is streamed and
+ * not yet consumed, false otherwise
+ */
+ public boolean isStreaming(); // don't expect an exception here
+
+
+ /**
+ * Consumes the remaining content of a streamed entity.
+ * This method is called to indicate that the content of this entity
+ * is no longer required.
+ * Streamed entities should dispose of the remaining content, if any.
+ * Self-contained entities can release allocated resources, but
+ * are not required to do anything.
+ * Wrapping entities should delegate this call to the wrapped entity.
+ * - *
+ * A wrapping entity that buffers it content if necessary. + * The buffered entity is always repeatable. + * If the wrapped entity is repeatable itself, calls are passed through. + * If the wrapped entity is not repeatable, the content is read into a + * buffer once and provided from there as often as required. + * * @author Oleg Kalnichevski * * @version $Revision$ * * @since 4.0 */ -public class BufferedHttpEntity implements HttpEntity { +public class BufferedHttpEntity extends HttpEntityWrapper + implements HttpEntity { - private final HttpEntity source; private final byte[] buffer; public BufferedHttpEntity(final HttpEntity entity) throws IOException { - super(); - if (entity == null) { - throw new IllegalArgumentException("HTTP entity may not be null"); - } - this.source = entity; + super(entity); + if (entity.isChunked() || !entity.isRepeatable() ) { this.buffer = EntityUtils.toByteArray(entity); } else { @@ -69,33 +70,36 @@ if (this.buffer != null) { return this.buffer.length; } else { - return this.source.getContentLength(); + return wrappedEntity.getContentLength(); } } - - public Header getContentType() { - return this.source.getContentType(); - } - public Header getContentEncoding() { - return this.source.getContentEncoding(); - } - public InputStream getContent() throws IOException { if (this.buffer != null) { return new ByteArrayInputStream(this.buffer); } else { - return this.source.getContent(); + return wrappedEntity.getContent(); } } - + + /** + * Tells that this entity does not have to be chunked. + * + * @returnfalse
+ */
public boolean isChunked() {
return false;
}
+ /**
+ * Tells that this entity is repeatable.
+ *
+ * @return true
+ */
public boolean isRepeatable() {
return true;
}
+
public boolean writeTo(final OutputStream outstream) throws IOException {
if (outstream == null) {
@@ -105,8 +109,14 @@
outstream.write(this.buffer);
return true;
} else {
- return this.source.writeTo(outstream);
+ return wrappedEntity.writeTo(outstream);
}
}
+
+
+ // non-javadoc, see interface HttpEntity
+ public boolean isStreaming() {
+ return (buffer == null) && wrappedEntity.isStreaming();
+ }
-}
+} // class BufferedHttpEntity
Index: trunk/http-core/src/java/org/apache/http/entity/ByteArrayEntity.java
===================================================================
--- trunk/http-core/src/java/org/apache/http/entity/ByteArrayEntity.java (revision 374885)
+++ trunk/http-core/src/java/org/apache/http/entity/ByteArrayEntity.java (working copy)
@@ -36,23 +36,20 @@
import org.apache.http.Header;
import org.apache.http.HttpEntity;
-import org.apache.http.protocol.HTTP;
+
/**
- * - *
+ * A self-contained entity obtaining content from a byte array. + * * @author Oleg Kalnichevski * * @version $Revision$ * * @since 4.0 */ -public class ByteArrayEntity implements HttpEntity { +public class ByteArrayEntity extends AbstractHttpEntity implements HttpEntity { private final byte[] content; - private String contentType = HTTP.DEFAULT_CONTENT_TYPE; - private String contentEncoding = null; - private boolean chunked = false; public ByteArrayEntity(final byte[] b) { super(); @@ -66,42 +63,10 @@ return true; } - public boolean isChunked() { - return this.chunked; - } - - public void setChunked(boolean b) { - this.chunked = b; - } - public long getContentLength() { return this.content.length; } - - public Header getContentType() { - if (this.contentType != null) { - return new Header(HTTP.CONTENT_TYPE, this.contentType); - } else { - return null; - } - } - public void setContentType(final String contentType) { - this.contentType = contentType; - } - - public Header getContentEncoding() { - if (this.contentEncoding != null) { - return new Header(HTTP.CONTENT_ENCODING, this.contentEncoding); - } else { - return null; - } - } - - public void setContentEncoding(final String contentEncoding) { - this.contentEncoding = contentEncoding; - } - public InputStream getContent() { return new ByteArrayInputStream(this.content); } @@ -115,4 +80,15 @@ return true; } -} + + /** + * Tells that this entity is not streaming. + * + * @returnfalse
+ */
+ public boolean isStreaming() {
+ return false;
+ }
+
+
+} // class ByteArrayEntity
Index: trunk/http-core/src/java/org/apache/http/entity/AbstractHttpEntity.java
===================================================================
--- trunk/http-core/src/java/org/apache/http/entity/AbstractHttpEntity.java (revision 0)
+++ trunk/http-core/src/java/org/apache/http/entity/AbstractHttpEntity.java (revision 0)
@@ -0,0 +1,214 @@
+/*
+ * $HeadURL$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ *
+ * Copyright 1999-2006 The Apache Software Foundation
+ *
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * null and false, respectively.
+ */
+ protected AbstractHttpEntity() {
+ super();
+ }
+
+
+ /**
+ * Obtains the Content-Type header.
+ * The default implementation returns the value of the
+ * {@link #contentType contentType} attribute.
+ *
+ * @return the Content-Type header, or null
+ */
+ public Header getContentType() {
+ return this.contentType;
+ }
+
+
+ /**
+ * Obtains the Content-Encoding header.
+ * The default implementation returns the value of the
+ * {@link #contentEncoding contentEncoding} attribute.
+ *
+ * @return the Content-Encoding header, or null
+ */
+ public Header getContentEncoding() {
+ return this.contentEncoding;
+ }
+
+ /**
+ * Obtains the 'chunked' flag.
+ * The default implementation returns the value of the
+ * {@link #chunked chunked} attribute.
+ *
+ * @return the 'chunked' flag
+ */
+ public boolean isChunked() {
+ return this.chunked;
+ }
+
+
+ /**
+ * Specifies the Content-Type header.
+ * The default implementation sets the value of the
+ * {@link #contentType contentType} attribute.
+ *
+ * @param contentType the new Content-Encoding header, or
+ * null to unset
+ */
+ public void setContentType(final Header contentType) {
+ this.contentType = contentType;
+ }
+
+ /**
+ * Specifies the Content-Type header, as a string.
+ * The default implementation calls
+ * {@link #setContentType(Header) setContentType(Header)}.
+ *
+ * @param ctString the new Content-Type header, or
+ * null to unset
+ */
+ public void setContentType(final String ctString) {
+ Header h = null;
+ if (ctString != null) {
+ h = new Header(HTTP.CONTENT_TYPE, ctString);
+ }
+ setContentType(h);
+ }
+
+
+ /**
+ * Specifies the Content-Encoding header.
+ * The default implementation sets the value of the
+ * {@link #contentEncoding contentEncoding} attribute.
+ *
+ * @param contentEncoding the new Content-Encoding header, or
+ * null to unset
+ */
+ public void setContentEncoding(final Header contentEncoding) {
+ this.contentEncoding = contentEncoding;
+ }
+
+ /**
+ * Specifies the Content-Encoding header, as a string.
+ * The default implementation calls
+ * {@link #setContentEncoding(Header) setContentEncoding(Header)}.
+ *
+ * @param ceString the new Content-Encoding header, or
+ * null to unset
+ */
+ public void setContentEncoding(final String ceString) {
+ Header h = null;
+ if (ceString != null) {
+ h = new Header(HTTP.CONTENT_ENCODING, ceString);
+ }
+ setContentEncoding(h);
+ }
+
+
+ /**
+ * Specifies the 'chunked' flag.
+ * The default implementation sets the value of the
+ * {@link #chunked chunked} attribute.
+ *
+ * @param b the new 'chunked' flag
+ */
+ public void setChunked(boolean b) {
+ this.chunked = b;
+ }
+
+
+ /**
+ * Does not consume anything.
+ * The default implementation does nothing if
+ * {@link HttpEntity#isStreaming isStreaming}
+ * returns false, and throws an exception
+ * if it returns true.
+ * This removes the burden of implementing
+ * an empty method for non-streaming entities.
+ *
+ * @throws IOException in case of an I/O problem
+ * @throws UnsupportedOperationException
+ * if a streaming subclass does not override this method
+ */
+ public void consumeContent()
+ throws IOException, UnsupportedOperationException{
+ if (isStreaming()) {
+ throw new UnsupportedOperationException
+ ("streaming entity does not implement consumeContent()");
+ }
+ } // consumeContent
+
+
+} // class AbstractHttpEntity
Property changes on: trunk/http-core/src/java/org/apache/http/entity/AbstractHttpEntity.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:keywords
+ Date Author Id Revision HeadURL
Name: svn:eol-style
+ native
Index: trunk/http-core/src/java/org/apache/http/entity/StringEntity.java
===================================================================
--- trunk/http-core/src/java/org/apache/http/entity/StringEntity.java (revision 374885)
+++ trunk/http-core/src/java/org/apache/http/entity/StringEntity.java (working copy)
@@ -39,21 +39,19 @@
import org.apache.http.HttpEntity;
import org.apache.http.protocol.HTTP;
+
/**
- * - *
+ * A self-contained entity obtaining content from a string. + * * @author Oleg Kalnichevski * * @version $Revision$ * * @since 4.0 */ -public class StringEntity implements HttpEntity { +public class StringEntity extends AbstractHttpEntity implements HttpEntity { private final byte[] content; - private String contentType = null; - private String contentEncoding = null; - private boolean chunked = false; public StringEntity(final String s, String charset) throws UnsupportedEncodingException { @@ -64,8 +62,8 @@ if (charset == null) { charset = HTTP.DEFAULT_CONTENT_CHARSET; } - this.contentType = HTTP.PLAIN_TEXT_TYPE + HTTP.CHARSET_PARAM + charset; this.content = s.getBytes(charset); + setContentType(HTTP.PLAIN_TEXT_TYPE + HTTP.CHARSET_PARAM + charset); } public StringEntity(final String s) @@ -77,42 +75,10 @@ return true; } - public boolean isChunked() { - return this.chunked; - } - - public void setChunked(boolean b) { - this.chunked = b; - } - public long getContentLength() { return this.content.length; } - public Header getContentType() { - if (this.contentType != null) { - return new Header(HTTP.CONTENT_TYPE, this.contentType); - } else { - return null; - } - } - - public void setContentType(final String contentType) { - this.contentType = contentType; - } - - public Header getContentEncoding() { - if (this.contentEncoding != null) { - return new Header(HTTP.CONTENT_ENCODING, this.contentEncoding); - } else { - return null; - } - } - - public void setContentEncoding(final String contentEncoding) { - this.contentEncoding = contentEncoding; - } - public InputStream getContent() throws IOException { return new ByteArrayInputStream(this.content); } @@ -126,4 +92,15 @@ return true; } -} + + /** + * Tells that this entity is not streaming. + * + * @returnfalse
+ */
+ public boolean isStreaming() {
+ return false;
+ }
+
+
+} // class StringEntity
Index: trunk/http-core/src/java/org/apache/http/entity/InputStreamEntity.java
===================================================================
--- trunk/http-core/src/java/org/apache/http/entity/InputStreamEntity.java (revision 374885)
+++ trunk/http-core/src/java/org/apache/http/entity/InputStreamEntity.java (working copy)
@@ -35,26 +35,25 @@
import org.apache.http.Header;
import org.apache.http.HttpEntity;
-import org.apache.http.protocol.HTTP;
+
/**
- * - *
+ * A streamed entity obtaining content from an {@link InputStream InputStream}. + * * @author Oleg Kalnichevski * * @version $Revision$ * * @since 4.0 */ -public class InputStreamEntity implements HttpEntity { +public class InputStreamEntity extends AbstractHttpEntity + implements HttpEntity { private final static int BUFFER_SIZE = 2048; private final InputStream content; private final long length; - private String contentType = HTTP.DEFAULT_CONTENT_TYPE; - private String contentEncoding = null; - private boolean chunked = false; + private boolean consumed = false; public InputStreamEntity(final InputStream instream, long length) { super(); @@ -69,42 +68,10 @@ return false; } - public boolean isChunked() { - return this.chunked; - } - - public void setChunked(boolean b) { - this.chunked = b; - } - public long getContentLength() { return this.length; } - - public Header getContentType() { - if (this.contentType != null) { - return new Header(HTTP.CONTENT_TYPE, this.contentType); - } else { - return null; - } - } - public void setContentType(final String contentType) { - this.contentType = contentType; - } - - public Header getContentEncoding() { - if (this.contentEncoding != null) { - return new Header(HTTP.CONTENT_ENCODING, this.contentEncoding); - } else { - return null; - } - } - - public void setContentEncoding(final String contentEncoding) { - this.contentEncoding = contentEncoding; - } - public InputStream getContent() throws IOException { return this.content; } @@ -126,5 +93,19 @@ } return true; } + + + // non-javadoc, see interface HttpEntity + public boolean isStreaming() { + return !consumed; + } + + // non-javadoc, see interface HttpEntity + public void consumeContent() throws IOException { + consumed = true; + // If the input stream is from a connection, closing it will read to + // the end of the content. Otherwise, we don't care what it does. + content.close(); + } -} +} // class InputStreamEntity Index: trunk/http-core/src/java/org/apache/http/entity/FileEntity.java =================================================================== --- trunk/http-core/src/java/org/apache/http/entity/FileEntity.java (revision 374885) +++ trunk/http-core/src/java/org/apache/http/entity/FileEntity.java (working copy) @@ -37,23 +37,20 @@ import org.apache.http.Header; import org.apache.http.HttpEntity; -import org.apache.http.protocol.HTTP; + /** - *- *
+ * A self-contained entity obtaining content from a file. + * * @author Oleg Kalnichevski * * @version $Revision$ * * @since 4.0 */ -public class FileEntity implements HttpEntity { +public class FileEntity extends AbstractHttpEntity implements HttpEntity { private final File file; - private String contentType = null; - private String contentEncoding = null; - private boolean chunked = false; public FileEntity(final File file, final String contentType) { super(); @@ -61,49 +58,17 @@ throw new IllegalArgumentException("File may not be null"); } this.file = file; - this.contentType = contentType; + setContentType(contentType); } public boolean isRepeatable() { return true; } - public boolean isChunked() { - return this.chunked; - } - - public void setChunked(boolean b) { - this.chunked = b; - } - public long getContentLength() { return this.file.length(); } - public Header getContentType() { - if (this.contentType != null) { - return new Header(HTTP.CONTENT_TYPE, this.contentType); - } else { - return null; - } - } - - public void setContentType(final String contentType) { - this.contentType = contentType; - } - - public Header getContentEncoding() { - if (this.contentEncoding != null) { - return new Header(HTTP.CONTENT_ENCODING, this.contentEncoding); - } else { - return null; - } - } - - public void setContentEncoding(final String contentEncoding) { - this.contentEncoding = contentEncoding; - } - public InputStream getContent() throws IOException { return new FileInputStream(this.file); } @@ -122,4 +87,15 @@ return true; } -} + + /** + * Tells that this entity is not streaming. + * + * @returnfalse
+ */
+ public boolean isStreaming() {
+ return false;
+ }
+
+
+} // class FileEntity
Index: trunk/http-core/src/java/org/apache/http/entity/HttpEntityWrapper.java
===================================================================
--- trunk/http-core/src/java/org/apache/http/entity/HttpEntityWrapper.java (revision 0)
+++ trunk/http-core/src/java/org/apache/http/entity/HttpEntityWrapper.java (revision 0)
@@ -0,0 +1,116 @@
+/*
+ * $HeadURL$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ *
+ * Copyright 1999-2006 The Apache Software Foundation
+ *
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ *