diff --git a/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheEntry.java b/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheEntry.java index 378251921..dfcd93c6b 100644 --- a/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheEntry.java +++ b/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheEntry.java @@ -27,7 +27,6 @@ package org.apache.http.client.cache; import java.io.IOException; -import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; @@ -52,7 +51,7 @@ import org.apache.http.message.BasicHeader; * @since 4.1 */ @Immutable -public abstract class HttpCacheEntry implements Serializable { +public class HttpCacheEntry implements Serializable { private static final long serialVersionUID = -6300496422359477413L; @@ -60,6 +59,7 @@ public abstract class HttpCacheEntry implements Serializable { private final Date responseDate; private final StatusLine statusLine; private final CachedHeaderGroup responseHeaders; + private final Resource resource; private final Set variantURIs; /** @@ -81,6 +81,7 @@ public abstract class HttpCacheEntry implements Serializable { final Date responseDate, final StatusLine statusLine, final Header[] responseHeaders, + final Resource resource, final Set variants) { super(); if (requestDate == null) { @@ -95,11 +96,15 @@ public abstract class HttpCacheEntry implements Serializable { if (responseHeaders == null) { throw new IllegalArgumentException("Response headers may not be null"); } + if (resource == null) { + throw new IllegalArgumentException("Resource may not be null"); + } this.requestDate = requestDate; this.responseDate = responseDate; this.statusLine = statusLine; this.responseHeaders = new CachedHeaderGroup(); this.responseHeaders.setHeaders(responseHeaders); + this.resource = resource; this.variantURIs = variants != null ? new HashSet(variants) : new HashSet(); } @@ -147,11 +152,9 @@ public abstract class HttpCacheEntry implements Serializable { return Collections.unmodifiableSet(this.variantURIs); } - public abstract Resource getResource(); - - public abstract InputStream getBody() throws IOException; - - public abstract long getBodyLength(); + public Resource getResource() { + return this.resource; + } private void writeObject(ObjectOutputStream out) throws IOException { diff --git a/httpclient-cache/src/main/java/org/apache/http/client/cache/Resource.java b/httpclient-cache/src/main/java/org/apache/http/client/cache/Resource.java index 0815089cf..74f5e9c4a 100644 --- a/httpclient-cache/src/main/java/org/apache/http/client/cache/Resource.java +++ b/httpclient-cache/src/main/java/org/apache/http/client/cache/Resource.java @@ -26,12 +26,20 @@ */ package org.apache.http.client.cache; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; + /** * Represents a disposable system resource. * * @since 4.1 */ -public interface Resource { +public interface Resource extends Serializable { + + InputStream getInputStream() throws IOException; + + long length(); void dispose(); diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheEntity.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheEntity.java index 877d3c20b..122830063 100644 --- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheEntity.java +++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheEntity.java @@ -66,18 +66,18 @@ class CacheEntity implements HttpEntity, Serializable { } public long getContentLength() { - return this.cacheEntry.getBodyLength(); + return this.cacheEntry.getResource().length(); } public InputStream getContent() throws IOException { - return this.cacheEntry.getBody(); + return this.cacheEntry.getResource().getInputStream(); } public void writeTo(final OutputStream outstream) throws IOException { if (outstream == null) { throw new IllegalArgumentException("Output stream may not be null"); } - InputStream instream = this.cacheEntry.getBody(); + InputStream instream = this.cacheEntry.getResource().getInputStream(); try { IOUtils.copy(instream, outstream); } finally { diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheEntryUpdater.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheEntryUpdater.java index 9c109a7c6..943bf5546 100644 --- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheEntryUpdater.java +++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheEntryUpdater.java @@ -86,7 +86,7 @@ class CacheEntryUpdater { Header[] mergedHeaders = mergeHeaders(entry, response); ByteArrayOutputStream outstream = new ByteArrayOutputStream(); - InputStream instream = entry.getBody(); + InputStream instream = entry.getResource().getInputStream(); byte[] buf = new byte[2048]; int len; while ((len = instream.read(buf)) != -1) { diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheValidityPolicy.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheValidityPolicy.java index 5bdb0e780..af0691fe1 100644 --- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheValidityPolicy.java +++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheValidityPolicy.java @@ -140,7 +140,7 @@ class CacheValidityPolicy { * @return boolean indicating whether actual length matches Content-Length */ protected boolean contentLengthHeaderMatchesActualLength(final HttpCacheEntry entry) { - return getContentLengthValue(entry) == entry.getBodyLength(); + return getContentLengthValue(entry) == entry.getResource().length(); } protected long getApparentAgeSecs(final HttpCacheEntry entry) { diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/FileCacheEntryFactory.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/FileCacheEntryFactory.java index 377202475..834c744a3 100644 --- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/FileCacheEntryFactory.java +++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/FileCacheEntryFactory.java @@ -38,6 +38,7 @@ import org.apache.http.StatusLine; import org.apache.http.annotation.Immutable; import org.apache.http.client.cache.HttpCacheEntry; import org.apache.http.client.cache.HttpCacheEntryFactory; +import org.apache.http.client.cache.Resource; /** * Generates {@link HttpCacheEntry} instances whose body is stored in a temporary file. @@ -56,14 +57,7 @@ public class FileCacheEntryFactory implements HttpCacheEntryFactory { this.idgen = new BasicIdGenerator(); } - public HttpCacheEntry generate( - final String requestId, - final Date requestDate, - final Date responseDate, - final StatusLine statusLine, - final Header[] headers, - byte[] body) throws IOException { - + private File generateUniqueCacheFile(final String requestId) { StringBuilder buffer = new StringBuilder(); this.idgen.generate(buffer); buffer.append('.'); @@ -76,19 +70,29 @@ public class FileCacheEntryFactory implements HttpCacheEntryFactory { buffer.append('-'); } } - File file = new File(this.cacheDir, buffer.toString()); + return new File(this.cacheDir, buffer.toString()); + } + + public HttpCacheEntry generate( + final String requestId, + final Date requestDate, + final Date responseDate, + final StatusLine statusLine, + final Header[] headers, + byte[] body) throws IOException { + File file = generateUniqueCacheFile(requestId); FileOutputStream outstream = new FileOutputStream(file); try { outstream.write(body); } finally { outstream.close(); } - return new FileCacheEntry( + return new HttpCacheEntry( requestDate, responseDate, statusLine, headers, - file, + new FileResource(file), null); } @@ -97,25 +101,25 @@ public class FileCacheEntryFactory implements HttpCacheEntryFactory { final HttpCacheEntry entry, final String variantURI) throws IOException { - String uid = this.idgen.generate(); - File file = new File(this.cacheDir, uid + "-" + requestId); + File file = generateUniqueCacheFile(requestId); Set variants = new HashSet(entry.getVariantURIs()); variants.add(variantURI); - if (entry instanceof FileCacheEntry) { - File src = ((FileCacheEntry) entry).getRawBody(); + Resource orig = entry.getResource(); + if (orig instanceof FileResource) { + File src = ((FileResource) orig).getFile(); IOUtils.copyFile(src, file); } else { FileOutputStream out = new FileOutputStream(file); - IOUtils.copyAndClose(entry.getBody(), out); + IOUtils.copyAndClose(orig.getInputStream(), out); } - return new FileCacheEntry( + return new HttpCacheEntry( entry.getRequestDate(), entry.getResponseDate(), entry.getStatusLine(), entry.getAllHeaders(), - file, + new FileResource(file), variants); } diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/FileCacheEntry.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/FileResource.java similarity index 53% rename from httpclient-cache/src/main/java/org/apache/http/impl/client/cache/FileCacheEntry.java rename to httpclient-cache/src/main/java/org/apache/http/impl/client/cache/FileResource.java index ef05fbb96..df64a1c33 100644 --- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/FileCacheEntry.java +++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/FileResource.java @@ -30,73 +30,57 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.util.Date; -import java.util.Set; -import org.apache.http.Header; -import org.apache.http.StatusLine; -import org.apache.http.annotation.Immutable; -import org.apache.http.client.cache.HttpCacheEntry; +import org.apache.http.annotation.ThreadSafe; import org.apache.http.client.cache.Resource; /** - * {@link File} backed {@link HttpCacheEntry} that requires explicit deallocation. + * Cache resource backed by a file. + * + * @since 4.1 */ -@Immutable -class FileCacheEntry extends HttpCacheEntry { +@ThreadSafe +class FileResource implements Resource { - private static final long serialVersionUID = -8396589100351931966L; + private static final long serialVersionUID = 4132244415919043397L; private final File file; - private final FileResource resource; - public FileCacheEntry( - final Date requestDate, - final Date responseDate, - final StatusLine statusLine, - final Header[] responseHeaders, - final File file, - final Set variants) { - super(requestDate, responseDate, statusLine, responseHeaders, variants); + private volatile boolean disposed; + + public FileResource(final File file) { + super(); this.file = file; - this.resource = new FileResource(file); + this.disposed = false; } - File getRawBody() { + private void ensureValid() { + if (this.disposed) { + throw new IllegalStateException("Resource has been deallocated"); + } + } + + synchronized File getFile() { + ensureValid(); return this.file; } - - @Override - public long getBodyLength() { - return this.file.length(); - } - @Override - public InputStream getBody() throws IOException { + public synchronized InputStream getInputStream() throws IOException { + ensureValid(); return new FileInputStream(this.file); } - @Override - public Resource getResource() { - return this.resource; + public synchronized long length() { + ensureValid(); + return this.file.length(); } - class FileResource implements Resource { - - private File file; - - FileResource(final File file) { - super(); - this.file = file; + public synchronized void dispose() { + if (this.disposed) { + return; } - - public synchronized void dispose() { - if (this.file != null) { - this.file.delete(); - this.file = null; - } - } - + this.disposed = true; + this.file.delete(); } } diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/MemCacheEntry.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/HeapResource.java similarity index 56% rename from httpclient-cache/src/main/java/org/apache/http/impl/client/cache/MemCacheEntry.java rename to httpclient-cache/src/main/java/org/apache/http/impl/client/cache/HeapResource.java index 4a746f26e..5d0545c10 100644 --- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/MemCacheEntry.java +++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/HeapResource.java @@ -28,54 +28,40 @@ package org.apache.http.impl.client.cache; import java.io.ByteArrayInputStream; import java.io.InputStream; -import java.util.Date; -import java.util.Set; -import org.apache.http.Header; -import org.apache.http.StatusLine; import org.apache.http.annotation.Immutable; -import org.apache.http.client.cache.HttpCacheEntry; import org.apache.http.client.cache.Resource; /** - * Basic {@link HttpCacheEntry} that does not depend on any system resources that may require - * explicit deallocation. + * Cache resource backed by a byte array on the heap. + * + * @since 4.1 */ @Immutable -class MemCacheEntry extends HttpCacheEntry { +class HeapResource implements Resource { - private static final long serialVersionUID = -8464486112875881235L; + private static final long serialVersionUID = -2078599905620463394L; - private final byte[] body; + private final byte[] b; - public MemCacheEntry( - final Date requestDate, - final Date responseDate, - final StatusLine statusLine, - final Header[] responseHeaders, - final byte[] body, - final Set variants) { - super(requestDate, responseDate, statusLine, responseHeaders, variants); - this.body = body; + public HeapResource(final byte[] b) { + super(); + this.b = b; } - byte[] getRawBody() { - return this.body; + byte[] getByteArray() { + return this.b; } - @Override - public long getBodyLength() { - return this.body.length; + public InputStream getInputStream() { + return new ByteArrayInputStream(this.b); } - @Override - public InputStream getBody() { - return new ByteArrayInputStream(this.body); + public long length() { + return this.b.length; } - @Override - public Resource getResource() { - return null; + public void dispose() { } } diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/MemCacheEntryFactory.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/MemCacheEntryFactory.java index 487bdcdc1..6528ca63a 100644 --- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/MemCacheEntryFactory.java +++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/MemCacheEntryFactory.java @@ -38,6 +38,7 @@ import org.apache.http.StatusLine; import org.apache.http.annotation.Immutable; import org.apache.http.client.cache.HttpCacheEntry; import org.apache.http.client.cache.HttpCacheEntryFactory; +import org.apache.http.client.cache.Resource; /** * Generates {@link HttpCacheEntry} instances stored entirely in memory. @@ -52,11 +53,11 @@ public class MemCacheEntryFactory implements HttpCacheEntryFactory { final Date responseDate, final HttpResponse response, final byte[] body) { - return new MemCacheEntry(requestDate, + return new HttpCacheEntry(requestDate, responseDate, response.getStatusLine(), response.getAllHeaders(), - body, + new HeapResource(body), null); } @@ -67,11 +68,11 @@ public class MemCacheEntryFactory implements HttpCacheEntryFactory { final StatusLine statusLine, final Header[] headers, byte[] body) throws IOException { - return new MemCacheEntry(requestDate, + return new HttpCacheEntry(requestDate, responseDate, statusLine, headers, - body, + new HeapResource(body), null); } @@ -80,23 +81,25 @@ public class MemCacheEntryFactory implements HttpCacheEntryFactory { final HttpCacheEntry entry, final String variantURI) throws IOException { byte[] body; - if (entry instanceof MemCacheEntry) { - body = ((MemCacheEntry) entry).getRawBody(); + + Resource orig = entry.getResource(); + if (orig instanceof HeapResource) { + body = ((HeapResource) orig).getByteArray(); } else { ByteArrayOutputStream outstream = new ByteArrayOutputStream(); - IOUtils.copyAndClose(entry.getBody(), outstream); + IOUtils.copyAndClose(orig.getInputStream(), outstream); body = outstream.toByteArray(); } Set variants = new HashSet(entry.getVariantURIs()); variants.add(variantURI); - return new MemCacheEntry( + return new HttpCacheEntry( entry.getRequestDate(), entry.getResponseDate(), entry.getStatusLine(), entry.getAllHeaders(), - body, + new HeapResource(body), variants); } diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/SizeLimitedResponseReader.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/SizeLimitedResponseReader.java index 9983f405b..73b15156d 100644 --- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/SizeLimitedResponseReader.java +++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/SizeLimitedResponseReader.java @@ -74,7 +74,7 @@ class SizeLimitedResponseReader { return isTooLarge; } - private synchronized boolean consumeResponse() throws IOException { + private boolean consumeResponse() throws IOException { if (responseIsConsumed) throw new IllegalStateException( @@ -106,7 +106,7 @@ class SizeLimitedResponseReader { return false; } - private synchronized void consumeOutputStream() { + private void consumeOutputStream() { if (outputStreamConsumed) throw new IllegalStateException( "underlying output stream has already been written to byte[]"); diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/CacheEntry.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/CacheEntry.java index ae93e3de5..4502a7c57 100644 --- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/CacheEntry.java +++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/CacheEntry.java @@ -29,24 +29,27 @@ package org.apache.http.impl.client.cache; import java.util.Date; import org.apache.http.Header; +import org.apache.http.client.cache.HttpCacheEntry; +import org.apache.http.client.cache.Resource; -public class CacheEntry extends MemCacheEntry { +public class CacheEntry extends HttpCacheEntry { private static final long serialVersionUID = 7964121802841871079L; + private static Resource BODY = new HeapResource(new byte[] {}); public static final long MAX_AGE = CacheValidityPolicy.MAX_AGE; public CacheEntry( Date requestDate, Date responseDate) { - super(requestDate, responseDate, new OKStatus(), new Header[] {}, new byte[] {}, null); + super(requestDate, responseDate, new OKStatus(), new Header[] {}, BODY, null); } public CacheEntry( Date requestDate, Date responseDate, Header[] headers) { - super(requestDate, responseDate, new OKStatus(), headers, new byte[] {}, null); + super(requestDate, responseDate, new OKStatus(), headers, BODY, null); } public CacheEntry( @@ -54,17 +57,17 @@ public class CacheEntry extends MemCacheEntry { Date responseDate, Header[] headers, byte[] content) { - super(requestDate, responseDate, new OKStatus(), headers, content, null); + super(requestDate, responseDate, new OKStatus(), headers, new HeapResource(content), null); } public CacheEntry( Header[] headers, byte[] content) { - super(new Date(), new Date(), new OKStatus(), headers, content, null); + super(new Date(), new Date(), new OKStatus(), headers, new HeapResource(content), null); } public CacheEntry(Header[] headers) { - super(new Date(), new Date(), new OKStatus(), headers, new byte[] {}, null); + super(new Date(), new Date(), new OKStatus(), headers, BODY, null); } public CacheEntry() { @@ -72,7 +75,7 @@ public class CacheEntry extends MemCacheEntry { } public CacheEntry(byte[] content) { - super(new Date(), new Date(), new OKStatus(), new Header[] {}, content, null); + super(new Date(), new Date(), new OKStatus(), new Header[] {}, new HeapResource(content), null); } } diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCache.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCache.java index bd5cb3095..8e60bc695 100644 --- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCache.java +++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCache.java @@ -144,12 +144,12 @@ public class TestResponseCache { cache.updateEntry("foo", new HttpCacheUpdateCallback() { public HttpCacheEntry update(HttpCacheEntry existing) { - HttpCacheEntry updated = new MemCacheEntry( + HttpCacheEntry updated = new HttpCacheEntry( existing.getRequestDate(), existing.getRequestDate(), existing.getStatusLine(), existing.getAllHeaders(), - expectedArray, + new HeapResource(expectedArray), null); return updated; } @@ -158,7 +158,7 @@ public class TestResponseCache { HttpCacheEntry afterUpdate = cache.getEntry("foo"); ByteArrayOutputStream outstream = new ByteArrayOutputStream(); - InputStream instream = afterUpdate.getBody(); + InputStream instream = afterUpdate.getResource().getInputStream(); byte[] buf = new byte[2048]; int len; while ((len = instream.read(buf)) != -1) {