-
Bug
-
Resolution: Fixed
-
P4
-
8, 9, 11, 13
-
b07
-
generic
-
generic
Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-8234673 | 11.0.7-oracle | Kiran Sidhartha Ravikumar | P4 | Resolved | Fixed | b01 |
JDK-8227146 | 11.0.5 | Joe Wang | P4 | Resolved | Fixed | b01 |
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);
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);
- backported by
-
JDK-8227146 Transformer.reset() resets the state only once
- Resolved
-
JDK-8234673 Transformer.reset() resets the state only once
- Resolved