Project

Profile

Help

Bug #2644 ยป TestSaxonConfigurationInstantiation.java

TestNG test for validating fix and workaround of deadlock issue - Gunther Rademacher, 2016-02-24 18:05

 
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import javax.xml.transform.TransformerFactory;

import net.sf.saxon.Configuration;

import org.testng.Assert;
import org.testng.annotations.Test;

import com.saxonica.config.EnterpriseConfiguration;

public class TestSaxonConfigurationInstantiation {
private static class TestForSaxonBug2327 {
protected static void testForDeadlock(Thread thread1, Thread thread2) throws InterruptedException {
thread1.start();
thread2.start();

// allow 5 seconds for threads to complete
long timeout = 5000;
long timestamp = System.currentTimeMillis();
thread1.join(timeout);
thread2.join(Math.max(1, timestamp + timeout - System.currentTimeMillis()));
System.out.println(thread1.isAlive() && thread2.isAlive() ? Result.SUSPECTED_DEADLOCK : Result.NORMAL_COMPLETION);
}
}
private static class UseWorkaroundForSaxonBug2327 extends TestForSaxonBug2327 {
@SuppressWarnings("unused")
public static void main(String[] args) throws InterruptedException {
testForDeadlock(
new Thread() {
public void run() {
new Configuration();
new EnterpriseConfiguration();
}
},
new Thread() {
public void run() {
new Configuration();
}
}
);
}
}
private static class UseFixForSaxonBug2327 extends TestForSaxonBug2327 {
@SuppressWarnings("unused")
public static void main(String[] args) throws InterruptedException {
testForDeadlock(
new Thread() {
public void run() {
new EnterpriseConfiguration();
}
},
new Thread() {
public void run() {
new Configuration();
}
}
);
}
}
private static enum Result {
SUSPECTED_DEADLOCK,
NORMAL_COMPLETION
};
@Test(invocationCount = 10)
public static void testWorkaroundForSaxonBug2327() throws IOException {
Process process = execute(UseWorkaroundForSaxonBug2327.class);
Assert.assertEquals(Result.valueOf(waitForNextOutputLine(process, 10000)), Result.NORMAL_COMPLETION, "The workaround did not prevent the deadlock during class loading.");
}
@Test(invocationCount = 10)
public static void testFixForSaxonBug2327() throws IOException {
Process process = execute(UseFixForSaxonBug2327.class);
Assert.assertEquals(Result.valueOf(waitForNextOutputLine(process, 10000)), Result.NORMAL_COMPLETION, "The fix did not prevent the deadlock during class loading.");
}

private static Process execute(Class<?> clazz) throws IOException {
List<String> args = new ArrayList<>();
args.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");
args.add("-cp");
args.add(System.getProperty("java.class.path"));
args.add(clazz.getName());
return Runtime.getRuntime().exec(args.toArray(new String[args.size()]));
}
private static String waitForNextOutputLine(Process process, long waitTime) throws IOException {
final AtomicReference<String> result = new AtomicReference<>();
final AtomicBoolean gotResult = new AtomicBoolean(false);
final AtomicReference<IOException> exception = new AtomicReference<>();
Thread reader = new Thread("result reader") {
@Override
public void run() {
try {
result.set(nextOutputLine(process));
gotResult.set(true);
}
catch (IOException e) {
exception.set(e);
}
}
};
reader.start();
try {
reader.join(waitTime);
}
catch (InterruptedException e) {}
if (exception.get() != null)
throw exception.get();
if (! gotResult.get())
throw new RuntimeException("waitForNextOutputLine timed out after " + waitTime +"ms");
return result.get();
}
public static String nextOutputLine(Process process) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
return reader.readLine();
}
}
    (1-1/1)