You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by jo...@apache.org on 2010/12/23 13:02:37 UTC

svn commit: r1052234 - in /httpcomponents/httpclient/trunk/httpclient-cache/src: main/java/org/apache/http/impl/client/cache/CacheInvalidator.java test/java/org/apache/http/impl/client/cache/TestCacheInvalidator.java

Author: jonm
Date: Thu Dec 23 12:02:37 2010
New Revision: 1052234

URL: http://svn.apache.org/viewvc?rev=1052234&view=rev
Log:
HTTPCLIENT-1035: finished cache-flushing logic for updated
entries mentioned by Content-Location in responses. Still
not hooked in to main request handling flow yet.


Modified:
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheInvalidator.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheInvalidator.java

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheInvalidator.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheInvalidator.java?rev=1052234&r1=1052233&r2=1052234&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheInvalidator.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheInvalidator.java Thu Dec 23 12:02:37 2010
@@ -54,7 +54,7 @@ import org.apache.http.impl.cookie.DateU
 class CacheInvalidator {
 
     private final HttpCacheStorage storage;
-    private final CacheKeyGenerator uriExtractor;
+    private final CacheKeyGenerator cacheKeyGenerator;
 
     private final Log log = LogFactory.getLog(getClass());
 
@@ -68,7 +68,7 @@ class CacheInvalidator {
     public CacheInvalidator(
             final CacheKeyGenerator uriExtractor,
             final HttpCacheStorage storage) {
-        this.uriExtractor = uriExtractor;
+        this.cacheKeyGenerator = uriExtractor;
         this.storage = storage;
     }
 
@@ -79,26 +79,24 @@ class CacheInvalidator {
      * @param host The backend host we are talking to
      * @param req The HttpRequest to that host
      */
-    public void flushInvalidatedCacheEntries(HttpHost host, HttpRequest req) throws IOException {
+    public void flushInvalidatedCacheEntries(HttpHost host, HttpRequest req)  {
         if (requestShouldNotBeCached(req)) {
             log.debug("Request should not be cached");
 
-            String theUri = uriExtractor.getURI(host, req);
+            String theUri = cacheKeyGenerator.getURI(host, req);
 
-            HttpCacheEntry parent = storage.getEntry(theUri);
+            HttpCacheEntry parent = getEntry(theUri);
 
             log.debug("parent entry: " + parent);
 
             if (parent != null) {
                 for (String variantURI : parent.getVariantMap().values()) {
-                    storage.removeEntry(variantURI);
+                    flushEntry(variantURI);
                 }
-                storage.removeEntry(theUri);
+                flushEntry(theUri);
             }
-            URL reqURL;
-            try {
-                reqURL = new URL(theUri);
-            } catch (MalformedURLException mue) {
+            URL reqURL = getAbsoluteURL(theUri);
+            if (reqURL == null) {
                 log.error("Couldn't transform request into valid URL");
                 return;
             }
@@ -116,35 +114,65 @@ class CacheInvalidator {
         }
     }
 
-    protected void flushUriIfSameHost(URL requestURL, URL targetURL) throws IOException {
-        URL canonicalTarget = new URL(uriExtractor.canonicalizeUri(targetURL.toString()));
-        if (canonicalTarget.getAuthority().equalsIgnoreCase(requestURL.getAuthority())) {
-            storage.removeEntry(canonicalTarget.toString());
+    private void flushEntry(String uri) {
+        try {
+            storage.removeEntry(uri);
+        } catch (IOException ioe) {
+            log.warn("unable to flush cache entry", ioe);
         }
     }
 
-    protected void flushRelativeUriFromSameHost(URL reqURL, String relUri) throws IOException {
-        URL relURL;
+    private HttpCacheEntry getEntry(String theUri) {
         try {
-            relURL = new URL(reqURL,relUri);
-        } catch (MalformedURLException e) {
-            log.debug("Invalid relative URI",e);
-            return;
+            return storage.getEntry(theUri);
+        } catch (IOException ioe) {
+            log.warn("could not retrieve entry from storage", ioe);
         }
+        return null;
+    }
+
+    protected void flushUriIfSameHost(URL requestURL, URL targetURL) {
+        URL canonicalTarget = getAbsoluteURL(cacheKeyGenerator.canonicalizeUri(targetURL.toString()));
+        if (canonicalTarget == null) return;
+        if (canonicalTarget.getAuthority().equalsIgnoreCase(requestURL.getAuthority())) {
+            flushEntry(canonicalTarget.toString());
+        }
+    }
+
+    protected void flushRelativeUriFromSameHost(URL reqURL, String relUri) {
+        URL relURL = getRelativeURL(reqURL, relUri);
+        if (relURL == null) return;
         flushUriIfSameHost(reqURL, relURL);
     }
 
-    protected boolean flushAbsoluteUriFromSameHost(URL reqURL, String uri) throws IOException {
-        URL absURL;
+
+    protected boolean flushAbsoluteUriFromSameHost(URL reqURL, String uri) {
+        URL absURL = getAbsoluteURL(uri);
+        if (absURL == null) return false;
+        flushUriIfSameHost(reqURL,absURL);
+        return true;
+    }
+
+    private URL getAbsoluteURL(String uri) {
+        URL absURL = null;
         try {
             absURL = new URL(uri);
         } catch (MalformedURLException mue) {
-            return false;
+            // nop
         }
-        flushUriIfSameHost(reqURL,absURL);
-        return true;
+        return absURL;
     }
 
+    private URL getRelativeURL(URL reqURL, String relUri) {
+        URL relURL = null;
+        try {
+            relURL = new URL(reqURL,relUri);
+        } catch (MalformedURLException e) {
+            // nop
+        }
+        return relURL;
+    }
+    
     protected boolean requestShouldNotBeCached(HttpRequest req) {
         String method = req.getRequestLine().getMethod();
         return notGetOrHeadRequest(method);
@@ -160,16 +188,28 @@ class CacheInvalidator {
      * @throws IOException 
      */
     public void flushInvalidatedCacheEntries(HttpHost host,
-            HttpRequest request, HttpResponse response) throws IOException {
-        Header contentLocation = response.getFirstHeader("Content-Location");
-        if (contentLocation == null) return;
-        HttpCacheEntry entry = storage.getEntry(contentLocation.getValue());
+            HttpRequest request, HttpResponse response) {
+        URL reqURL = getAbsoluteURL(cacheKeyGenerator.getURI(host, request));
+        if (reqURL == null) return;
+        URL canonURL = getContentLocationURL(reqURL, response);
+        if (canonURL == null) return;
+        String cacheKey = cacheKeyGenerator.canonicalizeUri(canonURL.toString());
+        HttpCacheEntry entry = getEntry(cacheKey);
         if (entry == null) return;
 
         if (!responseDateNewerThanEntryDate(response, entry)) return;
         if (!responseAndEntryEtagsDiffer(response, entry)) return;
         
-        storage.removeEntry(contentLocation.getValue());
+        flushUriIfSameHost(reqURL, canonURL);
+    }
+
+    private URL getContentLocationURL(URL reqURL, HttpResponse response) {
+        Header clHeader = response.getFirstHeader("Content-Location");
+        if (clHeader == null) return null;
+        String contentLocation = clHeader.getValue();
+        URL canonURL = getAbsoluteURL(contentLocation);
+        if (canonURL != null) return canonURL;
+        return getRelativeURL(reqURL, contentLocation); 
     }
 
     private boolean responseAndEntryEtagsDiffer(HttpResponse response,

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheInvalidator.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheInvalidator.java?rev=1052234&r1=1052233&r2=1052234&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheInvalidator.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheInvalidator.java Thu Dec 23 12:02:37 2010
@@ -72,6 +72,7 @@ public class TestCacheInvalidator {
         mockStorage = createMock(HttpCacheStorage.class);
         cacheKeyGenerator = new CacheKeyGenerator();
         mockEntry = createMock(HttpCacheEntry.class);
+        request = HttpTestUtils.makeDefaultRequest();
         response = HttpTestUtils.make200Response();
 
         impl = new CacheInvalidator(cacheKeyGenerator, mockStorage);
@@ -249,7 +250,7 @@ public class TestCacheInvalidator {
         verifyMocks();
     }
 
-    @Test(expected=IOException.class)
+    @Test
     public void testCacheFlushException() throws Exception {
         request = new BasicHttpRequest("POST","/",HTTP_1_1);
         String theURI = "http://foo.example.com:80/";
@@ -258,12 +259,12 @@ public class TestCacheInvalidator {
 
         replayMocks();
         impl.flushInvalidatedCacheEntries(host, request);
+        verifyMocks();
     }
     
     @Test
     public void doesNotFlushForResponsesWithoutContentLocation()
             throws Exception {
-        request = HttpTestUtils.makeDefaultRequest();
         replayMocks();
         impl.flushInvalidatedCacheEntries(host, request, response);
         verifyMocks();
@@ -291,6 +292,70 @@ public class TestCacheInvalidator {
     }
 
     @Test
+    public void flushesEntryIfFresherAndSpecifiedByNonCanonicalContentLocation()
+            throws Exception {
+        response.setHeader("ETag","\"new-etag\"");
+        response.setHeader("Date", formatDate(now));
+        String cacheKey = "http://foo.example.com:80/bar";
+        response.setHeader("Content-Location", "http://foo.example.com/bar");
+        
+        HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
+           new BasicHeader("Date", formatDate(tenSecondsAgo)),
+           new BasicHeader("ETag", "\"old-etag\"")
+        });
+        
+        expect(mockStorage.getEntry(cacheKey)).andReturn(entry).anyTimes();
+        mockStorage.removeEntry(cacheKey);
+        
+        replayMocks();
+        impl.flushInvalidatedCacheEntries(host, request, response);
+        verifyMocks();
+    }
+
+    @Test
+    public void flushesEntryIfFresherAndSpecifiedByRelativeContentLocation()
+            throws Exception {
+        response.setHeader("ETag","\"new-etag\"");
+        response.setHeader("Date", formatDate(now));
+        String cacheKey = "http://foo.example.com:80/bar";
+        response.setHeader("Content-Location", "/bar");
+        
+        HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
+           new BasicHeader("Date", formatDate(tenSecondsAgo)),
+           new BasicHeader("ETag", "\"old-etag\"")
+        });
+        
+        expect(mockStorage.getEntry(cacheKey)).andReturn(entry).anyTimes();
+        mockStorage.removeEntry(cacheKey);
+        
+        replayMocks();
+        impl.flushInvalidatedCacheEntries(host, request, response);
+        verifyMocks();
+    }
+
+    @Test
+    public void doesNotFlushEntryIfContentLocationFromDifferentHost()
+            throws Exception {
+        response.setHeader("ETag","\"new-etag\"");
+        response.setHeader("Date", formatDate(now));
+        String cacheKey = "http://baz.example.com:80/bar";
+        response.setHeader("Content-Location", cacheKey);
+        
+        HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
+           new BasicHeader("Date", formatDate(tenSecondsAgo)),
+           new BasicHeader("ETag", "\"old-etag\"")
+        });
+        
+        expect(mockStorage.getEntry(cacheKey)).andReturn(entry).anyTimes();
+        
+        replayMocks();
+        impl.flushInvalidatedCacheEntries(host, request, response);
+        verifyMocks();
+    }
+
+    
+    
+    @Test
     public void doesNotFlushEntrySpecifiedByContentLocationIfEtagsMatch()
             throws Exception {
         response.setHeader("ETag","\"same-etag\"");
@@ -423,6 +488,46 @@ public class TestCacheInvalidator {
         verifyMocks();
     }
 
+    @Test
+    public void doesNotFlushEntrySpecifiedByContentLocationIfResponseHasMalformedDate()
+            throws Exception {
+        response.setHeader("ETag","\"new-etag\"");
+        response.setHeader("Date", "blarg");
+        String theURI = "http://foo.example.com:80/bar";
+        response.setHeader("Content-Location", theURI);
+        
+        HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {                
+                new BasicHeader("ETag", "\"old-etag\""),
+                new BasicHeader("Date", formatDate(tenSecondsAgo))
+        });
+        
+        expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
+        
+        replayMocks();
+        impl.flushInvalidatedCacheEntries(host, request, response);
+        verifyMocks();
+    }
+    
+    @Test
+    public void doesNotFlushEntrySpecifiedByContentLocationIfEntryHasMalformedDate()
+            throws Exception {
+        response.setHeader("ETag","\"new-etag\"");
+        response.setHeader("Date", formatDate(now));
+        String theURI = "http://foo.example.com:80/bar";
+        response.setHeader("Content-Location", theURI);
+        
+        HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {                
+                new BasicHeader("ETag", "\"old-etag\""),
+                new BasicHeader("Date", "foo")
+        });
+        
+        expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
+        
+        replayMocks();
+        impl.flushInvalidatedCacheEntries(host, request, response);
+        verifyMocks();
+    }
+
     
     // Expectations
     private void cacheEntryHasVariantMap(Map<String,String> variantMap) {