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.Collection;
20  import java.util.HashMap;
21  import java.util.Iterator;
22  import java.util.Map;
23  
24  import javax.transaction.Status;
25  
26  import junit.framework.Test;
27  import junit.framework.TestCase;
28  import junit.framework.TestSuite;
29  
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  
33  import org.apache.commons.transaction.util.CommonsLoggingLogger;
34  import org.apache.commons.transaction.util.LoggerFacade;
35  import org.apache.commons.transaction.util.RendezvousBarrier;
36  
37  /**
38   * Tests for map wrapper. 
39   *
40   * @version $Id: MapWrapperTest.java 493628 2007-01-07 01:42:48Z joerg $
41   */
42  public class MapWrapperTest extends TestCase {
43  
44      private static final Log log = LogFactory.getLog(MapWrapperTest.class.getName());
45      private static final LoggerFacade sLogger = new CommonsLoggingLogger(log);
46  
47      protected static final long BARRIER_TIMEOUT = 20000;
48  
49      // XXX need this, as JUnit seems to print only part of these strings
50      protected static void report(String should, String is) {
51          if (!should.equals(is)) {
52              fail("\nWrong output:\n'" + is + "'\nShould be:\n'" + should + "'\n");
53          }
54      }
55  
56      protected static void checkCollection(Collection col, Object[] values) {
57          int cnt = 0;
58          int trueCnt = 0;
59  
60          for (Iterator it = col.iterator(); it.hasNext();) {
61              cnt++;
62              Object value1 = it.next();
63              for (int i = 0; i < values.length; i++) {
64                  Object value2 = values[i];
65                  if (value2.equals(value1))
66                      trueCnt++;
67              }
68          }
69          assertEquals(cnt, values.length);
70          assertEquals(trueCnt, values.length);
71      }
72  
73      public static Test suite() {
74          TestSuite suite = new TestSuite(MapWrapperTest.class);
75          return suite;
76      }
77  
78      public static void main(java.lang.String[] args) {
79          junit.textui.TestRunner.run(suite());
80      }
81  
82      public MapWrapperTest(String testName) {
83          super(testName);
84      }
85  
86      protected TransactionalMapWrapper getNewWrapper(Map map) {
87          return new TransactionalMapWrapper(map);
88      }
89  
90      public void testBasic() throws Throwable {
91  
92          sLogger.logInfo("Checking basic transaction features");
93  
94          final Map map1 = new HashMap();
95  
96          final TransactionalMapWrapper txMap1 = getNewWrapper(map1);
97  
98          assertTrue(txMap1.isEmpty());
99  
100         // make sure changes are propagated to wrapped map outside tx
101         txMap1.put("key1", "value1");
102         report("value1", (String) map1.get("key1"));
103         assertFalse(txMap1.isEmpty());
104 
105         // make sure changes are progated to wrapped map only after commit
106         txMap1.startTransaction();
107         assertFalse(txMap1.isEmpty());
108         txMap1.put("key1", "value2");
109         report("value1", (String) map1.get("key1"));
110         report("value2", (String) txMap1.get("key1"));
111         txMap1.commitTransaction();
112         report("value2", (String) map1.get("key1"));
113         report("value2", (String) txMap1.get("key1"));
114 
115         // make sure changes are reverted after rollback
116         txMap1.startTransaction();
117         txMap1.put("key1", "value3");
118         txMap1.rollbackTransaction();
119         report("value2", (String) map1.get("key1"));
120         report("value2", (String) txMap1.get("key1"));
121     }
122 
123     public void testContainsKeyWithNullValue() throws Throwable {
124 
125         sLogger.logInfo("Checking containsKey returns true when the value is null");
126 
127         final Map map1 = new HashMap();
128 
129         final TransactionalMapWrapper txMap1 = getNewWrapper(map1);
130 
131         assertTrue(txMap1.isEmpty());
132 
133         // make sure changes are propagated to wrapped map outside tx
134         txMap1.put("key1", null);
135         assertTrue(txMap1.containsKey("key1"));
136 
137         // make sure changes are progated to wrapped map after commit
138         txMap1.startTransaction();
139         txMap1.put("key2", null);
140         assertTrue(txMap1.containsKey("key2"));
141         txMap1.remove("key1");
142         assertTrue(map1.containsKey("key1"));
143         txMap1.commitTransaction();
144         assertTrue(txMap1.containsKey("key2"));
145         assertFalse(txMap1.containsKey("key1"));
146 
147         txMap1.startTransaction();
148         assertTrue(txMap1.containsKey("key2"));
149         txMap1.remove("key2");
150         assertFalse(txMap1.containsKey("key2"));
151         txMap1.commitTransaction();
152     }
153 
154     public void testComplex() throws Throwable {
155 
156         sLogger.logInfo("Checking advanced and complex transaction features");
157 
158         final Map map1 = new HashMap();
159 
160         final TransactionalMapWrapper txMap1 = getNewWrapper(map1);
161 
162         // first fill in some global values:
163         txMap1.put("key1", "value1");
164         txMap1.put("key2", "value2");
165 
166         // let's see if we have all values:
167         sLogger.logInfo("Checking if global values are present");
168 
169         assertTrue(txMap1.containsValue("value1"));
170         assertTrue(txMap1.containsValue("value2"));
171         assertFalse(txMap1.containsValue("novalue"));
172 
173         // ... and all keys
174         sLogger.logInfo("Checking if global keys are present");
175         assertTrue(txMap1.containsKey("key1"));
176         assertTrue(txMap1.containsKey("key2"));
177         assertFalse(txMap1.containsKey("nokey"));
178 
179         // and now some inside a transaction
180         txMap1.startTransaction();
181         txMap1.put("key3", "value3");
182         txMap1.put("key4", "value4");
183 
184         // let's see if we have all values:
185         sLogger.logInfo("Checking if values inside transactions are present");
186         assertTrue(txMap1.containsValue("value1"));
187         assertTrue(txMap1.containsValue("value2"));
188         assertTrue(txMap1.containsValue("value3"));
189         assertTrue(txMap1.containsValue("value4"));
190         assertFalse(txMap1.containsValue("novalue"));
191 
192         // ... and all keys
193         sLogger.logInfo("Checking if keys inside transactions are present");
194         assertTrue(txMap1.containsKey("key1"));
195         assertTrue(txMap1.containsKey("key2"));
196         assertTrue(txMap1.containsKey("key3"));
197         assertTrue(txMap1.containsKey("key4"));
198         assertFalse(txMap1.containsKey("nokey"));
199 
200         // now let's delete some old stuff
201         sLogger.logInfo("Checking remove inside transactions");
202         txMap1.remove("key1");
203         assertFalse(txMap1.containsKey("key1"));
204         assertFalse(txMap1.containsValue("value1"));
205         assertNull(txMap1.get("key1"));
206         assertEquals(3, txMap1.size());
207 
208         // and some newly created
209         txMap1.remove("key3");
210         assertFalse(txMap1.containsKey("key3"));
211         assertFalse(txMap1.containsValue("value3"));
212         assertNull(txMap1.get("key3"));
213         assertEquals(2, txMap1.size());
214 
215         sLogger.logInfo("Checking remove and propagation after commit");
216         txMap1.commitTransaction();
217 
218         txMap1.remove("key1");
219         assertFalse(txMap1.containsKey("key1"));
220         assertFalse(txMap1.containsValue("value1"));
221         assertNull(txMap1.get("key1"));
222         assertFalse(txMap1.containsKey("key3"));
223         assertFalse(txMap1.containsValue("value3"));
224         assertNull(txMap1.get("key3"));
225         assertEquals(2, txMap1.size());
226     }
227 
228     public void testSets() throws Throwable {
229 
230         sLogger.logInfo("Checking set opertaions");
231 
232         final Map map1 = new HashMap();
233 
234         final TransactionalMapWrapper txMap1 = getNewWrapper(map1);
235 
236         // first fill in some global values:
237         txMap1.put("key1", "value1");
238         txMap1.put("key2", "value200");
239 
240         // and now some inside a transaction
241         txMap1.startTransaction();
242         txMap1.put("key2", "value2"); // modify 
243         txMap1.put("key3", "value3");
244         txMap1.put("key4", "value4");
245 
246         // check entry set
247         boolean key1P, key2P, key3P, key4P;
248         key1P = key2P = key3P = key4P = false;
249         int cnt = 0;
250         for (Iterator it = txMap1.entrySet().iterator(); it.hasNext();) {
251             cnt++;
252             Map.Entry entry = (Map.Entry) it.next();
253             if (entry.getKey().equals("key1") && entry.getValue().equals("value1"))
254                 key1P = true;
255             else if (entry.getKey().equals("key2") && entry.getValue().equals("value2"))
256                 key2P = true;
257             else if (entry.getKey().equals("key3") && entry.getValue().equals("value3"))
258                 key3P = true;
259             else if (entry.getKey().equals("key4") && entry.getValue().equals("value4"))
260                 key4P = true;
261         }
262         assertEquals(cnt, 4);
263         assertTrue(key1P && key2P && key3P && key4P);
264 
265         checkCollection(txMap1.values(), new String[] { "value1", "value2", "value3", "value4" });
266         checkCollection(txMap1.keySet(), new String[] { "key1", "key2", "key3", "key4" });
267 
268         txMap1.commitTransaction();
269 
270         // check again after commit (should be the same)
271         key1P = key2P = key3P = key4P = false;
272         cnt = 0;
273         for (Iterator it = txMap1.entrySet().iterator(); it.hasNext();) {
274             cnt++;
275             Map.Entry entry = (Map.Entry) it.next();
276             if (entry.getKey().equals("key1") && entry.getValue().equals("value1"))
277                 key1P = true;
278             else if (entry.getKey().equals("key2") && entry.getValue().equals("value2"))
279                 key2P = true;
280             else if (entry.getKey().equals("key3") && entry.getValue().equals("value3"))
281                 key3P = true;
282             else if (entry.getKey().equals("key4") && entry.getValue().equals("value4"))
283                 key4P = true;
284         }
285         assertEquals(cnt, 4);
286         assertTrue(key1P && key2P && key3P && key4P);
287 
288         checkCollection(txMap1.values(), new String[] { "value1", "value2", "value3", "value4" });
289         checkCollection(txMap1.keySet(), new String[] { "key1", "key2", "key3", "key4" });
290 
291         // now try clean
292 
293         txMap1.startTransaction();
294 
295         // add
296         txMap1.put("key5", "value5");
297         // modify
298         txMap1.put("key4", "value400");
299         // delete
300         txMap1.remove("key1");
301 
302         assertEquals(txMap1.size(), 4);
303 
304         txMap1.clear();
305         assertEquals(txMap1.size(), 0);
306         assertEquals(map1.size(), 4);
307 
308         // add
309         txMap1.put("key5", "value5");
310         // delete
311         txMap1.remove("key1");
312 
313         // adding one, not removing anything gives size 1
314         assertEquals(txMap1.size(), 1);
315         assertEquals(map1.size(), 4);
316         assertNull(txMap1.get("key4"));
317         assertNotNull(txMap1.get("key5"));
318 
319         txMap1.commitTransaction();
320 
321         // after commit clear must have been propagated to wrapped map:
322         assertEquals(txMap1.size(), 1);
323         assertEquals(map1.size(), 1);
324         assertNull(txMap1.get("key4"));
325         assertNotNull(txMap1.get("key5"));
326         assertNull(map1.get("key4"));
327         assertNotNull(map1.get("key5"));
328     }
329 
330     public void testMulti() throws Throwable {
331         sLogger.logInfo("Checking concurrent transaction features");
332 
333         final Map map1 = new HashMap();
334 
335         final TransactionalMapWrapper txMap1 = getNewWrapper(map1);
336 
337         final RendezvousBarrier beforeCommitBarrier =
338             new RendezvousBarrier("Before Commit", 2, BARRIER_TIMEOUT, sLogger);
339 
340         final RendezvousBarrier afterCommitBarrier = new RendezvousBarrier("After Commit", 2, BARRIER_TIMEOUT, sLogger);
341 
342         Thread thread1 = new Thread(new Runnable() {
343             public void run() {
344                 txMap1.startTransaction();
345                 try {
346                     beforeCommitBarrier.meet();
347                     txMap1.put("key1", "value2");
348                     txMap1.commitTransaction();
349                     afterCommitBarrier.call();
350                 } catch (InterruptedException e) {
351                     sLogger.logWarning("Thread interrupted", e);
352                     afterCommitBarrier.reset();
353                     beforeCommitBarrier.reset();
354                 }
355             }
356         }, "Thread1");
357 
358         txMap1.put("key1", "value1");
359 
360         txMap1.startTransaction();
361         thread1.start();
362 
363         report("value1", (String) txMap1.get("key1"));
364         beforeCommitBarrier.call();
365         afterCommitBarrier.meet();
366         // we have read committed as isolation level, that's why I will see the new value of the other thread now
367         report("value2", (String) txMap1.get("key1"));
368 
369         // now when I override it it should of course be my value again
370         txMap1.put("key1", "value3");
371         report("value3", (String) txMap1.get("key1"));
372 
373         // after rollback it must be the value written by the other thread again
374         txMap1.rollbackTransaction();
375         report("value2", (String) txMap1.get("key1"));
376     }
377 
378     public void testTxControl() throws Throwable {
379         sLogger.logInfo("Checking advanced transaction control (heavily used in JCA implementation)");
380 
381         final Map map1 = new HashMap();
382 
383         final TransactionalMapWrapper txMap1 = getNewWrapper(map1);
384 
385         assertEquals(txMap1.getTransactionState(), Status.STATUS_NO_TRANSACTION);
386         txMap1.startTransaction();
387         assertEquals(txMap1.getTransactionState(), Status.STATUS_ACTIVE);
388 
389         assertTrue(txMap1.isReadOnly());
390         txMap1.put("key", "value");
391         assertFalse(txMap1.isReadOnly());
392 
393         assertFalse(txMap1.isTransactionMarkedForRollback());
394         txMap1.markTransactionForRollback();
395         assertTrue(txMap1.isTransactionMarkedForRollback());
396 
397         boolean failed = false;
398         try {
399             txMap1.commitTransaction();
400         } catch (IllegalStateException ise) {
401             failed = true;
402         }
403         assertTrue(failed);
404         txMap1.rollbackTransaction();
405         assertEquals(txMap1.getTransactionState(), Status.STATUS_NO_TRANSACTION);
406 
407         txMap1.startTransaction();
408         final TransactionalMapWrapper.TxContext ctx = txMap1.suspendTransaction();
409         final RendezvousBarrier afterSuspendBarrier =
410             new RendezvousBarrier("After Suspend", 2, BARRIER_TIMEOUT, sLogger);
411 
412         new Thread(new Runnable() {
413             public void run() {
414                 txMap1.resumeTransaction(ctx);
415                 txMap1.put("key2", "value2");
416                 txMap1.suspendTransaction();
417                 afterSuspendBarrier.call();
418             }
419         }).start();
420 
421         afterSuspendBarrier.meet();
422         txMap1.resumeTransaction(ctx);
423 
424         assertEquals(txMap1.size(), 1);
425         txMap1.put("key3", "value3");
426         assertEquals(txMap1.size(), 2);
427         assertEquals(map1.size(), 0);
428 
429         txMap1.commitTransaction();
430         assertEquals(txMap1.size(), 2);
431         assertEquals(map1.size(), 2);
432     }
433 
434 }