Coverage Report - org.webmacro.resource.ReloadingCacheManager
 
Classes in this File Line Coverage Branch Coverage Complexity
ReloadingCacheManager
65%
41/63
50%
16/32
1.952
ReloadingCacheManager$1
100%
5/5
N/A
1.952
ReloadingCacheManager$2
100%
4/4
N/A
1.952
ReloadingCacheManager$DirectScmCacheElement
0%
0/4
N/A
1.952
ReloadingCacheManager$MyCacheElement
83%
5/6
25%
1/4
1.952
ReloadingCacheManager$SoftScmCacheElement
100%
4/4
N/A
1.952
 
 1  
 package org.webmacro.resource;
 2  
 
 3  
 import java.lang.ref.SoftReference;
 4  
 import java.util.concurrent.ConcurrentHashMap;
 5  
 import java.util.concurrent.Executors;
 6  
 import java.util.concurrent.ScheduledExecutorService;
 7  
 import java.util.concurrent.ThreadFactory;
 8  
 import java.util.concurrent.TimeUnit;
 9  
 
 10  
 import org.slf4j.Logger;
 11  
 import org.slf4j.LoggerFactory;
 12  
 
 13  
 import org.webmacro.Broker;
 14  
 import org.webmacro.InitException;
 15  
 import org.webmacro.ResourceException;
 16  
 import org.webmacro.util.Settings;
 17  
 import org.webmacro.util.SubSettings;
 18  
 
 19  
 
 20  
 /**
 21  
  * ReloadingCacheManager -- a cache manager which supports reloading and expiration, backed
 22  
  * by a ConcurrentHashMap.  Derived from the old SMapCacheManager, but simplified.
 23  
  *
 24  
  * @author Brian Goetz
 25  
  */
 26  409
 public class ReloadingCacheManager implements CacheManager {
 27  
 
 28  2
     static Logger _log =  LoggerFactory.getLogger(ReloadDelayDecorator.class);
 29  
 
 30  
     private static final String NAME = "ReloadingCacheManager";
 31  
 
 32  12
     private final ConcurrentHashMap _cache = new ConcurrentHashMap();
 33  
     private int _cacheDurationMilliseconds;
 34  
     private String _resourceType;
 35  12
     private boolean _reloadOnChange = true, _useSoftReferences = true;
 36  12
     private boolean _delayReloadChecks = false;
 37  
     private long _checkForReloadDelay;
 38  
 
 39  
     // Used ClockDaemon instead of the original TimeLoop -- more efficient priority-queue based implementation
 40  
     // Then renamed in move to java.util.concurrent
 41  
     private ScheduledExecutorService _clockDaemon;
 42  
 
 43  
 
 44  3084
     private abstract class MyCacheElement extends CacheElement implements Cloneable
 45  
     {
 46  
 
 47  544
         private CacheReloadContext reloadContext = null;
 48  
 
 49  
         public void setReloadContext (CacheReloadContext rc)
 50  
         {
 51  224
             if (_delayReloadChecks && rc != null)
 52  0
                 this.reloadContext = new TimedReloadContext(rc, _checkForReloadDelay);
 53  
             else
 54  224
                 this.reloadContext = rc;
 55  224
         }
 56  
 
 57  
         public abstract Object getObject ();
 58  
 
 59  
         public abstract void setObject (Object o);
 60  
     }
 61  
 
 62  1088
     private final class SoftScmCacheElement extends MyCacheElement
 63  
     {
 64  
 
 65  
         private SoftReference reference;
 66  
 
 67  
         public Object getObject ()
 68  
         {
 69  998
             return reference.get();
 70  
         }
 71  
 
 72  
         public void setObject (Object o)
 73  
         {
 74  224
             reference = new SoftReference(o);
 75  224
         }
 76  
     }
 77  
 
 78  0
     private final class DirectScmCacheElement extends MyCacheElement
 79  
     {
 80  
 
 81  
         private Object object;
 82  
 
 83  
         public Object getObject ()
 84  
         {
 85  0
             return object;
 86  
         }
 87  
 
 88  
         public void setObject (Object o)
 89  
         {
 90  0
             object = o;
 91  0
         }
 92  
     }
 93  
 
 94  
     public ReloadingCacheManager()
 95  12
     {
 96  12
     }
 97  
 
 98  
     public void init (Broker b, Settings config, String resourceType)
 99  
             throws InitException
 100  
     {
 101  
         Settings ourSettings, defaultSettings;
 102  
 
 103  12
         _clockDaemon = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
 104  
             public Thread newThread(Runnable runnable) {
 105  6
                 _log.info("Creating new ClockDaemon thread");
 106  6
                 Thread t = new Thread(runnable);
 107  6
                 t.setDaemon(true);
 108  6
                 return t;
 109  
             }
 110  
         });
 111  
 
 112  12
         _resourceType = resourceType;
 113  
 
 114  12
         ourSettings = new SubSettings(config, NAME + "." + _resourceType);
 115  12
         defaultSettings = new SubSettings(config, NAME + ".*");
 116  
 
 117  12
         _cacheDurationMilliseconds =
 118  
                 ourSettings.getIntegerSetting("ExpireTime",
 119  
                         defaultSettings.getIntegerSetting("ExpireTime",
 120  
                                 config.getIntegerSetting("TemplateExpireTime", 0)));
 121  12
         _useSoftReferences =
 122  
                 (ourSettings.containsKey("UseSoftReferences"))
 123  
                 ? ourSettings.getBooleanSetting("UseSoftReferences")
 124  
                 : ((defaultSettings.containsKey("UseSoftReferences"))
 125  
                 ? defaultSettings.getBooleanSetting("UseSoftReferences") : true);
 126  12
         _reloadOnChange =
 127  
                 (ourSettings.containsKey("ReloadOnChange"))
 128  
                 ? ourSettings.getBooleanSetting("ReloadOnChange")
 129  
                 : ((defaultSettings.containsKey("ReloadOnChange"))
 130  
                 ? defaultSettings.getBooleanSetting("ReloadOnChange") : true);
 131  
 
 132  12
         _checkForReloadDelay =
 133  
                 (ourSettings.getIntegerSetting("CheckForReloadDelay",
 134  
                         defaultSettings.getIntegerSetting("CheckForReloadDelay", -1)));
 135  12
         _delayReloadChecks = _checkForReloadDelay > 0;
 136  
 
 137  12
         _log.info(NAME + "." + _resourceType + ": "
 138  
                 + "; expireTime=" + _cacheDurationMilliseconds
 139  
                 + "; reload=" + _reloadOnChange
 140  
                 + "; softReference=" + _useSoftReferences
 141  
                 + "; checkForReloadDelay=" + _checkForReloadDelay);
 142  12
     }
 143  
 
 144  
     private MyCacheElement newCacheElement() {
 145  544
         if (_useSoftReferences)
 146  544
             return new SoftScmCacheElement();
 147  
         else
 148  0
             return new DirectScmCacheElement();
 149  
     }
 150  
 
 151  
     /**
 152  
      * Clear the cache.
 153  
      */
 154  
     public void flush ()
 155  
     {
 156  0
         _cache.clear();
 157  0
     }
 158  
 
 159  
     /**
 160  
      * Close down the provider.
 161  
      */
 162  
     public void destroy ()
 163  
     {
 164  0
         _cache.clear();
 165  0
         _clockDaemon.shutdown();
 166  0
     }
 167  
 
 168  
     public boolean supportsReload ()
 169  
     {
 170  12
         return _reloadOnChange;
 171  
     }
 172  
 
 173  
     private final void scheduleRemoval(final Object key) {
 174  224
         _clockDaemon.schedule(
 175  
                 new Runnable()
 176  224
                 {
 177  
                     public void run ()
 178  
                     {
 179  185
                         _cache.remove(key);
 180  185
                         _log.debug("cache expired: " + key);
 181  185
                     }
 182  
                 },
 183  
                 _cacheDurationMilliseconds,
 184  
                 TimeUnit.MILLISECONDS);
 185  224
     }
 186  
 
 187  
     /**
 188  
      * Get the object associated with the specific query, first
 189  
      * trying to look it up in a cache. If it's not there, then
 190  
      * call load(String) to load it into the cache.
 191  
      */
 192  
     public Object get (final Object query, ResourceLoader helper)
 193  
             throws ResourceException
 194  
     {
 195  
         MyCacheElement r;
 196  1542
         Object o = null;
 197  1542
         boolean reload = false;
 198  
 
 199  1542
         r = (MyCacheElement) _cache.get(query);
 200  1542
         if (r != null) {
 201  998
             o = r.getObject();
 202  
 
 203  
             // should the template be reloaded, regardless of cached status?
 204  998
             if (o != null && r.reloadContext != null && _reloadOnChange)
 205  998
                 reload = r.reloadContext.shouldReload();
 206  
         }
 207  1542
         if (o == null || reload)
 208  
         {
 209  544
             r = newCacheElement();
 210  544
             o = helper.load(query, r);
 211  224
             if (o != null)
 212  
             {
 213  224
                 r.setObject(o);
 214  224
                 _cache.put(query, r);
 215  224
                 _log.debug("cached: " + query + " for " + _cacheDurationMilliseconds);
 216  
                 try
 217  
                 {
 218  
                     // if timeout is < 0,
 219  
                     // then don't schedule a removal from cache
 220  224
                     if (_cacheDurationMilliseconds >= 0)
 221  224
                         scheduleRemoval(query);
 222  
                 }
 223  0
                 catch (Exception e)
 224  
                 {
 225  0
                     _log.error("CachingProvider caught an exception", e);
 226  224
                 }
 227  
             }
 228  
         }
 229  1222
         return o;
 230  
     }
 231  
 
 232  
     /**
 233  
      * Get the object associated with the specific query,
 234  
      * trying to look it up in a cache. If it's not there, return null.
 235  
      */
 236  
     public Object get (final Object query)
 237  
     {
 238  0
         MyCacheElement r = (MyCacheElement) _cache.get(query);
 239  0
         if (r != null)
 240  0
             return r.getObject();
 241  
         else
 242  0
             return null;
 243  
     }
 244  
 
 245  
     /**
 246  
      * Put an object in the cache
 247  
      */
 248  
     public void put (final Object query, Object resource)
 249  
     {
 250  0
         MyCacheElement r = newCacheElement();
 251  0
         r.setObject(resource);
 252  
 
 253  0
         _cache.put(query, r);
 254  0
         if (_cacheDurationMilliseconds >= 0)
 255  
         {
 256  0
             _log.debug("cached: " + query + " for " + _cacheDurationMilliseconds);
 257  0
             scheduleRemoval(query);
 258  
         }
 259  0
     }
 260  
 
 261  
     /** Removes a specific entry from the cache. */
 262  
     public void invalidate (final Object query)
 263  
     {
 264  0
         _cache.remove(query);
 265  0
     }
 266  
 
 267  
     public String toString ()
 268  
     {
 269  0
         return NAME + "(type = " + _resourceType + ")";
 270  
     }
 271  
 }