Details
-
Bug
-
Resolution: Fixed
-
P4
-
8, 11, 15, 16
-
b28
-
Verified
Description
ADDITIONAL SYSTEM INFORMATION :
Not runtime specific
A DESCRIPTION OF THE PROBLEM :
When creating a filtered XMLStreamReader (via XMLInputFactory#createFilteredReader), if an exception is thrown by the underlying XMLStreamReader, the implementation class `com.sun.org.apache.xerces.internal.impl.XMLStreamFilterImpl` catches it, logs the exception to standard output, and returns without error from the constructor. The client application is unaware the exception has occurred, and subsequent calls to the filtered reader likely result in unanticipated runtime exceptions due to the indeterminate state of the reader.
Further, `com.sun.org.apache.xerces.internal.impl.XMLStreamFilterImpl#nextTag` incorrectly checks for `START_ELEMENT` twice. One of the comparisons should test for `END_ELEMENT` per the `XMLStreamReader` contract.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create an XMLStreamReader over an input source containing an error.
2. Create an XMLStreamReader via an XMLInputFactory using a StreamFilter that accepts only events that occur following the invalid XML in #1.
3. Advance the filtered reader, no error regarding the invalid source XML will be reported until later in the stream (or never, depending on the nature of the error).
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
An XMLStreamException should be thrown to the application when the filtered XMLStreamReader encounters an XMLStreamException advancing the wrapped XMLStreamReader.
ACTUAL -
The XMLStreamException is caught and logged to standard output.
---------- BEGIN SOURCE ----------
package com.example.xml.filter;
import java.io.Reader;
import java.io.StringReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
public class XMLStreamReaderFilterTest {
static final String XMLSOURCE = "<root>\n"
+ " <element1>\n"
+ " <element2>\n" // Unclosed element2
+ " </element1>\n"
+ " <element3>\n"
+ " </element3>\n"
+ "</root>";
public static void main(String[] args) {
testXMLStreamReaderExceptionThrown();
testXMLStreamReaderExceptionLost();
}
static void testXMLStreamReaderExceptionThrown() {
XMLInputFactory factory = XMLInputFactory.newInstance();
Throwable thrown = null;
try (Reader source = new StringReader(XMLSOURCE)) {
XMLStreamReader reader = factory.createXMLStreamReader(source);
while (reader.hasNext()) {
reader.next();
}
} catch (Exception e) {
thrown = e;
}
assert (""
+ "ParseError at [row,col]:[4,5]\n"
+ "Message: The element type \"element2\" must be "
+ "terminated by the matching end-tag "
+ "\"</element2>\".").equals(thrown.getMessage());
}
static void testXMLStreamReaderExceptionLost() {
XMLInputFactory factory = XMLInputFactory.newInstance();
Throwable thrown = null;
int firstFilteredEvent = -1;
String firstFilteredTag = null;
try (Reader source = new StringReader(XMLSOURCE)) {
XMLStreamReader reader = factory.createXMLStreamReader(source);
XMLStreamReader filtered = factory.createFilteredReader(reader, r -> {
return r.getEventType() == XMLStreamConstants.START_ELEMENT
&& r.getLocalName().equals("element3");
});
firstFilteredEvent = filtered.next();
firstFilteredTag = filtered.getLocalName();
while (filtered.hasName()) {
filtered.next();
}
} catch (Exception e) {
thrown = e;
}
assert XMLStreamConstants.START_ELEMENT == firstFilteredEvent;
assert "element3".equals(firstFilteredTag);
assert (""
+ "ParseError at [row,col]:[7,8]\n"
+ "Message: XML document structures must start "
+ "and end within the same entity.").equals(thrown.getMessage());
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Use a custom XMLStreamFilterImpl that is instantiated via `new` rather than using the XMLInputFactory.
FREQUENCY : always
Not runtime specific
A DESCRIPTION OF THE PROBLEM :
When creating a filtered XMLStreamReader (via XMLInputFactory#createFilteredReader), if an exception is thrown by the underlying XMLStreamReader, the implementation class `com.sun.org.apache.xerces.internal.impl.XMLStreamFilterImpl` catches it, logs the exception to standard output, and returns without error from the constructor. The client application is unaware the exception has occurred, and subsequent calls to the filtered reader likely result in unanticipated runtime exceptions due to the indeterminate state of the reader.
Further, `com.sun.org.apache.xerces.internal.impl.XMLStreamFilterImpl#nextTag` incorrectly checks for `START_ELEMENT` twice. One of the comparisons should test for `END_ELEMENT` per the `XMLStreamReader` contract.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create an XMLStreamReader over an input source containing an error.
2. Create an XMLStreamReader via an XMLInputFactory using a StreamFilter that accepts only events that occur following the invalid XML in #1.
3. Advance the filtered reader, no error regarding the invalid source XML will be reported until later in the stream (or never, depending on the nature of the error).
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
An XMLStreamException should be thrown to the application when the filtered XMLStreamReader encounters an XMLStreamException advancing the wrapped XMLStreamReader.
ACTUAL -
The XMLStreamException is caught and logged to standard output.
---------- BEGIN SOURCE ----------
package com.example.xml.filter;
import java.io.Reader;
import java.io.StringReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
public class XMLStreamReaderFilterTest {
static final String XMLSOURCE = "<root>\n"
+ " <element1>\n"
+ " <element2>\n" // Unclosed element2
+ " </element1>\n"
+ " <element3>\n"
+ " </element3>\n"
+ "</root>";
public static void main(String[] args) {
testXMLStreamReaderExceptionThrown();
testXMLStreamReaderExceptionLost();
}
static void testXMLStreamReaderExceptionThrown() {
XMLInputFactory factory = XMLInputFactory.newInstance();
Throwable thrown = null;
try (Reader source = new StringReader(XMLSOURCE)) {
XMLStreamReader reader = factory.createXMLStreamReader(source);
while (reader.hasNext()) {
reader.next();
}
} catch (Exception e) {
thrown = e;
}
assert (""
+ "ParseError at [row,col]:[4,5]\n"
+ "Message: The element type \"element2\" must be "
+ "terminated by the matching end-tag "
+ "\"</element2>\".").equals(thrown.getMessage());
}
static void testXMLStreamReaderExceptionLost() {
XMLInputFactory factory = XMLInputFactory.newInstance();
Throwable thrown = null;
int firstFilteredEvent = -1;
String firstFilteredTag = null;
try (Reader source = new StringReader(XMLSOURCE)) {
XMLStreamReader reader = factory.createXMLStreamReader(source);
XMLStreamReader filtered = factory.createFilteredReader(reader, r -> {
return r.getEventType() == XMLStreamConstants.START_ELEMENT
&& r.getLocalName().equals("element3");
});
firstFilteredEvent = filtered.next();
firstFilteredTag = filtered.getLocalName();
while (filtered.hasName()) {
filtered.next();
}
} catch (Exception e) {
thrown = e;
}
assert XMLStreamConstants.START_ELEMENT == firstFilteredEvent;
assert "element3".equals(firstFilteredTag);
assert (""
+ "ParseError at [row,col]:[7,8]\n"
+ "Message: XML document structures must start "
+ "and end within the same entity.").equals(thrown.getMessage());
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Use a custom XMLStreamFilterImpl that is instantiated via `new` rather than using the XMLInputFactory.
FREQUENCY : always