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 java.io.IOException;
20  import java.util.List;
21  import java.util.concurrent.locks.Lock;
22  import java.util.concurrent.locks.ReadWriteLock;
23  
24  import net.sf.ehcache.CacheException;
25  import net.sf.ehcache.Element;
26  import net.sf.ehcache.Status;
27  import net.sf.ehcache.concurrent.LockType;
28  import net.sf.ehcache.concurrent.ReadWriteLockSync;
29  import net.sf.ehcache.concurrent.StripedReadWriteLockSync;
30  import net.sf.ehcache.store.compound.ReadWriteCopyStrategy;
31  import net.sf.ehcache.writer.CacheWriterManager;
32  
33  /***
34   * Abstract class for stores which combine two other stores, one caching the other (aka authority)'s elements.
35   *
36   * @param <T> the cache tier store type
37   * @param <U> the authority tier store type
38   * @author Chris Dennis
39   * @author Ludovic Orban
40   */
41  public abstract class FrontEndCacheTier<T extends TierableStore, U extends TierableStore> extends AbstractStore {
42  
43      private static final int DEFAULT_LOCK_STRIPE_COUNT = 128;
44  
45      /***
46       * The cache tier store
47       */
48      protected final T cache;
49  
50      /***
51       * The authority tier store
52       */
53      protected final U authority;
54  
55      private final StripedReadWriteLockSync masterLocks = new StripedReadWriteLockSync(DEFAULT_LOCK_STRIPE_COUNT);
56      private final boolean copyOnRead;
57      private final boolean copyOnWrite;
58      private final ReadWriteCopyStrategy<Element> copyStrategy;
59  
60      /***
61       * Constructor for FrontEndCacheTier
62       *
63       * @param cache the caching tier
64       * @param authority the authority tier
65       * @param copyStrategy the copyStrategy to use
66       * @param copyOnWrite whether to copy on writes, false otherwise
67       * @param copyOnRead whether to copy on reads, false otherwise
68       */
69      public FrontEndCacheTier(T cache, U authority, ReadWriteCopyStrategy<Element> copyStrategy, boolean copyOnWrite, boolean copyOnRead) {
70          this.cache = cache;
71          this.authority = authority;
72          this.copyStrategy = copyStrategy;
73          this.copyOnWrite = copyOnWrite;
74          this.copyOnRead = copyOnRead;
75      }
76  
77      /***
78       * Perform copy on read on an element if configured
79       *
80       * @param element the element to copy for read
81       * @return a copy of the element with the reconstructed original value
82       */
83      protected Element copyElementForReadIfNeeded(Element element) {
84          if (copyOnRead && copyOnWrite) {
85              return copyStrategy.copyForRead(element);
86          } else if (copyOnRead) {
87              return copyStrategy.copyForRead(copyStrategy.copyForWrite(element));
88          } else {
89              return element;
90          }
91      }
92  
93      /***
94       * Perform copy on write on an element if configured
95       *
96       * @param element the element to copy for write
97       * @return a copy of the element with a storage-ready value
98       */
99      protected Element copyElementForWriteIfNeeded(Element element) {
100         if (copyOnRead && copyOnWrite) {
101             return copyStrategy.copyForWrite(element);
102         } else if (copyOnWrite) {
103             return copyStrategy.copyForRead(copyStrategy.copyForWrite(element));
104         } else {
105             return element;
106         }
107     }
108 
109     /***
110      * {@inheritDoc}
111      */
112     public Element get(Object key) {
113         if (key == null) {
114             return null;
115         }
116 
117         Lock lock = getLockFor(key).readLock();
118         lock.lock();
119         try {
120             Element e = cache.get(key);
121             if (e == null) {
122                 e = authority.get(key);
123                 if (e != null) {
124                     cache.put(e);
125                 }
126             }
127             return copyElementForReadIfNeeded(e);
128         } finally {
129             lock.unlock();
130         }
131     }
132 
133     /***
134      * {@inheritDoc}
135      */
136     public Element getQuiet(Object key) {
137         if (key == null) {
138             return null;
139         }
140 
141         Lock lock = getLockFor(key).readLock();
142         lock.lock();
143         try {
144             Element e = cache.getQuiet(key);
145             if (e == null) {
146                 e = authority.getQuiet(key);
147                 if (e != null) {
148                     cache.put(e);
149                 }
150             }
151             return copyElementForReadIfNeeded(e);
152         } finally {
153             lock.unlock();
154         }
155     }
156 
157     /***
158      * {@inheritDoc}
159      */
160     public boolean put(Element e) {
161         if (e == null) {
162             return true;
163         }
164 
165         Object key = e.getObjectKey();
166 
167         Lock lock = getLockFor(key).writeLock();
168         lock.lock();
169         try {
170             Element copy = copyElementForWriteIfNeeded(e);
171             cache.fill(copy);
172             return authority.put(copy);
173         } finally {
174             lock.unlock();
175         }
176     }
177 
178     /***
179      * {@inheritDoc}
180      */
181     public boolean putWithWriter(Element e, CacheWriterManager writer) {
182         if (e == null) {
183             return true;
184         }
185 
186         Object key = e.getObjectKey();
187 
188         Lock lock = getLockFor(key).writeLock();
189         lock.lock();
190         try {
191             Element copy = copyElementForWriteIfNeeded(e);
192             cache.fill(copy);
193             return authority.putWithWriter(copy, writer);
194         } finally {
195             lock.unlock();
196         }
197     }
198 
199     /***
200      * {@inheritDoc}
201      */
202     public Element remove(Object key) {
203         if (key == null) {
204             return null;
205         }
206 
207         Lock lock = getLockFor(key).writeLock();
208         lock.lock();
209         try {
210             cache.remove(key);
211             return copyElementForReadIfNeeded(authority.remove(key));
212         } finally {
213             lock.unlock();
214         }
215     }
216 
217     /***
218      * {@inheritDoc}
219      */
220     public Element removeWithWriter(Object key, CacheWriterManager writerManager) throws CacheException {
221         if (key == null) {
222             return null;
223         }
224 
225         Lock lock = getLockFor(key).writeLock();
226         lock.lock();
227         try {
228             cache.remove(key);
229             return copyElementForReadIfNeeded(authority.removeWithWriter(key, writerManager));
230         } finally {
231             lock.unlock();
232         }
233     }
234 
235     /***
236      * {@inheritDoc}
237      */
238     public Element putIfAbsent(Element e) throws NullPointerException {
239         Object key = e.getObjectKey();
240 
241         Lock lock = getLockFor(key).writeLock();
242         lock.lock();
243         try {
244             Element copy = copyElementForWriteIfNeeded(e);
245             Element old = authority.putIfAbsent(copy);
246             if (old == null) {
247                 cache.fill(copy);
248             }
249             return copyElementForReadIfNeeded(old);
250         } finally {
251             lock.unlock();
252         }
253     }
254 
255     /***
256      * {@inheritDoc}
257      */
258     public Element removeElement(Element e, ElementValueComparator comparator) throws NullPointerException {
259         Object key = e.getObjectKey();
260 
261         Lock lock = getLockFor(key).writeLock();
262         lock.lock();
263         try {
264             cache.remove(e.getObjectKey());
265             return copyElementForReadIfNeeded(authority.removeElement(e, comparator));
266         } finally {
267             lock.unlock();
268         }
269     }
270 
271     /***
272      * {@inheritDoc}
273      */
274     public boolean replace(Element old, Element e, ElementValueComparator comparator) throws NullPointerException, IllegalArgumentException {
275         Object key = old.getObjectKey();
276 
277         Lock lock = getLockFor(key).writeLock();
278         lock.lock();
279         try {
280             Element copy = copyElementForWriteIfNeeded(e);
281             cache.remove(old.getObjectKey());
282             return authority.replace(old, copy, comparator);
283         } finally {
284             lock.unlock();
285         }
286     }
287 
288     /***
289      * {@inheritDoc}
290      */
291     public Element replace(Element e) throws NullPointerException {
292         Object key = e.getObjectKey();
293 
294         Lock lock = getLockFor(key).writeLock();
295         lock.lock();
296         try {
297             Element copy = copyElementForWriteIfNeeded(e);
298             cache.remove(e.getObjectKey());
299             return copyElementForReadIfNeeded(authority.replace(copy));
300         } finally {
301             lock.unlock();
302         }
303     }
304 
305     /***
306      * {@inheritDoc}
307      */
308     public boolean containsKey(Object key) {
309         if (key == null) {
310             return false;
311         }
312 
313         Lock lock = getLockFor(key).readLock();
314         lock.lock();
315         try {
316             return cache.containsKey(key) || authority.containsKey(key);
317         } finally {
318             lock.unlock();
319         }
320     }
321 
322     /***
323      * {@inheritDoc}
324      */
325     public boolean containsKeyOnDisk(Object key) {
326         if (key == null) {
327             return false;
328         }
329 
330         Lock lock = getLockFor(key).readLock();
331         lock.lock();
332         try {
333             return cache.containsKeyOnDisk(key) || authority.containsKeyOnDisk(key);
334         } finally {
335             lock.unlock();
336         }
337     }
338 
339     /***
340      * {@inheritDoc}
341      */
342     public boolean containsKeyOffHeap(Object key) {
343         if (key == null) {
344             return false;
345         }
346 
347         Lock lock = getLockFor(key).readLock();
348         lock.lock();
349         try {
350             return cache.containsKeyOffHeap(key) || authority.containsKeyOffHeap(key);
351         } finally {
352             lock.unlock();
353         }
354     }
355 
356     /***
357      * {@inheritDoc}
358      */
359     public boolean containsKeyInMemory(Object key) {
360         if (key == null) {
361             return false;
362         }
363 
364         Lock lock = getLockFor(key).readLock();
365         lock.lock();
366         try {
367             return cache.containsKeyInMemory(key) || authority.containsKeyInMemory(key);
368         } finally {
369             lock.unlock();
370         }
371     }
372 
373     /***
374      * {@inheritDoc}
375      */
376     public List<?> getKeys() {
377         readLock();
378         try {
379             return authority.getKeys();
380         } finally {
381             readUnlock();
382         }
383     }
384 
385     /***
386      * {@inheritDoc}
387      */
388     public void removeAll() throws CacheException {
389         writeLock();
390         try {
391             cache.removeAll();
392             authority.removeAll();
393         } finally {
394             writeUnlock();
395         }
396     }
397 
398     /***
399      * {@inheritDoc}
400      */
401     public void dispose() {
402         cache.dispose();
403         authority.dispose();
404     }
405 
406     /***
407      * {@inheritDoc}
408      */
409     public int getSize() {
410         readLock();
411         try {
412             return Math.max(cache.getSize(), authority.getSize());
413         } finally {
414             readUnlock();
415         }
416     }
417 
418     /***
419      * {@inheritDoc}
420      */
421     public int getInMemorySize() {
422         readLock();
423         try {
424             return authority.getInMemorySize() + cache.getInMemorySize();
425         } finally {
426             readUnlock();
427         }
428     }
429 
430     /***
431      * {@inheritDoc}
432      */
433     public int getOffHeapSize() {
434         readLock();
435         try {
436             return authority.getOffHeapSize() + cache.getOffHeapSize();
437         } finally {
438             readUnlock();
439         }
440     }
441 
442     /***
443      * {@inheritDoc}
444      */
445     public int getOnDiskSize() {
446         readLock();
447         try {
448             return authority.getOnDiskSize() + cache.getOnDiskSize();
449         } finally {
450             readUnlock();
451         }
452     }
453 
454     /***
455      * {@inheritDoc}
456      */
457     public int getTerracottaClusteredSize() {
458         readLock();
459         try {
460             return authority.getTerracottaClusteredSize() + cache.getTerracottaClusteredSize();
461         } finally {
462             readUnlock();
463         }
464     }
465 
466     /***
467      * {@inheritDoc}
468      */
469     public long getInMemorySizeInBytes() {
470         return authority.getInMemorySizeInBytes() + cache.getInMemorySizeInBytes();
471     }
472 
473     /***
474      * {@inheritDoc}
475      */
476     public long getOffHeapSizeInBytes() {
477         return authority.getOffHeapSizeInBytes() + cache.getOffHeapSizeInBytes();
478     }
479 
480     /***
481      * {@inheritDoc}
482      */
483     public long getOnDiskSizeInBytes() {
484         return authority.getOnDiskSizeInBytes() + cache.getOnDiskSizeInBytes();
485     }
486 
487     /***
488      * {@inheritDoc}
489      */
490     public void expireElements() {
491         writeLock();
492         try {
493             authority.expireElements();
494             cache.expireElements();
495         } finally {
496             writeUnlock();
497         }
498     }
499 
500     /***
501      * {@inheritDoc}
502      */
503     //TODO : is this correct?
504     public void flush() throws IOException {
505         cache.flush();
506         authority.flush();
507     }
508 
509     /***
510      * {@inheritDoc}
511      */
512     public boolean bufferFull() {
513         return cache.bufferFull() || authority.bufferFull();
514     }
515 
516     private void readLock() {
517         for (ReadWriteLockSync lock : getAllLocks()) {
518             lock.lock(LockType.READ);
519         }
520     }
521 
522     private void readUnlock() {
523         for (ReadWriteLockSync lock : getAllLocks()) {
524             lock.unlock(LockType.READ);
525         }
526     }
527 
528     private void writeLock() {
529         for (ReadWriteLockSync lock : getAllLocks()) {
530             lock.lock(LockType.WRITE);
531         }
532     }
533 
534     private void writeUnlock() {
535         for (ReadWriteLockSync lock : getAllLocks()) {
536             lock.unlock(LockType.WRITE);
537         }
538     }
539 
540     /***
541      * Returns the ReadWriteLock guarding this key.
542      * 
543      * @param key key of interest
544      * @return lock for the supplied key
545      */
546     protected ReadWriteLock getLockFor(Object key) {
547         return masterLocks.getLockForKey(key);
548     }
549 
550     private List<ReadWriteLockSync> getAllLocks() {
551         return masterLocks.getAllSyncs();
552     }
553 
554     /***
555      * {@inheritDoc}
556      */
557     public Status getStatus() {
558         //TODO this might be wrong...
559         return authority.getStatus();
560     }
561 
562     /***
563      * {@inheritDoc}
564      */
565     public Policy getInMemoryEvictionPolicy() {
566         return cache.getInMemoryEvictionPolicy();
567     }
568 
569     /***
570      * {@inheritDoc}
571      */
572     public void setInMemoryEvictionPolicy(Policy policy) {
573         cache.setInMemoryEvictionPolicy(policy);
574     }
575 
576     /***
577      * {@inheritDoc}
578      */
579     public final Object getInternalContext() {
580         return masterLocks;
581     }
582 }