Author: lryan
Date: Wed May 21 16:00:01 2008
New Revision: 658918

URL: http://svn.apache.org/viewvc?rev=658918&view=rev
Log:
Make message bundle and spec caching respect http expiration with a min TTL

Modified:
    incubator/shindig/trunk/java/gadgets/conf/gadgets.properties
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicGadgetSpecFactory.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicMessageBundleFactory.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpResponse.java
    
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HttpUtil.java
    
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetTestFixture.java
    
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpResponseTest.java

Modified: incubator/shindig/trunk/java/gadgets/conf/gadgets.properties
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/conf/gadgets.properties?rev=658918&r1=658917&r2=658918&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/conf/gadgets.properties (original)
+++ incubator/shindig/trunk/java/gadgets/conf/gadgets.properties Wed May 21 
16:00:01 2008
@@ -11,3 +11,5 @@
 cache.capacity=10000
 gadget-spec.cache.capacity=0
 message-bundle.cache.capacity=0
+gadget-spec.cache.minTTL=300000
+message-bundle.cache.minTTL=300000

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicGadgetSpecFactory.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicGadgetSpecFactory.java?rev=658918&r1=658917&r2=658918&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicGadgetSpecFactory.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicGadgetSpecFactory.java
 Wed May 21 16:00:01 2008
@@ -51,7 +51,10 @@
 
   private final boolean enableRewrite;
 
-  private final Cache<URI, GadgetSpec> inMemorySpecCache;
+  private final long specMinTTL;
+
+  // A cache of GadgetSpecs with expirations
+  private final Cache<URI, SpecTimeoutPair> inMemorySpecCache;
 
   public GadgetSpec getGadgetSpec(GadgetContext context)
       throws GadgetException {
@@ -60,11 +63,16 @@
 
   public GadgetSpec getGadgetSpec(URI gadgetUri, boolean ignoreCache)
       throws GadgetException {
-    GadgetSpec spec;
+    GadgetSpec spec = null;
+    long expiration = -1;
     synchronized (inMemorySpecCache) {
-      spec = inMemorySpecCache.getElement(gadgetUri);
+      SpecTimeoutPair gadgetSpecEntry = 
inMemorySpecCache.getElement(gadgetUri);
+      if (gadgetSpecEntry != null) {
+        spec = gadgetSpecEntry.spec;
+        expiration = gadgetSpecEntry.timeout;
+      }
     }
-    if (ignoreCache || spec == null) {
+    if (ignoreCache || spec == null || expiration < 
System.currentTimeMillis()) {
       try {
         HttpRequest request = HttpRequest.getRequest(
             gadgetUri, ignoreCache);
@@ -84,9 +92,11 @@
               }
             }
           }
-          // Add the updated spec back to the cache
+          // Add the updated spec back to the cache and force the min TTL
+          expiration = Math
+              .max(response.getCacheExpiration(), System.currentTimeMillis() + 
specMinTTL);
           synchronized (inMemorySpecCache) {
-            inMemorySpecCache.addElement(gadgetUri, spec);
+            inMemorySpecCache.addElement(gadgetUri, new SpecTimeoutPair(spec, 
expiration));
           }
         }
       } catch (GadgetException ge) {
@@ -105,10 +115,23 @@
   public BasicGadgetSpecFactory(HttpFetcher specFetcher,
       ContentRewriter rewriter,
       @Named("content-rewrite.enabled")boolean defaultEnableRewrite,
-      @Named("gadget-spec.cache.capacity")int gadgetSpecCacheCapacity) {
+      @Named("gadget-spec.cache.capacity")int gadgetSpecCacheCapacity,
+      @Named("gadget-spec.cache.minTTL")long minTTL) {
     this.specFetcher = specFetcher;
     this.rewriter = rewriter;
     this.enableRewrite = defaultEnableRewrite;
-    inMemorySpecCache = new LruCache<URI, GadgetSpec>(gadgetSpecCacheCapacity);
+    this.inMemorySpecCache = new LruCache<URI, SpecTimeoutPair>(
+        gadgetSpecCacheCapacity);
+    this.specMinTTL = minTTL;
+  }
+
+  private static class SpecTimeoutPair {
+    private GadgetSpec spec;
+    private long timeout;
+
+    private SpecTimeoutPair(GadgetSpec spec, long timeout) {
+      this.spec = spec;
+      this.timeout = timeout;
+    }
   }
 }
