HTTPCLIENT-1032: variant information is now passed back from the HttpCache

as a Map<String,Variant> mapping Etags to specific information about the
variants (variant key, cache key, cache entry).


git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1049045 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Jonathan Moore 2010-12-14 11:12:36 +00:00
parent 022c25070e
commit 477fdb1a3c
8 changed files with 40 additions and 38 deletions

View File

@ -239,17 +239,19 @@ class BasicHttpCache implements HttpCache {
cacheInvalidator.flushInvalidatedCacheEntries(host, request); cacheInvalidator.flushInvalidatedCacheEntries(host, request);
} }
public Map<String, HttpCacheEntry> getVariantCacheEntriesWithEtags(HttpHost host, HttpRequest request) public Map<String, Variant> getVariantCacheEntriesWithEtags(HttpHost host, HttpRequest request)
throws IOException { throws IOException {
Map<String,HttpCacheEntry> variants = new HashMap<String,HttpCacheEntry>(); Map<String,Variant> variants = new HashMap<String,Variant>();
HttpCacheEntry root = storage.getEntry(uriExtractor.getURI(host, request)); HttpCacheEntry root = storage.getEntry(uriExtractor.getURI(host, request));
if (root == null || !root.hasVariants()) return variants; if (root == null || !root.hasVariants()) return variants;
for(String variantCacheKey : root.getVariantMap().values()) { for(Map.Entry<String, String> variant : root.getVariantMap().entrySet()) {
String variantKey = variant.getKey();
String variantCacheKey = variant.getValue();
HttpCacheEntry entry = storage.getEntry(variantCacheKey); HttpCacheEntry entry = storage.getEntry(variantCacheKey);
if (entry == null) continue;
Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG); Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG);
if (etagHeader != null) { if (etagHeader == null) continue;
variants.put(etagHeader.getValue(), entry); variants.put(etagHeader.getValue(), new Variant(variantKey, variantCacheKey, entry));
}
} }
return variants; return variants;
} }

View File

