View Javadoc

1   /***
2    *  Copyright 2003-2010 Terracotta, Inc.
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.store;
18  
19  import net.sf.ehcache.CacheEntry;
20  import net.sf.ehcache.CacheException;
21  import net.sf.ehcache.Ehcache;
22  import net.sf.ehcache.Element;
23  import net.sf.ehcache.Status;
24  import net.sf.ehcache.concurrent.CacheLockProvider;
25  import net.sf.ehcache.concurrent.ReadWriteLockSync;
26  import net.sf.ehcache.concurrent.Sync;
27  import net.sf.ehcache.config.CacheConfiguration;
28  import net.sf.ehcache.config.CacheConfigurationListener;
29  import net.sf.ehcache.config.PinningConfiguration;
30  import net.sf.ehcache.pool.Pool;
31  import net.sf.ehcache.pool.PoolAccessor;
32  import net.sf.ehcache.pool.PoolableStore;
33  import net.sf.ehcache.store.chm.SelectableConcurrentHashMap;
34  import net.sf.ehcache.store.disk.StoreUpdateException;
35  import net.sf.ehcache.util.ratestatistics.AtomicRateStatistic;
36  import net.sf.ehcache.util.ratestatistics.RateStatistic;
37  import net.sf.ehcache.writer.CacheWriterManager;
38  import org.slf4j.Logger;
39  import org.slf4j.LoggerFactory;
40  
41  import java.util.ArrayList;
42  import java.util.Collection;
43  import java.util.List;
44  import java.util.concurrent.TimeUnit;
45  import java.util.concurrent.locks.Lock;
46  
47  /***
48   * A Store implementation suitable for fast, concurrent in memory stores. The policy is determined by that
49   * configured in the cache.
50   *
51   * @author <a href="mailto:ssuravarapu@users.sourceforge.net">Surya Suravarapu</a>
52   * @version $Id: MemoryStore.html 13146 2011-08-01 17:12:39Z oletizi $
53   */
54  public final class MemoryStore extends AbstractStore implements TierableStore, PoolableStore, CacheConfigurationListener {
55  
56      /***
57       * This is the default from {@link java.util.concurrent.ConcurrentHashMap}. It should never be used, because we size
58       * the map to the max size of the store.
59       */
60      static final float DEFAULT_LOAD_FACTOR = 0.75f;
61  
62      /***
63       * Set optimisation for 100 concurrent threads.
64       */
65      private static final int CONCURRENCY_LEVEL = 100;
66  
67      private static final int MAX_EVICTION_RATIO = 5;
68  
69      private static final Logger LOG = LoggerFactory.getLogger(MemoryStore.class.getName());
70  
71      private final boolean alwaysPutOnHeap;
72  
73      /***
74       * The cache this store is associated with.
75       */
76      private final Ehcache cache;
77  
78      /***
79       * Map where items are stored by key.
80       */
81      private final SelectableConcurrentHashMap map;
82  
83      private final RateStatistic hitRate = new AtomicRateStatistic(1000, TimeUnit.MILLISECONDS);
84      private final RateStatistic missRate = new AtomicRateStatistic(1000, TimeUnit.MILLISECONDS);
85  
86      /***
87       * The maximum size of the store (0 == no limit)
88       */
89      private volatile int maximumSize;
90  
91      /***
92       * status.
93       */
94      private volatile Status status;
95  
96      /***
97       * The eviction policy to use
98       */
99      private volatile Policy policy;
100 
101     /***
102      * The pool accessor
103      */
104     private volatile PoolAccessor poolAccessor;
105 
106     private volatile CacheLockProvider lockProvider;
107 
108     private volatile boolean cachePinned;
109 
110     /***
111      * Constructs things that all MemoryStores have in common.
112      *
113      * @param cache the cache
114      * @param pool the pool tracking the on-heap usage
115      */
116     private MemoryStore(final Ehcache cache, Pool pool) {
117         status = Status.STATUS_UNINITIALISED;
118         this.cache = cache;
119         this.maximumSize = cache.getCacheConfiguration().getMaxElementsInMemory();
120         this.policy = determineEvictionPolicy(cache);
121 
122         // create the CHM with initialCapacity sufficient to hold maximumSize
123         int initialCapacity = getInitialCapacityForLoadFactor(maximumSize, DEFAULT_LOAD_FACTOR);
124         map = new SelectableConcurrentHashMap(initialCapacity, DEFAULT_LOAD_FACTOR, CONCURRENCY_LEVEL);
125 
126         this.poolAccessor = pool.createPoolAccessor(this);
127 
128         this.alwaysPutOnHeap = getAdvancedBooleanConfigProperty("alwaysPutOnHeap", cache.getCacheConfiguration().getName(), false);
129         this.cachePinned = determineCachePinned(cache.getCacheConfiguration());
130 
131         status = Status.STATUS_ALIVE;
132 
133         if (LOG.isDebugEnabled()) {
134             LOG.debug("Initialized " + this.getClass().getName() + " for " + cache.getName());
135         }
136     }
137 
138     private boolean determineCachePinned(CacheConfiguration cacheConfiguration) {
139         PinningConfiguration pinningConfiguration = cacheConfiguration.getPinningConfiguration();
140         if (pinningConfiguration == null) {
141             return false;
142         }
143 
144         switch (pinningConfiguration.getStore()) {
145             case LOCALHEAP:
146                 return true;
147 
148             case LOCALMEMORY:
149                 return !cacheConfiguration.isOverflowToOffHeap();
150 
151             case INCACHE:
152                 return !(cacheConfiguration.isOverflowToOffHeap() || cacheConfiguration.isOverflowToDisk() || cacheConfiguration.isDiskPersistent());
153 
154             default:
155                 throw new IllegalArgumentException();
156         }
157     }
158 
159     /***
160      * Calculates the initialCapacity for a desired maximumSize goal and loadFactor.
161      *
162      * @param maximumSizeGoal the desired maximum size goal
163      * @param loadFactor      the load factor
164      * @return the calculated initialCapacity. Returns 0 if the parameter <tt>maximumSizeGoal</tt> is less than or equal
165      *         to 0
166      */
167     static int getInitialCapacityForLoadFactor(int maximumSizeGoal, float loadFactor) {
168         double actualMaximum = Math.ceil(maximumSizeGoal / loadFactor);
169         return Math.max(0, actualMaximum >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) actualMaximum);
170     }
171 
172     /***
173      * A factory method to create a MemoryStore.
174      *
175      * @param cache the cache
176      * @param pool the pool tracking the on-heap usage
177      * @return an instance of a MemoryStore, configured with the appropriate eviction policy
178      */
179     public static MemoryStore create(final Ehcache cache, Pool pool) {
180         MemoryStore memoryStore = new MemoryStore(cache, pool);
181         cache.getCacheConfiguration().addConfigurationListener(memoryStore);
182         return memoryStore;
183     }
184 
185     private boolean isPinningEnabled(Element element) {
186         return cachePinned || element.isPinned();
187     }
188 
189     /***
190      * {@inheritDoc}
191      */
192     public void fill(Element element) {
193         if (alwaysPutOnHeap || isPinningEnabled(element) || remove(element.getObjectKey()) != null || canPutWithoutEvicting(element)) {
194             put(element);
195         }
196     }
197 
198     /***
199      * Puts an item in the store. Note that this automatically results in an eviction if the store is full.
200      *
201      * @param element the element to add
202      */
203     public boolean put(final Element element) throws CacheException {
204         if (element == null) {
205             return false;
206         }
207 
208         if (poolAccessor.add(element.getObjectKey(), element.getObjectValue(), map.storedObject(element), isPinningEnabled(element)) > -1) {
209             return putInternal(element, null);
210         } else {
211             remove(element.getObjectKey());
212             cache.getCacheEventNotificationService().notifyElementEvicted(element, false);
213             return true;
214         }
215     }
216 
217     /***
218      * {@inheritDoc}
219      */
220     public final boolean putWithWriter(Element element, CacheWriterManager writerManager) throws CacheException {
221         if (poolAccessor.add(element.getObjectKey(), element.getObjectValue(), map.storedObject(element), isPinningEnabled(element)) > -1) {
222             return putInternal(element, writerManager);
223         } else {
224             removeInternal(element.getObjectKey(), null);
225             cache.getCacheEventNotificationService().notifyElementEvicted(element, false);
226             return true;
227         }
228     }
229 
230     private boolean putInternal(Element element, CacheWriterManager writerManager) throws CacheException {
231         if (element == null) {
232             return true;
233         } else {
234             Element old = map.put(element.getObjectKey(), element);
235             if (old != null) {
236                 poolAccessor.delete(old.getObjectKey(), old.getObjectValue(), map.storedObject(old));
237             }
238             if (writerManager != null) {
239                 try {
240                     writerManager.put(element);
241                 } catch (RuntimeException e) {
242                     throw new StoreUpdateException(e, old != null);
243                 }
244             }
245             checkCapacity(element);
246             return old == null;
247         }
248     }
249 
250     /***
251      * Gets an item from the cache.
252      * <p/>
253      * The last access time in {@link net.sf.ehcache.Element} is updated.
254      *
255      * @param key the key of the Element
256      * @return the element, or null if there was no match for the key
257      */
258     public final Element get(final Object key) {
259         if (key == null) {
260             return null;
261         } else {
262             Element e = map.get(key);
263             if (e == null) {
264                 missRate.event();
265             } else {
266                 hitRate.event();
267             }
268             return e;
269         }
270     }
271 
272     /***
273      * Gets an item from the cache, without updating statistics.
274      *
275      * @param key the cache key
276      * @return the element, or null if there was no match for the key
277      */
278     public final Element getQuiet(Object key) {
279         return get(key);
280     }
281 
282     /***
283      * Removes an Element from the store.
284      *
285      * @param key the key of the Element, usually a String
286      * @return the Element if one was found, else null
287      */
288     public Element remove(final Object key) {
289         return removeInternal(key, null);
290     }
291 
292     /***
293      * {@inheritDoc}
294      */
295     public final Element removeWithWriter(Object key, CacheWriterManager writerManager) throws CacheException {
296         return removeInternal(key, writerManager);
297     }
298 
299     private Element removeInternal(Object key, CacheWriterManager writerManager) throws CacheException {
300 
301         if (key == null) {
302             return null;
303         }
304 
305         // remove single item.
306         Element element = map.remove(key);
307         if (writerManager != null) {
308             writerManager.remove(new CacheEntry(key, element));
309         }
310         if (element != null) {
311             poolAccessor.delete(element.getObjectKey(), element.getObjectValue(), map.storedObject(element));
312             return element;
313         } else {
314             if (LOG.isDebugEnabled()) {
315                 LOG.debug(cache.getName() + "Cache: Cannot remove entry as key " + key + " was not found");
316             }
317             return null;
318         }
319     }
320 
321     /***
322      * Memory stores are never backed up and always return false
323      */
324     public final boolean bufferFull() {
325         return false;
326     }
327 
328     /***
329      * Expire all elements.
330      * <p/>
331      * This is a default implementation which does nothing. Expiration on demand is only implemented for disk stores.
332      */
333     public void expireElements() {
334         // empty implementation
335     }
336 
337     /***
338      * Chooses the Policy from the cache configuration
339      * @param cache the cache
340      * @return the chosen eviction policy
341      */
342     private static Policy determineEvictionPolicy(Ehcache cache) {
343         MemoryStoreEvictionPolicy policySelection = cache.getCacheConfiguration().getMemoryStoreEvictionPolicy();
344 
345         if (policySelection.equals(MemoryStoreEvictionPolicy.LRU)) {
346             return new LruPolicy();
347         } else if (policySelection.equals(MemoryStoreEvictionPolicy.FIFO)) {
348             return new FifoPolicy();
349         } else if (policySelection.equals(MemoryStoreEvictionPolicy.LFU)) {
350             return new LfuPolicy();
351         }
352 
353         throw new IllegalArgumentException(policySelection + " isn't a valid eviction policy");
354     }
355 
356     /***
357      * Remove all of the elements from the store.
358      */
359     public final void removeAll() throws CacheException {
360         map.clear();
361         poolAccessor.clear();
362     }
363 
364     /***
365      * Prepares for shutdown.
366      */
367     public synchronized void dispose() {
368         if (status.equals(Status.STATUS_SHUTDOWN)) {
369             return;
370         }
371         status = Status.STATUS_SHUTDOWN;
372         flush();
373         poolAccessor.unlink();
374     }
375 
376     /***
377      * Flush to disk only if the cache is diskPersistent.
378      */
379     public final void flush() {
380         if (cache.getCacheConfiguration().isClearOnFlush()) {
381             removeAll();
382         }
383     }
384 
385     /***
386      * Gets an Array of the keys for all elements in the memory cache.
387      * <p/>
388      * Does not check for expired entries
389      *
390      * @return An List
391      */
392     public final List<?> getKeys() {
393         return new ArrayList<Object>(map.keySet());
394     }
395 
396     /***
397      * Returns the current store size.
398      *
399      * @return The size value
400      */
401     public final int getSize() {
402         return map.size();
403     }
404 
405     /***
406      * Returns nothing since a disk store isn't clustered
407      *
408      * @return returns 0
409      */
410     public final int getTerracottaClusteredSize() {
411         return 0;
412     }
413 
414     /***
415      * A check to see if a key is in the Store. No check is made to see if the Element is expired.
416      *
417      * @param key The Element key
418      * @return true if found. If this method return false, it means that an Element with the given key is definitely not
419      *         in the MemoryStore. If it returns true, there is an Element there. An attempt to get it may return null if
420      *         the Element has expired.
421      */
422     public final boolean containsKey(final Object key) {
423         return map.containsKey(key);
424     }
425 
426     /***
427      * Evict the <code>Element</code>.
428      * <p/>
429      * Evict means that the <code>Element</code> is:
430      * <ul>
431      * <li>if, the store is diskPersistent, the <code>Element</code> is spooled to the DiskStore
432      * <li>if not, the <code>Element</code> is removed.
433      * </ul>
434      *
435      * @param element the <code>Element</code> to be evicted.
436      */
437     private void notifyEviction(final Element element) {
438         cache.getCacheEventNotificationService().notifyElementEvicted(element, false);
439     }
440 
441     /***
442      * Before eviction elements are checked.
443      *
444      * @param element the element to notify about its expiry
445      */
446     private void notifyExpiry(final Element element) {
447         cache.getCacheEventNotificationService().notifyElementExpiry(element, false);
448     }
449 
450     /***
451      * An algorithm to tell if the MemoryStore is at or beyond its carrying capacity.
452      *
453      * @return true if the store is full, false otherwise
454      */
455     public final boolean isFull() {
456         return maximumSize > 0 && map.quickSize() >= maximumSize;
457     }
458 
459     /***
460      * Check if adding an element won't provoke an eviction.
461      *
462      * @param element the element
463      * @return true if the element can be added without provoking an eviction.
464      */
465     public final boolean canPutWithoutEvicting(Element element) {
466         if (element == null) {
467             return true;
468         }
469 
470         return !isFull() && poolAccessor.canAddWithoutEvicting(element.getObjectKey(), element.getObjectValue(), map.storedObject(element));
471     }
472 
473     /***
474      * If the store is over capacity, evict elements until capacity is reached
475      *
476      * @param elementJustAdded the element added by the action calling this check
477      */
478     private void checkCapacity(final Element elementJustAdded) {
479         if (maximumSize > 0) {
480             int evict = Math.min(map.quickSize() - maximumSize, MAX_EVICTION_RATIO);
481             for (int i = 0; i < evict; i++) {
482                 removeElementChosenByEvictionPolicy(elementJustAdded);
483             }
484         }
485     }
486 
487     /***
488      * Removes the element chosen by the eviction policy
489      *
490      * @param elementJustAdded it is possible for this to be null
491      * @return true if an element was removed, false otherwise.
492      */
493     private boolean removeElementChosenByEvictionPolicy(final Element elementJustAdded) {
494 
495         LOG.debug("Cache is full. Removing element ...");
496 
497         Element element = findEvictionCandidate(elementJustAdded);
498         if (element == null) {
499             LOG.debug("Eviction selection miss. Selected element is null");
500             return false;
501         }
502 
503         // If the element is expired, remove
504         if (element.isExpired()) {
505             remove(element.getObjectKey());
506             notifyExpiry(element);
507             return true;
508         }
509 
510         if (cachePinned) {
511             return false;
512         }
513 
514         if (remove(element.getObjectKey()) != null) {
515             notifyEviction(element);
516         }
517         return true;
518     }
519 
520     /***
521      * Find a "relatively" unused element.
522      *
523      * @param elementJustAdded the element added by the action calling this check
524      * @return the element chosen as candidate for eviction
525      */
526     private Element findEvictionCandidate(final Element elementJustAdded) {
527         Object objectKey = elementJustAdded != null ? elementJustAdded.getObjectKey() : null;
528         Element[] elements = sampleElements(objectKey);
529         // this can return null. Let the cache get bigger by one.
530         return policy.selectedBasedOnPolicy(elements, elementJustAdded);
531     }
532 
533     /***
534      * Uses random numbers to sample the entire map.
535      * <p/>
536      * This implemenation uses a key array.
537      *
538      * @param keyHint a key used as a hint indicating where the just added element is
539      * @return a random sample of elements
540      */
541     private Element[] sampleElements(Object keyHint) {
542         int size = AbstractPolicy.calculateSampleSize(map.quickSize());
543         return map.getRandomValues(size, keyHint);
544     }
545 
546     /***
547      * {@inheritDoc}
548      */
549     public Object getInternalContext() {
550         if (lockProvider != null) {
551             return lockProvider;
552         } else {
553             lockProvider = new LockProvider();
554             return lockProvider;
555         }
556     }
557 
558     /***
559      * Gets the status of the MemoryStore.
560      */
561     public final Status getStatus() {
562         return status;
563     }
564 
565     /***
566      * {@inheritDoc}
567      */
568     public void timeToIdleChanged(long oldTti, long newTti) {
569         // no-op
570     }
571 
572     /***
573      * {@inheritDoc}
574      */
575     public void timeToLiveChanged(long oldTtl, long newTtl) {
576         // no-op
577     }
578 
579     /***
580      * {@inheritDoc}
581      */
582     public void diskCapacityChanged(int oldCapacity, int newCapacity) {
583         // no-op
584     }
585 
586     /***
587      * {@inheritDoc}
588      */
589     public void loggingChanged(boolean oldValue, boolean newValue) {
590         // no-op
591     }
592 
593     /***
594      * {@inheritDoc}
595      */
596     public void memoryCapacityChanged(int oldCapacity, int newCapacity) {
597         maximumSize = newCapacity;
598     }
599 
600     /***
601      * {@inheritDoc}
602      */
603     public void registered(CacheConfiguration config) {
604         // no-op
605     }
606 
607     /***
608      * {@inheritDoc}
609      */
610     public void deregistered(CacheConfiguration config) {
611         // no-op
612     }
613 
614     /***
615      * {@inheritDoc}
616      */
617     public boolean containsKeyInMemory(Object key) {
618         return containsKey(key);
619     }
620 
621     /***
622      * {@inheritDoc}
623      */
624     public boolean containsKeyOffHeap(Object key) {
625         return false;
626     }
627 
628     /***
629      * {@inheritDoc}
630      */
631     public boolean containsKeyOnDisk(Object key) {
632         return false;
633     }
634 
635     /***
636      * {@inheritDoc}
637      */
638     public Policy getInMemoryEvictionPolicy() {
639         return policy;
640     }
641 
642     /***
643      * {@inheritDoc}
644      */
645     public int getInMemorySize() {
646         return getSize();
647     }
648 
649     /***
650      * {@inheritDoc}
651      */
652     public long getInMemorySizeInBytes() {
653         if (poolAccessor.getSize() < 0) {
654             long sizeInBytes = 0;
655             for (Object o : map.values()) {
656                 Element element = (Element) o;
657                 if (element != null) {
658                     sizeInBytes += element.getSerializedSize();
659                 }
660             }
661             return sizeInBytes;
662         }
663         return poolAccessor.getSize();
664     }
665 
666     /***
667      * {@inheritDoc}
668      */
669     public int getOffHeapSize() {
670         return 0;
671     }
672 
673     /***
674      * {@inheritDoc}
675      */
676     public long getOffHeapSizeInBytes() {
677         return 0;
678     }
679 
680     /***
681      * {@inheritDoc}
682      */
683     public int getOnDiskSize() {
684         return 0;
685     }
686 
687     /***
688      * {@inheritDoc}
689      */
690     public long getOnDiskSizeInBytes() {
691         return 0;
692     }
693 
694     /***
695      * {@inheritDoc}
696      */
697     public void setInMemoryEvictionPolicy(Policy policy) {
698         this.policy = policy;
699     }
700 
701     /***
702      * {@inheritDoc}
703      */
704     public Element putIfAbsent(Element element) throws NullPointerException {
705         if (element == null) {
706             return null;
707         }
708 
709         if (poolAccessor.add(element.getObjectKey(), element.getObjectValue(), map.storedObject(element), isPinningEnabled(element)) > -1) {
710             Element old = map.putIfAbsent(element.getObjectKey(), element);
711             if (old == null) {
712                 checkCapacity(element);
713             } else {
714                 poolAccessor.delete(element.getObjectKey(), element.getObjectValue(), map.storedObject(element));
715             }
716             return old;
717         } else {
718             remove(element.getObjectKey());
719             cache.getCacheEventNotificationService().notifyElementEvicted(element, false);
720             return null;
721         }
722     }
723 
724     /***
725      * {@inheritDoc}
726      */
727     public Element removeElement(Element element, ElementValueComparator comparator) throws NullPointerException {
728         if (element == null || element.getObjectKey() == null) {
729             return null;
730         }
731 
732         Object key = element.getObjectKey();
733 
734         Lock lock = getWriteLock(key);
735         lock.lock();
736         try {
737             Element toRemove = map.get(key);
738             if (comparator.equals(element, toRemove)) {
739                 map.remove(key);
740                 poolAccessor.delete(toRemove.getObjectKey(), toRemove.getObjectValue(), map.storedObject(toRemove));
741                 return toRemove;
742             } else {
743                 return null;
744             }
745         } finally {
746             lock.unlock();
747         }
748     }
749 
750     /***
751      * {@inheritDoc}
752      */
753     public boolean replace(Element old, Element element, ElementValueComparator comparator) throws NullPointerException,
754             IllegalArgumentException {
755         if (element == null || element.getObjectKey() == null) {
756             return false;
757         }
758 
759         Object key = element.getObjectKey();
760 
761         if (poolAccessor.add(element.getObjectKey(), element.getObjectValue(), map.storedObject(element), isPinningEnabled(element)) > -1) {
762             Lock lock = getWriteLock(key);
763             lock.lock();
764             try {
765                 Element toRemove = map.get(key);
766                 if (comparator.equals(old, toRemove)) {
767                     map.put(key, element);
768                     poolAccessor.delete(toRemove.getObjectKey(), toRemove.getObjectValue(), map.storedObject(toRemove));
769                     return true;
770                 } else {
771                     poolAccessor.delete(element.getObjectKey(), element.getObjectValue(), map.storedObject(element));
772                     return false;
773                 }
774             } finally {
775                 lock.unlock();
776             }
777         } else {
778             Element removed = remove(element.getObjectKey());
779             if (removed != null) {
780                 cache.getCacheEventNotificationService().notifyElementEvicted(removed, false);
781             }
782             return false;
783         }
784     }
785 
786     /***
787      * {@inheritDoc}
788      */
789     public Element replace(Element element) throws NullPointerException {
790         if (element == null || element.getObjectKey() == null) {
791             return null;
792         }
793 
794         Object key = element.getObjectKey();
795 
796         if (poolAccessor.add(element.getObjectKey(), element.getObjectValue(), map.storedObject(element), isPinningEnabled(element)) > -1) {
797             Lock lock = getWriteLock(key);
798             lock.lock();
799             try {
800                 Element toRemove = map.get(key);
801                 if (toRemove != null) {
802                     map.put(key, element);
803                     poolAccessor.delete(toRemove.getObjectKey(), toRemove.getObjectValue(), map.storedObject(toRemove));
804                     return toRemove;
805                 } else {
806                     poolAccessor.delete(element.getObjectKey(), element.getObjectValue(), map.storedObject(element));
807                     return null;
808                 }
809             } finally {
810                 lock.unlock();
811             }
812         } else {
813             Element removed = remove(element.getObjectKey());
814             if (removed != null) {
815                 cache.getCacheEventNotificationService().notifyElementEvicted(removed, false);
816             }
817             return null;
818         }
819     }
820 
821     /***
822      * {@inheritDoc}
823      */
824     public Object getMBean() {
825         return null;
826     }
827 
828     /***
829      * {@inheritDoc}
830      */
831     public boolean evictFromOnHeap(int count, long size) {
832         for (int i = 0; i < count; i++) {
833             boolean removed = removeElementChosenByEvictionPolicy(null);
834             if (!removed) {
835                 return false;
836             }
837         }
838         return true;
839     }
840 
841     /***
842      * {@inheritDoc}
843      */
844     public boolean evictFromOnDisk(int count, long size) {
845         return false;
846     }
847 
848     /***
849      * {@inheritDoc}
850      */
851     public float getApproximateDiskHitRate() {
852         return 0;
853     }
854 
855     /***
856      * {@inheritDoc}
857      */
858     public float getApproximateDiskMissRate() {
859         return 0;
860     }
861 
862     /***
863      * {@inheritDoc}
864      */
865     public long getApproximateDiskCountSize() {
866         return 0;
867     }
868 
869     /***
870      * {@inheritDoc}
871      */
872     public long getApproximateDiskByteSize() {
873         return 0;
874     }
875 
876     /***
877      * {@inheritDoc}
878      */
879     public float getApproximateHeapHitRate() {
880         return hitRate.getRate();
881     }
882 
883     /***
884      * {@inheritDoc}
885      */
886     public float getApproximateHeapMissRate() {
887         return missRate.getRate();
888     }
889 
890     /***
891      * {@inheritDoc}
892      */
893     public long getApproximateHeapCountSize() {
894         return map.quickSize();
895     }
896 
897     /***
898      * {@inheritDoc}
899      */
900     public long getApproximateHeapByteSize() {
901         return poolAccessor.getSize();
902     }
903 
904     private Lock getWriteLock(Object key) {
905         return map.lockFor(key).writeLock();
906     }
907 
908     /***
909      * Get a collection of the elements in this store
910      *
911      * @return element collection
912      */
913     public Collection<Element> elementSet() {
914         return map.values();
915     }
916 
917     /***
918      * LockProvider implementation that uses the segment locks.
919      */
920     private class LockProvider implements CacheLockProvider {
921 
922         /***
923          * {@inheritDoc}
924          */
925         public Sync getSyncForKey(Object key) {
926             return new ReadWriteLockSync(map.lockFor(key));
927         }
928     }
929 
930     private static boolean getAdvancedBooleanConfigProperty(String property, String cacheName, boolean defaultValue) {
931         String globalPropertyKey = "net.sf.ehcache.store.config." + property;
932         String cachePropertyKey = "net.sf.ehcache.store." + cacheName + ".config." + property;
933         return Boolean.parseBoolean(System.getProperty(cachePropertyKey, System.getProperty(globalPropertyKey, Boolean.toString(defaultValue))));
934     }
935 }
936