View Javadoc
1   package org.apache.fulcrum.cache.impl;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import org.apache.avalon.framework.activity.Disposable;
27  import org.apache.avalon.framework.activity.Initializable;
28  import org.apache.avalon.framework.configuration.Configurable;
29  import org.apache.avalon.framework.configuration.Configuration;
30  import org.apache.avalon.framework.configuration.ConfigurationException;
31  import org.apache.avalon.framework.logger.AbstractLogEnabled;
32  import org.apache.avalon.framework.thread.ThreadSafe;
33  import org.apache.fulcrum.cache.CachedObject;
34  import org.apache.fulcrum.cache.GlobalCacheService;
35  import org.apache.fulcrum.cache.ObjectExpiredException;
36  import org.apache.fulcrum.cache.RefreshableCachedObject;
37  
38  import net.sf.ehcache.Cache;
39  import net.sf.ehcache.CacheManager;
40  import net.sf.ehcache.Element;
41  
42  /**
43   * Default implementation of EHCacheService (Ehcache 2)
44   *
45   * @author <a href="mailto:epughNOSPAM@opensourceconnections.com">Eric Pugh</a>
46   * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
47   *
48   */
49  public class EHCacheService extends AbstractLogEnabled implements
50          GlobalCacheService, Runnable, Configurable, Disposable, Initializable, ThreadSafe
51  {
52      /**
53       * Cache check frequency in Millis (1000 Millis = 1 second). Value must be &gt;
54       * 0. Default = 5 seconds
55       */
56      public static final long DEFAULT_CACHE_CHECK_FREQUENCY = 5000; // 5 seconds
57  
58      /**
59       * cacheCheckFrequency (default - 5 seconds)
60       */
61      private long cacheCheckFrequency;
62  
63      /**
64       * Path name of the Ehcache configuration file
65       */
66      private String configFile;
67  
68      /**
69       * Constant value which provides a cache name
70       */
71      private static final String DEFAULT_CACHE_NAME = "fulcrum";
72  
73      /**
74       * A cache name
75       */
76      private String cacheName;
77  
78      /** thread for refreshing stale items in the cache */
79      private Thread refreshing;
80  
81      /** flag to stop the housekeeping thread when the component is disposed. */
82      private boolean continueThread;
83  
84      /** The EHCache manager instance */
85      private CacheManager cacheManager;
86  
87      /** A cache instance */
88      private Cache cache;
89  
90      public EHCacheService()
91      {
92       
93      }
94      // ---------------- Avalon Lifecycle Methods ---------------------
95  
96      /**
97       * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
98       */
99      @Override
100     public void configure(Configuration config) throws ConfigurationException
101     {
102         this.cacheCheckFrequency = config.getChild("cacheCheckFrequency")
103                 .getValueAsLong(DEFAULT_CACHE_CHECK_FREQUENCY);
104         this.cacheName = config.getChild("cacheName").getValue(DEFAULT_CACHE_NAME);
105         this.configFile = config.getChild("configurationFile").getValue(null);
106     }
107 
108     /**
109      * @see org.apache.avalon.framework.activity.Initializable#initialize()
110      */
111     @Override
112     public void initialize() throws Exception
113     {
114         if (this.configFile == null)
115         {
116             this.cacheManager = new CacheManager();
117             this.cacheManager.addCache(this.cacheName);
118         }
119         else
120         {
121             this.cacheManager = new CacheManager(this.configFile);
122         }
123 
124         this.cache = this.cacheManager.getCache(this.cacheName);
125 
126         // Start housekeeping thread.
127         this.continueThread = true;
128         this.refreshing = new Thread(this);
129 
130         // Indicate that this is a system thread. JVM will quit only when
131         // there are no more active user threads. Settings threads spawned
132         // internally by Turbine as daemons allows commandline applications
133         // using Turbine to terminate in an orderly manner.
134         this.refreshing.setDaemon(true);
135         this.refreshing.setName("EHCacheService Refreshing");
136         this.refreshing.start();
137 
138         getLogger().debug("EHCacheService started!");
139     }
140 
141     /**
142      * @see org.apache.avalon.framework.activity.Disposable#dispose()
143      */
144     @Override
145     public void dispose()
146     {
147         this.continueThread = false;
148         this.refreshing.interrupt();
149 
150         this.cacheManager.shutdown();
151         this.cacheManager = null;
152         this.cache = null;
153         getLogger().debug("EHCacheService stopped!");
154     }
155 
156     /**
157      * @see org.apache.fulcrum.cache.GlobalCacheService#addObject(java.lang.String, org.apache.fulcrum.cache.CachedObject)
158      */
159     @Override
160     public <T> void addObject(String objectId, CachedObject<T> object)
161     {
162         Element cacheElement = new Element(objectId, object);
163 
164         if (object instanceof RefreshableCachedObject)
165         {
166             cacheElement.setEternal(true);
167         }
168         else
169         {
170             cacheElement.setEternal(false);
171             cacheElement.setTimeToLive((int)(object.getExpires() + 500) / 1000);
172         }
173 
174         this.cache.put(cacheElement);
175     }
176 
177     /**
178      * @see org.apache.fulcrum.cache.GlobalCacheService#flushCache()
179      */
180     @Override
181     public void flushCache()
182     {
183         this.cache.removeAll();
184     }
185 
186     /**
187      * @see org.apache.fulcrum.cache.GlobalCacheService#getCachedObjects()
188      */
189     @Override
190     public List<CachedObject<?>> getCachedObjects()
191     {
192         ArrayList<CachedObject<?>> values = new ArrayList<CachedObject<?>>();
193 
194         for (String key : getKeys())
195         {
196             Element cachedElement = this.cache.get(key);
197 
198             if (cachedElement != null)
199             {
200                 values.add((CachedObject<?>)cachedElement.getObjectValue());
201             }
202         }
203 
204         return values;
205     }
206 
207     /**
208      * @see org.apache.fulcrum.cache.GlobalCacheService#getCacheSize()
209      */
210     @SuppressWarnings("deprecation")
211 	@Override
212     public int getCacheSize() throws IOException
213     {
214         return (int)this.cache.calculateInMemorySize();
215     }
216 
217     /**
218      * @see org.apache.fulcrum.cache.GlobalCacheService#getKeys()
219      */
220     @Override
221     public List<String> getKeys()
222     {
223         @SuppressWarnings("unchecked")
224         List<String> keysWithExpiryCheck = this.cache.getKeysWithExpiryCheck();
225         return keysWithExpiryCheck;
226     }
227 
228     /**
229      * @see org.apache.fulcrum.cache.GlobalCacheService#getNumberOfObjects()
230      */
231     @Override
232     public int getNumberOfObjects()
233     {
234         return getKeys().size();
235     }
236 
237     /**
238      * @see org.apache.fulcrum.cache.GlobalCacheService#getObject(java.lang.String)
239      */
240     @Override
241     public <T> CachedObject<T> getObject(String objectId) throws ObjectExpiredException
242     {
243         Element cachedElement = this.cache.get(objectId);
244 
245         if (cachedElement == null)
246         {
247             // Not in the cache.
248             throw new ObjectExpiredException();
249         }
250 
251         @SuppressWarnings("unchecked")
252         CachedObject<T> cachedObject = (CachedObject<T>)cachedElement.getObjectValue();
253 
254         if (cachedObject.isStale())
255         {
256             if (cachedObject instanceof RefreshableCachedObject)
257             {
258                 RefreshableCachedObject<?> refreshableCachedObject = (RefreshableCachedObject<?>) cachedObject;
259                 if (refreshableCachedObject.isUntouched())
260                 {
261                     // Do not refresh an object that has exceeded TimeToLive
262                     removeObject(objectId);
263                     throw new ObjectExpiredException();
264                 }
265 
266                 // Refresh Object
267                 refreshableCachedObject.refresh();
268                 if (refreshableCachedObject.isStale())
269                 {
270                     // Object is Expired, remove it from cache.
271                     removeObject(objectId);
272                     throw new ObjectExpiredException();
273                 }
274             }
275             else
276             {
277                 // Expired.
278                 removeObject(objectId);
279                 throw new ObjectExpiredException();
280             }
281         }
282 
283         if (cachedObject instanceof RefreshableCachedObject)
284         {
285             // notify it that it's being accessed.
286             RefreshableCachedObject<?> refreshableCachedObject = (RefreshableCachedObject<?>) cachedObject;
287             refreshableCachedObject.touch();
288         }
289 
290         return cachedObject;
291     }
292 
293     /**
294      * @see org.apache.fulcrum.cache.GlobalCacheService#removeObject(java.lang.String)
295      */
296     @Override
297     public void removeObject(String objectId)
298     {
299         this.cache.remove(objectId);
300     }
301 
302     /**
303      * Circle through the cache and refresh stale objects. Frequency is
304      * determined by the cacheCheckFrequency property.
305      */
306     @Override
307     public void run()
308     {
309         while (this.continueThread)
310         {
311             // Sleep for amount of time set in cacheCheckFrequency -
312             // default = 5 seconds.
313             try
314             {
315                 Thread.sleep(this.cacheCheckFrequency);
316             }
317             catch (InterruptedException exc)
318             {
319                 if (!this.continueThread)
320                 {
321                     return;
322                 }
323             }
324 
325             for (String key : getKeys())
326             {
327                 Element cachedElement = this.cache.get(key);
328 
329                 if (cachedElement == null)
330                 {
331                     this.cache.remove(key);
332                     continue;
333                 }
334 
335                 Object object = cachedElement.getObjectValue();
336 
337                 if (object instanceof RefreshableCachedObject)
338                 {
339                     RefreshableCachedObject<?> refreshableObject = (RefreshableCachedObject<?>) object;
340                     if (refreshableObject.isUntouched())
341                     {
342                         this.cache.remove(key);
343                     }
344                     else if (refreshableObject.isStale())
345                     {
346                     	refreshableObject.refresh();
347                     }
348                 }
349             }
350         }
351     }
352 }