View Javadoc

1   /**
2    *  Copyright 2003-2007 Greg Luck
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  
17  package net.sf.ehcache.jcache;
18  
19  import edu.emory.mathcs.backport.java.util.concurrent.ExecutionException;
20  import edu.emory.mathcs.backport.java.util.concurrent.Future;
21  import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingQueue;
22  import edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor;
23  import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
24  import net.sf.ehcache.Ehcache;
25  import net.sf.ehcache.Element;
26  import net.sf.jsr107cache.CacheEntry;
27  import net.sf.jsr107cache.CacheException;
28  import net.sf.jsr107cache.CacheListener;
29  import net.sf.jsr107cache.CacheLoader;
30  import net.sf.jsr107cache.CacheStatistics;
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  
34  import java.util.ArrayList;
35  import java.util.Collection;
36  import java.util.HashMap;
37  import java.util.HashSet;
38  import java.util.Iterator;
39  import java.util.List;
40  import java.util.Map;
41  import java.util.Set;
42  
43  /**
44   * A cache implementation that matches the JCACHE specification.
45   * <p/>
46   * It is not possible for one class to implement both JCACHE and Ehcache
47   * in the same class due to conflicts with method signatures.
48   * <p/>
49   * This implementation is a decorator for Ehcache, and should exhibit the same
50   * underlying characteristics as Ehcache.
51   * <p/>
52   * Note that JCACHE contains no lifecyle methods. JCaches cannot be stopped. Any resources, such
53   * as loader threads cannot be released, although they will die off after 60 seconds.
54   *
55   * @author Greg Luck
56   * @version $Id: JCache.java 512 2007-07-10 09:18:45Z gregluck $
57   */
58  public class JCache implements net.sf.jsr107cache.Cache {
59  
60      private static final Log LOG = LogFactory.getLog(JCache.class.getName());
61  
62      private static final int EXECUTOR_KEEP_ALIVE_TIME = 60000;
63      private static final int EXECUTOR_MAXIMUM_POOL_SIZE = 10;
64      private static final int EXECUTOR_CORE_POOL_SIZE = 0;
65  
66  
67      /**
68       * An Ehcache backing instance
69       */
70      private Ehcache cache;
71  
72      private CacheLoader cacheLoader;
73  
74  
75      /**
76       * A ThreadPoolExecutor which uses a thread pool to schedule loads in the order in which they are requested.
77       * <p/>
78       * Each JCache has its own one of these, if required. Because the Core Thread Pool is zero, no threads
79       * are used until actually needed. Threads are added to the pool up to a maximum of 10. The keep alive
80       * time is 60 seconds, after which, if they are not required they will be stopped and collected.
81       */
82      private ThreadPoolExecutor executorService;
83  
84      /**
85       * A constructor for JCache.
86       * <p/>
87       * JCache is an adaptor for an Ehcache, and therefore requires an Ehcace in its constructor.
88       * <p/>
89       * The {@link net.sf.ehcache.config.ConfigurationFactory} and clients can create these.
90       * <p/>
91       * A client can specify their own settings here and pass the {@link Ehcache} object
92       * into {@link net.sf.ehcache.CacheManager#addCache} to specify parameters other than the defaults.
93       * <p/>
94       * Only the CacheManager can initialise them.
95       *
96       * @param cache       An ehcache
97       * @param cacheLoader used to load entries when they are not in the cache. If this is null,
98       *                    which is legal, loads do a noop
99       * @since 1.3
100      */
101     public JCache(Ehcache cache, CacheLoader cacheLoader) {
102         this.cache = cache;
103         executorService = new ThreadPoolExecutor(EXECUTOR_CORE_POOL_SIZE, EXECUTOR_MAXIMUM_POOL_SIZE,
104                 EXECUTOR_KEEP_ALIVE_TIME, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
105         this.cacheLoader = cacheLoader;
106     }
107 
108     /**
109      * Setter for the CacheLoader. Changing the CacheLoader takes immediate effect.
110      *
111      * @param cacheLoader the loader to dynamically load new cache entries
112      */
113     public void setCacheLoader(CacheLoader cacheLoader) {
114         this.cacheLoader = cacheLoader;
115     }
116 
117     /**
118      * Add a listener to the list of cache listeners. The behaviour of JCACHE and Ehcache listeners is a little
119      * different. See {@link JCacheListenerAdaptor} for details on how each event is adapted.
120      *
121      * @param cacheListener a JCACHE CacheListener
122      * @see JCacheListenerAdaptor
123      */
124     public void addListener(CacheListener cacheListener) {
125         JCacheListenerAdaptor cacheListenerAdaptor = new JCacheListenerAdaptor(cacheListener);
126         cache.getCacheEventNotificationService().registerListener(cacheListenerAdaptor);
127     }
128 
129     /**
130      * The evict method will remove objects from the cache that are no longer valid.
131      * Objects where the specified expiration time has been reached.
132      * <p/>
133      * This implementation synchronously checks each store for expired elements. Note that the DiskStore
134      * has an expiryThread that runs periodically to do the same thing, and that the MemoryStore lazily checks
135      * for expiry on overflow and peek, thus reducing the utility of calling this method.
136      */
137     public void evict() {
138         cache.evictExpiredElements();
139     }
140 
141     /**
142      * The getAll method will return, from the cache, a Map of the objects associated with the Collection of keys in argument "keys".
143      * If the objects are not in the cache, the associated cache loader will be called. If no loader is associated with an object,
144      * a null is returned. If a problem is encountered during the retrieving or loading of the objects, an exception will be thrown.
145      * If the "arg" argument is set, the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference
146      * the object. If no "arg" value is provided a null will be passed to the loadAll method. The storing of null values in the cache
147      * is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding the object in
148      * the cache. In both cases a null is returned.
149      * <p/>
150      * The Ehcache native API provides similar functionality to loaders using the
151      * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
152      * <p/>
153      * Note. If the getAll exceeds the maximum cache
154      * size, the returned map will necessarily be less than the number specified.
155      *
156      * @param keys
157      * @return a Map populated from the Cache. If there are no elements, an empty Map is returned.
158      */
159     public Map getAll(Collection keys) throws CacheException {
160 
161         if (keys == null) {
162             return new HashMap(0);
163         }
164         Map map = new HashMap(keys.size());
165 
166         if (cacheLoader != null) {
167             try {
168                 map = new HashMap(keys.size());
169                 List futures = new ArrayList(keys.size());
170 
171                 for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
172                     Object key = iterator.next();
173 
174                     if (cache.isKeyInCache(key)) {
175                         map.put(key, get(key));
176                     } else {
177                         futures.add(new KeyedFuture(key, asynchronousLoad(key)));
178                     }
179                 }
180 
181                 //now wait for everything to load.
182                 for (int i = 0; i < futures.size(); i++) {
183                     KeyedFuture keyedFuture = (KeyedFuture) futures.get(i);
184                     keyedFuture.future.get();
185                     Object key = keyedFuture.key;
186                     map.put(key, get(key));
187                 }
188 
189             } catch (ExecutionException e) {
190                 throw new CacheException(e.getMessage(), e);
191             } catch (InterruptedException e) {
192                 throw new CacheException(e.getMessage(), e);
193             }
194         } else {
195             for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
196                 Object key = iterator.next();
197                 map.put(key, get(key));
198             }
199         }
200         return map;
201     }
202 
203     /**
204      * Gets the CacheLoader registered in this cache
205      *
206      * @return the loader, or null if there is none
207      * @proposed addition to jsr107
208      */
209     public CacheLoader getCacheLoader() {
210         return cacheLoader;
211     }
212 
213     /**
214      * Used to store a future and the key it is in respect of
215      */
216     class KeyedFuture {
217 
218         private Object key;
219         private Future future;
220 
221         /**
222          * Full constructor
223          *
224          * @param key
225          * @param future
226          */
227         public KeyedFuture(Object key, Future future) {
228             this.key = key;
229             this.future = future;
230         }
231     }
232 
233     /**
234      * returns the CacheEntry object associated with the key.
235      *
236      * @param key the key to look for in the cache
237      */
238     public CacheEntry getCacheEntry(Object key) {
239         Element element = cache.get(key);
240         if (element != null) {
241             return new net.sf.ehcache.jcache.JCacheEntry(element);
242         } else {
243             return null;
244         }
245     }
246 
247     /**
248      * Gets an immutable Statistics object representing the Cache statistics at the time. How the statistics are calculated
249      * depends on the statistics accuracy setting. The only aspect of statistics sensitive to the accuracy setting is
250      * object size. How that is calculated is discussed below.
251      * <h3>Best Effort Size</h3>
252      * This result is returned when the statistics accuracy setting is {@link CacheStatistics#STATISTICS_ACCURACY_BEST_EFFORT}.
253      * <p/>
254      * The size is the number of {@link Element}s in the {@link net.sf.ehcache.store.MemoryStore} plus
255      * the number of {@link Element}s in the {@link net.sf.ehcache.store.DiskStore}.
256      * <p/>
257      * This number is the actual number of elements, including expired elements that have
258      * not been removed. Any duplicates between stores are accounted for.
259      * <p/>
260      * Expired elements are removed from the the memory store when
261      * getting an expired element, or when attempting to spool an expired element to
262      * disk.
263      * <p/>
264      * Expired elements are removed from the disk store when getting an expired element,
265      * or when the expiry thread runs, which is once every five minutes.
266      * <p/>
267      * <h3>Guaranteed Accuracy Size</h3>
268      * This result is returned when the statistics accuracy setting is {@link CacheStatistics#STATISTICS_ACCURACY_GUARANTEED}.
269      * <p/>
270      * This method accounts for elements which might be expired or duplicated between stores. It take approximately
271      * 200ms per 1000 elements to execute.
272      * <h3>Fast but non-accurate Size</h3>
273      * This result is returned when the statistics accuracy setting is {@link CacheStatistics#STATISTICS_ACCURACY_NONE}.
274      * <p/>
275      * The number given may contain expired elements. In addition if the DiskStore is used it may contain some double
276      * counting of elements. It takes 6ms for 1000 elements to execute. Time to execute is O(log n). 50,000 elements take
277      * 36ms.
278      *
279      * @return the number of elements in the ehcache, with a varying degree of accuracy, depending on accuracy setting.
280      * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
281      */
282     public CacheStatistics getCacheStatistics() throws IllegalStateException {
283         return new JCacheStatistics(cache.getStatistics());
284     }
285 
286     /**
287      * The load method provides a means to "pre load" the cache. This method will, asynchronously, load the specified
288      * object into the cache using the associated cacheloader. If the object already exists in the cache, no action is
289      * taken. If no loader is associated with the object, no object will be loaded into the cache. If a problem is
290      * encountered during the retrieving or loading of the object, an exception should be logged. If the "arg" argument
291      * is set, the arg object will be passed to the CacheLoader.load method. The cache will not dereference the object.
292      * If no "arg" value is provided a null will be passed to the load method. The storing of null values in the cache
293      * is permitted, however, the get method will not distinguish returning a null stored in the cache and not finding
294      * the object in the cache. In both cases a null is returned.
295      * <p/>
296      * The Ehcache native API provides similar functionality to loaders using the
297      * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
298      *
299      * @param key key whose associated value to be loaded using the associated cacheloader if this cache doesn't contain it.
300      */
301     public void load(final Object key) throws CacheException {
302         if (cacheLoader == null) {
303             if (LOG.isDebugEnabled()) {
304                 LOG.debug("The CacheLoader is null. Returning.");
305             }
306             return;
307         }
308 
309         boolean existsOnCall = cache.isKeyInCache(key);
310         if (existsOnCall) {
311             if (LOG.isDebugEnabled()) {
312                 LOG.debug("The key " + key + " exists in the cache. Returning.");
313             }
314             return;
315         }
316 
317         asynchronousLoad(key);
318     }
319 
320     /**
321      * Does the asynchronous loading.
322      *
323      * @return a Future which can be used to monitor execution
324      */
325     Future asynchronousLoad(final Object key) {
326         Future future = executorService.submit(new Runnable() {
327 
328             /**
329              * Calls the CacheLoader and puts the result in the Cache
330              */
331             public void run() {
332                 try {
333                     //Test to see if it has turned up in the meantime
334                     boolean existsOnRun = cache.isKeyInCache(key);
335                     if (!existsOnRun) {
336 
337                         Object value = cacheLoader.load(key);
338                         put(key, value);
339                     }
340                 } catch (CacheException e) {
341                     LOG.debug("CacheException during load. Load will not be completed. Cause was " + e.getCause(), e);
342                 }
343             }
344         });
345         return future;
346     }
347 
348     /**
349      * The loadAll method provides a means to "pre load" objects into the cache. This method will, asynchronously, load
350      * the specified objects into the cache using the associated cache loader. If the an object already exists in the
351      * cache, no action is taken. If no loader is associated with the object, no object will be loaded into the cache.
352      * If a problem is encountered during the retrieving or loading of the objects, an exception (to be defined)
353      * should be logged. The getAll method will return, from the cache, a Map of the objects associated with the
354      * Collection of keys in argument "keys". If the objects are not in the cache, the associated cache loader will be
355      * called. If no loader is associated with an object, a null is returned. If a problem is encountered during the
356      * retrieving or loading of the objects, an exception (to be defined) will be thrown. If the "arg" argument is set,
357      * the arg object will be passed to the CacheLoader.loadAll method. The cache will not dereference the object.
358      * If no "arg" value is provided a null will be passed to the loadAll method.
359      * <p/>
360      * keys - collection of the keys whose associated values to be loaded into this cache by using the associated
361      * cacheloader if this cache doesn't contain them.
362      * <p/>
363      * The Ehcache native API provides similar functionality to loaders using the
364      * decorator {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}
365      */
366     public void loadAll(final Collection keys) throws CacheException {
367 
368         if (cacheLoader == null) {
369             if (LOG.isDebugEnabled()) {
370                 LOG.debug("The CacheLoader is null. Returning.");
371             }
372             return;
373         }
374         if (keys == null) {
375             return;
376         }
377         asynchronousLoadAll(keys);
378     }
379 
380     /**
381      * Does the asynchronous loading.
382      *
383      * @return a Future which can be used to monitor execution
384      */
385     Future asynchronousLoadAll(final Collection keys) {
386         Future future = executorService.submit(new Runnable() {
387             /**
388              * Calls the CacheLoader and puts the result in the Cache
389              */
390             public void run() {
391                 try {
392                     List nonLoadedKeys = new ArrayList(keys.size());
393                     for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
394                         Object key = iterator.next();
395                         if (!cache.isKeyInCache(key)) {
396                             nonLoadedKeys.add(key);
397                         }
398                     }
399                     Map map = cacheLoader.loadAll(nonLoadedKeys);
400                     putAll(map);
401                 } catch (CacheException e) {
402                     LOG.debug("CacheException during load. Load will not be completed. Cause was " + e.getCause(), e);
403                 }
404             }
405         });
406         return future;
407     }
408 
409     /**
410      * The peek method will return the object associated with "key" if it currently exists (and is valid) in the cache.
411      * If not, a null is returned. With "peek" the CacheLoader will not be invoked and other caches in the system will not be searched.
412      * <p/>
413      * In ehcache peek bahaves the same way as {@link #get}
414      *
415      * @param key
416      * @return the value stored in the cache by key, or null if it does not exist
417      */
418     public Object peek(Object key) {
419         Element element = cache.get(key);
420         if (element != null) {
421             return element.getObjectValue();
422         } else {
423             return null;
424         }
425     }
426 
427     /**
428      * Remove a listener from the list of cache listeners
429      *
430      * @param cacheListener a JCACHE CacheListener
431      * @see JCacheListenerAdaptor
432      */
433     public void removeListener(CacheListener cacheListener) {
434         JCacheListenerAdaptor cacheListenerAdaptor = new JCacheListenerAdaptor(cacheListener);
435         cache.getCacheEventNotificationService().unregisterListener(cacheListenerAdaptor);
436     }
437 
438     /**
439      * Returns the number of key-value mappings in this map.  If the
440      * map contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
441      * <tt>Integer.MAX_VALUE</tt>.
442      *
443      * @return the number of key-value mappings in this map.
444      */
445     public int size() {
446         return cache.getSize();
447     }
448 
449     /**
450      * Returns <tt>true</tt> if this map contains no key-value mappings.
451      *
452      * @return <tt>true</tt> if this map contains no key-value mappings.
453      */
454     public boolean isEmpty() {
455         return (size() == 0);
456     }
457 
458     /**
459      * Returns <tt>true</tt> if this map contains a mapping for the specified
460      * key.  More formally, returns <tt>true</tt> if and only if
461      * this map contains a mapping for a key <tt>k</tt> such that
462      * <tt>(key==null ? k==null : key.equals(k))</tt>.  (There can be
463      * at most one such mapping.)
464      *
465      * @param key key whose presence in this map is to be tested.
466      * @return <tt>true</tt> if this map contains a mapping for the specified
467      *         key.
468      */
469     public boolean containsKey(Object key) {
470         return cache.isKeyInCache(key);
471     }
472 
473     /**
474      * Returns <tt>true</tt> if this map maps one or more keys to the
475      * specified value.  More formally, returns <tt>true</tt> if and only if
476      * this map contains at least one mapping to a value <tt>v</tt> such that
477      * <tt>(value==null ? v==null : value.equals(v))</tt>.  This operation
478      * will probably require time linear in the map size for most
479      * implementations of the <tt>Map</tt> interface.
480      * <p/>
481      * Warning: This method is extremely slow. Ehcache is designed for efficient
482      * retrieval using keys, not values.
483      *
484      * @param value value whose presence in this map is to be tested.
485      * @return <tt>true</tt> if this map maps one or more keys to the
486      *         specified value.
487      */
488     public boolean containsValue(Object value) {
489         long start = System.currentTimeMillis();
490 
491         boolean inCache = cache.isValueInCache(value);
492         long end = System.currentTimeMillis();
493 
494         if (LOG.isWarnEnabled()) {
495             LOG.warn("Performance Warning: containsValue is not recommended. This call took "
496                     + (end - start) + " ms");
497         }
498         return inCache;
499     }
500 
501     /**
502      * The get method will return, from the cache, the object associated with
503      * the argument "key".
504      * <p/>
505      * If the object is not in the cache, the associated
506      * cache loader will be called. If no loader is associated with the object,
507      * a null is returned.
508      * <p/>
509      * If a problem is encountered during the retrieving
510      * or loading of the object, an exception (to be defined) will be thrown. (Until it is
511      * defined, the ehcache implementation throws a RuntimeException.)
512      * <p/>
513      * If the "arg" argument is set, the arg object will be passed to the
514      * CacheLoader.load method.  The cache will not dereference the object.
515      * If no "arg" value is provided a null will be passed to the load method.
516      * <p/>
517      * The storing of null values in the cache is permitted, however, the get
518      * method will not distinguish returning a null stored in the cache and
519      * not finding the object in the cache. In both cases a null is returned.
520      * <p/>
521      * Cache statistics are only updated for the initial attempt to get the cached entry.
522      *
523      * @param key key whose associated value is to be returned.
524      * @return the value to which this map maps the specified key, or
525      *         <tt>null</tt> if the map contains no mapping for this key after an attempt has been
526      *         made to load it.
527      * @throws RuntimeException JSR107 should really throw a CacheException here, but the
528      *                          spec does not allow it. Instead throw a RuntimeException if the underlying load method
529      *                          throws a CacheException.
530      * @see #containsKey(Object)
531      * todo consider removing async stuff
532      */
533     public Object get(Object key) throws RuntimeException {
534 
535         Element element = cache.get(key);
536         if (element != null) {
537             return element.getObjectValue();
538         }
539 
540         if (cacheLoader == null) {
541             return null;
542         }
543 
544         try {
545             //only allow one thread to load the missing key
546             synchronized (key) {
547                 Future future = asynchronousLoad(key);
548                 //wait for result
549                 future.get();
550             }
551         } catch (Exception e) {
552             throw new RuntimeException("Exception on load", e);
553         }
554         element = cache.getQuiet(key);
555         if (element == null) {
556             return null;
557         } else {
558             return element.getObjectValue();
559         }
560     }
561 
562     /**
563      * Associates the specified value with the specified key in this map
564      * (optional operation).  If the map previously contained a mapping for
565      * this key, the old value is replaced by the specified value.  (A map
566      * <tt>m</tt> is said to contain a mapping for a key <tt>k</tt> if and only
567      * if {@link #containsKey(Object) m.containsKey(k)} would return
568      * <tt>true</tt>.))
569      *
570      * @param key   key with which the specified value is to be associated.
571      * @param value value to be associated with the specified key.
572      * @return previous value associated with specified key, or <tt>null</tt>
573      *         if there was no mapping for key.  A <tt>null</tt> return can
574      *         also indicate that the map previously associated <tt>null</tt>
575      *         with the specified key, if the implementation supports
576      *         <tt>null</tt> values.
577      */
578     public Object put(Object key, Object value) {
579         Element element = null;
580         if (cache.isKeyInCache(key)) {
581             element = cache.getQuiet(key);
582         }
583 
584         cache.put(new Element(key, value));
585 
586         if (element != null) {
587             return element.getObjectValue();
588         } else {
589             return null;
590         }
591     }
592 
593     /**
594      * Removes the mapping for this key from this map if it is present
595      * (optional operation).   More formally, if this map contains a mapping
596      * from key <tt>k</tt> to value <tt>v</tt> such that
597      * <code>(key==null ?  k==null : key.equals(k))</code>, that mapping
598      * is removed.  (The map can contain at most one such mapping.)
599      * <p/>
600      * <p>Returns the value to which the map previously associated the key, or
601      * <tt>null</tt> if the map contained no mapping for this key.  (A
602      * <tt>null</tt> return can also indicate that the map previously
603      * associated <tt>null</tt> with the specified key if the implementation
604      * supports <tt>null</tt> values.)  The map will not contain a mapping for
605      * the specified  key once the call returns.
606      *
607      * @param key key whose mapping is to be removed from the map.
608      * @return previous value associated with specified key, or <tt>null</tt>
609      *         if there was no mapping for key.
610      */
611     public Object remove(Object key) {
612         Element element = cache.get(key);
613         cache.remove(key);
614         if (element != null) {
615             return element.getObjectValue();
616         } else {
617             return null;
618         }
619     }
620 
621     /**
622      * Copies all of the mappings from the specified map to this map
623      * (optional operation).  The effect of this call is equivalent to that
624      * of calling {@link #put(Object,Object) put(k, v)} on this map once
625      * for each mapping from key <tt>k</tt> to value <tt>v</tt> in the
626      * N specified map.  The behavior of this operation is unspecified if the
627      * specified map is modified while the operation is in progress.
628      *
629      * @param sourceMap Mappings to be stored in this map.
630      */
631 
632     public void putAll(Map sourceMap) {
633         if (sourceMap == null) {
634             return;
635         }
636         for (Iterator iterator = sourceMap.keySet().iterator(); iterator.hasNext();) {
637             Object key = iterator.next();
638             cache.put(new Element(key, sourceMap.get(key)));
639         }
640     }
641 
642     /**
643      * Removes all mappings from this map (optional operation).
644      */
645     public void clear() {
646         cache.removeAll();
647     }
648 
649     /**
650      * Returns a set view of the keys contained in this map.  The set is
651      * not live because ehcache is not backed by a simple map.
652      *
653      * @return a set view of the keys contained in this map.
654      */
655     public Set keySet() {
656         List list = cache.getKeys();
657         Set set = new HashSet();
658         set.addAll(list);
659         return set;
660     }
661 
662     /**
663      * Returns a collection view of the values contained in this map.  The
664      * collection is backed by the map, so changes to the map are reflected in
665      * the collection, and vice-versa.  If the map is modified while an
666      * iteration over the collection is in progress (except through the
667      * iterator's own <tt>remove</tt> operation), the results of the
668      * iteration are undefined.  The collection supports element removal,
669      * which removes the corresponding mapping from the map, via the
670      * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
671      * <tt>removeAll</tt>, <tt>retainAll</tt> and <tt>clear</tt> operations.
672      * It does not support the add or <tt>addAll</tt> operations.
673      * <p/>
674      * Contradicting the above Map contract, whether cache changes after this method returns are not
675      * reflected in the Collection. This is because ehcache is not backed by a single map.
676      *
677      * @return a collection view of the values contained in this map.
678      */
679     public Collection values() {
680         List list = cache.getKeysNoDuplicateCheck();
681         Set set = new HashSet(list.size());
682         for (int i = 0; i < list.size(); i++) {
683             Object key = list.get(i);
684             Element element = cache.get(key);
685             if (element != null) {
686                 set.add(element.getObjectValue());
687             }
688         }
689         return set;
690     }
691 
692     /**
693      * Returns a set view of the mappings contained in this map.  Each element
694      * in the returned set is a {@link java.util.Map.Entry}.  The set is backed by the
695      * map, so changes to the map are reflected in the set, and vice-versa.
696      * If the map is modified while an iteration over the set is in progress
697      * (except through the iterator's own <tt>remove</tt> operation, or through
698      * the <tt>setValue</tt> operation on a map entry returned by the iterator)
699      * the results of the iteration are undefined.  The set supports element
700      * removal, which removes the corresponding mapping from the map, via the
701      * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>, <tt>removeAll</tt>,
702      * <tt>retainAll</tt> and <tt>clear</tt> operations.  It does not support
703      * the <tt>add</tt> or <tt>addAll</tt> operations.
704      * <p/>
705      * Contradicting the above Map contract, whether or not changes to an entry affect the entry in the cache is undefined.
706      *
707      * @return a set view of the mappings contained in this map.
708      */
709     public Set entrySet() {
710         List list = cache.getKeysNoDuplicateCheck();
711         Set set = new HashSet(list.size());
712         for (int i = 0; i < list.size(); i++) {
713             Object key = list.get(i);
714             Element element = cache.get(key);
715             if (element != null) {
716                 set.add(new net.sf.ehcache.jcache.JCacheEntry(element));
717             }
718         }
719         return set;
720     }
721 
722     /**
723      * Sets the statistics accuracy.
724      *
725      * @param statisticsAccuracy one of {@link CacheStatistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link CacheStatistics#STATISTICS_ACCURACY_GUARANTEED}, {@link CacheStatistics#STATISTICS_ACCURACY_NONE}
726      */
727     public void setStatisticsAccuracy(int statisticsAccuracy) {
728         cache.setStatisticsAccuracy(statisticsAccuracy);
729     }
730 
731 
732     /**
733      * Gets the backing Ehcache
734      */
735     public Ehcache getBackingCache() {
736         return cache;
737     }
738 
739 
740     /**
741      * Returns a {@link String} representation of the underlying {@link net.sf.ehcache.Cache}.
742      *
743      * @return a string representation of the object.
744      */
745     public String toString() {
746         return cache.toString();
747     }
748 
749 
750     /**
751      * Gets the executor service. This is not publically accessible.
752      */
753     ThreadPoolExecutor getExecutorService() {
754         return executorService;
755     }
756 }