\ No newline at end of file

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicMessageBundleFactory.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicMessageBundleFactory.java?rev=658918&r1=658917&r2=658918&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicMessageBundleFactory.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicMessageBundleFactory.java
 Wed May 21 16:00:01 2008
@@ -43,7 +43,9 @@
 
   private final HttpFetcher bundleFetcher;
 
-  private final Cache<URI, MessageBundle> inMemoryBundleCache;
+  private final Cache<URI, BundleTimeoutPair> inMemoryBundleCache;
+
+  private final long bundleMinTTL;
 
   public MessageBundle getBundle(LocaleSpec localeSpec, GadgetContext context)
       throws GadgetException {
@@ -52,11 +54,16 @@
 
   public MessageBundle getBundle(URI bundleUrl, boolean ignoreCache)
       throws GadgetException {
-    MessageBundle bundle;
+    MessageBundle bundle = null;
+    long expiration = -1;
     synchronized (inMemoryBundleCache) {
-      bundle = inMemoryBundleCache.getElement(bundleUrl);
+      BundleTimeoutPair entry = inMemoryBundleCache.getElement(bundleUrl);
+      if (entry != null) {
+        bundle = entry.bundle;
+        expiration = entry.timeout;
+      }
     }
-    if (ignoreCache || bundle == null) {
+    if (ignoreCache || bundle == null || expiration < 
System.currentTimeMillis()) {
       try {
         HttpRequest request
             = HttpRequest.getRequest(bundleUrl, ignoreCache);
@@ -68,8 +75,12 @@
                   response.getHttpStatusCode());
         }
         bundle = new MessageBundle(bundleUrl, response.getResponseAsString());
+
+        // Add the updated bundle back to the cache and force the min TTL
+        expiration = Math
+            .max(response.getCacheExpiration(), System.currentTimeMillis() + 
bundleMinTTL);
         synchronized (inMemoryBundleCache) {
-          inMemoryBundleCache.addElement(bundleUrl, bundle);
+          inMemoryBundleCache.addElement(bundleUrl, new 
BundleTimeoutPair(bundle, expiration));
         }
       } catch (GadgetException ge) {
         if (bundle == null) {
@@ -85,8 +96,21 @@
 
   @Inject
   public BasicMessageBundleFactory(HttpFetcher bundleFetcher,
-      @Named("message-bundle.cache.capacity")int messageBundleCacheCapacity) {
+      @Named("message-bundle.cache.capacity")int messageBundleCacheCapacity,
+      @Named("message-bundle.cache.minTTL")long minTTL) {
     this.bundleFetcher = bundleFetcher;
-    this.inMemoryBundleCache = new LruCache<URI, 
MessageBundle>(messageBundleCacheCapacity);
+    this.inMemoryBundleCache =
+        new LruCache<URI, BundleTimeoutPair>(messageBundleCacheCapacity);
+    this.bundleMinTTL = minTTL;
+  }
+
+  private static class BundleTimeoutPair {
+    private MessageBundle bundle;
+    private long timeout;
+
+    private BundleTimeoutPair(MessageBundle bundle, long timeout) {
+      this.bundle = bundle;
+      this.timeout = timeout;
+    }
   }
 }

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpResponse.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpResponse.java?rev=658918&r1=658917&r2=658918&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpResponse.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/HttpResponse.java
 Wed May 21 16:00:01 2008
@@ -17,12 +17,15 @@
  */
 package org.apache.shindig.gadgets.http;
 
+import org.apache.shindig.gadgets.servlet.HttpUtil;
+
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -241,4 +244,83 @@
   public void setRewritten(HttpResponse rewritten) {
     this.rewritten = rewritten;
   }
+
+  /**
+   * @return consolidated cache expiration time or -1
+   */
+  public long getCacheExpiration() {
+    if (isStrictNoCache()) return -1;
+    long maxAgeExpiration = getCacheControlMaxAge() + 
System.currentTimeMillis();
+    long expiration = getExpiration();
+    if (expiration == -1) {
+      return maxAgeExpiration;
+    }
+    return expiration;
+  }
+
+  /**
+   * @return the expiration time from the Expires header or -1 if not set
+   */
+  public long getExpiration() {
+    String expires = getHeader("Expires");
+    if (expires != null) {
+      Date expiresDate = HttpUtil.parseDate(expires);
+      if (expiresDate != null) {
+        return expiresDate.getTime();
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * @return max-age value or -1 if invalid or not set
+   */
+  public long getCacheControlMaxAge() {
+    String cacheControl = getHeader("Cache-Control");
+    if (cacheControl != null) {
+      String[] directives = cacheControl.split(",");
+      for (String directive : directives) {
+        directive = directive.trim();
+        if (directive.startsWith("max-age")) {
+          String[] parts = directive.split("=");
+          if (parts.length == 2) {
+            try {
+              return Long.parseLong(parts[1]) * 1000;
+            } catch (NumberFormatException e) {
+              return -1;
+            }
+          }
+        }
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * @return true if a strict no-cache header is set in Cache-Control or Pragma
+   */
+  public boolean isStrictNoCache() {
+    String cacheControl = getHeader("Cache-Control");
+    if (cacheControl != null) {
+      String[] directives = cacheControl.split(",");
+      for (String directive : directives) {
+        directive = directive.trim();
+        if (directive.equalsIgnoreCase("no-cache")
+            || directive.equalsIgnoreCase("no-store")
+            || directive.equalsIgnoreCase("private")) {
+          return true;
+        }
+      }
+    }
+
+    List<String> pragmas = getHeaders("Pragma");
+    if (pragmas != null) {
+      for (String pragma : pragmas) {
+        if ("no-cache".equalsIgnoreCase(pragma)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
 }

Modified: 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HttpUtil.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HttpUtil.java?rev=658918&r1=658917&r2=658918&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HttpUtil.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/HttpUtil.java
 Wed May 21 16:00:01 2008
@@ -23,7 +23,6 @@
 import org.apache.shindig.gadgets.GadgetContext;
 import org.apache.shindig.gadgets.spec.GadgetSpec;
 import org.apache.shindig.gadgets.spec.View;
-
 import org.joda.time.DateTimeZone;
 import org.joda.time.format.DateTimeFormat;
 import org.joda.time.format.DateTimeFormatter;

Modified: 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetTestFixture.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetTestFixture.java?rev=658918&r1=658917&r2=658918&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetTestFixture.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetTestFixture.java
 Wed May 21 16:00:01 2008
@@ -40,9 +40,9 @@
   public final HttpFetcher fetcher = mock(HttpFetcher.class);
   public final GadgetBlacklist blacklist = mock(GadgetBlacklist.class);
   public final GadgetSpecFactory specFactory =
-      new BasicGadgetSpecFactory(fetcher, new NoOpContentRewriter(), true, 0);
+      new BasicGadgetSpecFactory(fetcher, new NoOpContentRewriter(), true, 0, 
0L);
   public final MessageBundleFactory bundleFactory =
-      new BasicMessageBundleFactory(fetcher, 0);
+      new BasicMessageBundleFactory(fetcher, 0, 0L);
   public GadgetFeatureRegistry registry;
   public ContainerConfig containerConfig;
   public final Executor executor = new Executor() {

Modified: 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpResponseTest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpResponseTest.java?rev=658918&r1=658917&r2=658918&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpResponseTest.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/HttpResponseTest.java
 Wed May 21 16:00:01 2008
@@ -17,10 +17,10 @@
  */
 package org.apache.shindig.gadgets.http;
 
-import org.apache.shindig.common.util.InputStreamConsumer;
-
 import junit.framework.TestCase;
 
+import org.apache.shindig.common.util.InputStreamConsumer;
+
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -90,4 +90,32 @@
     byte[] out = InputStreamConsumer.readToByteArray(response.getResponse());
     assertTrue(Arrays.equals(data, out));
   }
+
+  public void testStrictCacheControlNoCache() throws Exception {
+    addHeader("Cache-Control", "no-cache");
+    HttpResponse response = new HttpResponse(200, new byte[0], headers);
+    assertTrue(response.isStrictNoCache());
+  }
+
+  public void testStrictPragmaNoCache() throws Exception {
+    addHeader("Pragma", "no-cache");
+    HttpResponse response = new HttpResponse(200, new byte[0], headers);
+    assertTrue(response.isStrictNoCache());
+  }
+
+  public void testStrictPragmaJunk() throws Exception {
+    addHeader("Pragma", "junk");
+    HttpResponse response = new HttpResponse(200, new byte[0], headers);
+    assertFalse(response.isStrictNoCache());
+  }
+
+  /*
+   There seems to be some issue with the date parsing in joda?
+  public void testExpires() throws Exception {
+    long time = System.currentTimeMillis() + 10000L;
+    addHeader("Expires", HttpUtil.formatDate(new Date(time)));
+    HttpResponse response = new HttpResponse(200, new byte[0], headers);
+    assertEquals(response.getExpiration(), time);
+  }
+  */
 }


Reply via email to