Refactored HttpCacheEntry / Resource API: HttpCacheEntry is no longer abstract; the same class can be used with any Resource implementations

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@984197 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2010-08-10 20:15:03 +00:00
parent 1b58a2360d
commit cb4f012fa7
12 changed files with 119 additions and 128 deletions

View File

@ -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<String> 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<String> 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<String>(variants) : new HashSet<String>();
}
@ -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 {

View File

@ -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();

View File

@ -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 {

View File

@ -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) {

View File

@ -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) {

View File

@ -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<String> variants = new HashSet<String>(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);
}

View File

@ -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<String> 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();
}
}

View File

@ -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<String> 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() {
}
}

View File

@ -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<String> variants = new HashSet<String>(entry.getVariantURIs());
variants.add(variantURI);
return new MemCacheEntry(
return new HttpCacheEntry(
entry.getRequestDate(),
entry.getResponseDate(),
entry.getStatusLine(),
entry.getAllHeaders(),
body,
new HeapResource(body),
variants);
}

View File

@ -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[]");

View File

@ -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);
}
}

View File

@ -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) {