xmlgraphics-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jerem...@apache.org
Subject svn commit: r759144 - in /xmlgraphics/commons/trunk: ./ src/java/org/apache/xmlgraphics/image/loader/cache/ test/java/org/apache/xmlgraphics/image/loader/cache/
Date Fri, 27 Mar 2009 14:16:19 GMT
Author: jeremias
Date: Fri Mar 27 14:16:18 2009
New Revision: 759144

URL: http://svn.apache.org/viewvc?rev=759144&view=rev
Log:
Added 60 seconds expiration for invalid URIs in the image cache to recover from temporarily
unavailable images.

Added:
    xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/DefaultExpirationPolicyTestCase.java
  (with props)
    xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/MockTimeStampProvider.java
  (with props)
Modified:
    xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/image/loader/cache/ImageCache.java
    xmlgraphics/commons/trunk/status.xml
    xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/ImageCacheTestCase.java

Modified: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/image/loader/cache/ImageCache.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/image/loader/cache/ImageCache.java?rev=759144&r1=759143&r2=759144&view=diff
==============================================================================
--- xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/image/loader/cache/ImageCache.java
(original)
+++ xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/image/loader/cache/ImageCache.java
Fri Mar 27 14:16:18 2009
@@ -22,7 +22,8 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.Collections;
-import java.util.Set;
+import java.util.Iterator;
+import java.util.Map;
 
 import javax.xml.transform.Source;
 
@@ -44,18 +45,47 @@
  * <p>
  * Don't use one ImageCache instance in the context of multiple base URIs because relative
URIs
  * would not work correctly anymore.
