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.file; 018 019import java.io.BufferedReader; 020import java.io.File; 021import java.io.FileInputStream; 022import java.io.FileOutputStream; 023import java.io.IOException; 024import java.io.InputStream; 025import java.io.InputStreamReader; 026import java.io.OutputStream; 027 028import javax.transaction.Status; 029 030import junit.framework.Test; 031import junit.framework.TestCase; 032import junit.framework.TestSuite; 033 034import org.apache.commons.logging.Log; 035import org.apache.commons.logging.LogFactory; 036 037import org.apache.commons.transaction.util.CommonsLoggingLogger; 038import org.apache.commons.transaction.util.FileHelper; 039import org.apache.commons.transaction.util.LoggerFacade; 040import org.apache.commons.transaction.util.RendezvousBarrier; 041 042/** 043 * Tests for FileResourceManager. 044 * 045 * @version $Id: FileResourceManagerTest.java 493628 2007-01-07 01:42:48Z joerg $ 046 */ 047public class FileResourceManagerTest extends TestCase { 048 049 private static final Log log = LogFactory.getLog(FileResourceManagerTest.class.getName()); 050 private static final LoggerFacade sLogger = new CommonsLoggingLogger(log); 051 052 private static final String STORE = "tmp/store"; 053 private static final String WORK = "tmp/work"; 054 private static final String ENCODING = "ISO-8859-15"; 055 // FIXME 056 // XXX INCREASE THIS WHEN DEBUGGING OTHERWISE THE BARRIER WILL TIME OUT AFTER TWO SECONDS 057 // MOST LIKELY CONFUSING YOU COMPLETELY 058 private static final long BARRIER_TIMEOUT = 200000; 059 060 private static final String[] INITIAL_FILES = new String[] { STORE + "/olli/Hubert6", STORE + "/olli/Hubert" }; 061 062 private static final String STATUS_COMMITTING_CONTEXT = 063 "8\n10\n2000\n1063099404687\n"; 064 private static final String[] STATUS_COMMITTING_CONTEXT_CHANGE_FILES = 065 new String[] { "olli/Hubert40", "olli/Hubert50" }; 066 private static final String[] STATUS_COMMITTING_CONTEXT_DELETE_FILES = new String[] { "/olli/Hubert" }; 067 private static final String[] STATUS_COMMITTING_CONTEXT_RESULT_FILES = 068 new String[] { "Hubert6", "Hubert50", "Hubert40" }; 069 070 private static void initCommittingRecovery() throws Throwable { 071 String txId = "COMMITTING"; 072 createTxContextFile(txId, STATUS_COMMITTING_CONTEXT); 073 createTxDeleteFiles(txId, STATUS_COMMITTING_CONTEXT_DELETE_FILES); 074 createTxChangeFiles(txId, STATUS_COMMITTING_CONTEXT_CHANGE_FILES); 075 } 076 077 private static final String STATUS_COMMITTED_CONTEXT = 078 "3\n10\n2000\n1063099404687\n"; 079 private static final String[] STATUS_COMMITTED_CONTEXT_CHANGE_FILES = 080 new String[] { "olli/Hubert4", "olli/Hubert5" }; 081 private static final String[] STATUS_COMMITTED_CONTEXT_DELETE_FILES = new String[] { 082 }; 083 private static final String[] STATUS_COMMITTED_CONTEXT_RESULT_FILES = new String[] { "Hubert6", "Hubert" }; 084 085 protected static final long TIMEOUT = Long.MAX_VALUE; 086 087 private static int deadlockCnt = 0; 088 089 private static void initCommittedRecovery() throws Throwable { 090 String txId = "COMMITTED"; 091 createTxContextFile(txId, STATUS_COMMITTED_CONTEXT); 092 createTxDeleteFiles(txId, STATUS_COMMITTED_CONTEXT_DELETE_FILES); 093 createTxChangeFiles(txId, STATUS_COMMITTED_CONTEXT_CHANGE_FILES); 094 } 095 096 private static final String STATUS_ROLLING_BACK_CONTEXT = 097 "9\n10\n2000\n1063099404687\n"; 098 private static final String[] STATUS_ROLLING_BACK_CONTEXT_CHANGE_FILES = 099 new String[] { "olli/Hubert4", "olli/Hubert5" }; 100 private static final String[] STATUS_ROLLING_BACK_CONTEXT_DELETE_FILES = new String[] { 101 }; 102 private static final String[] STATUS_ROLLING_BACK_CONTEXT_RESULT_FILES = new String[] { "Hubert6", "Hubert" }; 103 104 private static void initRollingBackRecovery() throws Throwable { 105 String txId = "ROLLING_BACK"; 106 createTxContextFile(txId, STATUS_ROLLING_BACK_CONTEXT); 107 createTxDeleteFiles(txId, STATUS_ROLLING_BACK_CONTEXT_DELETE_FILES); 108 createTxChangeFiles(txId, STATUS_ROLLING_BACK_CONTEXT_CHANGE_FILES); 109 } 110 111 private static final String STATUS_ROLLEDBACK_CONTEXT = 112 "4\n10\n2000\n1063099404687\n"; 113 private static final String[] STATUS_ROLLEDBACK_CONTEXT_CHANGE_FILES = 114 new String[] { "olli/Hubert4", "olli/Hubert5" }; 115 private static final String[] STATUS_ROLLEDBACK_CONTEXT_DELETE_FILES = new String[] { 116 }; 117 private static final String[] STATUS_ROLLEDBACK_CONTEXT_RESULT_FILES = new String[] { "Hubert6", "Hubert" }; 118 119 private static void initRolledBackRecovery() throws Throwable { 120 String txId = "ROLLEDBACK"; 121 createTxContextFile(txId, STATUS_ROLLEDBACK_CONTEXT); 122 createTxDeleteFiles(txId, STATUS_ROLLEDBACK_CONTEXT_DELETE_FILES); 123 createTxChangeFiles(txId, STATUS_ROLLEDBACK_CONTEXT_CHANGE_FILES); 124 } 125 126 private static final String STATUS_ACTIVE_CONTEXT = "0\n10\n2000\n1063099404687\n"; 127 private static final String[] STATUS_ACTIVE_CONTEXT_CHANGE_FILES = new String[] { "olli/Hubert4", "olli/Hubert5" }; 128 private static final String[] STATUS_ACTIVE_CONTEXT_DELETE_FILES = new String[] { 129 }; 130 private static final String[] STATUS_ACTIVE_CONTEXT_RESULT_FILES = new String[] { "Hubert6", "Hubert" }; 131 132 private static void initActiveRecovery() throws Throwable { 133 String txId = "ACTIVE"; 134 createTxContextFile(txId, STATUS_ACTIVE_CONTEXT); 135 createTxDeleteFiles(txId, STATUS_ACTIVE_CONTEXT_DELETE_FILES); 136 createTxChangeFiles(txId, STATUS_ACTIVE_CONTEXT_CHANGE_FILES); 137 } 138 139 private static void removeRec(String dirPath) { 140 FileHelper.removeRec(new File(dirPath)); 141 } 142 143 private static final void createFiles(String[] filePaths) { 144 createFiles(filePaths, null, null); 145 } 146 147 private static final void createFiles(String[] filePaths, String dirPath) { 148 createFiles(filePaths, null, dirPath); 149 } 150 151 private static final void createFiles(String[] filePaths, String[] contents) { 152 createFiles(filePaths, contents, null); 153 } 154 155 private static final void createFiles(String[] filePaths, String[] contents, String dirPath) { 156 for (int i = 0; i < filePaths.length; i++) { 157 String filePath = filePaths[i]; 158 File file; 159 if (dirPath != null) { 160 file = new File(new File(dirPath), filePath); 161 } else { 162 file = new File(filePath); 163 } 164 file.getParentFile().mkdirs(); 165 try { 166 file.delete(); 167 file.createNewFile(); 168 String content = null; 169 if (contents != null && contents.length > i) { 170 content = contents[i]; 171 } 172 if (content != null) { 173 FileOutputStream stream = new FileOutputStream(file); 174 stream.write(contents[i].getBytes(ENCODING)); 175 stream.close(); 176 } 177 } catch (IOException e) { 178 } 179 } 180 } 181 182 private static final void checkIsEmpty(String dirPath) { 183 checkExactlyContains(dirPath, null); 184 } 185 private static final void checkExactlyContains(String dirPath, String[] fileNames) { 186 checkExactlyContains(dirPath, fileNames, null); 187 } 188 189 private static final void checkExactlyContains(String dirPath, String[] fileNames, 190 String[] contents) { 191 File dir = new File(dirPath); 192 193 if (dir.isDirectory()) { 194 File[] files = dir.listFiles(); 195 if (fileNames == null) { 196 if (files.length != 0) { 197 fail(dirPath + " must be empty"); 198 } else { 199 return; 200 } 201 } 202 203 if (files.length != fileNames.length) { 204 fail(dirPath + " contains " + files.length + " instead of " + fileNames.length 205 + " files"); 206 } 207 208 for (int i = 0; i < fileNames.length; i++) { 209 String fileName = fileNames[i]; 210 boolean match = false; 211 File file = null; 212 for (int j = 0; j < files.length; j++) { 213 file = files[j]; 214 if (file.getName().equals(fileName)) { 215 match = true; 216 break; 217 } 218 } 219 if (!match) { 220 fail(dirPath + " does not contain required " + fileName); 221 } 222 223 String content = null; 224 if (contents != null && i < contents.length) { 225 content = contents[i]; 226 } 227 if (content != null && !compare(file, content)) { 228 fail("Contents of " + fileName + " in " + dirPath 229 + " does not contain required content '" + content + "'"); 230 } 231 } 232 233 } else { 234 fail(dirPath + " is not directoy"); 235 } 236 } 237 238 private static boolean compare(FileInputStream stream, byte[] bytes) { 239 int read; 240 int count = 0; 241 try { 242 while ((read = stream.read()) != -1) { 243 if (bytes[count++] != read) { 244 return false; 245 } 246 } 247 } catch (IOException e) { 248 return false; 249 } 250 return true; 251 } 252 253 private static boolean compare(File file, String content) { 254 FileInputStream stream = null; 255 try { 256 byte[] bytes = content.getBytes(ENCODING); 257 stream = new FileInputStream(file); 258 return compare(stream, bytes); 259 } catch (Throwable t) { 260 return false; 261 } finally { 262 if (stream != null) { 263 try { 264 stream.close(); 265 } catch (IOException e) { 266 } 267 } 268 } 269 } 270 271 private static String workForTx(Object txId) { 272 return WORK + "/" + txId; 273 } 274 275 private static String changeForTx(Object txId) { 276 return workForTx(txId) + "/change"; 277 } 278 279 private static String deleteForTx(Object txId) { 280 return workForTx(txId) + "/delete"; 281 } 282 283 private static String logForTx(Object txId) { 284 return workForTx(txId) + "/transaction.log"; 285 } 286 287 private static void reset() { 288 removeRec(STORE); 289 removeRec(WORK); 290 new File(STORE).mkdirs(); 291 new File(WORK).mkdirs(); 292 } 293 294 private static void createInitialFiles() { 295 createFiles(INITIAL_FILES); 296 } 297 298 private static void createTxContextFile(Object txId, String content) { 299 createFiles(new String[] { logForTx(txId)}, new String[] { txId + "\n" + content }); 300 } 301 302 private static void createTxDeleteFiles(Object txId, String[] files) { 303 createFiles(files, deleteForTx(txId)); 304 } 305 306 private static void createTxChangeFiles(Object txId, String[] files) { 307 createFiles(files, changeForTx(txId)); 308 } 309 310 // XXX need this, as JUnit seems to print only part of these strings 311 private static void report(String should, String is) { 312 if (!is.equals(should)) { 313 fail("\nWrong output:\n'" + is + "'\nShould be:\n'" + should + "'\n"); 314 } 315 } 316 317 public static FileResourceManager createFRM() { 318 return new FileResourceManager(STORE, WORK, false, sLogger, true); 319 } 320 321 public static Test suite() { 322 TestSuite suite = new TestSuite(FileResourceManagerTest.class); 323 return suite; 324 } 325 326 public static void main(java.lang.String[] args) { 327 junit.textui.TestRunner.run(suite()); 328 } 329 330 public FileResourceManagerTest(String testName) { 331 super(testName); 332 } 333 334 public void testGlobal() throws Throwable { 335 reset(); 336 createInitialFiles(); 337 338 final FileResourceManager rm = createFRM(); 339 340 rm.start(); 341 342 final RendezvousBarrier shutdownBarrier = new RendezvousBarrier("Shutdown", 3, BARRIER_TIMEOUT, sLogger); 343 final RendezvousBarrier start2Barrier = new RendezvousBarrier("Start2", BARRIER_TIMEOUT, sLogger); 344 final RendezvousBarrier commit1Barrier = new RendezvousBarrier("Commit1", BARRIER_TIMEOUT, sLogger); 345 346 final Object txId1 = "Create"; 347 348 Thread create = new Thread(new Runnable() { 349 public void run() { 350 try { 351 rm.startTransaction(txId1); 352 353 shutdownBarrier.call(); 354 start2Barrier.call(); 355 356 rm.createResource(txId1, "/olli/Hubert4"); 357 rm.createResource(txId1, "/olli/Hubert5"); 358 String msg = "Greetings from " + txId1 + "\n"; 359 OutputStream out = rm.writeResource(txId1, "/olli/Hubert6"); 360 out.write(msg.getBytes(ENCODING)); 361 362 commit1Barrier.meet(); 363 364 checkExactlyContains( 365 changeForTx(txId1) + "/olli", 366 new String[] { "Hubert4", "Hubert5", "Hubert6" }, 367 new String[] { "", "", "Greetings from " + txId1 + "\n" }); 368 369 rm.commitTransaction(txId1); 370 371 checkExactlyContains( 372 STORE + "/olli", 373 new String[] { "Hubert", "Hubert4", "Hubert5", "Hubert6" }, 374 new String[] { "", "", "", "Greetings from " + txId1 + "\n" }); 375 376 } catch (Throwable e) { 377 System.err.println("Error: " + e); 378 e.printStackTrace(); 379 } 380 } 381 }, "Create Thread"); 382 383 Thread modify = new Thread(new Runnable() { 384 public void run() { 385 Object txId = null; 386 try { 387 388 { 389 InputStream in = rm.readResource("/olli/Hubert6"); 390 BufferedReader reader = new BufferedReader(new InputStreamReader(in, ENCODING)); 391 String line = reader.readLine(); 392 assertEquals(line, null); 393 in.close(); 394 } 395 396 txId = "Modify"; 397 rm.startTransaction(txId); 398 rm.setIsolationLevel(txId, ResourceManager.ISOLATION_LEVEL_READ_COMMITTED); 399 400 { 401 InputStream in = rm.readResource(txId, "/olli/Hubert6"); 402 BufferedReader reader = new BufferedReader(new InputStreamReader(in, ENCODING)); 403 String line = reader.readLine(); 404 assertEquals(line, null); 405 in.close(); 406 } 407 408 shutdownBarrier.call(); 409 410 rm.createResource(txId, "/olli/Hubert1"); 411 rm.createResource(txId, "/olli/Hubert2"); 412 rm.createResource(txId, "/olli/Hubert3"); 413 414 // wait until tx commits, so there already are Hubert4 and Hubert5 and 415 // Hubert6 changes 416 commit1Barrier.meet(); 417 418 rm.createResource(txId, "/olli/Hubert4"); 419 rm.createResource(txId, "/olli/Hubert5"); 420 421 rm.createResource(txId, "/olli/Hubert6"); 422 InputStream in = rm.readResource(txId, "/olli/Hubert6"); 423 BufferedReader reader = new BufferedReader(new InputStreamReader(in, ENCODING)); 424 String line = reader.readLine(); 425 // allow for update while in tx as this is READ_COMMITED 426 report("Greetings from " + txId1, line); 427 in.close(); 428 429 rm.deleteResource(txId, "/olli/Hubert"); 430 rm.deleteResource(txId, "/olli/Hubert2"); 431 rm.deleteResource(txId, "/olli/Hubert3"); 432 rm.deleteResource(txId, "/olli/Hubert4"); 433 rm.deleteResource(txId, "/olli/Hubert5"); 434 435 checkExactlyContains(deleteForTx(txId) + "/olli", new String[] { "Hubert", "Hubert4", "Hubert5" }); 436 437 checkExactlyContains(changeForTx(txId) + "/olli", new String[] { "Hubert1" }); 438 439 rm.commitTransaction(txId); 440 } catch (Throwable e) { 441 System.err.println("Error: " + e); 442 e.printStackTrace(); 443 } 444 } 445 }, "Modify Thread"); 446 447 create.start(); 448 // be sure first thread is started before trying next 449 start2Barrier.meet(); 450 modify.start(); 451 452 // let both transaction start before trying to shut down 453 shutdownBarrier.meet(); 454 455 assertTrue(rm.stop(ResourceManager.SHUTDOWN_MODE_NORMAL, 5000)); 456 457 checkExactlyContains( 458 STORE + "/olli", 459 new String[] { "Hubert1", "Hubert6" }, 460 new String[] { "", "Greetings from " + txId1 + "\n" }); 461 checkIsEmpty(WORK); 462 } 463 464 public void testCombinedRecovery() throws Throwable { 465 reset(); 466 createInitialFiles(); 467 initCommittingRecovery(); 468 initCommittedRecovery(); 469 initActiveRecovery(); 470 initRolledBackRecovery(); 471 initRollingBackRecovery(); 472 473 FileResourceManager rm =createFRM(); 474 475 // do nothing, just start and stop to check recovery of tx 476 rm.start(); 477 assertTrue(rm.stop(ResourceManager.SHUTDOWN_MODE_NORMAL, 5000)); 478 479 // all but committing should be rolled back 480 checkExactlyContains(STORE + "/olli", STATUS_COMMITTING_CONTEXT_RESULT_FILES); 481 checkIsEmpty(WORK); 482 } 483 484 public void testCommittingRecovery() throws Throwable { 485 reset(); 486 createInitialFiles(); 487 initCommittingRecovery(); 488 489 FileResourceManager rm = createFRM(); 490 491 // do nothing, just start and stop to check recovery of tx 492 rm.start(); 493 assertTrue(rm.stop(ResourceManager.SHUTDOWN_MODE_NORMAL, 5000)); 494 495 checkExactlyContains(STORE + "/olli", STATUS_COMMITTING_CONTEXT_RESULT_FILES); 496 checkIsEmpty(WORK); 497 } 498 499 public void testActiveRecovery() throws Throwable { 500 reset(); 501 createInitialFiles(); 502 initActiveRecovery(); 503 504 FileResourceManager rm = createFRM(); 505 506 // do nothing, just start and stop to check recovery of tx 507 rm.start(); 508 assertTrue(rm.stop(ResourceManager.SHUTDOWN_MODE_NORMAL, 5000)); 509 510 checkExactlyContains(STORE + "/olli", STATUS_ACTIVE_CONTEXT_RESULT_FILES); 511 checkIsEmpty(WORK); 512 } 513 514 public void testRolledbackRecovery() throws Throwable { 515 reset(); 516 createInitialFiles(); 517 initRolledBackRecovery(); 518 519 FileResourceManager rm = createFRM(); 520 521 // do nothing, just start and stop to check recovery of tx 522 rm.start(); 523 assertTrue(rm.stop(ResourceManager.SHUTDOWN_MODE_NORMAL, 5000)); 524 525 checkExactlyContains(STORE + "/olli", STATUS_ROLLEDBACK_CONTEXT_RESULT_FILES); 526 checkIsEmpty(WORK); 527 } 528 529 public void testRollingBackRecovery() throws Throwable { 530 reset(); 531 createInitialFiles(); 532 initRollingBackRecovery(); 533 534 FileResourceManager rm = createFRM(); 535 536 // do nothing, just start and stop to check recovery of tx 537 rm.start(); 538 assertTrue(rm.stop(ResourceManager.SHUTDOWN_MODE_NORMAL, 5000)); 539 540 checkExactlyContains(STORE + "/olli", STATUS_ROLLING_BACK_CONTEXT_RESULT_FILES); 541 checkIsEmpty(WORK); 542 } 543 544 public void testCommittedRecovery() throws Throwable { 545 reset(); 546 createInitialFiles(); 547 initCommittedRecovery(); 548 549 FileResourceManager rm = createFRM(); 550 551 // do nothing, just start and stop to check recovery of tx 552 rm.start(); 553 assertTrue(rm.stop(ResourceManager.SHUTDOWN_MODE_NORMAL, 5000)); 554 555 checkExactlyContains(STORE + "/olli", STATUS_COMMITTED_CONTEXT_RESULT_FILES); 556 checkIsEmpty(WORK); 557 } 558 559 public void testInteractiveDirtyRecovery() throws Throwable { 560 reset(); 561 createInitialFiles(); 562 563 FileResourceManager rm = createFRM(); 564 565 rm.start(); 566 567 String txId = "DIRTY"; 568 rm.startTransaction(txId); 569 rm.createResource(txId, "/olli/Hubert100"); 570 571 // fake a failed commit 572 FileResourceManager.TransactionContext context = rm.getContext(txId); 573 // needing synchronization in order not to interfer with shutdown thread 574 synchronized (context) { 575 sLogger.logFine("Committing Tx " + txId); 576 577 context.status = Status.STATUS_COMMITTING; 578 context.saveState(); 579 rm.dirty = true; 580 context.finalCleanUp(); 581 context.notifyFinish(); 582 } 583 584 // should be allowed 585 rm.readResource(txId, "/olli/Hubert"); 586 587 // should be disallowed 588 boolean writeDeniedByDirty = false; 589 try { 590 rm.createResource(txId, "/olli/Hubert10"); 591 } catch (ResourceManagerSystemException rmse) { 592 writeDeniedByDirty = true; 593 } 594 assertTrue(writeDeniedByDirty); 595 596 // on success (expected) resets dirty flag 597 rm.recover(); 598 599 // should all be allowed again 600 txId = "DIRTYTEST"; 601 rm.startTransaction(txId); 602 rm.readResource(txId, "/olli/Hubert"); 603 rm.createResource(txId, "/olli/Hubert10"); 604 rm.commitTransaction(txId); 605 606 assertTrue(rm.stop(ResourceManager.SHUTDOWN_MODE_NORMAL, 5000)); 607 608 // tx rolled forward created "/olli/Hubert100", so it should be here as well 609 checkExactlyContains(STORE + "/olli", new String[] { "Hubert", "Hubert100", "Hubert6", "Hubert10" }); 610 checkIsEmpty(WORK); 611 } 612 613 public void testConflict() throws Throwable { 614 sLogger.logInfo("Checking concurrent transaction features"); 615 616 reset(); 617 createInitialFiles(); 618 619 final FileResourceManager rm = createFRM(); 620 621 rm.start(); 622 623 final RendezvousBarrier restart = new RendezvousBarrier("restart", 624 TIMEOUT, sLogger); 625 626 for (int i = 0; i < 25; i++) { 627 628 final RendezvousBarrier deadlockBarrier1 = new RendezvousBarrier("deadlock" + i, 629 TIMEOUT, sLogger); 630 631 Thread thread1 = new Thread(new Runnable() { 632 public void run() { 633 try { 634 rm.startTransaction("tx1"); 635 // first both threads get a lock, this one on res2 636 rm.createResource("tx1", "key2"); 637 synchronized (deadlockBarrier1) { 638 deadlockBarrier1.meet(); 639 deadlockBarrier1.reset(); 640 } 641 // if I am first, the other thread will be dead, i.e. 642 // exactly one 643 rm.createResource("tx1", "key1"); 644 rm.commitTransaction("tx1"); 645 } catch (InterruptedException ie) { 646 } catch (ResourceManagerException e) { 647 assertEquals(e.getStatus(), ResourceManagerErrorCodes.ERR_DEAD_LOCK); 648 deadlockCnt++; 649 try { 650 rm.rollbackTransaction("tx1"); 651 } catch (ResourceManagerException e1) { 652 // TODO Auto-generated catch block 653 e1.printStackTrace(); 654 } 655 } finally { 656 try { 657 synchronized (restart) { 658 restart.meet(); 659 restart.reset(); 660 } 661 } catch (InterruptedException ie) {} 662 663 } 664 } 665 }, "Thread1"); 666 667 thread1.start(); 668 669 rm.startTransaction("tx2"); 670 try { 671 // first both threads get a lock, this one on res2 672 rm.deleteResource("tx2", "key1"); 673 synchronized (deadlockBarrier1) { 674 deadlockBarrier1.meet(); 675 deadlockBarrier1.reset(); 676 } 677 // if I am first, the other thread will be dead, i.e. exactly 678 // one 679 rm.deleteResource("tx2", "key2"); 680 rm.commitTransaction("tx2"); 681 } catch (ResourceManagerException e) { 682 assertEquals(e.getStatus(), ResourceManagerErrorCodes.ERR_DEAD_LOCK); 683 deadlockCnt++; 684 try { 685 rm.rollbackTransaction("tx2"); 686 } catch (ResourceManagerException e1) { 687 // TODO Auto-generated catch block 688 e1.printStackTrace(); 689 } 690 } finally { 691 try { 692 synchronized (restart) { 693 restart.meet(); 694 restart.reset(); 695 } 696 } catch (InterruptedException ie) {} 697 698 } 699 700 // XXX in special scenarios the current implementation might cause both 701 // owners to be deadlock victims 702 if (deadlockCnt != 1) { 703 sLogger.logWarning("More than one thread was deadlock victim!"); 704 } 705 assertTrue(deadlockCnt >= 1); 706 deadlockCnt = 0; 707 } 708 } 709 710 public void testCopyRec() throws Throwable { 711 sLogger.logInfo("Checking file copy"); 712 reset(); 713 createInitialFiles(); 714 FileHelper.copyRec(new File(INITIAL_FILES[0]), new File(STORE + "/olli/NewFile")); 715 } 716 717}