/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Feb 16, 2007
*/
package com.bigdata.journal;
import java.io.File;
import java.io.IOException;
import java.nio.channels.OverlappingFileLockException;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import org.apache.log4j.Logger;
import com.bigdata.util.InnerCause;
/**
* Test the ability to rollback a commit.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public class TestDoubleOpen extends ProxyTestCase<Journal> {
private static final transient Logger log = Logger
.getLogger(TestDoubleOpen.class);
/**
*
*/
public TestDoubleOpen() {
}
/**
* @param name
*/
public TestDoubleOpen(String name) {
super(name);
}
/**
* This unit test was written to track down an exception which is not always
* thrown for this condition. When the exception is thrown, it is thrown on
* the first double-open request. If the first double-open request succeeds,
* then the next 99 will also succeed. What we SHOULD be seeing is a nice
* {@link OverlappingFileLockException}, and that does get thrown a good
* percentage of the time.
* <p>
* I have since modified the test to accept the 'The handle is invalid'
* IOException as well. That is just making do with reality.
*
* <pre>
* java.util.concurrent.ExecutionException: java.lang.RuntimeException: file=C:\DOCUME~1\BRYANT~1\LOCALS~1\Temp\bigdata-Disk-6474551553928984593.jnl
* at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
* at java.util.concurrent.FutureTask.get(FutureTask.java:83)
* at com.bigdata.journal.TestDoubleOpen.test_doubleOpen(TestDoubleOpen.java:119)
* at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
* at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
* at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
* at java.lang.reflect.Method.invoke(Method.java:597)
* at junit.framework.TestCase.runTest(TestCase.java:154)
* at junit.framework.TestCase.runBare(TestCase.java:127)
* at junit.framework.TestResult$1.protect(TestResult.java:106)
* at junit.framework.TestResult.runProtected(TestResult.java:124)
* at junit.framework.TestResult.run(TestResult.java:109)
* at junit.framework.TestCase.run(TestCase.java:118)
* at junit.framework.TestSuite.runTest(TestSuite.java:208)
* at junit.framework.TestSuite.run(TestSuite.java:203)
* at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130)
* at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
* at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
* at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
* at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
* at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
* Caused by: java.lang.RuntimeException: file=C:\DOCUME~1\BRYANT~1\LOCALS~1\Temp\bigdata-Disk-6474551553928984593.jnl
* at com.bigdata.journal.FileMetadata.<init>(FileMetadata.java:760)
* at com.bigdata.journal.AbstractJournal.<init>(AbstractJournal.java:1066)
* at com.bigdata.journal.AbstractJournal.<init>(AbstractJournal.java:659)
* at com.bigdata.journal.Journal.<init>(Journal.java:136)
* at com.bigdata.journal.TestDoubleOpen$1.call(TestDoubleOpen.java:103)
* at com.bigdata.journal.TestDoubleOpen$1.call(TestDoubleOpen.java:1)
* at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
* at java.util.concurrent.FutureTask.run(FutureTask.java:138)
* at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
* at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
* at java.lang.Thread.run(Thread.java:619)
* Caused by: java.io.IOException: The handle is invalid
* at java.io.RandomAccessFile.length(Native Method)
* at com.bigdata.journal.FileMetadata.<init>(FileMetadata.java:420)
* ... 10 more
* </pre>
* Here is another odd exception which can be thrown (Windows XP)
* <pre>
* Caused by: java.lang.NullPointerException
* at sun.nio.ch.FileChannelImpl$SharedFileLockTable.remove(FileChannelImpl.java:1100)
* at sun.nio.ch.FileChannelImpl.tryLock(FileChannelImpl.java:881)
* at com.bigdata.journal.FileMetadata.reopenChannel(FileMetadata.java:1188)
* at com.bigdata.journal.FileMetadata.access$0(FileMetadata.java:1156)
* at com.bigdata.journal.FileMetadata$1.reopenChannel(FileMetadata.java:1141)
* at com.bigdata.journal.FileMetadata$1.reopenChannel(FileMetadata.java:1)
* at com.bigdata.journal.FileMetadata.<init>(FileMetadata.java:923)
* at com.bigdata.journal.FileMetadata.createInstance(FileMetadata.java:1448)
* at com.bigdata.journal.AbstractJournal.<init>(AbstractJournal.java:870)
* at com.bigdata.journal.Journal.<init>(Journal.java:228)
* at com.bigdata.journal.Journal.<init>(Journal.java:221)
* at com.bigdata.journal.TestDoubleOpen$DoubleOpenTask.call(TestDoubleOpen.java:239)
* ... 6 more
* </pre>
*
* @todo test read-only for 1st open and read-write for 2nd.
*
* @todo test read-write for first open and read-only for 2nd.
*
* @todo test read-only for both opens after initial create.
*
* @throws InterruptedException
* @throws ExecutionException
*/
public void test_doubleOpen() throws InterruptedException, ExecutionException {
final Journal journal = new Journal(getProperties());
try {
if (!journal.isStable()) {
// test is only for stable journals (backed by a file).
return;
}
final File file = journal.getFile();
final Properties p = new Properties(journal.getProperties());
p.setProperty(Journal.Options.CREATE_TEMP_FILE, "false");
p.setProperty(Journal.Options.FILE, file.toString());
final int LIMIT = 5000;
final int nthreads = 4;
// Setup the tasks to be run.
final List<Callable<Void>> tasks = new LinkedList<Callable<Void>>();
for (int i = 0; i < LIMIT; i++) {
tasks.add(new DoubleOpenTask(i,p));
}
final ExecutorService service = Executors.newFixedThreadPool(nthreads);
try {
((ThreadPoolExecutor)service).prestartAllCoreThreads();
// Run tasks.
final List<Future<Void>> futures = service.invokeAll(tasks);
// Check futures. Stops at the first error.
for(Future<Void> f : futures) {
if(f.isCancelled()) {
// Ignore cancelled tasks.
continue;
}
f.get();
}
// for (int i = 0; i < LIMIT; i++) {
//
// if (log.isInfoEnabled())
// log.info("Pass # " + i + " of " + LIMIT);
//
// /*
// * Try to double-open the journal.
// */
//
// final Future<Void> future = journal.getExecutorService()
// .submit(new DoubleOpenTask(p));
//
// future.get();
//
// }
} finally {
service.shutdownNow();
}
} finally {
journal.destroy();
}
}
/**
* Helper class attempts to open a {@link Journal} which should already be
* open in the main thread.
*/
private static class DoubleOpenTask implements Callable<Void> {
final int i;
final Properties p;
public DoubleOpenTask(final int i, final Properties p) {
this.p = p;
this.i = i;
}
//@Override
public Void call() throws Exception {
Journal tmp = null;
// boolean didDoubleOpen = false;
try {
tmp = new Journal(p);
// /*
// * Note: An assertion will be throw after
// * the try / catch clause.
// */
// didDoubleOpen = true;
//
// if (didDoubleOpen)
fail("Double-open of journal is not allowed: index=" + i);
} catch (Throwable t) {
Throwable cause;
if ((cause = InnerCause.getInnerCause(t,
OverlappingFileLockException.class)) != null) {
/*
* This is the expected exception per
* the javadoc.
*/
if (log.isInfoEnabled())
log.info("Double-open refused: index=" + i + " : "
+ cause, t);
} else if ((cause = InnerCause
.getInnerCause(t, IOException.class)) != null) {
/*
* This is another exception which does
* in fact occur some percentage of the
* time.
*/
if (log.isInfoEnabled())
log.info("Double-open refused: index=" + i + " : "
+ cause, t);
} else {
fail("Expecting: index=" + i + " : "
+ OverlappingFileLockException.class + " or "
+ IOException.class + " ('The handle is invalid')",
t);
}
} finally {
if (tmp != null) {
// Ensure closed if double opened.
tmp.close();
}
}
return null;
}
}
}