+ * <p>
+ * By default, the URIs of inaccessible images are remembered but these entries are discarded
+ * after 60 seconds (which causes a retry next time the same URI is requested). This allows
+ * to counteract performance loss when accessing invalid or temporarily unavailable images
+ * over slow connections.
  */
 public class ImageCache {
 
     /** logger */
     protected static Log log = LogFactory.getLog(ImageCache.class);
 
-    private Set invalidURIs = Collections.synchronizedSet(new java.util.HashSet());
+    //Handling of invalid URIs
+    private Map invalidURIs = Collections.synchronizedMap(new java.util.HashMap());
+    private ExpirationPolicy invalidURIExpirationPolicy;
 
+    //Actual image cache
     private SoftMapCache imageInfos = new SoftMapCache(true);
     private SoftMapCache images = new SoftMapCache(true);
 
     private ImageCacheListener cacheListener;
+    private TimeStampProvider timeStampProvider;
+    private long lastHouseKeeping;
+
+    /**
+     * Default constructor with default settings.
+     */
+    public ImageCache() {
+        this(new TimeStampProvider(), new DefaultExpirationPolicy());
+    }
+
+    /**
+     * Constructor for customized behaviour and testing.
+     * @param timeStampProvider the time stamp provider to use
+     * @param invalidURIExpirationPolicy the expiration policy for invalid URIs
+     */
+    public ImageCache(TimeStampProvider timeStampProvider,
+            ExpirationPolicy invalidURIExpirationPolicy) {
+        this.timeStampProvider = timeStampProvider;
+        this.invalidURIExpirationPolicy = invalidURIExpirationPolicy;
+        this.lastHouseKeeping = this.timeStampProvider.getTimeStamp();
+    }
 
     /**
      * Sets an ImageCacheListener instance so the events in the image cache can be observed.
@@ -114,15 +144,28 @@
      * @return true if the URI is invalid
      */
     public boolean isInvalidURI(String uri) {
-        if (invalidURIs.contains(uri)) {
-            if (cacheListener != null) {
-                cacheListener.invalidHit(uri);
+        Long timestamp = (Long)invalidURIs.get(uri);
+        if (timestamp != null) {
+            boolean expired = removeInvalidURIIfExpired(uri, timestamp.longValue());
+            if (!expired) {
+                if (cacheListener != null) {
+                    cacheListener.invalidHit(uri);
+                }
+                return true;
             }
-            return true;
         }
         return false;
     }
 
+    private boolean removeInvalidURIIfExpired(String uri, long timestamp) {
+        boolean expired = this.invalidURIExpirationPolicy.isExpired(
+                this.timeStampProvider, timestamp);
+        if (expired) {
+            this.invalidURIs.remove(uri);
+        }
+        return expired;
+    }
+
     /**
      * Returns an ImageInfo instance from the cache or null if none is found.
      * @param uri the image's URI
@@ -151,18 +194,16 @@
         imageInfos.put(info.getOriginalURI(), info);
     }
 
+    private static final long ONE_HOUR = 60 * 60 * 1000;
+
     /**
      * Registers a URI as invalid so getImageInfo can indicate that quickly with no I/O access.
      * @param uri the URI of the invalid image
      */
-    private void registerInvalidURI(String uri) {
-        synchronized (invalidURIs) {
-            // cap size of invalid list
-            if (invalidURIs.size() > 100) {
-                invalidURIs.clear();
-            }
-            invalidURIs.add(uri);
-        }
+    void registerInvalidURI(String uri) {
+        invalidURIs.put(uri, new Long(timeStampProvider.getTimeStamp()));
+
+        considerHouseKeeping();
     }
 
     /**
@@ -225,12 +266,35 @@
         doHouseKeeping();
     }
 
+    private void considerHouseKeeping() {
+        long ts = timeStampProvider.getTimeStamp();
+        if (this.lastHouseKeeping + ONE_HOUR > ts) {
+            //Housekeeping is only triggered through registration of an invalid URI at the
moment.
+            //Depending on the environment this could be triggered next to never.
+            //Doing this check for every image access could be relatively costly.
+            //The only alternative is a cleanup thread which is rather heavy-weight.
+            this.lastHouseKeeping = ts;
+            doHouseKeeping();
+        }
+    }
+
     /**
      * Triggers some house-keeping, i.e. removes stale entries.
      */
     public void doHouseKeeping() {
         imageInfos.doHouseKeeping();
         images.doHouseKeeping();
+        doInvalidURIHouseKeeping();
+    }
+
+    private void doInvalidURIHouseKeeping() {
+        synchronized(this.invalidURIs) {
+            Iterator iter = this.invalidURIs.entrySet().iterator();
+            while (iter.hasNext()) {
+                Map.Entry entry = (Map.Entry)iter.next();
+                removeInvalidURIIfExpired((String)entry.getKey(), ((Long)entry.getValue()).longValue());
+            }
+        }
     }
 
 }

Modified: xmlgraphics/commons/trunk/status.xml
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/status.xml?rev=759144&r1=759143&r2=759144&view=diff
==============================================================================
--- xmlgraphics/commons/trunk/status.xml (original)
+++ xmlgraphics/commons/trunk/status.xml Fri Mar 27 14:16:18 2009
@@ -40,6 +40,10 @@
   </contexts>
   <changes>
     <release version="Trunk" date="n/a">
+      <action context="Code" dev="JM" type="add">
+        Added 60 seconds expiration for invalid URIs in the image cache to recover from
+        temporarily unavailable images.
+      </action>
       <action context="Code" dev="JM" type="add" fixes-bug="46583" due-to="Jiří
Kunhart">
         Added TexturePaint support for PSGraphics2D (PostScript tiling patterns).
       </action>

Added: xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/DefaultExpirationPolicyTestCase.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/DefaultExpirationPolicyTestCase.java?rev=759144&view=auto
==============================================================================
--- xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/DefaultExpirationPolicyTestCase.java
(added)
+++ xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/DefaultExpirationPolicyTestCase.java
Fri Mar 27 14:16:18 2009
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.xmlgraphics.image.loader.cache;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests {@link DefaultExpirationPolicy}.
+ */
+public class DefaultExpirationPolicyTestCase extends TestCase {
+
+    /**
+     * Never expire.
+     * @throws Exception if an error occurs
+     */
+    public void testNeverExpire() throws Exception {
+        ExpirationPolicy policy;
+        policy = new DefaultExpirationPolicy(DefaultExpirationPolicy.EXPIRATION_NEVER);
+
+        MockTimeStampProvider provider = new MockTimeStampProvider();
+
+        long ts = 1000000;
+        assertFalse(policy.isExpired(provider, ts));
+        provider.setTimeStamp(ts + Integer.MAX_VALUE);
+        assertFalse(policy.isExpired(provider, ts));
+    }
+
+    /**
+     * Normal expiration
+     * @throws Exception if an error occurs
+     */
+    public void testNormalExpiration() throws Exception {
+        ExpirationPolicy policy;
+        policy = new DefaultExpirationPolicy(2);
+
+        MockTimeStampProvider provider = new MockTimeStampProvider();
+
+        long ts = 1000000;
+        provider.setTimeStamp(ts);
+        assertFalse(policy.isExpired(provider, ts));
+        provider.setTimeStamp(ts + 1000);
+        assertFalse(policy.isExpired(provider, ts));
+
+        provider.setTimeStamp(ts + 2000);
+        assertTrue(policy.isExpired(provider, ts));
+        provider.setTimeStamp(ts + 3000);
+        assertTrue(policy.isExpired(provider, ts));
+    }
+
+}

Propchange: xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/DefaultExpirationPolicyTestCase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/DefaultExpirationPolicyTestCase.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/ImageCacheTestCase.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/ImageCacheTestCase.java?rev=759144&r1=759143&r2=759144&view=diff
==============================================================================
--- xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/ImageCacheTestCase.java
(original)
+++ xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/ImageCacheTestCase.java
Fri Mar 27 14:16:18 2009
@@ -35,24 +35,33 @@
  */
 public class ImageCacheTestCase extends TestCase {
 
+    private static final boolean DEBUG = false;
+
     private MockImageContext imageContext = MockImageContext.getInstance();
+    private ImageSessionContext sessionContext = imageContext.newSessionContext();
+    private ImageManager manager = imageContext.getImageManager();
+    private ImageCacheStatistics statistics = (DEBUG
+                ? new ImageCacheLoggingStatistics(true) : new ImageCacheStatistics(true));
+
+    /** {@inheritDoc} */
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        manager.getCache().clearCache();
+        statistics.reset();
+        manager.getCache().setCacheListener(statistics);
+    }
 
     /**
      * Tests the ImageInfo cache.
      * @throws Exception if an error occurs
      */
     public void testImageInfoCache() throws Exception {
-
-        ImageSessionContext sessionContext = imageContext.newSessionContext();
-        ImageManager manager = imageContext.getImageManager();
-
         String invalid1 = "invalid1.jpg";
         String invalid2 = "invalid2.jpg";
         String valid1 = "bgimg300dpi.bmp";
         String valid2 = "big-image.png";
 
-        ImageCacheStatistics statistics = new ImageCacheLoggingStatistics(true);
-        manager.getCache().setCacheListener(statistics);
 
         ImageInfo info1, info2;
         info1 = manager.getImageInfo(valid1, sessionContext);
@@ -102,54 +111,52 @@
         assertEquals(2, statistics.getImageInfoCacheMisses());
         assertEquals(1, statistics.getInvalidHits());
         statistics.reset();
-
     }
 
-    /**
-     * Tests the image cache reusing a cacheable Image created by the ImageLoader.
-     * @throws Exception if an error occurs
-     */
-    public void testImageCache1() throws Exception {
-        ImageSessionContext sessionContext = imageContext.newSessionContext();
-        ImageManager manager = imageContext.getImageManager();
-
-        String valid1 = "bgimg72dpi.gif";
+    public void testInvalidURIExpiration() throws Exception {
+        MockTimeStampProvider provider = new MockTimeStampProvider();
+        ImageCache cache = new ImageCache(provider, new DefaultExpirationPolicy(2));
+        cache.setCacheListener(statistics);
 
-        ImageCacheStatistics statistics = new ImageCacheLoggingStatistics(true);
-        manager.getCache().setCacheListener(statistics);
+        String invalid1 = "invalid1.jpg";
+        String invalid2 = "invalid2.jpg";
+        String valid1 = "valid1.jpg";
 
-        ImageInfo info = manager.getImageInfo(valid1, sessionContext);
-        assertNotNull(info);
+        provider.setTimeStamp(1000);
+        cache.registerInvalidURI(invalid1);
+        provider.setTimeStamp(1100);
+        cache.registerInvalidURI(invalid2);
 
-        ImageBuffered img1 = (ImageBuffered)manager.getImage(
-                info, ImageFlavor.BUFFERED_IMAGE, sessionContext);
-        assertNotNull(img1);
-        assertNotNull(img1.getBufferedImage());
-
-        ImageBuffered img2 = (ImageBuffered)manager.getImage(
-                info, ImageFlavor.BUFFERED_IMAGE, sessionContext);
-        //ImageBuffered does not have to be the same instance but we want at least the
-        //BufferedImage to be reused.
-        assertTrue("BufferedImage must be reused",
-                img1.getBufferedImage() == img2.getBufferedImage());
+        assertEquals(0, statistics.getInvalidHits());
 
-        assertEquals(1, statistics.getImageCacheHits());
-        assertEquals(1, statistics.getImageCacheMisses());
+        //not expired, yet
+        provider.setTimeStamp(1200);
+        assertFalse(cache.isInvalidURI(valid1));
+        assertTrue(cache.isInvalidURI(invalid1));
+        assertTrue(cache.isInvalidURI(invalid2));
+        assertEquals(2, statistics.getInvalidHits());
+
+        //first expiration time reached
+        provider.setTimeStamp(3050);
+        assertFalse(cache.isInvalidURI(valid1));
+        assertFalse(cache.isInvalidURI(invalid1));
+        assertTrue(cache.isInvalidURI(invalid2));
+        assertEquals(3, statistics.getInvalidHits());
+
+        //second expiration time reached
+        provider.setTimeStamp(3200);
+        assertFalse(cache.isInvalidURI(valid1));
+        assertFalse(cache.isInvalidURI(invalid1));
+        assertFalse(cache.isInvalidURI(invalid2));
+        assertEquals(3, statistics.getInvalidHits());
     }
 
     /**
-     * Tests the image cache reusing a cacheable Image created by one of the ImageConverters
in
-     * a converter pipeline.
+     * Tests the image cache reusing a cacheable Image created by the ImageLoader.
      * @throws Exception if an error occurs
      */
-    public void DISABLEDtestImageCache2() throws Exception {
-        ImageSessionContext sessionContext = imageContext.newSessionContext();
-        ImageManager manager = imageContext.getImageManager();
-
-        String valid1 = "test/resources/images/img-w-size.svg";
-
-        ImageCacheStatistics statistics = new ImageCacheLoggingStatistics(true);
-        manager.getCache().setCacheListener(statistics);
+    public void testImageCache1() throws Exception {
+        String valid1 = "bgimg72dpi.gif";
 
         ImageInfo info = manager.getImageInfo(valid1, sessionContext);
         assertNotNull(info);
@@ -166,8 +173,8 @@
         assertTrue("BufferedImage must be reused",
                 img1.getBufferedImage() == img2.getBufferedImage());
 
-        assertEquals(1, statistics.getImageCacheHits()); //1=BufferedImage
-        assertEquals(3, statistics.getImageCacheMisses()); //3=BufferedImage,Graphics2DImage,DOM
+        assertEquals(1, statistics.getImageCacheHits());
+        assertEquals(1, statistics.getImageCacheMisses());
     }
 
 }

Added: xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/MockTimeStampProvider.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/MockTimeStampProvider.java?rev=759144&view=auto
==============================================================================
--- xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/MockTimeStampProvider.java
(added)
+++ xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/MockTimeStampProvider.java
Fri Mar 27 14:16:18 2009
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.xmlgraphics.image.loader.cache;
+
+/**
+ * Mock subclass of the TimeStampProvider.
+ */
+class MockTimeStampProvider extends TimeStampProvider {
+
+    private long timestamp;
+
+    public MockTimeStampProvider() {
+        this(0);
+    }
+
+    public MockTimeStampProvider(long timestamp) {
+        setTimeStamp(timestamp);
+    }
+
+    public void setTimeStamp(long timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    /** {@inheritDoc} */
+    public long getTimeStamp() {
+        return this.timestamp;
+    }
+
+}

Propchange: xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/MockTimeStampProvider.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/image/loader/cache/MockTimeStampProvider.java
------------------------------------------------------------------------------
    svn:keywords = Id



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: commits-help@xmlgraphics.apache.org


Mime
View raw message