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.io.Serializable;
21 import java.util.ArrayList;
22 import java.util.HashSet;
23 import java.util.List;
24
25 import net.sf.ehcache.CacheException;
26 import net.sf.ehcache.Element;
27 import net.sf.ehcache.Status;
28 import net.sf.ehcache.concurrent.LockType;
29 import net.sf.ehcache.concurrent.StripedReadWriteLockSync;
30 import net.sf.ehcache.concurrent.Sync;
31 import net.sf.ehcache.config.CacheConfiguration;
32 import net.sf.ehcache.event.RegisteredEventListeners;
33 import net.sf.ehcache.writer.CacheWriterManager;
34
35 /***
36 * A wrapper to convert a legacy pair of stores into a new style compound store.
37 *
38 * @author Chris Dennis
39 */
40 public class LegacyStoreWrapper extends AbstractStore {
41
42 private static final int SYNC_STRIPES = 64;
43
44 private final Store memory;
45 private final Store disk;
46 private final RegisteredEventListeners eventListeners;
47 private final CacheConfiguration config;
48
49 private final StripedReadWriteLockSync sync = new StripedReadWriteLockSync(SYNC_STRIPES);
50
51 /***
52 * Create a correctly locked store wrapper around the supplied in-memory and on disk stores.
53 *
54 * @param memory in-memory store
55 * @param disk on disk store
56 * @param eventListeners event listener to fire on
57 * @param config cache configuration
58 */
59 public LegacyStoreWrapper(Store memory, Store disk, RegisteredEventListeners eventListeners, CacheConfiguration config) {
60 this.memory = memory;
61 this.disk = disk;
62 this.eventListeners = eventListeners;
63 this.config = config;
64 }
65
66 /***
67 * {@inheritDoc}
68 */
69 public boolean bufferFull() {
70 if (disk == null) {
71 return false;
72 } else {
73 return disk.bufferFull();
74 }
75 }
76
77 /***
78 * {@inheritDoc}
79 */
80 public boolean containsKey(Object key) {
81 Sync s = sync.getSyncForKey(key);
82 s.lock(LockType.READ);
83 try {
84 if (key instanceof Serializable && (disk != null)) {
85 return disk.containsKey(key) || memory.containsKey(key);
86 } else {
87 return memory.containsKey(key);
88 }
89 } finally {
90 s.unlock(LockType.READ);
91 }
92 }
93
94 /***
95 * {@inheritDoc}
96 */
97 public boolean containsKeyInMemory(Object key) {
98 Sync s = sync.getSyncForKey(key);
99 s.lock(LockType.READ);
100 try {
101 return memory.containsKey(key);
102 } finally {
103 s.unlock(LockType.READ);
104 }
105 }
106
107 /***
108 * {@inheritDoc}
109 */
110 public boolean containsKeyOffHeap(Object key) {
111 return false;
112 }
113
114 /***
115 * {@inheritDoc}
116 */
117 public boolean containsKeyOnDisk(Object key) {
118 Sync s = sync.getSyncForKey(key);
119 s.lock(LockType.READ);
120 try {
121 if (disk != null) {
122 return disk.containsKey(key);
123 } else {
124 return false;
125 }
126 } finally {
127 s.unlock(LockType.READ);
128 }
129 }
130
131 /***
132 * {@inheritDoc}
133 */
134 public void dispose() {
135 memory.dispose();
136 if (disk != null) {
137 disk.dispose();
138 }
139 }
140
141 /***
142 * {@inheritDoc}
143 */
144 public void expireElements() {
145
146 for (Object key : memory.getKeys()) {
147 Sync s = sync.getSyncForKey(key);
148 s.lock(LockType.WRITE);
149 try {
150 Element element = memory.getQuiet(key);
151 if (element != null) {
152 if (element.isExpired(config)) {
153 Element e = remove(key);
154 if (e != null) {
155 eventListeners.notifyElementExpiry(e, false);
156 }
157 }
158 }
159 } finally {
160 s.unlock(LockType.WRITE);
161 }
162 }
163
164
165 if (disk != null) {
166 disk.expireElements();
167 }
168 }
169
170 /***
171 * {@inheritDoc}
172 */
173 public void flush() throws IOException {
174 memory.flush();
175 if (disk != null) {
176 disk.flush();
177 }
178 }
179
180 /***
181 * {@inheritDoc}
182 */
183 public Element get(Object key) {
184 Sync s = sync.getSyncForKey(key);
185 s.lock(LockType.READ);
186 try {
187 Element e = memory.get(key);
188 if (e == null && disk != null) {
189 e = disk.get(key);
190 if (e != null) {
191 memory.put(e);
192 }
193 }
194 return e;
195 } finally {
196 s.unlock(LockType.READ);
197 }
198 }
199
200 /***
201 * {@inheritDoc}
202 */
203 public Policy getInMemoryEvictionPolicy() {
204 return memory.getInMemoryEvictionPolicy();
205 }
206
207 /***
208 * {@inheritDoc}
209 */
210 public int getInMemorySize() {
211 return memory.getSize();
212 }
213
214 /***
215 * {@inheritDoc}
216 */
217 public long getInMemorySizeInBytes() {
218 return memory.getInMemorySizeInBytes();
219 }
220
221 /***
222 * {@inheritDoc}
223 */
224 public Object getInternalContext() {
225 return sync;
226 }
227
228 /***
229 * {@inheritDoc}
230 */
231 public List getKeys() {
232 if (disk == null) {
233 return memory.getKeys();
234 } else {
235 HashSet<Object> keys = new HashSet<Object>();
236 keys.addAll(memory.getKeys());
237 keys.addAll(disk.getKeys());
238 return new ArrayList(keys);
239 }
240 }
241
242 /***
243 * {@inheritDoc}
244 */
245 public int getOffHeapSize() {
246 if (disk == null) {
247 return memory.getOffHeapSize();
248 } else {
249 return memory.getOffHeapSize() + disk.getOffHeapSize();
250 }
251 }
252
253 /***
254 * {@inheritDoc}
255 */
256 public long getOffHeapSizeInBytes() {
257 if (disk == null) {
258 return memory.getOffHeapSizeInBytes();
259 } else {
260 return memory.getOffHeapSizeInBytes() + disk.getOffHeapSizeInBytes();
261 }
262 }
263
264 /***
265 * {@inheritDoc}
266 */
267 public int getOnDiskSize() {
268 if (disk != null) {
269 return disk.getSize();
270 } else {
271 return 0;
272 }
273 }
274
275 /***
276 * {@inheritDoc}
277 */
278 public long getOnDiskSizeInBytes() {
279 if (disk != null) {
280 return disk.getOnDiskSizeInBytes();
281 } else {
282 return 0;
283 }
284 }
285
286 /***
287 * {@inheritDoc}
288 */
289 public Element getQuiet(Object key) {
290 Sync s = sync.getSyncForKey(key);
291 s.lock(LockType.READ);
292 try {
293 Element e = memory.getQuiet(key);
294 if (e == null && disk != null) {
295 e = disk.getQuiet(key);
296 if (e != null) {
297 memory.put(e);
298 }
299 }
300 return e;
301 } finally {
302 s.unlock(LockType.READ);
303 }
304 }
305
306 /***
307 * {@inheritDoc}
308 * <p/>
309 * The size is the number of {@link Element}s in the memory store
310 * plus the number of {@link Element}s in the disk store.
311 */
312 public int getSize() {
313 if (disk != null) {
314 HashSet<Object> keys = new HashSet<Object>();
315 keys.addAll(memory.getKeys());
316 keys.addAll(disk.getKeys());
317 return keys.size();
318 } else {
319 return memory.getSize();
320 }
321 }
322
323 /***
324 * {@inheritDoc}
325 */
326 public Status getStatus() {
327 return memory.getStatus();
328 }
329
330 /***
331 * {@inheritDoc}
332 */
333 public int getTerracottaClusteredSize() {
334 return 0;
335 }
336
337 /***
338 * {@inheritDoc}
339 */
340 public boolean put(Element element) throws CacheException {
341 if (element == null) {
342 return false;
343 }
344
345 Sync s = sync.getSyncForKey(element.getObjectKey());
346 s.lock(LockType.WRITE);
347 try {
348 boolean notOnDisk = !containsKeyOnDisk(element.getObjectKey());
349 return memory.put(element) && notOnDisk;
350 } finally {
351 s.unlock(LockType.WRITE);
352 }
353 }
354
355 /***
356 * {@inheritDoc}
357 */
358 public boolean putWithWriter(Element element, CacheWriterManager writerManager) throws CacheException {
359 if (element == null) {
360 return false;
361 }
362
363 Sync s = sync.getSyncForKey(element.getObjectKey());
364 s.lock(LockType.WRITE);
365 try {
366 boolean notOnDisk = !containsKey(element.getObjectKey());
367 return memory.putWithWriter(element, writerManager) && notOnDisk;
368 } finally {
369 s.unlock(LockType.WRITE);
370 }
371 }
372
373 /***
374 * {@inheritDoc}
375 */
376 public Element remove(Object key) {
377 Sync s = sync.getSyncForKey(key);
378 s.lock(LockType.WRITE);
379 try {
380 Element m = memory.remove(key);
381 if (disk != null && key instanceof Serializable) {
382 Element d = disk.remove(key);
383 if (m == null) {
384 return d;
385 }
386 }
387 return m;
388 } finally {
389 s.unlock(LockType.WRITE);
390 }
391 }
392
393 /***
394 * {@inheritDoc}
395 */
396 public void removeAll() throws CacheException {
397 memory.removeAll();
398 if (disk != null) {
399 disk.removeAll();
400 }
401 }
402
403 /***
404 * {@inheritDoc}
405 */
406 public Element removeWithWriter(Object key, CacheWriterManager writerManager) throws CacheException {
407 Sync s = sync.getSyncForKey(key);
408 s.lock(LockType.WRITE);
409 try {
410 Element m = memory.removeWithWriter(key, writerManager);
411 if (disk != null && key instanceof Serializable) {
412 Element d = disk.removeWithWriter(key, writerManager);
413 if (m == null) {
414 return d;
415 }
416 }
417 return m;
418 } finally {
419 s.unlock(LockType.WRITE);
420 }
421 }
422
423 /***
424 * {@inheritDoc}
425 */
426 public void setInMemoryEvictionPolicy(Policy policy) {
427 memory.setInMemoryEvictionPolicy(policy);
428 }
429
430 /***
431 * Returns the underlying disk store for this legacy wrapper.
432 */
433 public Store getDiskStore() {
434 return disk;
435 }
436
437 /***
438 * Returns the underlying memory store for this legacy wrapper.
439 */
440 public Store getMemoryStore() {
441 return memory;
442 }
443
444 /***
445 * {@inheritDoc}
446 */
447 public Element putIfAbsent(Element element) throws NullPointerException {
448 Sync lock = sync.getSyncForKey(element.getObjectKey());
449
450 lock.lock(LockType.WRITE);
451 try {
452 Element e = getQuiet(element.getObjectKey());
453 if (e == null) {
454 put(element);
455 }
456 return e;
457 } finally {
458 lock.unlock(LockType.WRITE);
459 }
460 }
461
462 /***
463 * {@inheritDoc}
464 */
465 public Element removeElement(Element element, ElementValueComparator comparator) throws NullPointerException {
466 Sync lock = sync.getSyncForKey(element.getObjectKey());
467
468 lock.lock(LockType.WRITE);
469 try {
470 Element current = getQuiet(element.getObjectKey());
471 if (comparator.equals(element, current)) {
472 return remove(current.getObjectKey());
473 } else {
474 return null;
475 }
476 } finally {
477 lock.unlock(LockType.WRITE);
478 }
479 }
480
481 /***
482 * {@inheritDoc}
483 */
484 public boolean replace(Element old, Element element, ElementValueComparator comparator)
485 throws NullPointerException, IllegalArgumentException {
486 Sync lock = sync.getSyncForKey(old.getObjectKey());
487
488 lock.lock(LockType.WRITE);
489 try {
490 Element current = getQuiet(old.getObjectKey());
491 if (comparator.equals(old, current)) {
492 put(element);
493 return true;
494 } else {
495 return false;
496 }
497 } finally {
498 lock.unlock(LockType.WRITE);
499 }
500 }
501
502 /***
503 * {@inheritDoc}
504 */
505 public Element replace(Element element) throws NullPointerException {
506 Sync lock = sync.getSyncForKey(element.getObjectKey());
507
508 lock.lock(LockType.WRITE);
509 try {
510 Element current = getQuiet(element.getObjectKey());
511 if (current != null) {
512 put(element);
513 }
514 return current;
515 } finally {
516 lock.unlock(LockType.WRITE);
517 }
518 }
519
520 /***
521 * {@inheritDoc}
522 */
523 public Object getMBean() {
524 return null;
525 }
526 }