diff --git a/core/src/main/java/org/apache/cxf/databinding/source/XMLStreamDataReader.java b/core/src/main/java/org/apache/cxf/databinding/source/XMLStreamDataReader.java index 77e832366e1..a6c1bbb70a6 100644 --- a/core/src/main/java/org/apache/cxf/databinding/source/XMLStreamDataReader.java +++ b/core/src/main/java/org/apache/cxf/databinding/source/XMLStreamDataReader.java @@ -28,7 +28,6 @@ import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; -import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.Source; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; @@ -118,7 +117,7 @@ public Object read(final QName name, XMLStreamReader input, Class type) { if (type != null) { Object retVal = null; if (SAXSource.class.isAssignableFrom(type) - || StaxSource.class.isAssignableFrom(type)) { + || StaxSource.class.isAssignableFrom(type)) { retVal = new StaxSource(resetForStreaming(input)); } else if (StreamSource.class.isAssignableFrom(type)) { retVal = new StreamSource(getInputStream(input)); @@ -160,7 +159,7 @@ public OutputStream getOutputStream() throws IOException { throw new Fault("COULD_NOT_READ_XML_STREAM", LOG, e); } catch (XMLStreamException e) { throw new Fault("COULD_NOT_READ_XML_STREAM_CAUSED_BY", LOG, e, - e.getMessage()); + e.getMessage()); } } @@ -216,41 +215,44 @@ public void close() throws XMLStreamException { } private Element validate(XMLStreamReader input) throws XMLStreamException, IOException { - DOMSource ds = read(input); - final Element rootElement; - if (ds.getNode() instanceof Document) { - rootElement = ((Document)ds.getNode()).getDocumentElement(); - } else { - rootElement = (Element)ds.getNode(); - } - + Element rootElement; WoodstoxValidationImpl impl = new WoodstoxValidationImpl(); - XMLStreamWriter nullWriter = null; + XMLStreamReader inputWithoutXop = null; if (impl.canValidate()) { - nullWriter = StaxUtils.createXMLStreamWriter(new NUllOutputStream()); - impl.setupValidation(nullWriter, message.getExchange().getEndpoint(), - message.getExchange().getService().getServiceInfos().get(0)); + //filter xop node, which causes validation to fail + XMLStreamReader filteredReader = + StaxUtils.createFilteredReader(input, new StaxStreamFilter(XOP)); + try (CachedOutputStream out = new CachedOutputStream()) { + StaxUtils.copy(filteredReader, out); + inputWithoutXop = StaxUtils.createXMLStreamReader(out.getInputStream()); + impl.setupValidation(inputWithoutXop, message.getExchange().getEndpoint(), + message.getExchange().getService().getServiceInfos().get(0)); + filteredReader.close(); + } } //check if the impl can still validate after the setup, possible issue loading schemas or similar if (impl.canValidate()) { //Can use the MSV libs and woodstox to handle the schema validation during - //parsing and processing. Much faster and single traversal - //filter xop node - XMLStreamReader reader = StaxUtils.createXMLStreamReader(ds); - XMLStreamReader filteredReader = - StaxUtils.createFilteredReader(reader, - new StaxStreamFilter(new QName[] {XOP})); + //parsing and processing. Much faster and single traversal + rootElement = StaxUtils.read(inputWithoutXop).getDocumentElement(); - StaxUtils.copy(filteredReader, nullWriter); } else { + + DOMSource ds = read(input); + if (ds.getNode() instanceof Document) { + rootElement = ((Document)ds.getNode()).getDocumentElement(); + } else { + rootElement = (Element)ds.getNode(); + } + //MSV not available, use a slower method of cloning the data, replace the xop's, validate LOG.fine("NO_MSV_AVAILABLE"); Element newElement = rootElement; if (DOMUtils.hasElementWithName(rootElement, "http://www.w3.org/2004/08/xop/include", "Include")) { newElement = (Element)rootElement.cloneNode(true); List elems = DOMUtils.findAllElementsByTagNameNS(newElement, - "http://www.w3.org/2004/08/xop/include", - "Include"); + "http://www.w3.org/2004/08/xop/include", + "Include"); for (Element include : elems) { Node parentNode = include.getParentNode(); parentNode.removeChild(include); @@ -269,7 +271,7 @@ private Element validate(XMLStreamReader input) throws XMLStreamException, IOExc } private InputStream getInputStream(XMLStreamReader input) - throws XMLStreamException, IOException { + throws XMLStreamException, IOException { try (CachedOutputStream out = new CachedOutputStream()) { StaxUtils.copy(input, out); @@ -298,7 +300,7 @@ public DOMSource read(XMLStreamReader reader) { return new DOMSource(document); } catch (XMLStreamException e) { throw new Fault("COULD_NOT_READ_XML_STREAM_CAUSED_BY", LOG, e, - e.getMessage()); + e.getMessage()); } } @@ -314,14 +316,4 @@ public void setProperty(String prop, Object value) { message = (Message)value; } } - - static class NUllOutputStream extends OutputStream { - public void write(byte[] b, int off, int len) { - } - public void write(int b) { - } - - public void write(byte[] b) throws IOException { - } - } } diff --git a/core/src/test/java/org/apache/cxf/databinding/source/XMLStreamDataReaderTest.java b/core/src/test/java/org/apache/cxf/databinding/source/XMLStreamDataReaderTest.java index 8d6684fe66e..e3d90156171 100755 --- a/core/src/test/java/org/apache/cxf/databinding/source/XMLStreamDataReaderTest.java +++ b/core/src/test/java/org/apache/cxf/databinding/source/XMLStreamDataReaderTest.java @@ -22,19 +22,45 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URL; +import javax.xml.XMLConstants; import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamReader; +import javax.xml.transform.dom.DOMSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import org.w3c.dom.Document; + +import com.ctc.wstx.msv.W3CSchemaFactory; + +import org.apache.cxf.endpoint.Endpoint; import org.apache.cxf.interceptor.Fault; +import org.apache.cxf.message.Exchange; +import org.apache.cxf.message.ExchangeImpl; import org.apache.cxf.message.Message; import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.service.Service; +import org.apache.cxf.service.ServiceImpl; +import org.apache.cxf.service.model.MessageInfo; +import org.apache.cxf.service.model.MessagePartInfo; +import org.apache.cxf.service.model.ServiceInfo; import org.apache.cxf.staxutils.StaxUtils; +import org.codehaus.stax2.XMLStreamReader2; +import org.codehaus.stax2.validation.XMLValidationSchema; import org.junit.Test; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * @@ -42,7 +68,33 @@ public class XMLStreamDataReaderTest { private static final byte[] DUMMY_DATA = "".getBytes(); private static final byte[] DUMMY_DATA_NULL_BYTE = - "".getBytes(); + "".getBytes(); + + // Parse some XML with a null byte and check we don't return internal Woodstox package names in the error message + @Test + public void testParseNullByte() throws Exception { + XMLStreamDataReader reader = new XMLStreamDataReader(); + Message msg = new MessageImpl(); + + TestInputStream in1 = new TestInputStream(DUMMY_DATA_NULL_BYTE); + + msg.setContent(InputStream.class, in1); + + reader.setProperty(Message.class.getName(), msg); + + Object obj = reader.read(new QName("http://www.apache.org/cxf", "dummy"), + StaxUtils.createXMLStreamReader(in1), XMLStreamReader.class); + + assertTrue(obj instanceof XMLStreamReader); + + try { + reader.read((XMLStreamReader) obj); + } catch (Fault f) { + assertFalse(f.getMessage().contains("com.ctc.wstx")); + } + + ((XMLStreamReader)obj).close(); + } @Test public void testCloseOriginalInputStream() throws Exception { @@ -66,30 +118,76 @@ public void testCloseOriginalInputStream() throws Exception { assertTrue(in1.isClosed()); } - // Parse some XML with a null byte and check we don't return internal Woodstox package names in the error message @Test - public void testParseNullByte() throws Exception { + public void testValid() throws Exception { + testValidate("resources/schema.xsd", "resources/test-valid.xml", false); + } + + @Test + public void testInValid() throws Exception { + testValidate("resources/schema.xsd", "resources/test-invalid.xml", true); + } + + + private void testValidate(String schemaPath, String xmlPath, boolean exceptionExpected) throws Exception { + + //create schema + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + URL schemaURI = getClass().getResource(schemaPath); + Document wsdl = documentBuilder.parse(schemaURI.openStream()); + String wsdlSystemId = schemaURI.toExternalForm(); + DOMSource source = new DOMSource(wsdl); + source.setSystemId(wsdlSystemId); + source.setSystemId(wsdlSystemId); + + XMLValidationSchema schemaw3c = + W3CSchemaFactory.newInstance(XMLValidationSchema.SCHEMA_ID_W3C_SCHEMA).createSchema(schemaURI); + SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + Schema schema = schemaFactory.newSchema(schemaURI); + XMLStreamDataReader reader = new XMLStreamDataReader(); + reader.setSchema(schema); + + + InputStream testIS = getClass().getResourceAsStream(xmlPath); Message msg = new MessageImpl(); + Exchange exchange = new ExchangeImpl(); - TestInputStream in1 = new TestInputStream(DUMMY_DATA_NULL_BYTE); + ServiceInfo serviceInfo = new ServiceInfo(); - msg.setContent(InputStream.class, in1); + Endpoint endpoint = mock(Endpoint.class); + when(endpoint.get(XMLValidationSchema.class.getName())).thenReturn(schemaw3c); - reader.setProperty(Message.class.getName(), msg); + Service svc = new ServiceImpl(serviceInfo); - Object obj = reader.read(new QName("http://www.apache.org/cxf", "dummy"), - StaxUtils.createXMLStreamReader(in1), XMLStreamReader.class); + exchange.put(Service.class, svc); + exchange.put(Endpoint.class, endpoint); - assertTrue(obj instanceof XMLStreamReader); + msg.setExchange(exchange); + msg.setContent(InputStream.class, testIS); + reader.setProperty(Message.class.getName(), msg); + XMLInputFactory xmlFactory = XMLInputFactory.newInstance(); + XMLStreamReader2 xmlStreamReader = (XMLStreamReader2) xmlFactory.createXMLStreamReader(testIS, "utf-8"); + + MessageInfo messageInfo = new MessageInfo(null, + MessageInfo.Type.INPUT, + new QName("http://www.test.org/services", + "NullTestOperationRequest")); + MessagePartInfo messagePartInfo = new MessagePartInfo(new QName( + "http://www.test.org/services", "NullTestOperationRequest"), messageInfo); + messagePartInfo.setElement(true); + boolean exceptionCaught = false; try { - reader.read((XMLStreamReader) obj); - } catch (Fault f) { - assertFalse(f.getMessage().contains("com.ctc.wstx")); + reader.read(messagePartInfo, xmlStreamReader); + } catch (Fault fault) { + exceptionCaught = true; + } catch (Exception exc) { + fail(exc.getMessage()); } - - ((XMLStreamReader)obj).close(); + assertEquals(exceptionExpected, exceptionCaught); } private static class TestInputStream extends ByteArrayInputStream { diff --git a/core/src/test/java/org/apache/cxf/databinding/source/resources/schema.xsd b/core/src/test/java/org/apache/cxf/databinding/source/resources/schema.xsd new file mode 100644 index 00000000000..a69a05d55ca --- /dev/null +++ b/core/src/test/java/org/apache/cxf/databinding/source/resources/schema.xsd @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/core/src/test/java/org/apache/cxf/databinding/source/resources/test-invalid.xml b/core/src/test/java/org/apache/cxf/databinding/source/resources/test-invalid.xml new file mode 100644 index 00000000000..74d88711633 --- /dev/null +++ b/core/src/test/java/org/apache/cxf/databinding/source/resources/test-invalid.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/core/src/test/java/org/apache/cxf/databinding/source/resources/test-valid.xml b/core/src/test/java/org/apache/cxf/databinding/source/resources/test-valid.xml new file mode 100644 index 00000000000..e96ff05fd6d --- /dev/null +++ b/core/src/test/java/org/apache/cxf/databinding/source/resources/test-valid.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file