Author: etnu
Date: Fri Jun 20 02:32:48 2008
New Revision: 669839

URL: http://svn.apache.org/viewvc?rev=669839&view=rev
Log:
Added upper bounds for manifest file caching to avoid situations where an item 
has been removed from the HttpCache, but is stuck in the parsed object caches. 
This can be a common issue for systems backed by a shared cache.

If you want the old behavior, just set the maxTTL values to something 
arbitrarily large.

Also cleaned up Basic*Factory to make the code more compatible. These really do 
need to be merged.


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/test/java/org/apache/shindig/gadgets/BasicGadgetSpecFactoryTest.java
    
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/BasicMessageBundleFactoryTest.java
    
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetTestFixture.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=669839&r1=669838&r2=669839&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/conf/gadgets.properties (original)
+++ incubator/shindig/trunk/java/gadgets/conf/gadgets.properties Fri Jun 20 
02:32:48 2008
@@ -14,4 +14,6 @@
 gadget-spec.cache.capacity=0
 message-bundle.cache.capacity=0
 gadget-spec.cache.minTTL=300000
+gadget-spec.cache.maxTTL=600000
 message-bundle.cache.minTTL=300000
+message-bundle.cache.maxTTL=600000

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=669839&r1=669838&r2=669839&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
 Fri Jun 20 02:32:48 2008
