View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.transaction.memory;
18  
19  import java.util.HashMap;
20  import java.util.Map;
21  
22  import junit.framework.Test;
23  import junit.framework.TestSuite;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  
28  import org.apache.commons.transaction.locking.LockException;
29  import org.apache.commons.transaction.util.CommonsLoggingLogger;
30  import org.apache.commons.transaction.util.LoggerFacade;
31  import org.apache.commons.transaction.util.RendezvousBarrier;
32  
33  /**
34   * Tests for map wrapper. 
35   *
36   * @version $Id: PessimisticMapWrapperTest.java 493628 2007-01-07 01:42:48Z joerg $
37   */
38  public class PessimisticMapWrapperTest extends MapWrapperTest {
39  
40      private static final Log log = LogFactory.getLog(PessimisticMapWrapperTest.class.getName());
41      private static final LoggerFacade sLogger = new CommonsLoggingLogger(log);
42  
43      protected static final long TIMEOUT = Long.MAX_VALUE;
44  
45      private static int deadlockCnt = 0;
46  
47      public static Test suite() {
48          TestSuite suite = new TestSuite(PessimisticMapWrapperTest.class);
49          return suite;
50      }
51  
52      public static void main(java.lang.String[] args) {
53          junit.textui.TestRunner.run(suite());
54      }
55  
56      public PessimisticMapWrapperTest(String testName) {
57          super(testName);
58      }
59  
60      protected TransactionalMapWrapper getNewWrapper(Map map) {
61          return new PessimisticMapWrapper(map, sLogger);
62      }
63  
64      // XXX no need for this code, just to make clear those tests are run as well 
65      public void testBasic() throws Throwable {
66          super.testBasic();
67      }
68  
69      public void testComplex() throws Throwable {
70          super.testComplex();
71      }
72  
73      public void testSets() throws Throwable {
74          super.testSets();
75      }
76  
77      public void testMulti() throws Throwable {
78          sLogger.logInfo("Checking concurrent transaction features");
79  
80          final Map map1 = new HashMap();
81  
82          final PessimisticMapWrapper txMap1 = (PessimisticMapWrapper) getNewWrapper(map1);
83  
84          Thread thread1 = new Thread(new Runnable() {
85              public void run() {
86                  txMap1.startTransaction();
87                  txMap1.put("key1", "value2");
88                  synchronized (txMap1) {
89                      txMap1.commitTransaction();
90                      report("value2", (String) txMap1.get("key1"));
91                  }
92              }
93          }, "Thread1");
94  
95          txMap1.put("key1", "value1");
96  
97          txMap1.startTransaction();
98  
99          report("value1", (String) txMap1.get("key1"));
100 
101         thread1.start();
102 
103         // we have serializable as isolation level, that's why I will still see the old value
104         report("value1", (String) txMap1.get("key1"));
105 
106         txMap1.put("key1", "value3");
107 
108         // after commit it must be our value
109         synchronized (txMap1) {
110             txMap1.commitTransaction();
111             report("value3", (String) txMap1.get("key1"));
112         }
113     }
114 
115     public void testConflict() throws Throwable {
116         sLogger.logInfo("Checking concurrent transaction features");
117 
118         final Map map1 = new HashMap();
119 
120         final PessimisticMapWrapper txMap1 = (PessimisticMapWrapper) getNewWrapper(map1);
121 
122         final RendezvousBarrier restart = new RendezvousBarrier("restart",
123                 TIMEOUT, sLogger);
124 
125         for (int i = 0; i < 25; i++) {
126 
127             final RendezvousBarrier deadlockBarrier1 = new RendezvousBarrier("deadlock" + i,
128                     TIMEOUT, sLogger);
129 
130             Thread thread1 = new Thread(new Runnable() {
131                 public void run() {
132                     txMap1.startTransaction();
133                     try {
134                         // first both threads get a lock, this one on res2
135                         txMap1.put("key2", "value2");
136                         synchronized (deadlockBarrier1) {
137                             deadlockBarrier1.meet();
138                             deadlockBarrier1.reset();
139                         }
140                         // if I am first, the other thread will be dead, i.e.
141                         // exactly one
142                         txMap1.put("key1", "value2");
143                         txMap1.commitTransaction();
144                     } catch (LockException le) {
145                         assertEquals(le.getCode(), LockException.CODE_DEADLOCK_VICTIM);
146                         deadlockCnt++;
147                         txMap1.rollbackTransaction();
148                     } catch (InterruptedException ie) {
149                     } finally {
150                         try {
151                         synchronized (restart) {
152                             restart.meet();
153                             restart.reset();
154                         }
155                         } catch (InterruptedException ie) {}
156 
157                     }
158                 }
159             }, "Thread1");
160 
161             thread1.start();
162 
163             txMap1.startTransaction();
164             try {
165                 // first both threads get a lock, this one on res2
166                 txMap1.get("key1");
167                 synchronized (deadlockBarrier1) {
168                     deadlockBarrier1.meet();
169                     deadlockBarrier1.reset();
170                 }
171                 //          if I am first, the other thread will be dead, i.e. exactly
172                 // one
173                 txMap1.get("key2");
174                 txMap1.commitTransaction();
175             } catch (LockException le) {
176                 assertEquals(le.getCode(), LockException.CODE_DEADLOCK_VICTIM);
177                 deadlockCnt++;
178                 txMap1.rollbackTransaction();
179             } finally {
180                 try {
181                 synchronized (restart) {
182                     restart.meet();
183                     restart.reset();
184                 }
185                 } catch (InterruptedException ie) {}
186 
187             }
188 
189             // XXX in special scenarios the current implementation might cause both
190             // owners to be deadlock victims
191             if (deadlockCnt != 1) {
192                 sLogger.logWarning("More than one thread was deadlock victim!");
193             }
194             assertTrue(deadlockCnt >= 1);
195             deadlockCnt = 0;
196         }
197     }
198 
199     public void testTxControl() throws Throwable {
200         super.testTxControl();
201     }
202 
203 }