You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2010/10/19 22:17:17 UTC

svn commit: r1024393 - in /httpcomponents/httpclient/trunk/httpclient-cache/src: main/java/org/apache/http/impl/client/cache/ test/java/org/apache/http/impl/client/cache/

Author: olegk
Date: Tue Oct 19 20:17:16 2010
New Revision: 1024393

URL: http://svn.apache.org/viewvc?rev=1024393&view=rev
Log:
HTTPCLIENT-990: Allow heuristic freshness caching
Contributed by Michajlo Matijkiw <michajlo_matijkiw at comcast.com>

Modified:
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheConfig.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheValidityPolicy.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedResponseSuitabilityChecker.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheValidityPolicy.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachedResponseSuitabilityChecker.java

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheConfig.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheConfig.java?rev=1024393&r1=1024392&r2=1024393&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheConfig.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheConfig.java Tue Oct 19 20:17:16 2010
@@ -47,10 +47,25 @@ public class CacheConfig {
      */
     public final static int DEFAULT_MAX_UPDATE_RETRIES = 1;
 
+    /** Default setting for heuristic caching
+     */
+    public final static boolean DEFAULT_HEURISTIC_CACHING_ENABLED = false;
+
+    /** Default coefficient used to heuristically determine freshness lifetime from
+     * cache entry.
+     */
+    public final static float DEFAULT_HEURISTIC_COEFFICIENT = 0.1f;
+
+    /** Default lifetime to be assumed when we cannot calculate freshness heuristically
+     */
+    public final static long DEFAULT_HEURISTIC_LIFETIME = 0;
+
     private int maxObjectSizeBytes = DEFAULT_MAX_OBJECT_SIZE_BYTES;
     private int maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES;
     private int maxUpdateRetries = DEFAULT_MAX_UPDATE_RETRIES;
-
+    private boolean heuristicCachingEnabled = false;
+    private float heuristicCoefficient = DEFAULT_HEURISTIC_COEFFICIENT;
+    private long heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME;
     private boolean isSharedCache = true;
 
     /**
@@ -118,4 +133,55 @@ public class CacheConfig {
     public void setMaxUpdateRetries(int maxUpdateRetries){
         this.maxUpdateRetries = maxUpdateRetries;
     }
+
+    /**
+     * Returns if heuristic freshness caching is in enabled
+     * @return
+     */
+    public boolean isHeuristicCachingEnabled() {
+        return heuristicCachingEnabled;
+    }
+
+    /**
+     * Set if heuristic freshness caching is enabled
+     * @param heursiticCachingEnabled
+     */
+    public void setHeuristicCachingEnabled(boolean heuristicCachingEnabled) {
+        this.heuristicCachingEnabled = heuristicCachingEnabled;
+    }
+
+    /**
+     * Returns coefficient used in heuristic freshness caching
+     * @return
+     */
+    public float getHeuristicCoefficient() {
+        return heuristicCoefficient;
+    }
+
+    /**
+     * Set coefficient to be used in heuristic freshness caching
+     * @param heuristicCoefficient
+     */
+    public void setHeuristicCoefficient(float heuristicCoefficient) {
+        this.heuristicCoefficient = heuristicCoefficient;
+    }
+
+    /**
+     * Get the default lifetime to be used if heuristic freshness calculation is
+     * not possible
+     * @return
+     */
+    public long getHeuristicDefaultLifetime() {
+        return heuristicDefaultLifetime;
+    }
+
+    /**
+     * Set default lifetime to be used if heuristic freshness calculation is not possible
+     * @param heuristicDefaultLifetime
+     */
+    public void setHeuristicDefaultLifetime(long heuristicDefaultLifetime) {
+        this.heuristicDefaultLifetime = heuristicDefaultLifetime;
+    }
+
+
 }

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheValidityPolicy.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheValidityPolicy.java?rev=1024393&r1=1024392&r2=1024393&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheValidityPolicy.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheValidityPolicy.java Tue Oct 19 20:17:16 2010
@@ -73,6 +73,39 @@ class CacheValidityPolicy {
         return (getCurrentAgeSecs(entry, now) < getFreshnessLifetimeSecs(entry));
     }
 
