Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8180901

Transformer.reset() resets the state only once

    XMLWordPrintable

Details

    Backports

      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);

        Attachments

          Issue Links

            Activity

              People

                joehw Joe Wang
                webbuggrp Webbug Group
                Votes:
                0 Vote for this issue
                Watchers:
                4 Start watching this issue

                Dates

                  Created:
                  Updated:
                  Resolved: