Project

Profile

Help

Bug #3159

closed

Possible concurrency bug when using strip-space

Added by Cory O over 7 years ago. Updated over 7 years ago.

Status:
Closed
Priority:
Normal
Assignee:
Category:
Multithreading
Sprint/Milestone:
-
Start date:
2017-03-11
Due date:
% Done:

100%

Estimated time:
Legacy ID:
Applies to branch:
9.7
Fix Committed on Branch:
9.7, trunk
Fixed in Maintenance Release:
Platforms:

Description

I am randomly receiving the following exception when processing the same input using a Templates object in multiple threads.

Unknown element: <html/>
Error in xsl:element/@name on line 7 column 35 
  XTMM9000: Processing terminated by xsl:message at line 7 in 
  in built-in template rule
; SystemID: ; Line#: 7; Column#: 35
net.sf.saxon.expr.instruct.TerminationException: Processing terminated by xsl:message at line 7 in 
	at net.sf.saxon.expr.instruct.Message.processLeavingTail(Message.java:223)
	at net.sf.saxon.expr.instruct.TemplateRule.applyLeavingTail(TemplateRule.java:353)
	at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:456)
	at net.sf.saxon.trans.TextOnlyCopyRuleSet.process(TextOnlyCopyRuleSet.java:65)
	at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:433)
	at net.sf.saxon.Controller.transformDocument(Controller.java:2321)
	at net.sf.saxon.Controller.transform(Controller.java:1892)
	at net.sf.saxon.s9api.XsltTransformer.transform(XsltTransformer.java:579)
	at net.sf.saxon.jaxp.TransformerImpl.transform(TransformerImpl.java:185)
	at z.test.UnsafeXslt.run(UnsafeXslt.java:89)
	at java.lang.Thread.run(Thread.java:745)

As I understand it, Templates.newTransformer() should be thread safe.

I even wrapped it within a synchronized block to be sure.

The problem only seems to occur when I include the following in my XSLT:

<xsl:strip-space elements="*"/>

You may need to execute the following multiple times before seeing the exception.

unsafe.main.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:html="http://www.w3.org/1999/xhtml">
	<xsl:strip-space elements="*"/>
	<xsl:template match="*">
		<xsl:message terminate="yes">
			<xsl:text>Unknown element: </xsl:text>
			<xsl:element name="{name()}" />
		</xsl:message>
	</xsl:template>
	<xsl:template match="html:html">
		<root>
			<xsl:apply-templates />
		</root>
	</xsl:template>
	<xsl:template match="html:body">
		<record>
			<xsl:apply-templates />
		</record>
	</xsl:template>
</xsl:stylesheet>

unsafe.input.xml

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"><body>content</body></html>

UnsafeXslt.java

package z.test;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.URIResolver;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

public class UnsafeXslt implements Runnable {
	private static final Templates templates;
	static {
		final TransformerFactory factory = TransformerFactory.newInstance();
		factory.setURIResolver(new URIResolver() {
			@Override
			public Source resolve(String href, String base) throws TransformerException {
				final InputStream input = UnsafeXslt.class.getResourceAsStream(href);
				if (input == null) {
					throw new TransformerException("Unable to find '" + href + "' in '" + base + "'.");
				}
				return new StreamSource(input, "ccm:"+href);
			}
		});
		try {
			templates = factory.newTemplates(new StreamSource(UnsafeXslt.class.getResourceAsStream("/unsafe.main.xslt")));
		} catch (TransformerConfigurationException e) {
			throw new RuntimeException(e);
		}
	}
	private static final String input;
	static {
		try {
			input = String.join("\n", Files.readAllLines(Paths.get(UnsafeXslt.class.getResource("/unsafe.input.xml").toURI())));
		} catch (IOException | URISyntaxException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static void main(String[] args) throws Throwable {
		final int threadSize = 30;
		final CountDownLatch latch = new CountDownLatch(threadSize);
		final ThreadFactory threadFactory = Executors.defaultThreadFactory();
		final Thread[] threads = new Thread[threadSize];
		for (int i = 0; i < threadSize; ++i) {
			threads[i] = threadFactory.newThread(new UnsafeXslt(latch));
		}
		for (int i = 0; i < threadSize; ++i) {
			threads[i].start();
		}
		for (int i = 0; i < threadSize; ++i) {
			threads[i].join();
		}
	}
	
	private CountDownLatch latch;
	
	public UnsafeXslt(final CountDownLatch latch) {
		this.latch = latch;
	}
	
	@Override
	public void run() {
		try {
			latch.countDown();
			latch.await();
			final int size = 1000;
			final ByteArrayOutputStream output = new ByteArrayOutputStream();
			final StreamResult result = new StreamResult(output);
			for (int i = 0; i < size; ++i) {
				output.reset();
				final Transformer transformer;
				synchronized (templates) {
					transformer = templates.newTransformer();
				}
				transformer.transform(new StreamSource(new StringReader(input), "ccm:input"), result);
			}
		} catch (Throwable t) {
			t.printStackTrace(System.out);
		}
	}
}

Please register to edit this issue

Also available in: Atom PDF