+    /**
+     * Decides if this response is fresh enough based Last-Modified and Date, if available.
+     * This entry is meant to be used when isResponseFresh returns false.  The algorithm is as follows:
+     *
+     * if last-modified and date are defined, freshness lifetime is coefficient*(date-lastModified),
+     * else freshness lifetime is defaultLifetime
+     *
+     * @param entry
+     * @param now
+     * @param coefficient
+     * @param defaultLifetime
+     * @return
+     */
+    public boolean isResponseHeuristicallyFresh(final HttpCacheEntry entry,
+            Date now, float coefficient, long defaultLifetime) {
+        return (getCurrentAgeSecs(entry, now) < getHeuristicFreshnessLifetimeSecs(entry, coefficient, defaultLifetime));
+    }
+
+    public long getHeuristicFreshnessLifetimeSecs(HttpCacheEntry entry,
+            float coefficient, long defaultLifetime) {
+        Date dateValue = getDateValue(entry);
+        Date lastModifiedValue = getLastModifiedValue(entry);
+
+        if (dateValue != null && lastModifiedValue != null) {
+            long diff = dateValue.getTime() - lastModifiedValue.getTime();
+            if (diff < 0)
+                return 0;
+            return (long)(coefficient * (diff / 1000));
+        }
+
+        return defaultLifetime;
+    }
+
     public boolean isRevalidatable(final HttpCacheEntry entry) {
         return entry.getFirstHeader(HeaderConstants.ETAG) != null
                 || entry.getFirstHeader(HeaderConstants.LAST_MODIFIED) != null;
@@ -98,6 +131,18 @@ class CacheValidityPolicy {
         return null;
     }
 
+    protected Date getLastModifiedValue(final HttpCacheEntry entry) {
+        Header dateHdr = entry.getFirstHeader(HeaderConstants.LAST_MODIFIED);
+        if (dateHdr == null)
+            return null;
+        try {
+            return DateUtils.parseDate(dateHdr.getValue());
+        } catch (DateParseException dpe) {
+            // ignore malformed date
+        }
+        return null;
+    }
+
     protected long getContentLengthValue(final HttpCacheEntry entry) {
         Header cl = entry.getFirstHeader(HTTP.CONTENT_LEN);
         if (cl == null)

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedResponseSuitabilityChecker.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedResponseSuitabilityChecker.java?rev=1024393&r1=1024392&r2=1024393&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedResponseSuitabilityChecker.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedResponseSuitabilityChecker.java Tue Oct 19 20:17:16 2010
@@ -52,6 +52,9 @@ class CachedResponseSuitabilityChecker {
     private final Log log = LogFactory.getLog(getClass());
 
     private final boolean sharedCache;
+    private final boolean useHeuristicCaching;
+    private final float heuristicCoefficient;
+    private final long heuristicDefaultLifetime;
     private final CacheValidityPolicy validityStrategy;
 
     CachedResponseSuitabilityChecker(final CacheValidityPolicy validityStrategy,
@@ -59,6 +62,9 @@ class CachedResponseSuitabilityChecker {
         super();
         this.validityStrategy = validityStrategy;
         this.sharedCache = config.isSharedCache();
+        this.useHeuristicCaching = config.isHeuristicCachingEnabled();
+        this.heuristicCoefficient = config.getHeuristicCoefficient();
+        this.heuristicDefaultLifetime = config.getHeuristicDefaultLifetime();
     }
 
     CachedResponseSuitabilityChecker(CacheConfig config) {
@@ -67,6 +73,9 @@ class CachedResponseSuitabilityChecker {
 
     private boolean isFreshEnough(HttpCacheEntry entry, HttpRequest request, Date now) {
         if (validityStrategy.isResponseFresh(entry, now)) return true;
+        if (useHeuristicCaching &&
+                validityStrategy.isResponseHeuristicallyFresh(entry, now, heuristicCoefficient, heuristicDefaultLifetime))
+            return true;
         if (originInsistsOnFreshness(entry)) return false;
         long maxstale = getMaxStale(request);
         if (maxstale == -1) return false;

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheValidityPolicy.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheValidityPolicy.java?rev=1024393&r1=1024392&r2=1024393&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheValidityPolicy.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheValidityPolicy.java Tue Oct 19 20:17:16 2010
@@ -251,6 +251,48 @@ public class TestCacheValidityPolicy {
     }
 
     @Test
+    public void testHeuristicFreshnessLifetime() {
+        Date now = new Date();
+        Date oneSecondAgo = new Date(now.getTime() - 1 * 1000L);
+        Date elevenSecondsAgo = new Date(now.getTime() - 11 * 1000L);
+
+        Header[] headers = new Header[] {
+                new BasicHeader("Date", DateUtils.formatDate(oneSecondAgo)),
+                new BasicHeader("Last-Modified", DateUtils.formatDate(elevenSecondsAgo))
+        };
+
+        HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
+        CacheValidityPolicy impl = new CacheValidityPolicy();
+        Assert.assertEquals(1, impl.getHeuristicFreshnessLifetimeSecs(entry, 0.1f, 0));
+    }
+
+    @Test
+    public void testHeuristicFreshnessLifetimeDefaultsProperly() {
+        long defaultFreshness = 10;
+
+        HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
+
+        CacheValidityPolicy impl = new CacheValidityPolicy();
+        Assert.assertEquals(defaultFreshness, impl.getHeuristicFreshnessLifetimeSecs(entry, 0.1f, defaultFreshness));
+    }
+
+    @Test
+    public void testHeuristicFreshnessLifetimeIsNonNegative() {
+        Date now = new Date();
+        Date oneSecondAgo = new Date(now.getTime() - 1 * 1000L);
+        Date elevenSecondsAgo = new Date(now.getTime() - 1 * 1000L);
+
+        Header[] headers = new Header[] {
+                new BasicHeader("Date", DateUtils.formatDate(elevenSecondsAgo)),
+                new BasicHeader("Last-Modified", DateUtils.formatDate(oneSecondAgo))
+        };
+
+        HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
+        CacheValidityPolicy impl = new CacheValidityPolicy();
+        Assert.assertTrue(impl.getHeuristicFreshnessLifetimeSecs(entry, 0.1f, 10) >= 0);
+    }
+
+    @Test
     public void testResponseIsFreshIfFreshnessLifetimeExceedsCurrentAge() {
         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
         final Date now = new Date();

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachedResponseSuitabilityChecker.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachedResponseSuitabilityChecker.java?rev=1024393&r1=1024392&r2=1024393&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachedResponseSuitabilityChecker.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachedResponseSuitabilityChecker.java Tue Oct 19 20:17:16 2010
@@ -223,4 +223,41 @@ public class TestCachedResponseSuitabili
         Assert.assertFalse(impl.canCachedResponseBeUsed(host, request, entry, now));
     }
 
+    @Test
+    public void testSuitableIfCacheEntryIsHeuristicallyFreshEnough() {
+        Date oneSecondAgo = new Date(now.getTime() - 1 * 1000L);
+        Date twentyOneSecondsAgo = new Date(now.getTime() - 21 * 1000L);
+
+        Header[] headers = {
+                new BasicHeader("Date", DateUtils.formatDate(oneSecondAgo)),
+                new BasicHeader("Last-Modified", DateUtils.formatDate(twentyOneSecondsAgo)),
+                new BasicHeader("Content-Length", "128")
+        };
+
+        entry = HttpTestUtils.makeCacheEntry(oneSecondAgo, oneSecondAgo, headers);
+
+        CacheConfig config = new CacheConfig();
+        config.setHeuristicCachingEnabled(true);
+        config.setHeuristicCoefficient(0.1f);
+        impl = new CachedResponseSuitabilityChecker(config);
+
+        Assert.assertTrue(impl.canCachedResponseBeUsed(host, request, entry, now));
+    }
+
+    @Test
+    public void testSuitableIfCacheEntryIsHeuristicallyFreshEnoughByDefault() {
+        Header[] headers = {
+                new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
+                new BasicHeader("Content-Length", "128")
+        };
+
+        entry = getEntry(headers);
+
+        CacheConfig config = new CacheConfig();
+        config.setHeuristicCachingEnabled(true);
+        config.setHeuristicDefaultLifetime(20);
+        impl = new CachedResponseSuitabilityChecker(config);
+
+        Assert.assertTrue(impl.canCachedResponseBeUsed(host, request, entry, now));
+    }
 }
\ No newline at end of file