package org.nate.threadsafetytest; import static org.junit.Assert.assertNotNull; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * Test showing that even the accessors for org.w3c.dom.Document are not thread safe. * This sometimes fails. */ public class TestOfW3CSafety { private static final Document DOC = parseXml("<div><p>p1</p><p>p2</p></div>"); private static final int NUM_THREADS = 20; private static final long DURATION_IN_MILLIS = 5 * 1000; private static final CyclicBarrier START_BARRIER = new CyclicBarrier(NUM_THREADS); private static final List<Throwable> errors = Collections.synchronizedList(new ArrayList<Throwable>()); // @Test public void cloneShouldBeThreadSafe() throws Throwable { ArrayList<Thread> threads = new ArrayList<Thread>(); long finishTime = System.currentTimeMillis() + DURATION_IN_MILLIS; for(int i = 0; i < NUM_THREADS; i++) { Thread thread = new Thread(new ThreadSafetyTask(finishTime)); thread.start(); threads.add(thread); } for (Thread thread : threads) { thread.join(2 * DURATION_IN_MILLIS); } if (!errors.isEmpty()) { throw errors.get(0); } } private static class ThreadSafetyTask implements Runnable { private final long finishTime; public ThreadSafetyTask(long finishTime) { this.finishTime = finishTime; } @Override public void run() { try { START_BARRIER.await(2, TimeUnit.SECONDS); while(System.currentTimeMillis() < finishTime) { for(int i = 0; i < 10; i++) { assertNotNull(DOC.getDocumentElement()); } } } catch (Throwable e) { errors.add(e); e.printStackTrace(); } } } private static Document parseXml(String input) { try { return parseXml(new ByteArrayInputStream(input.getBytes())); } catch (Exception e) { throw new RuntimeException(e); } } private static Document parseXml(InputStream inputStream) throws Exception { return createDocumentParser().parse(inputStream); } private static DocumentBuilder createDocumentParser() throws ParserConfigurationException { DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = domFactory.newDocumentBuilder(); builder.setEntityResolver(NULL_ENTITY_RESOLVER); return builder; } private static final EntityResolver NULL_ENTITY_RESOLVER = new EntityResolver() { public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { return new InputSource(new StringReader("")); } }; }