On Sat, Oct 18, 2008 at 11:38 AM, <[EMAIL PROTECTED]> wrote:

> Author: etnu
> Date: Sat Oct 18 11:38:25 2008
> New Revision: 705904
>
> URL: http://svn.apache.org/viewvc?rev=705904&view=rev
> Log:
> Added negative caching to BasicGadgetSpecFactory to prevent re-parsing /
> re-fetching of gadgets in a known error state. This saves a substantial
> amount of CPU time over the current implementation, which re-parses on every
> render even when caching is turned on.
>
>
> Modified:
>
>  
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicGadgetSpecFactory.java
>
>  
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/BasicGadgetSpecFactoryTest.java
>
> 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=705904&r1=705903&r2=705904&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
> Sat Oct 18 11:38:25 2008
> @@ -40,7 +40,8 @@
>  public class BasicGadgetSpecFactory implements GadgetSpecFactory {
>   static final String RAW_GADGETSPEC_XML_PARAM_NAME = "rawxml";
>   static final Uri RAW_GADGET_URI = Uri.parse("http://localhost/raw.xml";);
> -
> +  static final String ERROR_SPEC = "<Module><ModulePrefs
> title='Error'/><Content/></Module>";
> +  static final String ERROR_KEY = "parse.exception";
>   static final Logger logger =
> Logger.getLogger(BasicGadgetSpecFactory.class.getName());
>
>   private final HttpFetcher fetcher;
> @@ -48,7 +49,7 @@
>   private final long minTtl;
>   private final long maxTtl;
>
> -   @Inject
> +  @Inject
>   public BasicGadgetSpecFactory(HttpFetcher fetcher,
>                                 CacheProvider cacheProvider,
>
> @Named("shindig.gadget-spec.cache.capacity") int capacity,
> @@ -89,15 +90,25 @@
>       try {
>         return fetchObjectAndCache(uri, ignoreCache);
>       } catch (GadgetException e) {
> -        // Failed to re-fetch raw object. Use cached object if it exists.
> -        if (cached.obj == null) {
> -          throw e;
> +        // Enforce negative caching.
> +        GadgetSpec spec;
> +        if (cached.obj != null) {
> +          spec = cached.obj;
>         } else {
> -          logger.info("GadgetSpec fetch failed for " + uri + " -  using
> cached.");
> +          // We create this dummy spec to avoid the cost of re-parsing
> when a remote site is out.
> +          spec = new GadgetSpec(uri, ERROR_SPEC);
> +          spec.setAttribute(ERROR_KEY, e);
> +          cached.obj = spec;
>         }


This fix doesn't structurally seem quite right, and the comment is
incorrect: the re-parse happens when the remote site is in but returns bad
content.

One fundamental issue here is that there's no disambiguation between cause
of GadgetException: parse error vs. fetch exception vs. fetch non-OK status.
In lieu of exception refactoring, it seems to me that it would be best to
put this code in fetchObjectAndCache(...) eg:

GadgetSpec spec = null;
try {
  spec = new GadgetSpec(url, response.getResponseAsString());
} catch (GadgetException e) {
  // poor form in a method that throws a GadgetException itself maybe, but
boxes in parsing logic
  // generate error spec
}

// ...code goes on to (negative) cache the spec as it should.

Reintroduction of negative caching on GadgetExceptions caused in the other
noted ways is of course very important.


>
> +        logger.info("GadgetSpec fetch failed for " + uri + " - using
> cached for " + minTtl + " ms");
> +        ttlCache.addElementWithTtl(uri, spec, minTtl);
>       }
>     }
>
> +    GadgetException exception = (GadgetException)
> cached.obj.getAttribute(ERROR_KEY);
> +    if (exception != null) {
> +      throw exception;
> +    }
>     return cached.obj;
>   }
>
>
> 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=705904&r1=705903&r2=705904&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
> Sat Oct 18 11:38:25 2008
> @@ -21,6 +21,7 @@
>  import static org.easymock.EasyMock.expect;
>  import static org.easymock.classextension.EasyMock.replay;
>  import static org.junit.Assert.assertEquals;
> +import static org.junit.Assert.fail;
>
>  import org.apache.shindig.common.cache.CacheProvider;
>  import org.apache.shindig.common.cache.DefaultCacheProvider;
> @@ -103,7 +104,7 @@
>   private final CacheProvider cacheProvider = new DefaultCacheProvider();
>
>   private final BasicGadgetSpecFactory specFactory
> -      = new BasicGadgetSpecFactory(fetcher, cacheProvider, 5, -1000,
> 1000);
> +      = new BasicGadgetSpecFactory(fetcher, cacheProvider, 5, 1000, 1000);
>
>   @Test
>   public void specFetched() throws Exception {
> @@ -220,6 +221,45 @@
>   }
>
>   @Test(expected = GadgetException.class)
> +  public void badFetchServesCached() throws Exception {
> +    HttpRequest firstRequest = new
> HttpRequest(SPEC_URL).setIgnoreCache(false);
> +    expect(fetcher.fetch(firstRequest)).andReturn(new
> HttpResponse(LOCAL_SPEC_XML)).once();
> +    HttpRequest secondRequest = new
> HttpRequest(SPEC_URL).setIgnoreCache(true);
> +
>  expect(fetcher.fetch(secondRequest)).andReturn(HttpResponse.error()).once();
> +    replay(fetcher);
> +
> +    GadgetSpec original = specFactory.getGadgetSpec(SPEC_URL.toJavaUri(),
> false);
> +    GadgetSpec cached = specFactory.getGadgetSpec(SPEC_URL.toJavaUri(),
> true);
> +
> +    assertEquals(original.getUrl(), cached.getUrl());
> +    assertEquals(original.getChecksum(), cached.getChecksum());
> +  }
> +
> +  @Test(expected = GadgetException.class)
> +  public void malformedGadgetSpecThrows() throws Exception {
> +    HttpRequest request = new HttpRequest(SPEC_URL).setIgnoreCache(true);
> +    expect(fetcher.fetch(request)).andReturn(new HttpResponse("malformed
> junk"));
> +    replay(fetcher);
> +
> +    specFactory.getGadgetSpec(SPEC_URL.toJavaUri(), true);
> +  }
> +
> +  @Test(expected = GadgetException.class)
> +  public void malformedGadgetSpecIsCachedAndThrows() throws Exception {
> +    HttpRequest request = new HttpRequest(SPEC_URL).setIgnoreCache(false);
> +    expect(fetcher.fetch(request)).andReturn(new HttpResponse("malformed
> junk")).once();
> +    replay(fetcher);
> +
> +    try {
> +      specFactory.getGadgetSpec(SPEC_URL.toJavaUri(), false);
> +      fail("No exception thrown on bad parse");
> +    } catch (GadgetException e) {
> +      // Expected.
> +    }
> +    specFactory.getGadgetSpec(SPEC_URL.toJavaUri(), false);
> +  }
> +
> +  @Test(expected = GadgetException.class)
>   public void throwingFetcherRethrows() throws Exception {
>     HttpRequest request = new HttpRequest(SPEC_URL).setIgnoreCache(true);
>     expect(fetcher.fetch(request)).andThrow(
>
>
>

Reply via email to