JDK-8180901 : Transformer.reset() resets the state only once
  • Type: Bug
  • Component: xml
  • Sub-Component: javax.xml.transform
  • Affected Version: 8,9,11,13
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2017-05-19
  • Updated: 2019-11-22
  • Resolved: 2019-02-05
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11 JDK 13
11.0.5Fixed 13 b07Fixed
Description
FULL PRODUCT VERSION :
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

A DESCRIPTION OF THE PROBLEM :
The reset() method of com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl has a buggy implementation of the internal state reset.

The result is that the state can only be reset once. Any further reset will not restore the initial state, but the state after the first reset.

The reason is that reset() calls setOutputProperties(null), thus executing _properties = _propertiesClone. _propertiesClone is the initial state. Consequently, any further call to setOutputProperties (e.g. in a second usage of the Transformer after a first reset) operates on the *stored backup copy* of the properties. It modifies the backup, meaning that the initial state can never be restored any more.

A fix could be to use  _properties = _propertiesClone.clone() in the setOutputProperties(null) flow.




REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
public class TranformerResetBug {

    private static final String INPUT_XML = "<a><b>123</b><c/></a>";

    public static void main(String[] args) throws Exception {
        TransformerFactory transformerFactory =
                new com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl();

        // run #1
        Transformer transformer = transformerFactory.newTransformer();

        setDefaultOutputProperties(transformer);
        String outputXml1 = transform(INPUT_XML, transformer);
        System.out.println("#1 output XML: " + outputXml1);

        // run #2
        transformer.reset();

        setDefaultOutputProperties(transformer);
        // use different output properties in run #2 (after the 1st reset):
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        String outputXml2 = transform(INPUT_XML, transformer);
        System.out.println("#2 output XML: " + outputXml2);

        // run #3
        transformer.reset();

        setDefaultOutputProperties(transformer);
        // use same output properties as run #1 => expect same output
        String outputXml3 = transform(INPUT_XML, transformer);
        System.out.println("#3 output XML: " + outputXml3);

        if (!outputXml3.equals(outputXml1)) {
            System.err.println("Incorrect XML; expected was: '" + outputXml1 + "'");
        }
    }

    private static void setDefaultOutputProperties(Transformer transformer) {
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.METHOD, "xml");
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    }

    private static String transform(String xml, Transformer transformer)
            throws TransformerException, UnsupportedEncodingException {
        StringWriter writer = new StringWriter();
        transformer.transform(new StreamSource(new ByteArrayInputStream(xml.getBytes("UTF-8"))),
                              new StreamResult(writer));
        return writer.toString();
    }

}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Before calling Transformer.reset(), take another backup the initial state and restore it after the reset call via reflection:

Class<Transformer> transformerClass = (Class<Transformer>) transformer.getClass();
Properties propertiesClone = Unsafe.getFieldValue(transformerClass, transformer, "_propertiesClone");
Properties newClone = (Properties) propertiesClone.clone();

transformer.reset();

Unsafe.setFieldValue(transformerClass, transformer, "_propertiesClone", newClone);


Comments
Fix Request (11u) Fixes misbehaving XLST transformer: while "indent" option used in regression test does not break conformance, there are other options that might produce XML output not expected downstream (e.g. with incorrect encoding). Patch applies cleanly to 11u. New test fails without the patch, passes with it. Patched 11u passes tier1, tier2 tests.
27-06-2019

To reproduce the issue, run the attached test case. JDK 8u131 - Fail JDK 9ea + 170 - Fail Following is the output: #1 output XML: <a><b>123</b><c/></a> #2 output XML: <a> <b>123</b> <c/> </a> #3 output XML: <a> <b>123</b> <c/> </a> Incorrect XML; expected was: '<a><b>123</b><c/></a>'
24-05-2017