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 }