Actions
Bug #3159
closedPossible concurrency bug when using strip-space
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
Actions