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
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
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
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
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
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
570 }
571
572 /***
573 * {@inheritDoc}
574 */
575 public void timeToLiveChanged(long oldTtl, long newTtl) {
576
577 }
578
579 /***
580 * {@inheritDoc}
581 */
582 public void diskCapacityChanged(int oldCapacity, int newCapacity) {
583
584 }
585
586 /***
587 * {@inheritDoc}
588 */
589 public void loggingChanged(boolean oldValue, boolean newValue) {
590
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
605 }
606
607 /***
608 * {@inheritDoc}
609 */
610 public void deregistered(CacheConfiguration config) {
611
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