View Javadoc

1   package com.atlassian.cache.ehcache;
2   
3   import java.io.UnsupportedEncodingException;
4   import java.net.URLEncoder;
5   
6   import com.atlassian.cache.CacheSettings;
7   
8   import net.sf.ehcache.CacheManager;
9   import net.sf.ehcache.Ehcache;
10  import net.sf.ehcache.config.CacheConfiguration;
11  import net.sf.ehcache.config.CacheConfiguration.CacheEventListenerFactoryConfiguration;
12  import net.sf.ehcache.config.PersistenceConfiguration;
13  
14  import static java.util.concurrent.TimeUnit.MILLISECONDS;
15  import static java.util.concurrent.TimeUnit.SECONDS;
16  import static net.sf.ehcache.config.PersistenceConfiguration.Strategy.NONE;
17  
18  /**
19   * Helper for building EhCache caches
20   *
21   * @since 2.0
22   */
23  class EhCacheHelper
24  {
25      // These are all the flags supported by RMICacheReplicatorFactory
26      private static final String CACHE_PROPERTIES =
27              "replicateAsynchronously=%s," +
28              "replicatePuts=%s," +
29              "replicatePutsViaCopy=%s," +
30              "replicateUpdates=%s," +
31              "replicateUpdatesViaCopy=%s," +
32              "replicateRemovals=true";
33  
34      static final PersistenceConfiguration.Strategy PERSISTENCE_STRATEGY = NONE;
35  
36      private static final PersistenceConfiguration PERSISTENCE_CONFIGURATION =
37              new PersistenceConfiguration().strategy(PERSISTENCE_STRATEGY);
38  
39      private static final String URL_ENCODING = "UTF-8";
40  
41      private static CacheEventListenerFactoryConfiguration getCacheEventListenerFactoryConfiguration(
42              final CacheSettings settings, final boolean selfLoading)
43      {
44          final boolean replicateAsynchronously = settings.getReplicateAsynchronously(false);
45          final boolean replicateViaCopy = settings.getReplicateViaCopy(false);
46  
47          // If the cache is self loading there is no need to replicate puts or updates as the loader takes care of
48          // populating the cache as needed.
49          // Else if the cache is replicating via copy then puts and updates should be replicated.
50          // Else replicating puts is dangerous as it may lead to invalidations ping ponging between nodes. Whether to
51          // replicate updates is ambiguous, but setting it may help to prevent stale caches while being less vulnerable
52          // to the invalidation storm that comes with simply replicating puts.
53          final boolean replicatePuts = !selfLoading && replicateViaCopy;
54          final boolean replicateUpdates = !selfLoading;
55  
56          final String cacheProperties =
57                  String.format(CACHE_PROPERTIES, replicateAsynchronously, replicatePuts, replicateViaCopy, replicateUpdates, replicateViaCopy);
58          return new CacheEventListenerFactoryConfiguration()
59                  .className(RMICacheReplicatorFactory.class.getName())
60                  .properties(cacheProperties);
61      }
62  
63      Ehcache getEhcache(final String name, final CacheManager ehMgr, final CacheSettings settings, final boolean selfLoading, final boolean statisticsEnabled)
64      {
65          //Create a Cache specifying its configuration.
66          CacheConfiguration config = ehMgr.getConfiguration().getDefaultCacheConfiguration().clone()
67                  .name(name)
68                  .statistics(statisticsEnabled)
69                  .persistence(PERSISTENCE_CONFIGURATION);
70  
71          final boolean replicateCache = isReplicateCache(ehMgr, settings);
72          if (replicateCache)
73          {
74              config.cacheEventListenerFactory(
75                      getCacheEventListenerFactoryConfiguration(settings, selfLoading));
76          }
77  
78          if (null != settings.getMaxEntries())
79          {
80              config.setMaxEntriesLocalHeap(settings.getMaxEntries());
81          }
82  
83          if (null != settings.getExpireAfterAccess())
84          {
85              config.timeToIdleSeconds(SECONDS.convert(settings.getExpireAfterAccess(), MILLISECONDS));
86          }
87  
88          if (null != settings.getExpireAfterWrite())
89          {
90              config.timeToLiveSeconds(SECONDS.convert(settings.getExpireAfterWrite(), MILLISECONDS));
91          }
92  
93          // Cache should not be eternal if expiry has been set
94          if (settings.getExpireAfterAccess() != null || settings.getExpireAfterWrite() != null)
95          {
96              config.setEternal(false);
97          }
98  
99          ehMgr.addCacheIfAbsent(new net.sf.ehcache.Cache(config));
100         return ehMgr.getCache(getCacheName(name, replicateCache));
101     }
102 
103     private String getCacheName(final String name, final boolean replicateCache)
104     {
105         if (replicateCache)
106         {
107             try
108             {
109                 return URLEncoder.encode(name, URL_ENCODING);
110             }
111             catch (final UnsupportedEncodingException e)
112             {
113                 throw new IllegalStateException(e);
114             }
115         }
116         return name;
117     }
118 
119     private boolean isReplicateCache(final CacheManager ehMgr, final CacheSettings settings)
120     {
121         final boolean isLocalCacheSetting = settings.getLocal(false); // default to non-local as per API
122         return !isLocalCacheSetting && ehMgr.getCacheManagerPeerProvider("RMI") != null;
123     }
124 }