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 package net.sf.ehcache.transaction;
17
18 import java.io.IOException;
19 import java.util.Collection;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.ListIterator;
23 import java.util.Map;
24 import java.util.Set;
25
26 import net.sf.ehcache.CacheException;
27 import net.sf.ehcache.Element;
28 import net.sf.ehcache.Status;
29 import net.sf.ehcache.config.InvalidConfigurationException;
30 import net.sf.ehcache.search.Attribute;
31 import net.sf.ehcache.search.Result;
32 import net.sf.ehcache.search.Results;
33 import net.sf.ehcache.search.SearchException;
34 import net.sf.ehcache.search.attribute.AttributeExtractor;
35 import net.sf.ehcache.store.AbstractStore;
36 import net.sf.ehcache.store.Policy;
37 import net.sf.ehcache.store.Store;
38 import net.sf.ehcache.store.StoreQuery;
39 import net.sf.ehcache.store.TerracottaStore;
40 import net.sf.ehcache.store.compound.ReadWriteCopyStrategy;
41 import net.sf.ehcache.terracotta.TerracottaNotRunningException;
42
43 /***
44 * Abstract transactional store which provides implementation of all non-transactional methods
45 *
46 * @author Ludovic Orban
47 */
48 public abstract class AbstractTransactionStore extends AbstractStore implements TerracottaStore {
49
50 /***
51 * The underlying store wrapped by this store
52 */
53 protected final Store underlyingStore;
54
55 /***
56 * The copy strategy for this store
57 */
58 protected final ReadWriteCopyStrategy<Element> copyStrategy;
59
60 /***
61 * Constructor
62 * @param underlyingStore the underlying store
63 */
64 protected AbstractTransactionStore(Store underlyingStore, ReadWriteCopyStrategy<Element> copyStrategy) {
65 this.underlyingStore = underlyingStore;
66 this.copyStrategy = copyStrategy;
67 }
68
69 /***
70 * Copy element for read operation
71 *
72 * @param element
73 * @return copied element
74 */
75 protected Element copyElementForRead(Element element) {
76 return copyStrategy.copyForRead(element);
77 }
78
79 /***
80 * Copy element for write operation
81 *
82 * @param element
83 * @return copied element
84 */
85 protected Element copyElementForWrite(Element element) {
86 return copyStrategy.copyForWrite(element);
87 }
88
89 /***
90 * {@inheritDoc}
91 */
92 @Override
93 public Results executeQuery(StoreQuery query) {
94 Results results = underlyingStore.executeQuery(query);
95 if (results instanceof TxSearchResults) {
96
97 return results;
98 }
99 return new TxSearchResults(underlyingStore.executeQuery(query));
100 }
101
102
103
104 /***
105 * {@inheritDoc}
106 */
107 public int getInMemorySize() {
108 return underlyingStore.getInMemorySize();
109 }
110
111 /***
112 * {@inheritDoc}
113 */
114 public int getOffHeapSize() {
115 return underlyingStore.getOffHeapSize();
116 }
117
118 /***
119 * {@inheritDoc}
120 */
121 public int getOnDiskSize() {
122 return underlyingStore.getOnDiskSize();
123 }
124
125 /***
126 * {@inheritDoc}
127 */
128 public long getInMemorySizeInBytes() {
129 return underlyingStore.getInMemorySizeInBytes();
130 }
131
132 /***
133 * {@inheritDoc}
134 */
135 public long getOffHeapSizeInBytes() {
136 return underlyingStore.getOffHeapSizeInBytes();
137 }
138
139 /***
140 * {@inheritDoc}
141 */
142 public long getOnDiskSizeInBytes() {
143 return underlyingStore.getOnDiskSizeInBytes();
144 }
145
146 /***
147 * {@inheritDoc}
148 */
149 public boolean containsKeyOnDisk(Object key) {
150 return underlyingStore.containsKeyOnDisk(key);
151 }
152
153 /***
154 * {@inheritDoc}
155 */
156 public boolean containsKeyOffHeap(Object key) {
157 return underlyingStore.containsKeyOffHeap(key);
158 }
159
160 /***
161 * {@inheritDoc}
162 */
163 public boolean containsKeyInMemory(Object key) {
164 return underlyingStore.containsKeyInMemory(key);
165 }
166
167 /***
168 * {@inheritDoc}
169 */
170 public void dispose() {
171 underlyingStore.dispose();
172 }
173
174 /***
175 * {@inheritDoc}
176 */
177 public Status getStatus() {
178 return underlyingStore.getStatus();
179 }
180
181 /***
182 * {@inheritDoc}
183 */
184 public void expireElements() {
185 underlyingStore.expireElements();
186 }
187
188 /***
189 * {@inheritDoc}
190 */
191 public void flush() throws IOException {
192 underlyingStore.flush();
193 }
194
195 /***
196 * {@inheritDoc}
197 */
198 public boolean bufferFull() {
199 return underlyingStore.bufferFull();
200 }
201
202 /***
203 * {@inheritDoc}
204 */
205 public Policy getInMemoryEvictionPolicy() {
206 return underlyingStore.getInMemoryEvictionPolicy();
207 }
208
209 /***
210 * {@inheritDoc}
211 */
212 public void setInMemoryEvictionPolicy(Policy policy) {
213 underlyingStore.setInMemoryEvictionPolicy(policy);
214 }
215
216 /***
217 * {@inheritDoc}
218 */
219 public Object getInternalContext() {
220 return underlyingStore.getInternalContext();
221 }
222
223 /***
224 * {@inheritDoc}
225 */
226 public Object getMBean() {
227 return underlyingStore.getMBean();
228 }
229
230 /***
231 * {@inheritDoc}
232 */
233 @Override
234 public void setNodeCoherent(boolean coherent) {
235 if (!coherent) {
236 throw new InvalidConfigurationException("a transactional cache cannot be incoherent");
237 }
238 underlyingStore.setNodeCoherent(coherent);
239 }
240
241 /***
242 * {@inheritDoc}
243 *
244 * @see net.sf.ehcache.store.Store#isNodeCoherent()
245 */
246 @Override
247 public boolean isNodeCoherent() {
248 return underlyingStore.isNodeCoherent();
249 }
250
251 /***
252 * {@inheritDoc}
253 *
254 * @see net.sf.ehcache.store.Store#isCacheCoherent()
255 */
256 @Override
257 public boolean isCacheCoherent() {
258 return underlyingStore.isCacheCoherent();
259 }
260
261 /***
262 * {@inheritDoc}
263 *
264 * @see net.sf.ehcache.store.Store#isClusterCoherent()
265 */
266 @Override
267 public boolean isClusterCoherent() {
268 return underlyingStore.isClusterCoherent();
269 }
270
271 /***
272 * {@inheritDoc}
273 * @throws InterruptedException
274 * @throws UnsupportedOperationException
275 * @throws TerracottaNotRunningException
276 */
277 @Override
278 public void waitUntilClusterCoherent() throws TerracottaNotRunningException, UnsupportedOperationException, InterruptedException {
279 underlyingStore.waitUntilClusterCoherent();
280 }
281
282 /***
283 * {@inheritDoc}
284 */
285 @Override
286 public void setAttributeExtractors(Map<String, AttributeExtractor> extractors) {
287 underlyingStore.setAttributeExtractors(extractors);
288 }
289
290 /***
291 * {@inheritDoc}
292 */
293 @Override
294 public <T> Attribute<T> getSearchAttribute(String attributeName) throws CacheException {
295 return underlyingStore.getSearchAttribute(attributeName);
296 }
297
298
299
300 /***
301 * {@inheritDoc}
302 */
303 public Element unsafeGet(Object key) {
304 if (underlyingStore instanceof TerracottaStore) {
305 return ((TerracottaStore) underlyingStore).unsafeGet(key);
306 }
307 throw new CacheException("underlying store is not an instance of TerracottaStore");
308 }
309
310 /***
311 * {@inheritDoc}
312 */
313 public Element unsafeGetQuiet(Object key) {
314 if (underlyingStore instanceof TerracottaStore) {
315 return ((TerracottaStore) underlyingStore).unsafeGetQuiet(key);
316 }
317 throw new CacheException("underlying store is not an instance of TerracottaStore");
318 }
319
320 /***
321 * {@inheritDoc}
322 */
323 public Element unlockedGet(Object key) {
324 if (underlyingStore instanceof TerracottaStore) {
325 return ((TerracottaStore) underlyingStore).unlockedGet(key);
326 }
327 throw new CacheException("underlying store is not an instance of TerracottaStore");
328 }
329
330 /***
331 * {@inheritDoc}
332 */
333 public Element unlockedGetQuiet(Object key) {
334 if (underlyingStore instanceof TerracottaStore) {
335 return ((TerracottaStore) underlyingStore).unlockedGetQuiet(key);
336 }
337 throw new CacheException("underlying store is not an instance of TerracottaStore");
338 }
339
340 /***
341 * {@inheritDoc}
342 */
343 public Set getLocalKeys() {
344 if (underlyingStore instanceof TerracottaStore) {
345 return ((TerracottaStore) underlyingStore).getLocalKeys();
346 }
347 throw new CacheException("underlying store is not an instance of TerracottaStore");
348 }
349
350 /***
351 * Wrap search results so that Result.getValue() can use copy strategy
352 *
353 * @author teck
354 */
355 private class TxSearchResults implements Results {
356
357 private final Results results;
358
359 TxSearchResults(Results results) {
360 this.results = results;
361 }
362
363 public void discard() {
364 results.discard();
365 }
366
367 public List<Result> all() throws SearchException {
368 return new TxResultsList(results.all());
369 }
370
371 public List<Result> range(int start, int count) throws SearchException, IndexOutOfBoundsException {
372 return new TxResultsList(results.range(start, count));
373 }
374
375 public int size() {
376 return results.size();
377 }
378
379 public boolean hasKeys() {
380 return results.hasKeys();
381 }
382
383 public boolean hasValues() {
384 return results.hasValues();
385 }
386
387 public boolean hasAttributes() {
388 return results.hasAttributes();
389 }
390
391 public boolean hasAggregators() {
392 return results.hasAggregators();
393 }
394 }
395
396 /***
397 * Wrap search results so that Result.getValue() can use copy strategy
398 *
399 * @author teck
400 */
401 private class TxResultsList implements List<Result> {
402
403 private final List<Result> results;
404
405 TxResultsList(List<Result> results) {
406 this.results = results;
407 }
408
409 public int size() {
410 return results.size();
411 }
412
413 public boolean isEmpty() {
414 return results.isEmpty();
415 }
416
417 public boolean contains(Object o) {
418 return results.contains(unwrapIfNeeded(o));
419 }
420
421 public Iterator<Result> iterator() {
422 return new TxResultsIterator(results.iterator());
423 }
424
425 public Object[] toArray() {
426 return wrapResultArray(results.toArray());
427 }
428
429 public <T> T[] toArray(T[] a) {
430 return wrapResultArray(results.toArray(a));
431 }
432
433 private <T> T[] wrapResultArray(T[] array) {
434 for (int i = 0; i < array.length; i++) {
435 array[i] = (T) new TxResult((Result) array[i]);
436 }
437 return array;
438 }
439
440 public boolean add(Result o) {
441 throw new UnsupportedOperationException();
442 }
443
444 public boolean remove(Object o) {
445 throw new UnsupportedOperationException();
446 }
447
448 public boolean containsAll(Collection<?> c) {
449 for (Object o : c) {
450 if (!contains(o)) {
451 return false;
452 }
453 }
454 return true;
455 }
456
457 public boolean addAll(Collection<? extends Result> c) {
458 throw new UnsupportedOperationException();
459 }
460
461 public boolean addAll(int index, Collection<? extends Result> c) {
462 throw new UnsupportedOperationException();
463 }
464
465 public boolean removeAll(Collection<?> c) {
466 throw new UnsupportedOperationException();
467 }
468
469 public boolean retainAll(Collection<?> c) {
470 throw new UnsupportedOperationException();
471 }
472
473 public void clear() {
474 throw new UnsupportedOperationException();
475 }
476
477 @Override
478 public boolean equals(Object o) {
479 if (o == this) {
480 return true;
481 }
482 if (o == null) {
483 return false;
484 }
485
486 if (o instanceof List) {
487 List other = (List) o;
488 if (size() != other.size()) {
489 return false;
490 }
491
492 Iterator thisIter = results.iterator();
493 Iterator otherIter = other.iterator();
494 while (thisIter.hasNext()) {
495 Object otherItem = unwrapIfNeeded(otherIter.next());
496 Object thisItem = thisIter.next();
497 if (otherItem == null && thisItem == null) {
498 continue;
499 }
500 if (otherItem != null && thisItem == null) {
501 return false;
502 }
503 if (thisItem != null && otherItem == null) {
504 return false;
505 }
506
507 if (!thisItem.equals(otherItem)) {
508 return false;
509 }
510 }
511
512 return true;
513 }
514
515 return false;
516 }
517
518 @Override
519 public int hashCode() {
520 return results.hashCode();
521 }
522
523 public Result get(int index) {
524 return new TxResult(results.get(index));
525 }
526
527 public Result set(int index, Result element) {
528 throw new UnsupportedOperationException();
529 }
530
531 public void add(int index, Result element) {
532 throw new UnsupportedOperationException();
533 }
534
535 public Result remove(int index) {
536 throw new UnsupportedOperationException();
537 }
538
539 public int indexOf(Object o) {
540 return results.indexOf(unwrapIfNeeded(o));
541 }
542
543 public int lastIndexOf(Object o) {
544 return results.lastIndexOf(unwrapIfNeeded(o));
545 }
546
547 public ListIterator<Result> listIterator() {
548 return new TxResultsListIterator(results.listIterator());
549 }
550
551 public ListIterator<Result> listIterator(int index) {
552 return new TxResultsListIterator(results.listIterator(index));
553 }
554
555 public List<Result> subList(int fromIndex, int toIndex) {
556 return new TxResultsList(results.subList(fromIndex, toIndex));
557 }
558
559 private Object unwrapIfNeeded(Object o) {
560 if (o instanceof TxResult) {
561 return ((TxResult) o).getUnderylingResult();
562 }
563 return o;
564 }
565
566 }
567
568 /***
569 * Wrap search results so that Result.getValue() can use copy strategy
570 *
571 * @author teck
572 */
573 private class TxResult implements Result {
574 private final Result result;
575
576 TxResult(Result result) {
577 this.result = result;
578 }
579
580 Result getUnderylingResult() {
581 return result;
582 }
583
584 public Object getKey() throws SearchException {
585 return result.getKey();
586 }
587
588 public Object getValue() throws SearchException {
589 return copyElementForRead(new Element(result.getKey(), result.getValue())).getObjectValue();
590 }
591
592 public <T> T getAttribute(Attribute<T> attribute) throws SearchException {
593 return result.getAttribute(attribute);
594 }
595
596 public List<Object> getAggregatorResults() throws SearchException {
597 return result.getAggregatorResults();
598 }
599 }
600
601 /***
602 * Wrap search results so that Result.getValue() can use copy strategy
603 *
604 * @author teck
605 */
606 private class TxResultsIterator implements Iterator<Result> {
607
608 private final Iterator<Result> iterator;
609
610 TxResultsIterator(Iterator<Result> iterator) {
611 this.iterator = iterator;
612 }
613
614 public boolean hasNext() {
615 return iterator.hasNext();
616 }
617
618 public Result next() {
619 return new TxResult(iterator.next());
620 }
621
622 public void remove() {
623 throw new UnsupportedOperationException();
624 }
625 }
626
627 /***
628 * Wrap search results so that Result.getValue() can use copy strategy
629 *
630 * @author teck
631 */
632 private class TxResultsListIterator implements ListIterator<Result> {
633
634 private final ListIterator<Result> listIterator;
635
636 TxResultsListIterator(ListIterator<Result> listIterator) {
637 this.listIterator = listIterator;
638 }
639
640 public boolean hasNext() {
641 return listIterator.hasNext();
642 }
643
644 public Result next() {
645 return new TxResult(listIterator.next());
646 }
647
648 public boolean hasPrevious() {
649 return listIterator.hasPrevious();
650 }
651
652 public Result previous() {
653 return new TxResult(listIterator.previous());
654 }
655
656 public int nextIndex() {
657 return listIterator.nextIndex();
658 }
659
660 public int previousIndex() {
661 return listIterator.previousIndex();
662 }
663
664 public void remove() {
665 throw new UnsupportedOperationException();
666 }
667
668 public void set(Result o) {
669 throw new UnsupportedOperationException();
670 }
671
672 public void add(Result o) {
673 throw new UnsupportedOperationException();
674 }
675 }
676
677 }