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.file;
18  
19  import java.io.BufferedReader;
20  import java.io.File;
21  import java.io.FileInputStream;
22  import java.io.FileOutputStream;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.InputStreamReader;
26  import java.io.OutputStream;
27  
28  import javax.transaction.Status;
29  
30  import junit.framework.Test;
31  import junit.framework.TestCase;
32  import junit.framework.TestSuite;
33  
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  
37  import org.apache.commons.transaction.util.CommonsLoggingLogger;
38  import org.apache.commons.transaction.util.FileHelper;
39  import org.apache.commons.transaction.util.LoggerFacade;
40  import org.apache.commons.transaction.util.RendezvousBarrier;
41  
42  /**
43   * Tests for FileResourceManager. 
44   *
45   * @version $Id: FileResourceManagerTest.java 493628 2007-01-07 01:42:48Z joerg $
46   */
47  public class FileResourceManagerTest extends TestCase {
48  
49      private static final Log log = LogFactory.getLog(FileResourceManagerTest.class.getName());
50      private static final LoggerFacade sLogger = new CommonsLoggingLogger(log); 
51  
52      private static final String STORE = "tmp/store";
53      private static final String WORK = "tmp/work";
54      private static final String ENCODING = "ISO-8859-15";
55      // FIXME
56      // XXX INCREASE THIS WHEN DEBUGGING OTHERWISE THE BARRIER WILL TIME OUT AFTER TWO SECONDS
57      // MOST LIKELY CONFUSING YOU COMPLETELY
58      private static final long BARRIER_TIMEOUT = 200000;
59  
60      private static final String[] INITIAL_FILES = new String[] { STORE + "/olli/Hubert6", STORE + "/olli/Hubert" };
61  
62      private static final String STATUS_COMMITTING_CONTEXT =
63          "8\n10\n2000\n1063099404687\n";
64      private static final String[] STATUS_COMMITTING_CONTEXT_CHANGE_FILES =
65          new String[] { "olli/Hubert40", "olli/Hubert50" };
66      private static final String[] STATUS_COMMITTING_CONTEXT_DELETE_FILES = new String[] { "/olli/Hubert" };
67      private static final String[] STATUS_COMMITTING_CONTEXT_RESULT_FILES =
68          new String[] { "Hubert6", "Hubert50", "Hubert40" };
69  
70      private static void initCommittingRecovery() throws Throwable {
71          String txId = "COMMITTING";
72          createTxContextFile(txId, STATUS_COMMITTING_CONTEXT);
73          createTxDeleteFiles(txId, STATUS_COMMITTING_CONTEXT_DELETE_FILES);
74          createTxChangeFiles(txId, STATUS_COMMITTING_CONTEXT_CHANGE_FILES);
75      }
76  
77      private static final String STATUS_COMMITTED_CONTEXT =
78          "3\n10\n2000\n1063099404687\n";
79      private static final String[] STATUS_COMMITTED_CONTEXT_CHANGE_FILES =
80          new String[] { "olli/Hubert4", "olli/Hubert5" };
81      private static final String[] STATUS_COMMITTED_CONTEXT_DELETE_FILES = new String[] {
82      };
83      private static final String[] STATUS_COMMITTED_CONTEXT_RESULT_FILES = new String[] { "Hubert6", "Hubert" };
84  
85      protected static final long TIMEOUT = Long.MAX_VALUE;
86  
87      private static int deadlockCnt = 0;
88  
89      private static void initCommittedRecovery() throws Throwable {
90          String txId = "COMMITTED";
91          createTxContextFile(txId, STATUS_COMMITTED_CONTEXT);
92          createTxDeleteFiles(txId, STATUS_COMMITTED_CONTEXT_DELETE_FILES);
93          createTxChangeFiles(txId, STATUS_COMMITTED_CONTEXT_CHANGE_FILES);
94      }
95  
96      private static final String STATUS_ROLLING_BACK_CONTEXT =
97          "9\n10\n2000\n1063099404687\n";
98      private static final String[] STATUS_ROLLING_BACK_CONTEXT_CHANGE_FILES =
99          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 }