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.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         //This is called regularly by the expiry thread, but call it here synchronously
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 }