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