@ -412,15 +412,15 @@ public class CachingHttpClient implements HttpClient {
"Gateway Timeout"); "Gateway Timeout");
} }
Map<String,HttpCacheEntry> variantEntries = null; Map<String,Variant> variants = null;
try { try {
variantEntries = responseCache.getVariantCacheEntriesWithEtags(target, request); variants = responseCache.getVariantCacheEntriesWithEtags(target, request);
} catch (IOException ioe) { } catch (IOException ioe) {
log.warn("Unable to retrieve variant entries from cache", ioe); log.warn("Unable to retrieve variant entries from cache", ioe);
} }
if (variantEntries != null && variantEntries.size() > 0) { if (variants != null && variants.size() > 0) {
try { try {
return negotiateResponseFromVariants(target, request, context, variantEntries); return negotiateResponseFromVariants(target, request, context, variants);
} catch (ProtocolException e) { } catch (ProtocolException e) {
throw new ClientProtocolException(e); throw new ClientProtocolException(e);
} }
@ -594,8 +594,8 @@ public class CachingHttpClient implements HttpClient {
HttpResponse negotiateResponseFromVariants(HttpHost target, HttpResponse negotiateResponseFromVariants(HttpHost target,
HttpRequest request, HttpContext context, HttpRequest request, HttpContext context,
Map<String, HttpCacheEntry> variantEntries) throws IOException, ProtocolException { Map<String, Variant> variants) throws IOException, ProtocolException {
HttpRequest conditionalRequest = conditionalRequestBuilder.buildConditionalRequestFromVariants(request, variantEntries); HttpRequest conditionalRequest = conditionalRequestBuilder.buildConditionalRequestFromVariants(request, variants);
Date requestDate = getCurrentDate(); Date requestDate = getCurrentDate();
HttpResponse backendResponse = backend.execute(target, conditionalRequest, context); HttpResponse backendResponse = backend.execute(target, conditionalRequest, context);
@ -605,7 +605,6 @@ public class CachingHttpClient implements HttpClient {
if (backendResponse.getStatusLine().getStatusCode() != HttpStatus.SC_NOT_MODIFIED) { if (backendResponse.getStatusLine().getStatusCode() != HttpStatus.SC_NOT_MODIFIED) {
return handleBackendResponse(target, request, requestDate, responseDate, backendResponse); return handleBackendResponse(target, request, requestDate, responseDate, backendResponse);
// return handleBackendResponse(target, conditionalRequest, requestDate, responseDate, backendResponse);
} }
Header resultEtagHeader = backendResponse.getFirstHeader(HeaderConstants.ETAG); Header resultEtagHeader = backendResponse.getFirstHeader(HeaderConstants.ETAG);
@ -615,13 +614,15 @@ public class CachingHttpClient implements HttpClient {
} }
String resultEtag = resultEtagHeader.getValue(); String resultEtag = resultEtagHeader.getValue();
HttpCacheEntry matchedEntry = variantEntries.get(resultEtag); Variant matchingVariant = variants.get(resultEtag);
if (matchedEntry == null) { if (matchingVariant == null) {
log.debug("304 response did not contain ETag matching one sent in If-None-Match"); log.debug("304 response did not contain ETag matching one sent in If-None-Match");
return callBackend(target, request, context); return callBackend(target, request, context);
} }
HttpCacheEntry matchedEntry = matchingVariant.getEntry();
if (revalidationResponseIsTooOld(backendResponse, matchedEntry)) { if (revalidationResponseIsTooOld(backendResponse, matchedEntry)) {
HttpRequest unconditional = conditionalRequestBuilder HttpRequest unconditional = conditionalRequestBuilder
.buildUnconditionalRequest(request, matchedEntry); .buildUnconditionalRequest(request, matchedEntry);

View File

@ -93,7 +93,7 @@ class ConditionalRequestBuilder {
* @throws ProtocolException when I am unable to build a new origin request. * @throws ProtocolException when I am unable to build a new origin request.
*/ */
public HttpRequest buildConditionalRequestFromVariants(HttpRequest request, public HttpRequest buildConditionalRequestFromVariants(HttpRequest request,
Map<String, HttpCacheEntry> variantEntries) Map<String, Variant> variants)
throws ProtocolException { throws ProtocolException {
RequestWrapper wrapperRequest = new RequestWrapper(request); RequestWrapper wrapperRequest = new RequestWrapper(request);
wrapperRequest.resetHeaders(); wrapperRequest.resetHeaders();
@ -101,7 +101,7 @@ class ConditionalRequestBuilder {
// we do not support partial content so all etags are used // we do not support partial content so all etags are used
StringBuilder etags = new StringBuilder(); StringBuilder etags = new StringBuilder();
boolean first = true; boolean first = true;
for(String etag : variantEntries.keySet()) { for(String etag : variants.keySet()) {
if (!first) { if (!first) {
etags.append(","); etags.append(",");
} }

View File

@ -75,7 +75,7 @@ interface HttpCache {
* @return a <code>Map</code> mapping Etags to variant cache entries * @return a <code>Map</code> mapping Etags to variant cache entries
* @throws IOException * @throws IOException
*/ */
Map<String,HttpCacheEntry> getVariantCacheEntriesWithEtags(HttpHost host, HttpRequest request) Map<String,Variant> getVariantCacheEntriesWithEtags(HttpHost host, HttpRequest request)
throws IOException; throws IOException;
/** /**

View File

@ -33,7 +33,6 @@ import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion; import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient; import org.apache.http.client.HttpClient;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.message.BasicHttpRequest; import org.apache.http.message.BasicHttpRequest;
import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpContext;
import org.easymock.IExpectationSetters; import org.easymock.IExpectationSetters;
@ -104,7 +103,7 @@ public abstract class AbstractProtocolTest {
EasyMock.expect(mockCache.getCacheEntry(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class))) EasyMock.expect(mockCache.getCacheEntry(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class)))
.andReturn(null).anyTimes(); .andReturn(null).anyTimes();
EasyMock.expect(mockCache.getVariantCacheEntriesWithEtags(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class))) EasyMock.expect(mockCache.getVariantCacheEntriesWithEtags(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class)))
.andReturn(new HashMap<String,HttpCacheEntry>()).anyTimes(); .andReturn(new HashMap<String,Variant>()).anyTimes();
mockCache.flushCacheEntriesFor(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class)); mockCache.flushCacheEntriesFor(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class));
EasyMock.expectLastCall().anyTimes(); EasyMock.expectLastCall().anyTimes();

View File

@ -338,7 +338,7 @@ public class TestBasicHttpCache {
HttpHost host = new HttpHost("foo.example.com"); HttpHost host = new HttpHost("foo.example.com");
HttpRequest request = new HttpGet("http://foo.example.com/bar"); HttpRequest request = new HttpGet("http://foo.example.com/bar");
Map<String,HttpCacheEntry> variants = impl.getVariantCacheEntriesWithEtags(host, request); Map<String,Variant> variants = impl.getVariantCacheEntriesWithEtags(host, request);
assertNotNull(variants); assertNotNull(variants);
assertEquals(0, variants.size()); assertEquals(0, variants.size());
@ -372,7 +372,7 @@ public class TestBasicHttpCache {
impl.cacheAndReturnResponse(host, req1, resp1, new Date(), new Date()); impl.cacheAndReturnResponse(host, req1, resp1, new Date(), new Date());
impl.cacheAndReturnResponse(host, req2, resp2, new Date(), new Date()); impl.cacheAndReturnResponse(host, req2, resp2, new Date(), new Date());
Map<String,HttpCacheEntry> variants = impl.getVariantCacheEntriesWithEtags(host, req1); Map<String,Variant> variants = impl.getVariantCacheEntriesWithEtags(host, req1);
assertNotNull(variants); assertNotNull(variants);
assertEquals(2, variants.size()); assertEquals(2, variants.size());

View File

@ -300,7 +300,7 @@ public class TestCachingHttpClient {
cacheInvalidatorWasCalled(); cacheInvalidatorWasCalled();
requestPolicyAllowsCaching(true); requestPolicyAllowsCaching(true);
getCacheEntryReturns(null); getCacheEntryReturns(null);
getVariantCacheEntriesReturns(new HashMap<String,HttpCacheEntry>()); getVariantCacheEntriesReturns(new HashMap<String,Variant>());
requestProtocolValidationIsCalled(); requestProtocolValidationIsCalled();
requestIsFatallyNonCompliant(null); requestIsFatallyNonCompliant(null);
@ -450,7 +450,7 @@ public class TestCachingHttpClient {
@Test @Test
public void testRevalidationRetriesUnconditionallyIfOlderResponseReceived() public void testRevalidationRetriesUnconditionallyIfOlderResponseReceived()
throws Exception { throws Exception {
// TODO
mockImplMethods(GET_CURRENT_DATE); mockImplMethods(GET_CURRENT_DATE);
Date now = new Date(); Date now = new Date();
@ -1593,10 +1593,10 @@ public class TestCachingHttpClient {
HttpCacheEntry variant3 = HttpTestUtils HttpCacheEntry variant3 = HttpTestUtils
.makeCacheEntry(new Header[] {new BasicHeader(HeaderConstants.ETAG, "\"etag3\"") }); .makeCacheEntry(new Header[] {new BasicHeader(HeaderConstants.ETAG, "\"etag3\"") });
Map<String,HttpCacheEntry> variants = new HashMap<String,HttpCacheEntry>(); Map<String,Variant> variants = new HashMap<String,Variant>();
variants.put("\"etag1\"", variant1); variants.put("\"etag1\"", new Variant("A","B",variant1));
variants.put("\"etag2\"", variant2); variants.put("\"etag2\"", new Variant("C","D",variant2));
variants.put("\"etag3\"", variant3); variants.put("\"etag3\"", new Variant("E","F",variant3));
HttpRequest variantConditionalRequest = new BasicHttpRequest("GET", "http://foo.com/bar", HttpVersion.HTTP_1_1); HttpRequest variantConditionalRequest = new BasicHttpRequest("GET", "http://foo.com/bar", HttpVersion.HTTP_1_1);
variantConditionalRequest.addHeader(new BasicHeader(HeaderConstants.IF_NONE_MATCH, "etag1, etag2, etag3")); variantConditionalRequest.addHeader(new BasicHeader(HeaderConstants.IF_NONE_MATCH, "etag1, etag2, etag3"));
@ -1641,10 +1641,10 @@ public class TestCachingHttpClient {
HttpCacheEntry cacheEntry = null; HttpCacheEntry cacheEntry = null;
Map<String,HttpCacheEntry> variants = new HashMap<String,HttpCacheEntry>(); Map<String,Variant> variants = new HashMap<String,Variant>();
variants.put("\"etag1\"", variant1); variants.put("\"etag1\"", new Variant("A","B",variant1));
variants.put("\"etag2\"", variant2); variants.put("\"etag2\"", new Variant("C","D",variant2));
variants.put("\"etag3\"", variant3); variants.put("\"etag3\"", new Variant("E","F",variant3));
HttpRequest variantConditionalRequest = new BasicHttpRequest("GET", "http://foo.com/bar", HttpVersion.HTTP_1_1); HttpRequest variantConditionalRequest = new BasicHttpRequest("GET", "http://foo.com/bar", HttpVersion.HTTP_1_1);
variantConditionalRequest.addHeader(new BasicHeader(HeaderConstants.IF_NONE_MATCH, "etag1, etag2, etag3")); variantConditionalRequest.addHeader(new BasicHeader(HeaderConstants.IF_NONE_MATCH, "etag1, etag2, etag3"));
@ -1957,7 +1957,7 @@ public class TestCachingHttpClient {
EasyMock.expect(mockCache.getCacheEntry(host, request)).andReturn(result); EasyMock.expect(mockCache.getCacheEntry(host, request)).andReturn(result);
} }
private void getVariantCacheEntriesReturns(Map<String,HttpCacheEntry> result) throws IOException { private void getVariantCacheEntriesReturns(Map<String,Variant> result) throws IOException {
EasyMock.expect(mockCache.getVariantCacheEntriesWithEtags(host, request)).andReturn(result); EasyMock.expect(mockCache.getVariantCacheEntriesWithEtags(host, request)).andReturn(result);
} }
@ -1989,7 +1989,7 @@ public class TestCachingHttpClient {
EasyMock.<HttpCacheEntry>anyObject())).andReturn(b); EasyMock.<HttpCacheEntry>anyObject())).andReturn(b);
} }
private void conditionalVariantRequestBuilderReturns(Map<String,HttpCacheEntry> variantEntries, HttpRequest validate) private void conditionalVariantRequestBuilderReturns(Map<String,Variant> variantEntries, HttpRequest validate)
throws Exception { throws Exception {
EasyMock.expect(mockConditionalRequestBuilder.buildConditionalRequestFromVariants(request, variantEntries)) EasyMock.expect(mockConditionalRequestBuilder.buildConditionalRequestFromVariants(request, variantEntries))
.andReturn(validate); .andReturn(validate);

View File

@ -307,10 +307,10 @@ public class TestConditionalRequestBuilder {
String etag2 = "\"456\""; String etag2 = "\"456\"";
String etag3 = "\"789\""; String etag3 = "\"789\"";
Map<String,HttpCacheEntry> variantEntries = new HashMap<String,HttpCacheEntry>(); Map<String,Variant> variantEntries = new HashMap<String,Variant>();
variantEntries.put(etag1, HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", etag1) })); variantEntries.put(etag1, new Variant("A","B",HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", etag1) })));
variantEntries.put(etag2, HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", etag2) })); variantEntries.put(etag2, new Variant("C","D",HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", etag2) })));
variantEntries.put(etag3, HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", etag3) })); variantEntries.put(etag3, new Variant("E","F",HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", etag3) })));
HttpRequest conditional = impl.buildConditionalRequestFromVariants(request, variantEntries); HttpRequest conditional = impl.buildConditionalRequestFromVariants(request, variantEntries);