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}