@@ -39,20 +39,24 @@
 import java.util.logging.Logger;
 
 /**
- * Basic implementation of a gadget spec factory
+ * Basic implementation of a gadget spec factory.
+ *
+ * TODO: This needs to be unified with message bundle fetching. We've 
basically copied the class in
+ * two places, which is horrible.
  */
 @Singleton
 public class BasicGadgetSpecFactory implements GadgetSpecFactory {
 
   private static final Logger logger = 
Logger.getLogger(BasicGadgetSpecFactory.class.getName());
 
-  private final HttpFetcher specFetcher;
+  private final HttpFetcher fetcher;
   private final ContentRewriter rewriter;
   private final Executor executor;
-  private final long specMinTTL;
+  private final long minTtl;
+  private final long maxTtl;
 
   // A cache of GadgetSpecs with expirations
-  private final Cache<URI, SpecTimeoutPair> inMemorySpecCache;
+  private final Cache<URI, TimeoutPair> cache;
 
   public GadgetSpec getGadgetSpec(GadgetContext context) throws 
GadgetException {
     return getGadgetSpec(context.getUrl(), context.getIgnoreCache());
@@ -61,32 +65,38 @@
   /**
    * Retrieves a gadget specification from the cache or from the Internet.
    */
-  public GadgetSpec getGadgetSpec(URI gadgetUri, boolean ignoreCache) throws 
GadgetException {
+  public GadgetSpec getGadgetSpec(URI url, boolean ignoreCache) throws 
GadgetException {
     if (ignoreCache) {
-      return fetchGadgetSpecFromWeb(gadgetUri, true);
+      return fetchFromWeb(url, true);
     }
 
     GadgetSpec spec = null;
     long expiration = -1;
 
     // Attempt to retrieve the gadget spec from the cache.
-    synchronized (inMemorySpecCache) {
-      SpecTimeoutPair gadgetSpecEntry = 
inMemorySpecCache.getElement(gadgetUri);
+    synchronized (cache) {
+      TimeoutPair gadgetSpecEntry = cache.getElement(url);
       if (gadgetSpecEntry != null) {
         spec = gadgetSpecEntry.spec;
         expiration = gadgetSpecEntry.timeout;
       }
     }
 
+    long now = System.currentTimeMillis();
+
     // If the gadget spec is not in the cache or has expired, fetch it from 
its URI.
-    if (spec == null || expiration < System.currentTimeMillis()) {
+    if (spec == null || expiration < now) {
       try {
-        return fetchGadgetSpecFromWeb(gadgetUri, false);
+        return fetchFromWeb(url, false);
       } catch (GadgetException e) {
         if (spec == null) {
           throw e;
         } else {
-          logger.info("Gadget spec fetch failed for " + gadgetUri + " -  using 
cached ");
+          logger.info("Gadget spec fetch failed for " + url + " -  using 
cached ");
+          // Try again later...
+          synchronized (cache) {
+            cache.addElement(url, new TimeoutPair(spec, now + minTtl));
+          }
         }
       }
     }
@@ -98,16 +108,15 @@
    * Retrieves a gadget specification from the Internet, processes its views 
and
    * adds it to the cache.
    */
-  private GadgetSpec fetchGadgetSpecFromWeb(URI gadgetUri, boolean ignoreCache)
-      throws GadgetException {
-    HttpRequest request = HttpRequest.getRequest(gadgetUri, ignoreCache);
-    HttpResponse response = specFetcher.fetch(request);
+  private GadgetSpec fetchFromWeb(URI url, boolean ignoreCache) throws 
GadgetException {
+    HttpRequest request = HttpRequest.getRequest(url, ignoreCache);
+    HttpResponse response = fetcher.fetch(request);
     if (response.getHttpStatusCode() != HttpResponse.SC_OK) {
       throw new 
GadgetException(GadgetException.Code.FAILED_TO_RETRIEVE_CONTENT,
                                 "Unable to retrieve gadget xml. HTTP error " +
                                 response.getHttpStatusCode());
     }
-    GadgetSpec spec = new GadgetSpec(gadgetUri, 
response.getResponseAsString());
+    GadgetSpec spec = new GadgetSpec(url, response.getResponseAsString());
 
     // Find the type=HTML views that link to their content externally.
     List<View> hrefViewList = new ArrayList<View>();
@@ -120,7 +129,7 @@
     // Retrieve all external view contents simultaneously.
     CountDownLatch latch = new CountDownLatch(hrefViewList.size());
     for (View v : hrefViewList) {
-      executor.execute(new ViewContentFetcher(v, latch, specFetcher, 
ignoreCache));
+      executor.execute(new ViewContentFetcher(v, latch, fetcher, ignoreCache));
     }
     try {
       latch.await();
@@ -142,34 +151,39 @@
       }
     }
 
-    // Add the updated spec back to the cache and force the min TTL
-    long expiration = Math.max(
-        response.getCacheExpiration(), System.currentTimeMillis() + 
specMinTTL);
-    synchronized (inMemorySpecCache) {
-      inMemorySpecCache.addElement(gadgetUri, new SpecTimeoutPair(spec, 
expiration));
+    // We enforce the lower bound limit here for situations where a remote 
server temporarily serves
+    // the wrong cache control headers. This allows any distributed caches to 
be updated and for the
+    // updates to eventually cascade back into the factory.
+    long now = System.currentTimeMillis();
+    long expiration = response.getCacheExpiration();
+    expiration = Math.min(now + minTtl, Math.max(now + maxTtl, expiration));
+    synchronized (cache) {
+      cache.addElement(url, new TimeoutPair(spec, expiration));
     }
 
     return spec;
   }
 
   @Inject
-  public BasicGadgetSpecFactory(HttpFetcher specFetcher,
+  public BasicGadgetSpecFactory(HttpFetcher fetcher,
                                 ContentRewriter rewriter,
                                 Executor executor,
                                 @Named("gadget-spec.cache.capacity")int 
gadgetSpecCacheCapacity,
-                                @Named("gadget-spec.cache.minTTL")long minTTL) 
{
-    this.specFetcher = specFetcher;
+                                @Named("gadget-spec.cache.minTTL")long minTtl,
+                                @Named("gadget-spec.cache.maxTTL")long maxTtl) 
{
+    this.fetcher = fetcher;
     this.rewriter = rewriter;
     this.executor = executor;
-    this.inMemorySpecCache = new LruCache<URI, 
SpecTimeoutPair>(gadgetSpecCacheCapacity);
-    this.specMinTTL = minTTL;
+    this.cache = new LruCache<URI, TimeoutPair>(gadgetSpecCacheCapacity);
+    this.minTtl = minTtl;
+    this.maxTtl = maxTtl;
   }
 
-  private static class SpecTimeoutPair {
+  private static class TimeoutPair {
     private final GadgetSpec spec;
     private final long timeout;
 
-    private SpecTimeoutPair(GadgetSpec spec, long timeout) {
+    private TimeoutPair(GadgetSpec spec, long timeout) {
       this.spec = spec;
       this.timeout = timeout;
     }

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=669839&r1=669838&r2=669839&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
 Fri Jun 20 02:32:48 2008
@@ -31,7 +31,6 @@
 import com.google.inject.name.Named;
 
 import java.net.URI;
-import java.util.logging.Level;
 import java.util.logging.Logger;
 
 /**
@@ -43,11 +42,12 @@
   private static final Logger logger
       = Logger.getLogger(BasicMessageBundleFactory.class.getName());
 
-  private final HttpFetcher bundleFetcher;
+  private final HttpFetcher fetcher;
 
-  private final Cache<URI, BundleTimeoutPair> inMemoryBundleCache;
+  private final Cache<URI, TimeoutPair> cache;
 
-  private final long bundleMinTTL;
+  private final long minTtl;
+  private final long maxTtl;
 
   public MessageBundle getBundle(LocaleSpec localeSpec, GadgetContext context)
       throws GadgetException {
@@ -61,72 +61,79 @@
     return getBundle(messages, context.getIgnoreCache());
   }
 
-  public MessageBundle getBundle(URI bundleUrl, boolean ignoreCache)
-      throws GadgetException {
+  public MessageBundle getBundle(URI url, boolean ignoreCache) throws 
GadgetException {
     if (ignoreCache) {
-      return fetchBundleFromWeb(bundleUrl, true);
+      return fetchFromWeb(url, true);
     }
 
     MessageBundle bundle = null;
     long expiration = -1;
-    synchronized (inMemoryBundleCache) {
-      BundleTimeoutPair entry = inMemoryBundleCache.getElement(bundleUrl);
+    synchronized (cache) {
+      TimeoutPair entry = cache.getElement(url);
       if (entry != null) {
         bundle = entry.bundle;
         expiration = entry.timeout;
       }
     }
-    if (bundle == null || expiration < System.currentTimeMillis()) {
+
+    long now = System.currentTimeMillis();
+    if (bundle == null || expiration < now) {
       try {
-        return fetchBundleFromWeb(bundleUrl, false);
-      } catch (GadgetException ge) {
+        return fetchFromWeb(url, false);
+      } catch (GadgetException e) {
         if (bundle == null) {
-          throw ge;
+          throw e;
         } else {
-          logger.log(Level.WARNING,
-              "Msg bundle fetch failed for " + bundleUrl + " -  using cached 
", ge);
+          logger.info("Message bundle fetch failed for " + url + " -  using 
cached ");
+          // Try again later...
+          synchronized (cache) {
+            cache.addElement(url, new TimeoutPair(bundle, now + minTtl));
+          }
         }
       }
     }
     return bundle;
   }
 
-  private MessageBundle fetchBundleFromWeb(URI bundleUrl, boolean ignoreCache)
-      throws GadgetException {
-    HttpRequest request = HttpRequest.getRequest(bundleUrl, ignoreCache);
-    HttpResponse response = bundleFetcher.fetch(request);
+  private MessageBundle fetchFromWeb(URI url, boolean ignoreCache) throws 
GadgetException {
+    HttpRequest request = HttpRequest.getRequest(url, ignoreCache);
+    HttpResponse response = fetcher.fetch(request);
     if (response.getHttpStatusCode() != HttpResponse.SC_OK) {
       throw new 
GadgetException(GadgetException.Code.FAILED_TO_RETRIEVE_CONTENT,
           "Unable to retrieve message bundle xml. HTTP error " +
           response.getHttpStatusCode());
     }
-    MessageBundle bundle
-        = new MessageBundle(bundleUrl, response.getResponseAsString());
 
-    synchronized (inMemoryBundleCache) {
-      long expiration = Math.max(response.getCacheExpiration(),
-          System.currentTimeMillis() + bundleMinTTL);
-      inMemoryBundleCache.addElement(bundleUrl,
-          new BundleTimeoutPair(bundle, expiration));
+    MessageBundle bundle  = new MessageBundle(url, 
response.getResponseAsString());
+
+    // We enforce the lower bound limit here for situations where a remote 
server temporarily serves
+    // the wrong cache control headers. This allows any distributed caches to 
be updated and for the
+    // updates to eventually cascade back into the factory.
+    long now = System.currentTimeMillis();
+    long expiration = response.getCacheExpiration();
+    expiration = Math.min(now + minTtl, Math.max(now + maxTtl, expiration));
+    synchronized (cache) {
+      cache.addElement(url, new TimeoutPair(bundle, expiration));
     }
     return bundle;
   }
 
   @Inject
-  public BasicMessageBundleFactory(HttpFetcher bundleFetcher,
-      @Named("message-bundle.cache.capacity")int messageBundleCacheCapacity,
-      @Named("message-bundle.cache.minTTL")long minTTL) {
-    this.bundleFetcher = bundleFetcher;
-    this.inMemoryBundleCache =
-        new LruCache<URI, BundleTimeoutPair>(messageBundleCacheCapacity);
-    this.bundleMinTTL = minTTL;
+  public BasicMessageBundleFactory(HttpFetcher fetcher,
+                                   @Named("message-bundle.cache.capacity")int 
capacity,
+                                   @Named("message-bundle.cache.minTTL")long 
minTtl,
+                                   @Named("message-bundle.cache.maxTTL")long 
maxTtl) {
+    this.fetcher = fetcher;
+    this.cache = new LruCache<URI, TimeoutPair>(capacity);
+    this.minTtl = minTtl;
+    this.maxTtl = maxTtl;
   }
 
-  private static class BundleTimeoutPair {
+  private static class TimeoutPair {
     private MessageBundle bundle;
     private long timeout;
 
-    private BundleTimeoutPair(MessageBundle bundle, long timeout) {
+    private TimeoutPair(MessageBundle bundle, long timeout) {
       this.bundle = bundle;
       this.timeout = timeout;
     }

Modified: 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/BasicGadgetSpecFactoryTest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/BasicGadgetSpecFactoryTest.java?rev=669839&r1=669838&r2=669839&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/BasicGadgetSpecFactoryTest.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/BasicGadgetSpecFactoryTest.java
 Fri Jun 20 02:32:48 2008
@@ -90,7 +90,7 @@
   private final CaptureRewriter rewriter = new CaptureRewriter();
 
   private final BasicGadgetSpecFactory specFactory
-      = new BasicGadgetSpecFactory(fetcher, rewriter, FAKE_EXECUTOR, 5, -1000);
+      = new BasicGadgetSpecFactory(fetcher, rewriter, FAKE_EXECUTOR, 5, -1000, 
1000);
 
   @Test
   public void specFetched() throws Exception {

Modified: 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/BasicMessageBundleFactoryTest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/BasicMessageBundleFactoryTest.java?rev=669839&r1=669838&r2=669839&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/BasicMessageBundleFactoryTest.java
 (original)
+++ 
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/BasicMessageBundleFactoryTest.java
 Fri Jun 20 02:32:48 2008
@@ -70,7 +70,7 @@
   private final MessageBundleFactory bundleFactory;
 
   public BasicMessageBundleFactoryTest() {
-    bundleFactory = new BasicMessageBundleFactory(fetcher, 5, -1000);
+    bundleFactory = new BasicMessageBundleFactory(fetcher, 5, -1000, 1000);
   }
 
   @Test

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=669839&r1=669838&r2=669839&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
 Fri Jun 20 02:32:48 2008
@@ -40,7 +40,7 @@
   public final HttpFetcher fetcher = mock(HttpFetcher.class);
   public final GadgetBlacklist blacklist = mock(GadgetBlacklist.class);
   public final MessageBundleFactory bundleFactory =
-      new BasicMessageBundleFactory(fetcher, 0, 0L);
+      new BasicMessageBundleFactory(fetcher, 0, 0L, 0L);
   public GadgetFeatureRegistry registry;
   public ContainerConfig containerConfig = mock(ContainerConfig.class);
   public final Executor executor = new Executor() {
@@ -49,7 +49,7 @@
     }
   };
   public final GadgetSpecFactory specFactory = new BasicGadgetSpecFactory(
-      fetcher, new NoOpContentRewriter(), executor, 0, 0L);
+      fetcher, new NoOpContentRewriter(), executor, 0, 0L, 0L);
 
 
   public GadgetTestFixture() {


Reply via email to