Refactored HttpCacheEntry; new API should allow for cache entries backed by a temporary file

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@980937 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2010-07-30 20:15:39 +00:00
parent 4ae188acbb
commit e0d37d5502
14 changed files with 99 additions and 103 deletions

View File

@ -27,6 +27,7 @@
package org.apache.http.client.cache; package org.apache.http.client.cache;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.io.Serializable; import java.io.Serializable;
@ -36,7 +37,6 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.apache.http.Header; import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion; import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine; import org.apache.http.StatusLine;
@ -60,7 +60,6 @@ public abstract class HttpCacheEntry implements Serializable {
private final Date responseDate; private final Date responseDate;
private final StatusLine statusLine; private final StatusLine statusLine;
private final CachedHeaderGroup responseHeaders; private final CachedHeaderGroup responseHeaders;
private final HttpEntity body;
private final Set<String> variantURIs; private final Set<String> variantURIs;
/** /**
@ -72,23 +71,16 @@ public abstract class HttpCacheEntry implements Serializable {
* @param responseDate * @param responseDate
* Date/time that the response came back (Used for age * Date/time that the response came back (Used for age
* calculations) * calculations)
* @param version * @param statusLine
* HTTP Response Version * HTTP status line
* @param responseHeaders * @param responseHeaders
* Header[] from original HTTP Response * Header[] from original HTTP Response
* @param body
* HttpEntity representing the body of the response
* @param status
* Numeric HTTP Status Code
* @param reason
* String message from HTTP Status Line
*/ */
public HttpCacheEntry( public HttpCacheEntry(
final Date requestDate, final Date requestDate,
final Date responseDate, final Date responseDate,
final StatusLine statusLine, final StatusLine statusLine,
final Header[] responseHeaders, final Header[] responseHeaders,
final HttpEntity body,
final Set<String> variants) { final Set<String> variants) {
super(); super();
if (requestDate == null) { if (requestDate == null) {
@ -103,17 +95,12 @@ public abstract class HttpCacheEntry implements Serializable {
if (responseHeaders == null) { if (responseHeaders == null) {
throw new IllegalArgumentException("Response headers may not be null"); throw new IllegalArgumentException("Response headers may not be null");
} }
if (body == null) {
throw new IllegalArgumentException("Response body may not be null");
}
this.requestDate = requestDate; this.requestDate = requestDate;
this.responseDate = responseDate; this.responseDate = responseDate;
this.statusLine = statusLine; this.statusLine = statusLine;
this.responseHeaders = new CachedHeaderGroup(); this.responseHeaders = new CachedHeaderGroup();
this.responseHeaders.setHeaders(responseHeaders); this.responseHeaders.setHeaders(responseHeaders);
this.body = body;
this.variantURIs = variants != null ? new HashSet<String>(variants) : new HashSet<String>(); this.variantURIs = variants != null ? new HashSet<String>(variants) : new HashSet<String>();
} }
public StatusLine getStatusLine() { public StatusLine getStatusLine() {
@ -140,10 +127,6 @@ public abstract class HttpCacheEntry implements Serializable {
return responseDate; return responseDate;
} }
public HttpEntity getBody() {
return body;
}
public Header[] getAllHeaders() { public Header[] getAllHeaders() {
return responseHeaders.getAllHeaders(); return responseHeaders.getAllHeaders();
} }
@ -164,6 +147,12 @@ public abstract class HttpCacheEntry implements Serializable {
return Collections.unmodifiableSet(this.variantURIs); return Collections.unmodifiableSet(this.variantURIs);
} }
public abstract Resource getResource();
public abstract InputStream getBody();
public abstract long getBodyLength();
private void writeObject(ObjectOutputStream out) throws IOException { private void writeObject(ObjectOutputStream out) throws IOException {
// write CacheEntry // write CacheEntry
@ -201,8 +190,6 @@ public abstract class HttpCacheEntry implements Serializable {
this.responseHeaders.setHeaders(headers); this.responseHeaders.setHeaders(headers);
} }
public abstract Resource getResource();
@Override @Override
public String toString() { public String toString() {
return "[request date=" + this.requestDate + "; response date=" + this.responseDate return "[request date=" + this.requestDate + "; response date=" + this.responseDate

View File

@ -26,11 +26,12 @@
*/ */
package org.apache.http.impl.client.cache; package org.apache.http.impl.client.cache;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Date; import java.util.Date;
import java.util.Set; import java.util.Set;
import org.apache.http.Header; import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.StatusLine; import org.apache.http.StatusLine;
import org.apache.http.annotation.Immutable; import org.apache.http.annotation.Immutable;
import org.apache.http.client.cache.HttpCacheEntry; import org.apache.http.client.cache.HttpCacheEntry;
@ -45,14 +46,27 @@ public class BasicHttpCacheEntry extends HttpCacheEntry {
private static final long serialVersionUID = -8464486112875881235L; private static final long serialVersionUID = -8464486112875881235L;
private final byte[] body;
public BasicHttpCacheEntry( public BasicHttpCacheEntry(
final Date requestDate, final Date requestDate,
final Date responseDate, final Date responseDate,
final StatusLine statusLine, final StatusLine statusLine,
final Header[] responseHeaders, final Header[] responseHeaders,
final HttpEntity body, final byte[] body,
final Set<String> variants) { final Set<String> variants) {
super(requestDate, responseDate, statusLine, responseHeaders, body, variants); super(requestDate, responseDate, statusLine, responseHeaders, variants);
this.body = body;
}
@Override
public long getBodyLength() {
return this.body.length;
}
@Override
public InputStream getBody() {
return new ByteArrayInputStream(this.body);
} }
@Override @Override

View File

@ -26,7 +26,6 @@
*/ */
package org.apache.http.impl.client.cache; package org.apache.http.impl.client.cache;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -35,43 +34,27 @@ import java.io.Serializable;
import org.apache.http.Header; import org.apache.http.Header;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.annotation.Immutable; import org.apache.http.annotation.Immutable;
import org.apache.http.message.BasicHeader; import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HTTP;
@Immutable @Immutable
class CacheEntity implements HttpEntity, Cloneable, Serializable { class CacheEntity implements HttpEntity, Serializable {
private static final long serialVersionUID = -3467082284120936233L; private static final long serialVersionUID = -3467082284120936233L;
private final byte[] content; private final HttpCacheEntry cacheEntry;
private final String contentType;
private final String contentEncoding;
public CacheEntity(final byte[] b, final String contentType, final String contentEncoding) { public CacheEntity(final HttpCacheEntry cacheEntry) {
super(); super();
this.content = b; this.cacheEntry = cacheEntry;
this.contentType = contentType;
this.contentEncoding = contentEncoding;
}
public CacheEntity(final byte[] b) {
this(b, null, null);
} }
public Header getContentType() { public Header getContentType() {
if (this.contentType == null) { return this.cacheEntry.getFirstHeader(HTTP.CONTENT_TYPE);
return null;
}
return new BasicHeader(HTTP.CONTENT_TYPE, this.contentType);
} }
public Header getContentEncoding() { public Header getContentEncoding() {
if (this.contentEncoding == null) { return this.cacheEntry.getFirstHeader(HTTP.CONTENT_ENCODING);
return null;
}
return new BasicHeader(HTTP.CONTENT_ENCODING, this.contentEncoding);
} }
public boolean isChunked() { public boolean isChunked() {
@ -83,19 +66,23 @@ class CacheEntity implements HttpEntity, Cloneable, Serializable {
} }
public long getContentLength() { public long getContentLength() {
return this.content.length; return this.cacheEntry.getBodyLength();
} }
public InputStream getContent() { public InputStream getContent() {
return new ByteArrayInputStream(this.content); return this.cacheEntry.getBody();
} }
public void writeTo(final OutputStream outstream) throws IOException { public void writeTo(final OutputStream outstream) throws IOException {
if (outstream == null) { if (outstream == null) {
throw new IllegalArgumentException("Output stream may not be null"); throw new IllegalArgumentException("Output stream may not be null");
} }
outstream.write(this.content); InputStream instream = this.cacheEntry.getBody();
outstream.flush(); byte[] buf = new byte[2048];
int len;
while ((len = instream.read(buf)) != -1) {
outstream.write(buf, 0, len);
}
} }
public boolean isStreaming() { public boolean isStreaming() {

View File

@ -26,15 +26,16 @@
*/ */
package org.apache.http.impl.client.cache; package org.apache.http.impl.client.cache;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.apache.http.Header;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.annotation.Immutable; import org.apache.http.annotation.Immutable;
import org.apache.http.client.cache.HttpCacheEntry; import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.protocol.HTTP;
/** /**
* Generates a {@link CacheEntry} from a {@link HttpResponse} * Generates a {@link CacheEntry} from a {@link HttpResponse}
@ -49,29 +50,32 @@ class CacheEntryGenerator {
Date responseDate, Date responseDate,
HttpResponse response, HttpResponse response,
byte[] body) { byte[] body) {
Header ct = response.getFirstHeader(HTTP.CONTENT_TYPE);
Header ce = response.getFirstHeader(HTTP.CONTENT_ENCODING);
CacheEntity entity = new CacheEntity(
body,
ct != null ? ct.getValue() : null,
ce != null ? ce.getValue() : null);
return new BasicHttpCacheEntry(requestDate, return new BasicHttpCacheEntry(requestDate,
responseDate, responseDate,
response.getStatusLine(), response.getStatusLine(),
response.getAllHeaders(), response.getAllHeaders(),
entity, body,
null); null);
} }
public HttpCacheEntry copyWithVariant(final HttpCacheEntry entry, final String variantURI){ public HttpCacheEntry copyWithVariant(
final HttpCacheEntry entry, final String variantURI) throws IOException {
Set<String> variants = new HashSet<String>(entry.getVariantURIs()); Set<String> variants = new HashSet<String>(entry.getVariantURIs());
variants.add(variantURI); variants.add(variantURI);
ByteArrayOutputStream outstream = new ByteArrayOutputStream();
InputStream instream = entry.getBody();
byte[] buf = new byte[2048];
int len;
while ((len = instream.read(buf)) != -1) {
outstream.write(buf, 0, len);
}
return new BasicHttpCacheEntry( return new BasicHttpCacheEntry(
entry.getRequestDate(), entry.getRequestDate(),
entry.getResponseDate(), entry.getResponseDate(),
entry.getStatusLine(), entry.getStatusLine(),
entry.getAllHeaders(), entry.getAllHeaders(),
entry.getBody(), variants); outstream.toByteArray(),
variants);
} }
} }

View File

@ -26,7 +26,9 @@
*/ */
package org.apache.http.impl.client.cache; package org.apache.http.impl.client.cache;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
@ -69,10 +71,17 @@ class CacheEntryUpdater {
HttpResponse response) throws IOException { HttpResponse response) throws IOException {
Header[] mergedHeaders = mergeHeaders(entry, response); Header[] mergedHeaders = mergeHeaders(entry, response);
ByteArrayOutputStream outstream = new ByteArrayOutputStream();
InputStream instream = entry.getBody();
byte[] buf = new byte[2048];
int len;
while ((len = instream.read(buf)) != -1) {
outstream.write(buf, 0, len);
}
HttpCacheEntry updated = new BasicHttpCacheEntry(requestDate, responseDate, HttpCacheEntry updated = new BasicHttpCacheEntry(requestDate, responseDate,
entry.getStatusLine(), entry.getStatusLine(),
mergedHeaders, mergedHeaders,
entry.getBody(), outstream.toByteArray(),
null); null);
return updated; return updated;
} }

View File

@ -140,7 +140,7 @@ class CacheValidityPolicy {
* @return boolean indicating whether actual length matches Content-Length * @return boolean indicating whether actual length matches Content-Length
*/ */
protected boolean contentLengthHeaderMatchesActualLength(final HttpCacheEntry entry) { protected boolean contentLengthHeaderMatchesActualLength(final HttpCacheEntry entry) {
return getContentLengthValue(entry) == entry.getBody().getContentLength(); return getContentLengthValue(entry) == entry.getBodyLength();
} }
protected long getApparentAgeSecs(final HttpCacheEntry entry) { protected long getApparentAgeSecs(final HttpCacheEntry entry) {

View File

@ -70,10 +70,10 @@ class CachedHttpResponseGenerator {
.getStatusCode(), entry.getReasonPhrase()); .getStatusCode(), entry.getReasonPhrase());
if (entry.getStatusCode() != HttpStatus.SC_NOT_MODIFIED) { if (entry.getStatusCode() != HttpStatus.SC_NOT_MODIFIED) {
HttpEntity entity = entry.getBody(); HttpEntity entity = new CacheEntity(entry);
response.setEntity(entity);
response.setHeaders(entry.getAllHeaders()); response.setHeaders(entry.getAllHeaders());
addMissingContentLengthHeader(response, entity); addMissingContentLengthHeader(response, entity);
response.setEntity(entity);
} }
long age = this.validityStrategy.getCurrentAgeSecs(entry); long age = this.validityStrategy.getCurrentAgeSecs(entry);

View File

@ -519,7 +519,7 @@ public class CachingHttpClient implements HttpClient {
HttpCacheUpdateCallback callback = new HttpCacheUpdateCallback() { HttpCacheUpdateCallback callback = new HttpCacheUpdateCallback() {
public HttpCacheEntry update(HttpCacheEntry existing) { public HttpCacheEntry update(HttpCacheEntry existing) throws IOException {
return doGetUpdatedParentEntry(existing, entry, variantURI); return doGetUpdatedParentEntry(existing, entry, variantURI);
} }
@ -530,7 +530,7 @@ public class CachingHttpClient implements HttpClient {
HttpCacheEntry doGetUpdatedParentEntry( HttpCacheEntry doGetUpdatedParentEntry(
HttpCacheEntry existing, HttpCacheEntry existing,
HttpCacheEntry entry, String variantURI) { HttpCacheEntry entry, String variantURI) throws IOException {
if (existing != null) { if (existing != null) {
return cacheEntryGenerator.copyWithVariant(existing, variantURI); return cacheEntryGenerator.copyWithVariant(existing, variantURI);
} else { } else {

View File

@ -29,8 +29,6 @@ package org.apache.http.impl.client.cache;
import java.util.Date; import java.util.Date;
import org.apache.http.Header; import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.StatusLine;
public class CacheEntry extends BasicHttpCacheEntry { public class CacheEntry extends BasicHttpCacheEntry {
@ -38,28 +36,17 @@ public class CacheEntry extends BasicHttpCacheEntry {
public static final long MAX_AGE = CacheValidityPolicy.MAX_AGE; public static final long MAX_AGE = CacheValidityPolicy.MAX_AGE;
public CacheEntry(
Date requestDate,
Date responseDate,
StatusLine statusLine,
Header[] responseHeaders,
HttpEntity body) {
super(requestDate, responseDate, statusLine, responseHeaders, body, null);
}
public CacheEntry( public CacheEntry(
Date requestDate, Date requestDate,
Date responseDate) { Date responseDate) {
super(requestDate, responseDate, new OKStatus(), new Header[] {}, super(requestDate, responseDate, new OKStatus(), new Header[] {}, new byte[] {}, null);
new CacheEntity(new byte[] {}), null);
} }
public CacheEntry( public CacheEntry(
Date requestDate, Date requestDate,
Date responseDate, Date responseDate,
Header[] headers) { Header[] headers) {
super(requestDate, responseDate, new OKStatus(), headers, super(requestDate, responseDate, new OKStatus(), headers, new byte[] {}, null);
new CacheEntity(new byte[] {}), null);
} }
public CacheEntry( public CacheEntry(
@ -67,20 +54,17 @@ public class CacheEntry extends BasicHttpCacheEntry {
Date responseDate, Date responseDate,
Header[] headers, Header[] headers,
byte[] content) { byte[] content) {
super(requestDate, responseDate, new OKStatus(), headers, super(requestDate, responseDate, new OKStatus(), headers, content, null);
new CacheEntity(content), null);
} }
public CacheEntry( public CacheEntry(
Header[] headers, Header[] headers,
byte[] content) { byte[] content) {
super(new Date(), new Date(), new OKStatus(), headers, super(new Date(), new Date(), new OKStatus(), headers, content, null);
new CacheEntity(content), null);
} }
public CacheEntry(Header[] headers) { public CacheEntry(Header[] headers) {
super(new Date(), new Date(), new OKStatus(), headers, super(new Date(), new Date(), new OKStatus(), headers, new byte[] {}, null);
new CacheEntity(new byte[] {}), null);
} }
public CacheEntry() { public CacheEntry() {
@ -88,8 +72,7 @@ public class CacheEntry extends BasicHttpCacheEntry {
} }
public CacheEntry(byte[] content) { public CacheEntry(byte[] content) {
super(new Date(), new Date(), new OKStatus(), new Header[] {}, super(new Date(), new Date(), new OKStatus(), new Header[] {}, content, null);
new CacheEntity(content), null);
} }
} }

View File

@ -103,7 +103,7 @@ public class TestCacheEntry {
} }
@Test @Test
public void testCacheEntryCanStoreMultipleVariantUris() { public void testCacheEntryCanStoreMultipleVariantUris() throws Exception {
Header[] headers = new Header[]{}; Header[] headers = new Header[]{};
CacheEntry entry = new CacheEntry(headers); CacheEntry entry = new CacheEntry(headers);

View File

@ -1220,7 +1220,7 @@ public class TestCachingHttpClient {
bytes)).andReturn(mockCacheEntry); bytes)).andReturn(mockCacheEntry);
} }
private void copyCacheEntry(CacheEntry entry, String variantURI) { private void copyCacheEntry(CacheEntry entry, String variantURI) throws IOException {
EasyMock.expect( EasyMock.expect(
mockEntryGenerator.copyWithVariant(entry, variantURI)).andReturn(entry); mockEntryGenerator.copyWithVariant(entry, variantURI)).andReturn(entry);
} }

View File

@ -27,6 +27,7 @@
package org.apache.http.impl.client.cache; package org.apache.http.impl.client.cache;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import org.apache.http.client.cache.HttpCacheEntry; import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.client.cache.HttpCacheUpdateCallback; import org.apache.http.client.cache.HttpCacheUpdateCallback;
@ -148,7 +149,7 @@ public class TestResponseCache {
existing.getRequestDate(), existing.getRequestDate(),
existing.getStatusLine(), existing.getStatusLine(),
existing.getAllHeaders(), existing.getAllHeaders(),
new CacheEntity(expectedArray), expectedArray,
null); null);
return updated; return updated;
} }
@ -156,9 +157,14 @@ public class TestResponseCache {
HttpCacheEntry afterUpdate = cache.getEntry("foo"); HttpCacheEntry afterUpdate = cache.getEntry("foo");
ByteArrayOutputStream stream = new ByteArrayOutputStream(); ByteArrayOutputStream outstream = new ByteArrayOutputStream();
afterUpdate.getBody().writeTo(stream); InputStream instream = afterUpdate.getBody();
byte[] bytes = stream.toByteArray(); byte[] buf = new byte[2048];
int len;
while ((len = instream.read(buf)) != -1) {
outstream.write(buf, 0, len);
}
byte[] bytes = outstream.toByteArray();
Assert.assertArrayEquals(expectedArray, bytes); Assert.assertArrayEquals(expectedArray, bytes);
} }

View File

@ -632,10 +632,16 @@ public abstract class AbstractHttpClient implements HttpClient {
URI requestURI = request.getURI(); URI requestURI = request.getURI();
if (requestURI.isAbsolute()) { if (requestURI.isAbsolute()) {
String host = requestURI.getHost(); String ssp = requestURI.getSchemeSpecificPart();
ssp = ssp.substring(2, ssp.length()); //remove "//" prefix
int end = ssp.indexOf(':') > 0 ? ssp.indexOf(':') :
ssp.indexOf('/') > 0 ? ssp.indexOf('/') :
ssp.indexOf('?') > 0 ? ssp.indexOf('?') : ssp.length();
String host = ssp.substring(0, end);
int port = requestURI.getPort(); int port = requestURI.getPort();
String scheme = requestURI.getScheme(); String scheme = requestURI.getScheme();
if (host == null) { if (host == null || "".equals(host)) {
throw new ClientProtocolException( throw new ClientProtocolException(
"URI does not specify a valid host name: " + requestURI); "URI does not specify a valid host name: " + requestURI);
} }

View File

@ -37,11 +37,11 @@ import org.apache.http.client.HttpClient;
import org.apache.http.client.UserTokenHandler; import org.apache.http.client.UserTokenHandler;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ManagedClientConnection; import org.apache.http.conn.ManagedClientConnection;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.entity.StringEntity; import org.apache.http.entity.StringEntity;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.localserver.ServerTestBase; import org.apache.http.localserver.ServerTestBase;
import org.apache.http.params.BasicHttpParams; import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams; import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.ExecutionContext; import org.apache.http.protocol.ExecutionContext;
@ -83,7 +83,7 @@ public class TestStatefulConnManagement extends ServerTestBase {
HttpHost target = new HttpHost("localhost", port); HttpHost target = new HttpHost("localhost", port);
HttpParams params = new BasicHttpParams(); HttpParams params = new BasicHttpParams();
ConnManagerParams.setTimeout(params, 10L); HttpConnectionParams.setConnectionTimeout(params, 10);
ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(supportedSchemes); ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(supportedSchemes);
mgr.setMaxTotal(workerCount); mgr.setMaxTotal(workerCount);