1 package net.sf.ehcache.transaction.local;
2
3 import junit.framework.AssertionFailedError;
4 import junit.framework.TestCase;
5 import net.sf.ehcache.CacheManager;
6 import net.sf.ehcache.Ehcache;
7 import net.sf.ehcache.Element;
8 import net.sf.ehcache.TransactionController;
9 import net.sf.ehcache.store.DefaultElementValueComparator;
10 import net.sf.ehcache.store.ElementValueComparator;
11 import net.sf.ehcache.transaction.DeadLockException;
12 import net.sf.ehcache.transaction.TransactionException;
13 import net.sf.ehcache.transaction.TransactionInterruptedException;
14 import net.sf.ehcache.transaction.TransactionTimeoutException;
15
16 import java.util.Arrays;
17 import java.util.concurrent.CyclicBarrier;
18 import java.util.concurrent.atomic.AtomicBoolean;
19
20 /***
21 * @author lorban
22 */
23 public class LocalTransactionTest extends TestCase {
24
25 private final ElementValueComparator elementValueComparator = new DefaultElementValueComparator();
26 private CacheManager cacheManager;
27 private Ehcache cache1;
28 private Ehcache cache2;
29 private TransactionController transactionController;
30
31 @Override
32 protected void setUp() throws Exception {
33 cacheManager = new CacheManager(LocalTransactionTest.class.getResourceAsStream("/ehcache-tx-local.xml"));
34 transactionController = cacheManager.getTransactionController();
35 transactionController.begin();
36 cache1 = cacheManager.getEhcache("txCache1");
37 cache1.removeAll();
38 cache2 = cacheManager.getEhcache("txCache2");
39 cache2.removeAll();
40 transactionController.commit();
41 }
42
43 @Override
44 protected void tearDown() throws Exception {
45 if (transactionController.getCurrentTransactionContext() != null) {
46 transactionController.rollback();
47 }
48 cacheManager.shutdown();
49 }
50
51 public void testTransactionContextLifeCycle() throws Exception {
52 assertNull(transactionController.getCurrentTransactionContext());
53 transactionController.begin();
54 try {
55 transactionController.begin();
56 fail("expected TransactionException");
57 } catch (TransactionException e) {
58
59 }
60 assertNotNull(transactionController.getCurrentTransactionContext());
61 transactionController.commit();
62 try {
63 transactionController.commit();
64 fail("expected TransactionException");
65 } catch (Exception e) {
66
67 }
68 try {
69 transactionController.rollback();
70 fail("expected TransactionException");
71 } catch (Exception e) {
72
73 }
74 assertNull(transactionController.getCurrentTransactionContext());
75 }
76
77 public void testInterruption() throws Exception {
78 transactionController.begin();
79 Thread.currentThread().interrupt();
80 try {
81 cache1.get(1);
82 fail("expected TransactionInterruptedException");
83 } catch (TransactionInterruptedException e) {
84
85 }
86
87
88 cache1.get(1);
89
90 cache1.put(new Element(1, "one"));
91
92 final CyclicBarrier barrier = new CyclicBarrier(2);
93 TxThread tx2 = new TxThread() {
94 @Override
95 public void exec() throws Exception {
96 transactionController.begin();
97
98
99 barrier.await();
100 try {
101 cache1.put(new Element(1, "one#tx2"));
102 } catch (TransactionInterruptedException e) {
103
104 }
105
106 transactionController.commit();
107 }
108 };
109 tx2.start();
110
111 barrier.await();
112 tx2.interrupt();
113
114 tx2.join();
115 tx2.assertNotFailed();
116
117 transactionController.commit();
118 }
119
120 public void testTimeout() throws Exception {
121 transactionController.setDefaultTransactionTimeout(1);
122 transactionController.begin();
123
124 cache1.put(new Element(1, "one"));
125
126 Thread.sleep(1500);
127
128 try {
129 cache1.get(1);
130 fail("expected TransactionTimeoutException");
131 } catch (TransactionTimeoutException e) {
132
133 }
134
135 try {
136 cache1.getQuiet(1);
137 fail("expected TransactionTimeoutException");
138 } catch (TransactionTimeoutException e) {
139
140 }
141
142 try {
143 cache1.getKeys();
144 fail("expected TransactionTimeoutException");
145 } catch (TransactionTimeoutException e) {
146
147 }
148
149 try {
150 cache1.getSize();
151 fail("expected TransactionTimeoutException");
152 } catch (TransactionTimeoutException e) {
153
154 }
155
156 try {
157 cache1.removeAll();
158 fail("expected TransactionTimeoutException");
159 } catch (TransactionTimeoutException e) {
160
161 }
162
163 try {
164 cache1.put(new Element(2, "two"));
165 fail("expected TransactionTimeoutException");
166 } catch (TransactionTimeoutException e) {
167
168 }
169
170 try {
171 cache1.remove(1);
172 fail("expected TransactionTimeoutException");
173 } catch (TransactionTimeoutException e) {
174
175 }
176
177 transactionController.rollback();
178 }
179
180 public void testDefaultTimeout() throws Exception {
181 transactionController.setDefaultTransactionTimeout(1);
182 transactionController.begin();
183
184 Thread.sleep(1500);
185
186 try {
187 transactionController.commit();
188 fail("expected TransactionTimeoutException");
189 } catch (TransactionTimeoutException e) {
190
191 }
192 }
193
194 public void testCopyOnRead() throws Exception {
195 transactionController.begin();
196 Object putValue = new Object[]{"one#1"};
197 cache1.put(new Element(1, putValue));
198
199 Element one = cache1.get(1);
200 Object getValue = one.getObjectValue();
201
202 assertFalse(putValue == getValue);
203 transactionController.commit();
204 }
205
206 public void testCopyOnWrite() throws Exception {
207 transactionController.begin();
208 Object[] putValue = new Object[]{"one#1"};
209 cache1.put(new Element(1, putValue));
210 putValue[0] = "one#2";
211
212 Element one = cache1.get(1);
213 Object[] getValue = (Object[]) one.getObjectValue();
214
215 assertEquals("one#1", getValue[0]);
216 transactionController.commit();
217 }
218
219 public void testTwoPuts() throws Exception {
220 transactionController.begin();
221 cache1.put(new Element(1, new Object[]{"one#1"}));
222 transactionController.commit();
223
224 transactionController.begin();
225 cache1.put(new Element(1, new Object[]{"one#2"}));
226 transactionController.commit();
227 }
228
229 public void testTwoCaches() throws Exception {
230 transactionController.begin();
231
232 cache1.put(new Element(1, "one"));
233 cache2.put(new Element(1, "one"));
234
235 TxThread tx2 = new TxThread() {
236 @Override
237 public void exec() {
238 transactionController.begin();
239
240 assertNull(cache1.get(1));
241 assertNull(cache2.get(1));
242
243 transactionController.commit();
244 }
245 };
246 tx2.start();
247 tx2.join();
248 tx2.assertNotFailed();
249
250 transactionController.commit();
251 }
252
253
254 public void testPut() throws Exception {
255 transactionController.begin();
256
257 cache1.put(new Element(1, "one"));
258
259 assertEquals(new Element(1, "one"), cache1.get(1));
260
261 TxThread tx2 = new TxThread() {
262 @Override
263 public void exec() {
264 transactionController.begin();
265
266 assertNull(cache1.get(1));
267
268 transactionController.commit();
269 }
270 };
271 tx2.start();
272 tx2.join();
273 tx2.assertNotFailed();
274
275 transactionController.commit();
276
277 transactionController.begin();
278 assertTrue(elementValueComparator.equals(new Element(1, "one"), cache1.get(1)));
279 transactionController.commit();
280
281 TxThread tx3 = new TxThread() {
282 @Override
283 public void exec() {
284 transactionController.begin();
285
286 assertTrue(elementValueComparator.equals(new Element(1, "one"), cache1.get(1)));
287
288 transactionController.commit();
289 }
290 };
291 tx3.start();
292 tx3.join();
293 tx3.assertNotFailed();
294 }
295
296 public void testRemove() throws Exception {
297 transactionController.begin();
298 cache1.put(new Element(1, "one"));
299 transactionController.commit();
300
301 transactionController.begin();
302
303 assertTrue(elementValueComparator.equals(new Element(1, "one"), cache1.get(1)));
304 cache1.put(new Element(2, "two"));
305 assertTrue(cache1.remove(1));
306 assertNull(cache1.get(1));
307 assertTrue(elementValueComparator.equals(new Element(2, "two"), cache1.get(2)));
308
309
310 TxThread tx2 = new TxThread() {
311 @Override
312 public void exec() {
313 transactionController.begin(1);
314
315 assertTrue(elementValueComparator.equals(new Element(1, "one"), cache1.get(1)));
316
317 try {
318 cache1.remove(1);
319 fail("expected DeadLockException");
320 } catch (DeadLockException e) {
321
322 }
323
324 transactionController.rollback();
325 }
326 };
327 tx2.start();
328 tx2.join();
329 tx2.assertNotFailed();
330
331 transactionController.commit();
332
333 transactionController.begin();
334 assertNull(cache1.get(1));
335 assertTrue(elementValueComparator.equals(new Element(2, "two"), cache1.get(2)));
336 transactionController.commit();
337 }
338
339 public void testRollback() throws Exception {
340 transactionController.begin();
341 cache1.put(new Element(1, "one"));
342 transactionController.rollback();
343
344 transactionController.begin();
345 assertNull(cache1.get(1));
346 transactionController.rollback();
347 }
348
349 public void testRollbackOnly() throws Exception {
350 transactionController.begin();
351
352 cache1.put(new Element(1, "one"));
353
354 transactionController.setRollbackOnly();
355 try {
356 transactionController.commit();
357 fail("expected TransactionException");
358 } catch (TransactionException e) {
359
360 }
361
362 transactionController.begin();
363
364 assertNull(cache1.get(1));
365
366 transactionController.commit();
367 }
368
369 public void testTwoConcurrentUpdates() throws Exception {
370 final long WAIT_TIME = 1500;
371 final long ERROR_MARGIN = 200;
372 final long[] times = new long[2];
373
374
375 transactionController.begin();
376
377 cache1.put(new Element(1, "tx1-one"));
378
379 TxThread tx2 = new TxThread() {
380 @Override
381 public void exec() throws InterruptedException {
382
383 transactionController.begin(4);
384
385 times[0] = System.currentTimeMillis();
386 cache1.put(new Element(1, "tx2-one"));
387 times[1] = System.currentTimeMillis();
388
389 Thread.sleep(WAIT_TIME);
390
391
392 transactionController.commit();
393 }
394 };
395 tx2.start();
396 tx2.join(WAIT_TIME);
397
398
399 transactionController.commit();
400
401
402 transactionController.begin();
403 assertTrue(elementValueComparator.equals(new Element(1, "tx1-one"), cache1.get(1)));
404
405 transactionController.commit();
406
407 tx2.join();
408 tx2.assertNotFailed();
409
410 assertTrue("expected TX1 to be on hold for more than " + WAIT_TIME + "ms, waited: " + (times[1] - times[0]), times[1] - times[0]
411 >= (WAIT_TIME - ERROR_MARGIN));
412
413
414 transactionController.begin();
415 assertTrue(elementValueComparator.equals(new Element(1, "tx2-one"), cache1.get(1)));
416
417 transactionController.commit();
418 }
419
420 public void testDeadlock() throws Exception {
421 final String[] losingTx = new String[1];
422
423 transactionController.begin(2);
424
425 cache1.put(new Element(1, "tx1-one"));
426
427 final CyclicBarrier barrier1 = new CyclicBarrier(2);
428 final CyclicBarrier barrier2 = new CyclicBarrier(2);
429 TxThread tx2 = new TxThread() {
430 @Override
431 public void exec() throws Exception {
432 transactionController.begin();
433
434 cache1.put(new Element(2, "tx2-two"));
435
436
437 barrier1.await();
438 barrier2.await();
439
440 try {
441 cache1.put(new Element(1, "tx2-one"));
442
443 cache1.put(new Element("tx2", ""));
444
445 transactionController.commit();
446 } catch (DeadLockException e) {
447 losingTx[0] = "tx2";
448 transactionController.rollback();
449 }
450 }
451 };
452 tx2.start();
453 barrier1.await();
454
455 try {
456 cache1.put(new Element(2, "tx1-two"));
457
458 cache1.put(new Element("tx1", ""));
459
460 transactionController.commit();
461 } catch (DeadLockException e) {
462 losingTx[0] = "tx1";
463 transactionController.rollback();
464 }
465
466
467 barrier2.await();
468 tx2.join();
469 tx2.assertNotFailed();
470
471
472 transactionController.begin();
473 Element el1 = cache1.get(1);
474 Element el2 = cache1.get(2);
475
476
477 assertNotNull(losingTx[0]);
478
479
480 String el1TxName = ((String) el1.getValue()).substring(0, 3);
481 String el2TxName = ((String) el2.getValue()).substring(0, 3);
482 assertEquals(el1TxName, el2TxName);
483
484
485 String winningTx = losingTx[0].equals("tx1") ? "tx2" : "tx1";
486 assertNotNull(cache1.get(winningTx));
487
488
489 assertNull(cache1.get(losingTx[0]));
490
491 transactionController.commit();
492 }
493
494 public void testGetKeys() throws Exception {
495 transactionController.begin();
496
497 cache1.put(new Element(1, "one"));
498 assertEquals(1, cache1.getKeys().size());
499 assertTrue(cache1.getKeys().containsAll(Arrays.asList(1)));
500
501 cache1.put(new Element(2, "two"));
502 assertEquals(2, cache1.getKeys().size());
503 assertTrue(cache1.getKeys().containsAll(Arrays.asList(1, 2)));
504
505 cache1.remove(1);
506 assertEquals(1, cache1.getKeys().size());
507 assertTrue(cache1.getKeys().containsAll(Arrays.asList(2)));
508
509 transactionController.commit();
510
511
512 transactionController.begin();
513
514 cache1.put(new Element(1, "one"));
515
516 final AtomicBoolean tx2Success = new AtomicBoolean(false);
517 TxThread tx2 = new TxThread() {
518 @Override
519 public void exec() {
520 transactionController.begin();
521
522 assertEquals(1, cache1.getKeys().size());
523 assertTrue(cache1.getKeys().containsAll(Arrays.asList(2)));
524
525 transactionController.commit();
526 tx2Success.set(true);
527 }
528 };
529 tx2.start();
530 tx2.join();
531 tx2.assertNotFailed();
532
533 transactionController.commit();
534 assertTrue(tx2Success.get());
535 }
536
537 public void testRemoveAll() throws Exception {
538 transactionController.begin();
539
540 cache1.put(new Element(1, "one"));
541 cache1.put(new Element(2, "two"));
542 assertEquals(2, cache1.getSize());
543
544 transactionController.commit();
545
546
547 transactionController.begin();
548
549 assertEquals(2, cache1.getSize());
550 cache1.removeAll();
551 assertEquals(0, cache1.getSize());
552
553 final CyclicBarrier barrier = new CyclicBarrier(2);
554 TxThread tx2 = new TxThread() {
555 @Override
556 public void exec() throws Exception {
557 transactionController.begin();
558
559 assertEquals(2, cache1.getSize());
560
561 cache1.put(new Element(3, "three"));
562
563
564 barrier.await();
565 barrier.await();
566
567 transactionController.commit();
568 }
569 };
570 tx2.start();
571 barrier.await();
572 assertEquals(0, cache1.getSize());
573
574
575 barrier.await();
576
577 tx2.join();
578 tx2.assertNotFailed();
579
580 transactionController.commit();
581 }
582
583 public void testGetSize() throws Exception {
584 transactionController.begin();
585 assertEquals(0, cache1.getSize());
586 cache1.put(new Element(1, "one"));
587 cache1.put(new Element(2, "two"));
588 assertEquals(2, cache1.getSize());
589
590 TxThread tx2 = new TxThread() {
591 @Override
592 public void exec() {
593 transactionController.begin();
594
595 assertEquals(0, cache1.getSize());
596 cache1.put(new Element(3, "three"));
597 assertEquals(1, cache1.getSize());
598
599 transactionController.commit();
600 }
601 };
602 tx2.start();
603 tx2.join();
604 tx2.assertNotFailed();
605
606 assertEquals(3, cache1.getSize());
607
608 transactionController.commit();
609 }
610
611 public void testPutIfAbsent() throws Exception {
612 transactionController.begin();
613 assertNull(cache1.putIfAbsent(new Element(0, "zero")));
614 transactionController.commit();
615
616 transactionController.begin();
617 assertNull(cache1.putIfAbsent(new Element(1, "one")));
618 assertTrue(elementValueComparator.equals(new Element(1, "one"), cache1.putIfAbsent(new Element(1, "one#2"))));
619 assertTrue(cache1.remove(1));
620 assertNull(cache1.putIfAbsent(new Element(1, "one")));
621
622 final CyclicBarrier barrier = new CyclicBarrier(2);
623 TxThread tx2 = new TxThread() {
624 @Override
625 public void exec() throws Exception {
626 transactionController.begin();
627
628 assertTrue(elementValueComparator.equals(new Element(0, "zero"), cache1.putIfAbsent(new Element(0, "zero#2"))));
629
630 assertEquals(1, cache1.getSize());
631 cache1.put(new Element(2, "two"));
632 assertEquals(2, cache1.getSize());
633
634
635 barrier.await();
636 assertTrue(elementValueComparator.equals(new Element(1, "one"), cache1.putIfAbsent(new Element(1, "one#tx2"))));
637
638 transactionController.commit();
639 }
640 };
641 tx2.start();
642 barrier.await();
643
644 assertEquals(2, cache1.getSize());
645 assertNull(cache1.get(2));
646
647 transactionController.commit();
648 tx2.join();
649 tx2.assertNotFailed();
650
651
652 transactionController.begin();
653
654 assertEquals(3, cache1.getSize());
655 assertTrue(elementValueComparator.equals(new Element(0, "zero"), cache1.get(0)));
656 assertTrue(elementValueComparator.equals(new Element(1, "one"), cache1.get(1)));
657 assertTrue(elementValueComparator.equals(new Element(2, "two"), cache1.get(2)));
658
659 transactionController.commit();
660 }
661
662 public void testRemoveElement() throws Exception {
663 transactionController.begin();
664 assertEquals(0, cache1.getSize());
665
666 cache1.put(new Element(1, "one"));
667 assertEquals(1, cache1.getSize());
668
669 final CyclicBarrier barrier = new CyclicBarrier(2);
670 TxThread tx2 = new TxThread() {
671 @Override
672 public void exec() throws Exception {
673 transactionController.begin();
674
675
676 barrier.await();
677 assertTrue(cache1.removeElement(new Element(1, "one")));
678
679 barrier.await();
680 assertEquals(0, cache1.getSize());
681 transactionController.commit();
682 }
683 };
684 tx2.start();
685
686 barrier.await();
687 transactionController.commit();
688
689 transactionController.begin();
690 assertEquals(1, cache1.getSize());
691 transactionController.commit();
692 barrier.await();
693
694 tx2.join();
695 tx2.assertNotFailed();
696
697 transactionController.begin();
698 assertEquals(0, cache1.getSize());
699 assertFalse(cache1.removeElement(new Element(1, "one")));
700 cache1.put(new Element(1, "one"));
701 assertTrue(cache1.removeElement(new Element(1, "one")));
702 transactionController.commit();
703 }
704
705 public void testReplace() throws Exception {
706 transactionController.begin();
707
708 assertNull(cache1.replace(new Element(1, "one")));
709 cache1.put(new Element(1, "one"));
710 assertTrue(elementValueComparator.equals(new Element(1, "one"), cache1.replace(new Element(1, "one#2"))));
711 assertTrue(elementValueComparator.equals(new Element(1, "one#2"), cache1.replace(new Element(1, "one#3"))));
712
713 assertTrue(elementValueComparator.equals(new Element(1, "one#3"), cache1.get(1)));
714
715 transactionController.commit();
716 }
717
718 public void testReplace2Args() throws Exception {
719 transactionController.begin();
720
721 assertFalse(cache1.replace(new Element(1, "one"), new Element(1, "one#2")));
722 cache1.put(new Element(1, "one"));
723 assertTrue(cache1.replace(new Element(1, "one"), new Element(1, "one#2")));
724 assertFalse(cache1.replace(new Element(1, "one"), new Element(1, "one#3")));
725 assertTrue(cache1.replace(new Element(1, "one#2"), new Element(1, "one")));
726
727 assertTrue(elementValueComparator.equals(new Element(1, "one"), cache1.get(1)));
728
729 transactionController.commit();
730 }
731
732 public void testMemoryOnly() throws Exception {
733 Ehcache txCache = cacheManager.getEhcache("txCacheMemoryOnly");
734 transactionController.begin();
735 txCache.removeAll();
736 transactionController.commit();
737
738 transactionController.begin();
739 txCache.put(new Element(1, "one"));
740 assertEquals(new Element(1, "one"), txCache.get(1));
741 assertEquals(1, txCache.getSize());
742
743 txCache.put(new Element(2, "two"));
744 assertEquals(new Element(2, "two"), txCache.get(2));
745 assertEquals(2, txCache.getSize());
746 transactionController.commit();
747
748 transactionController.begin();
749 assertEquals(new Element(1, "one"), txCache.get(1));
750 assertEquals(new Element(2, "two"), txCache.get(2));
751
752 txCache.put(new Element(1, "one#2"));
753 txCache.put(new Element(2, "two#2"));
754
755 assertEquals(new Element(1, "one#2"), txCache.get(1));
756 assertEquals(new Element(2, "two#2"), txCache.get(2));
757 assertNull(txCache.get(3));
758 assertEquals(2, txCache.getSize());
759 transactionController.commit();
760
761 transactionController.begin();
762 assertEquals(new Element(1, "one#2"), txCache.get(1));
763 assertEquals(new Element(2, "two#2"), txCache.get(2));
764 transactionController.commit();
765 }
766
767 public void testPersistent() throws Exception {
768 Ehcache txCachePersistent = cacheManager.getEhcache("txCachePersistent");
769 transactionController.begin();
770 txCachePersistent.removeAll();
771 transactionController.commit();
772
773 transactionController.begin();
774 txCachePersistent.put(new Element(1, "one"));
775 assertEquals(new Element(1, "one"), txCachePersistent.get(1));
776 assertEquals(1, txCachePersistent.getSize());
777
778 txCachePersistent.put(new Element(2, "two"));
779 assertEquals(new Element(2, "two"), txCachePersistent.get(2));
780 assertEquals(2, txCachePersistent.getSize());
781 transactionController.commit();
782
783 transactionController.begin();
784 assertEquals(new Element(1, "one"), txCachePersistent.get(1));
785 assertEquals(new Element(2, "two"), txCachePersistent.get(2));
786
787 txCachePersistent.put(new Element(1, "one#2"));
788 txCachePersistent.put(new Element(2, "two#2"));
789
790 assertEquals(new Element(1, "one#2"), txCachePersistent.get(1));
791 assertEquals(new Element(2, "two#2"), txCachePersistent.get(2));
792 assertNull(txCachePersistent.get(3));
793 assertEquals(2, txCachePersistent.getSize());
794 transactionController.commit();
795
796 transactionController.begin();
797 assertEquals(new Element(1, "one#2"), txCachePersistent.get(1));
798 assertEquals(new Element(2, "two#2"), txCachePersistent.get(2));
799 transactionController.commit();
800 }
801
802 public void testOverflow() throws Exception {
803 final Ehcache txCacheOverflow = cacheManager.getEhcache("txCacheOverflow");
804
805 transactionController.begin();
806 txCacheOverflow.put(new Element(1, "one"));
807 assertEquals(new Element(1, "one"), txCacheOverflow.get(1));
808 assertEquals(1, txCacheOverflow.getSize());
809
810 txCacheOverflow.put(new Element(2, "two"));
811 assertEquals(new Element(2, "two"), txCacheOverflow.get(2));
812 assertEquals(2, txCacheOverflow.getSize());
813 transactionController.commit();
814
815 transactionController.begin();
816 assertEquals(new Element(1, "one"), txCacheOverflow.get(1));
817 assertEquals(new Element(2, "two"), txCacheOverflow.get(2));
818
819 txCacheOverflow.put(new Element(1, "one#2"));
820 txCacheOverflow.put(new Element(2, "two#2"));
821
822 assertEquals(new Element(1, "one#2"), txCacheOverflow.get(1));
823 assertEquals(new Element(2, "two#2"), txCacheOverflow.get(2));
824 assertNull(txCacheOverflow.get(3));
825 assertEquals(2, txCacheOverflow.getSize());
826 transactionController.commit();
827
828 transactionController.begin();
829 assertEquals(new Element(1, "one#2"), txCacheOverflow.get(1));
830 assertEquals(new Element(2, "two#2"), txCacheOverflow.get(2));
831 transactionController.commit();
832 }
833
834 private static class TxThread extends Thread {
835 private volatile boolean failed;
836
837 @Override
838 public final void run() {
839 try {
840 exec();
841 } catch (Throwable t) {
842 t.printStackTrace();
843 failed = true;
844 }
845 }
846
847 public void exec() throws Exception {
848 }
849
850 public void assertNotFailed() {
851 if (failed) {
852 throw new AssertionFailedError("TxThread failed");
853 }
854 }
855 }
856
857 }