001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.transaction.memory; 018 019import java.util.Collection; 020import java.util.HashMap; 021import java.util.Iterator; 022import java.util.Map; 023 024import javax.transaction.Status; 025 026import junit.framework.Test; 027import junit.framework.TestCase; 028import junit.framework.TestSuite; 029 030import org.apache.commons.logging.Log; 031import org.apache.commons.logging.LogFactory; 032 033import org.apache.commons.transaction.util.CommonsLoggingLogger; 034import org.apache.commons.transaction.util.LoggerFacade; 035import org.apache.commons.transaction.util.RendezvousBarrier; 036 037/** 038 * Tests for map wrapper. 039 * 040 * @version $Id: MapWrapperTest.java 493628 2007-01-07 01:42:48Z joerg $ 041 */ 042public class MapWrapperTest extends TestCase { 043 044 private static final Log log = LogFactory.getLog(MapWrapperTest.class.getName()); 045 private static final LoggerFacade sLogger = new CommonsLoggingLogger(log); 046 047 protected static final long BARRIER_TIMEOUT = 20000; 048 049 // XXX need this, as JUnit seems to print only part of these strings 050 protected static void report(String should, String is) { 051 if (!should.equals(is)) { 052 fail("\nWrong output:\n'" + is + "'\nShould be:\n'" + should + "'\n"); 053 } 054 } 055 056 protected static void checkCollection(Collection col, Object[] values) { 057 int cnt = 0; 058 int trueCnt = 0; 059 060 for (Iterator it = col.iterator(); it.hasNext();) { 061 cnt++; 062 Object value1 = it.next(); 063 for (int i = 0; i < values.length; i++) { 064 Object value2 = values[i]; 065 if (value2.equals(value1)) 066 trueCnt++; 067 } 068 } 069 assertEquals(cnt, values.length); 070 assertEquals(trueCnt, values.length); 071 } 072 073 public static Test suite() { 074 TestSuite suite = new TestSuite(MapWrapperTest.class); 075 return suite; 076 } 077 078 public static void main(java.lang.String[] args) { 079 junit.textui.TestRunner.run(suite()); 080 } 081 082 public MapWrapperTest(String testName) { 083 super(testName); 084 } 085 086 protected TransactionalMapWrapper getNewWrapper(Map map) { 087 return new TransactionalMapWrapper(map); 088 } 089 090 public void testBasic() throws Throwable { 091 092 sLogger.logInfo("Checking basic transaction features"); 093 094 final Map map1 = new HashMap(); 095 096 final TransactionalMapWrapper txMap1 = getNewWrapper(map1); 097 098 assertTrue(txMap1.isEmpty()); 099 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}