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
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
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 }