[CXF-7269]schemavalidate failed when use mtom and Provider # Conflicts: # core/src/main/java/org/apache/cxf/databinding/source/XMLStreamDataWriter.java # systests/uncategorized/src/test/java/org/apache/cxf/systest/mtom/ClientMtomXopTest.java
Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/e81a5c95 Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/e81a5c95 Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/e81a5c95 Branch: refs/heads/3.1.x-fixes Commit: e81a5c953173abb072372ff28e68ba4fd17dc653 Parents: 161439d Author: Freeman Fang <[email protected]> Authored: Tue Mar 7 16:44:03 2017 +0800 Committer: Daniel Kulp <[email protected]> Committed: Tue Mar 7 14:07:28 2017 -0500 ---------------------------------------------------------------------- .../databinding/source/XMLStreamDataWriter.java | 111 +++++++++++++++++- .../cxf/systest/mtom/ClientMtomXopTest.java | 115 ++++++++++++++++++- testutils/pom.xml | 4 + .../cxf/mtom_xop/TestMtomProviderImpl.java | 104 +++++++++++++++++ testutils/src/main/resources/wsdl/mtom_xop.wsdl | 3 + 5 files changed, 333 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/e81a5c95/core/src/main/java/org/apache/cxf/databinding/source/XMLStreamDataWriter.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/cxf/databinding/source/XMLStreamDataWriter.java b/core/src/main/java/org/apache/cxf/databinding/source/XMLStreamDataWriter.java index c0938d5..bec5b14 100644 --- a/core/src/main/java/org/apache/cxf/databinding/source/XMLStreamDataWriter.java +++ b/core/src/main/java/org/apache/cxf/databinding/source/XMLStreamDataWriter.java @@ -29,12 +29,17 @@ import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.Source; import javax.xml.transform.dom.DOMSource; import javax.xml.validation.Schema; +import javax.xml.validation.Validator; import org.w3c.dom.Document; import org.w3c.dom.DocumentFragment; import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + import org.apache.cxf.common.i18n.Message; import org.apache.cxf.common.logging.LogUtils; @@ -46,9 +51,15 @@ import org.apache.cxf.staxutils.StaxUtils; import org.apache.cxf.staxutils.W3CDOMStreamWriter; public class XMLStreamDataWriter implements DataWriter<XMLStreamWriter> { + private static final Logger LOG = LogUtils.getL7dLogger(XMLStreamDataWriter.class); + private Schema schema; + + public XMLStreamDataWriter() { + + } public void write(Object obj, MessagePartInfo part, XMLStreamWriter output) { write(obj, output); @@ -61,7 +72,10 @@ public class XMLStreamDataWriter implements DataWriter<XMLStreamWriter> { DataSource ds = (DataSource)obj; if (schema != null) { DOMSource domSource = new DOMSource(StaxUtils.read(ds.getInputStream())); - schema.newValidator().validate(domSource); + Validator schemaValidator = schema.newValidator(); + schemaValidator.setErrorHandler( + new MtomValidationErrorHandler(schemaValidator.getErrorHandler(), domSource.getNode())); + schemaValidator.validate(domSource); StaxUtils.copy(domSource, writer); } else { reader = StaxUtils.createXMLStreamReader(ds.getInputStream()); @@ -71,7 +85,10 @@ public class XMLStreamDataWriter implements DataWriter<XMLStreamWriter> { } else if (obj instanceof Node) { if (schema != null) { - schema.newValidator().validate(new DOMSource((Node)obj)); + Validator schemaValidator = schema.newValidator(); + schemaValidator.setErrorHandler( + new MtomValidationErrorHandler(schemaValidator.getErrorHandler(), (Node)obj)); + schemaValidator.validate(new DOMSource((Node)obj)); } Node nd = (Node)obj; writeNode(nd, writer); @@ -82,7 +99,10 @@ public class XMLStreamDataWriter implements DataWriter<XMLStreamWriter> { //make the source re-readable. s = new DOMSource(StaxUtils.read(s)); } - schema.newValidator().validate(s); + Validator schemaValidator = schema.newValidator(); + schemaValidator.setErrorHandler( + new MtomValidationErrorHandler(schemaValidator.getErrorHandler(), ((DOMSource)s).getNode())); + schemaValidator.validate(s); } if (s instanceof DOMSource && ((DOMSource) s).getNode() == null) { @@ -153,4 +173,89 @@ public class XMLStreamDataWriter implements DataWriter<XMLStreamWriter> { public void setProperty(String key, Object value) { } + private static class MtomValidationErrorHandler implements ErrorHandler { + private ErrorHandler origErrorHandler; + private Node node; + + MtomValidationErrorHandler(ErrorHandler origErrorHandler, Node node) { + this.origErrorHandler = origErrorHandler; + this.node = node; + } + + + @Override + public void warning(SAXParseException exception) throws SAXException { + + if (this.origErrorHandler != null) { + this.origErrorHandler.warning(exception); + } else { + // do nothing + } + } + + @Override + public void error(SAXParseException exception) throws SAXException { + if (this.isCVC312Exception(exception)) { + String elementName = this.getAttachmentElementName(exception); + if (node != null && this.findIncludeNode(node, elementName)) { + return; + } + } + + if (this.origErrorHandler != null) { + this.origErrorHandler.error(exception); + } else { + throw exception; + } + + } + + @Override + public void fatalError(SAXParseException exception) throws SAXException { + if (this.origErrorHandler != null) { + this.origErrorHandler.fatalError(exception); + } else { + throw exception; + } + + } + + private boolean isCVC312Exception(SAXParseException exception) { + String msg = exception.getMessage(); + return msg.startsWith("cvc-type.3.1.2: ") + && msg.endsWith("is a simple type, so it must have no element information item [children]."); + + + } + + private String getAttachmentElementName(SAXParseException exception) { + String msg = exception.getMessage(); + String str[] = msg.split("'"); + return str[1]; + } + + private boolean findIncludeNode(Node checkNode, String mtomElement) { + boolean ret = false; + NodeList nList = checkNode.getChildNodes(); + for (int i = 0; i < nList.getLength(); i++) { + Node nNode = nList.item(i); + if (nNode.getLocalName() != null + && nNode.getLocalName().equals(mtomElement)) { + NodeList subNodeList = nNode.getChildNodes(); + for (int j = 0; j < subNodeList.getLength(); j++) { + Node subNode = subNodeList.item(j); + if (subNode.getNamespaceURI().equals("http://www.w3.org/2004/08/xop/include") + && subNode.getLocalName().equals("Include")) { + // This is the Mtom element which break the SchemaValidation so ignore this + return true; + } + } + } else { + ret = findIncludeNode(nNode, mtomElement); + } + } + return ret; + } + } + } http://git-wip-us.apache.org/repos/asf/cxf/blob/e81a5c95/systests/uncategorized/src/test/java/org/apache/cxf/systest/mtom/ClientMtomXopTest.java ---------------------------------------------------------------------- diff --git a/systests/uncategorized/src/test/java/org/apache/cxf/systest/mtom/ClientMtomXopTest.java b/systests/uncategorized/src/test/java/org/apache/cxf/systest/mtom/ClientMtomXopTest.java index d88d117..c5f6abb 100644 --- a/systests/uncategorized/src/test/java/org/apache/cxf/systest/mtom/ClientMtomXopTest.java +++ b/systests/uncategorized/src/test/java/org/apache/cxf/systest/mtom/ClientMtomXopTest.java @@ -51,6 +51,7 @@ import org.apache.cxf.message.Message; import org.apache.cxf.mime.TestMtom; import org.apache.cxf.mime.types.XopStringType; import org.apache.cxf.mtom_xop.TestMtomImpl; +import org.apache.cxf.mtom_xop.TestMtomProviderImpl; import org.apache.cxf.service.Service; import org.apache.cxf.service.model.EndpointInfo; import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase; @@ -63,7 +64,9 @@ import org.junit.Test; public class ClientMtomXopTest extends AbstractBusClientServerTestBase { public static final String PORT = allocatePort(ClientMtomXopTest.class); public static final QName MTOM_PORT = new QName("http://cxf.apache.org/mime", "TestMtomPort"); + public static final QName MTOM_PORT_PROVIDER = new QName("http://cxf.apache.org/mime", "TestMtomProviderPort"); public static final QName MTOM_SERVICE = new QName("http://cxf.apache.org/mime", "TestMtomService"); + public static class Server extends AbstractBusTestServerBase { @@ -71,14 +74,21 @@ public class ClientMtomXopTest extends AbstractBusClientServerTestBase { protected void run() { Object implementor = new TestMtomImpl(); String address = "http://localhost:" + PORT + "/mime-test"; + String addressProvider = "http://localhost:" + PORT + "/mime-test-provider"; try { jaxep = (EndpointImpl) javax.xml.ws.Endpoint.publish(address, implementor); Endpoint ep = jaxep.getServer().getEndpoint(); ep.getInInterceptors().add(new TestMultipartMessageInterceptor()); ep.getOutInterceptors().add(new TestAttachmentOutInterceptor()); - + jaxep.getInInterceptors().add(new LoggingInInterceptor()); + jaxep.getOutInterceptors().add(new LoggingOutInterceptor()); SOAPBinding jaxWsSoapBinding = (SOAPBinding) jaxep.getBinding(); jaxWsSoapBinding.setMTOMEnabled(true); + EndpointImpl endpoint = + (EndpointImpl)javax.xml.ws.Endpoint.publish(addressProvider, new TestMtomProviderImpl()); + endpoint.getProperties().put("schema-validation-enabled", "true"); + endpoint.getInInterceptors().add(new LoggingInInterceptor()); + endpoint.getOutInterceptors().add(new LoggingOutInterceptor()); } catch (Exception e) { Thread.currentThread().interrupt(); @@ -197,6 +207,109 @@ public class ClientMtomXopTest extends AbstractBusClientServerTestBase { throw ex; } } + + + @Test + public void testMtomXopProvider() throws Exception { + TestMtom mtomPort = createPort(MTOM_SERVICE, MTOM_PORT_PROVIDER, TestMtom.class, true, true); + try { + Holder<DataHandler> param = new Holder<DataHandler>(); + Holder<String> name; + byte bytes[]; + InputStream in; + + InputStream pre = this.getClass().getResourceAsStream("/wsdl/mtom_xop.wsdl"); + int fileSize = 0; + for (int i = pre.read(); i != -1; i = pre.read()) { + fileSize++; + } + + int count = 50; + byte[] data = new byte[fileSize * count]; + for (int x = 0; x < count; x++) { + this.getClass().getResourceAsStream("/wsdl/mtom_xop.wsdl").read(data, + fileSize * x, + fileSize); + } + + Object[] validationTypes = new Object[]{Boolean.TRUE, SchemaValidationType.IN, SchemaValidationType.BOTH}; + + for (Object validationType : validationTypes) { + ((BindingProvider)mtomPort).getRequestContext().put(Message.SCHEMA_VALIDATION_ENABLED, + validationType); + + param.value = new DataHandler(new ByteArrayDataSource(data, "application/octet-stream")); + name = new Holder<String>("call detail"); + mtomPort.testXop(name, param); + assertEquals("name unchanged", "return detail + call detail", name.value); + assertNotNull(param.value); + + in = param.value.getInputStream(); + bytes = IOUtils.readBytesFromStream(in); + assertEquals(data.length, bytes.length); + in.close(); + + param.value = new DataHandler(new ByteArrayDataSource(data, "application/octet-stream")); + name = new Holder<String>("call detail"); + mtomPort.testXop(name, param); + assertEquals("name unchanged", "return detail + call detail", name.value); + assertNotNull(param.value); + + in = param.value.getInputStream(); + bytes = IOUtils.readBytesFromStream(in); + assertEquals(data.length, bytes.length); + in.close(); + } + + validationTypes = new Object[]{Boolean.FALSE, SchemaValidationType.OUT, SchemaValidationType.NONE}; + for (Object validationType : validationTypes) { + ((BindingProvider)mtomPort).getRequestContext().put(Message.SCHEMA_VALIDATION_ENABLED, + validationType); + SAAJOutInterceptor saajOut = new SAAJOutInterceptor(); + SAAJInInterceptor saajIn = new SAAJInInterceptor(); + + param.value = new DataHandler(new ByteArrayDataSource(data, "application/octet-stream")); + name = new Holder<String>("call detail"); + mtomPort.testXop(name, param); + assertEquals("name unchanged", "return detail + call detail", name.value); + assertNotNull(param.value); + + in = param.value.getInputStream(); + bytes = IOUtils.readBytesFromStream(in); + assertEquals(data.length, bytes.length); + in.close(); + + ClientProxy.getClient(mtomPort).getInInterceptors().add(saajIn); + ClientProxy.getClient(mtomPort).getInInterceptors().add(saajOut); + param.value = new DataHandler(new ByteArrayDataSource(data, "application/octet-stream")); + name = new Holder<String>("call detail"); + mtomPort.testXop(name, param); + assertEquals("name unchanged", "return detail + call detail", name.value); + assertNotNull(param.value); + + in = param.value.getInputStream(); + bytes = IOUtils.readBytesFromStream(in); + assertEquals(data.length, bytes.length); + in.close(); + + ClientProxy.getClient(mtomPort).getInInterceptors().remove(saajIn); + ClientProxy.getClient(mtomPort).getInInterceptors().remove(saajOut); + } + } catch (UndeclaredThrowableException ex) { + throw (Exception)ex.getCause(); + } catch (Exception ex) { + if (ex.getMessage().contains("Connection reset") + && System.getProperty("java.specification.version", "1.5").contains("1.6")) { + //There seems to be a bug/interaction with Java 1.6 and Jetty where + //Jetty will occasionally send back a RST prior to all the data being + //sent back to the client when using localhost (which is what we do) + //we'll ignore for now + return; + } + System.out.println(System.getProperties()); + throw ex; + } + } @Test public void testMtomWithFileName() throws Exception { http://git-wip-us.apache.org/repos/asf/cxf/blob/e81a5c95/testutils/pom.xml ---------------------------------------------------------------------- diff --git a/testutils/pom.xml b/testutils/pom.xml index 54f8244..8aec280 100644 --- a/testutils/pom.xml +++ b/testutils/pom.xml @@ -53,6 +53,10 @@ <optional>true</optional> </dependency> <dependency> + <groupId>org.apache.geronimo.specs</groupId> + <artifactId>geronimo-javamail_1.4_spec</artifactId> + </dependency> + <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <scope>provided</scope> http://git-wip-us.apache.org/repos/asf/cxf/blob/e81a5c95/testutils/src/main/java/org/apache/cxf/mtom_xop/TestMtomProviderImpl.java ---------------------------------------------------------------------- diff --git a/testutils/src/main/java/org/apache/cxf/mtom_xop/TestMtomProviderImpl.java b/testutils/src/main/java/org/apache/cxf/mtom_xop/TestMtomProviderImpl.java new file mode 100644 index 0000000..de1f442 --- /dev/null +++ b/testutils/src/main/java/org/apache/cxf/mtom_xop/TestMtomProviderImpl.java @@ -0,0 +1,104 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.mtom_xop; + +import java.io.InputStream; + +import javax.activation.DataHandler; +import javax.mail.util.ByteArrayDataSource; +import javax.xml.soap.AttachmentPart; +import javax.xml.soap.MessageFactory; +import javax.xml.soap.SOAPBody; +import javax.xml.soap.SOAPBodyElement; +import javax.xml.soap.SOAPElement; +import javax.xml.soap.SOAPEnvelope; +import javax.xml.soap.SOAPMessage; +import javax.xml.soap.SOAPPart; +import javax.xml.ws.Provider; +import javax.xml.ws.Service.Mode; +import javax.xml.ws.ServiceMode; +import javax.xml.ws.WebServiceProvider; +import javax.xml.ws.soap.MTOM; + + +@WebServiceProvider(portName = "TestMtomProviderPort", +serviceName = "TestMtomService", +targetNamespace = "http://cxf.apache.org/mime", +wsdlLocation = "testutils/mtom_xop.wsdl") +@ServiceMode(value = Mode.MESSAGE) +@MTOM + +public class TestMtomProviderImpl implements Provider<SOAPMessage> { + + public SOAPMessage invoke(final SOAPMessage request) { + try { + System.out.println("=== Received client request ==="); + + // create the SOAPMessage + SOAPMessage message = MessageFactory.newInstance().createMessage(); + SOAPPart part = message.getSOAPPart(); + SOAPEnvelope envelope = part.getEnvelope(); + SOAPBody body = envelope.getBody(); + + + SOAPBodyElement testResponse = body + .addBodyElement(envelope.createName("testXopResponse", null, "http://cxf.apache.org/mime/types")); + SOAPElement name = testResponse.addChildElement("name", null, "http://cxf.apache.org/mime/types"); + name.setTextContent("return detail + call detail"); + SOAPElement attachinfo = testResponse.addChildElement( + "attachinfo", null, "http://cxf.apache.org/mime/types"); + SOAPElement include = attachinfo.addChildElement("Include", "xop", + "http://www.w3.org/2004/08/xop/include"); + + InputStream pre = this.getClass().getResourceAsStream("/wsdl/mtom_xop.wsdl"); + int fileSize = 0; + for (int i = pre.read(); i != -1; i = pre.read()) { + fileSize++; + } + + int count = 50; + byte[] data = new byte[fileSize * count]; + for (int x = 0; x < count; x++) { + this.getClass().getResourceAsStream("/wsdl/mtom_xop.wsdl").read(data, + fileSize * x, + fileSize); + } + + + DataHandler dh = new DataHandler(new ByteArrayDataSource(data, "application/octet-stream")); + + // create the image attachment + AttachmentPart attachment = message.createAttachmentPart(dh); + attachment.setContentId("mtom_xop.wsdl"); + message.addAttachmentPart(attachment); + System.out + .println("Adding attachment: " + attachment.getContentId() + ":" + attachment.getSize()); + + // add the reference to the image attachment + include.addAttribute(envelope.createName("href"), "cid:" + attachment.getContentId()); + + return message; + + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/e81a5c95/testutils/src/main/resources/wsdl/mtom_xop.wsdl ---------------------------------------------------------------------- diff --git a/testutils/src/main/resources/wsdl/mtom_xop.wsdl b/testutils/src/main/resources/wsdl/mtom_xop.wsdl index 02b15fd..b84ded2 100644 --- a/testutils/src/main/resources/wsdl/mtom_xop.wsdl +++ b/testutils/src/main/resources/wsdl/mtom_xop.wsdl @@ -96,6 +96,9 @@ <wsdl:port name="TestMtomPort" binding="tns:TestMtomBinding"> <soap:address location="http://localhost:9036/mime-test"/> </wsdl:port> + <wsdl:port name="TestMtomProviderPort" binding="tns:TestMtomBinding"> + <soap:address location="http://localhost:9036/mime-test-provider"/> + </wsdl:port> </wsdl:service> <wsdl:service name="TestMtomJMSService"> <wsdl:port name="TestMtomJMSPort" binding="tns:TestMtomBinding">
