Author: scheu Date: Mon Dec 14 18:43:09 2009 New Revision: 890433 URL: http://svn.apache.org/viewvc?rev=890433&view=rev Log: WSCOMMONS-512 Contributor:Rich Scheuerle Avoid OMSourcedElement expansion during StreamingOMSerializer usage. Also added 3 validation tests
Added: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/resources/soap/noprettyprint.xml Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/OMNavigator.java webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/OMStAXWrapper.java webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/SwitchingWrapper.java webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/serialize/StreamingOMSerializer.java webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/om/impl/serializer/OMSerializerTest.java Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/OMNavigator.java URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/OMNavigator.java?rev=890433&r1=890432&r2=890433&view=diff ============================================================================== --- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/OMNavigator.java (original) +++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/OMNavigator.java Mon Dec 14 18:43:09 2009 @@ -20,6 +20,7 @@ package org.apache.axiom.om.impl; import org.apache.axiom.om.OMContainer; +import org.apache.axiom.om.OMDataSource; import org.apache.axiom.om.OMDocument; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMNode; @@ -55,6 +56,10 @@ /** Field start */ private boolean start = true; + + // Indicates if an OMSourcedElement with an OMDataSource should + // be considered as an interior node or a leaf node. + private boolean isDataSourceALeaf = false; /** Constructor OMNavigator. */ public OMNavigator() { @@ -79,6 +84,16 @@ root = node; backtracked = false; } + + /** + * Indicate if an OMSourcedElement with a OMDataSource + * should be considered as an interior element node or as + * a leaf. + * @param value boolean + */ + public void setDataSourceIsLeaf(boolean value) { + isDataSourceALeaf = value; + } /** * Gets the next node. @@ -109,7 +124,7 @@ /** Private method to encapsulate the searching logic. */ private void updateNextNode() { - if ((next instanceof OMElement) && !visited) { + if (!isLeaf(next) && !visited) { OMNode firstChild = _getFirstChild((OMElement) next); if (firstChild != null) { next = firstChild; @@ -131,6 +146,29 @@ } } } + + /** + * @param n OMNode + * @return true if this OMNode should be considered a leaf node + */ + private boolean isLeaf(OMNode n) { + if (n instanceof OMElement) { + if (this.isDataSourceALeaf && (n instanceof OMSourcedElement)) { + OMDataSource ds = null; + try { + ds = ((OMSourcedElement) n).getDataSource(); + } catch (UnsupportedOperationException e) { + ; // Operation unsupported for DOM impl + } + if (ds != null) { + return true; + } + } + return false; + } else { + return true; + } + } /** * @param node Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/OMStAXWrapper.java URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/OMStAXWrapper.java?rev=890433&r1=890432&r2=890433&view=diff ============================================================================== --- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/OMStAXWrapper.java (original) +++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/OMStAXWrapper.java Mon Dec 14 18:43:09 2009 @@ -27,6 +27,7 @@ import javax.xml.stream.util.StreamReaderDelegate; import org.apache.axiom.om.OMAttachmentAccessor; +import org.apache.axiom.om.OMDataSource; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMException; import org.apache.axiom.om.OMXMLParserWrapper; @@ -171,4 +172,23 @@ public void setNamespaceURIInterning(boolean b) { switchingWrapper.setNamespaceURIInterning(b); } + + + /** + * @return OMDataSource if available + */ + public OMDataSource getDataSource() { + return switchingWrapper.getDataSource(); + } + + /** + * If enabled, treat OMSourcedElements that have + * a OMDataSource as leaf nodes. The caller + * should use the getDataSource method to obtain + * the OMDataSource for these events. + * @param value boolean + */ + public void enableDataSourceEvents(boolean value) { + switchingWrapper.enableDataSourceEvents(value); + } } Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/SwitchingWrapper.java URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/SwitchingWrapper.java?rev=890433&r1=890432&r2=890433&view=diff ============================================================================== --- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/SwitchingWrapper.java (original) +++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/SwitchingWrapper.java Mon Dec 14 18:43:09 2009 @@ -39,6 +39,7 @@ import org.apache.axiom.om.OMAttribute; import org.apache.axiom.om.OMComment; import org.apache.axiom.om.OMContainer; +import org.apache.axiom.om.OMDataSource; import org.apache.axiom.om.OMDocument; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMNamespace; @@ -1552,4 +1553,37 @@ } } + + /** + * @return OMDataSource associated with the current node or Null + */ + public OMDataSource getDataSource() { + OMDataSource ds = null; + if (lastNode != null && + lastNode instanceof OMSourcedElement) { + try { + ds = ((OMSourcedElement) lastNode).getDataSource(); + } catch (UnsupportedOperationException e) { + ds =null; + } + if (log.isDebugEnabled()) { + if (ds != null) { + log.debug("OMSourcedElement exposed an OMDataSource." + ds); + } else { + log.debug("OMSourcedElement does not have a OMDataSource."); + } + } + } + return ds; + } + + /** + * Enable if an OMSourcedElement with an OMDataSource should be treated as a + * leaf node. Disable (the default) if the OMDataSource should be parsed and + * converted into events. + * @param value boolean + */ + public void enableDataSourceEvents(boolean value) { + navigator.setDataSourceIsLeaf(value); + } } Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/serialize/StreamingOMSerializer.java URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/serialize/StreamingOMSerializer.java?rev=890433&r1=890432&r2=890433&view=diff ============================================================================== --- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/serialize/StreamingOMSerializer.java (original) +++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/serialize/StreamingOMSerializer.java Mon Dec 14 18:43:09 2009 @@ -21,7 +21,10 @@ import org.apache.axiom.ext.stax.datahandler.DataHandlerReader; import org.apache.axiom.ext.stax.datahandler.DataHandlerWriter; +import org.apache.axiom.om.OMDataSource; import org.apache.axiom.om.OMSerializer; +import org.apache.axiom.om.impl.MTOMXMLStreamWriter; +import org.apache.axiom.om.impl.OMStAXWrapper; import org.apache.axiom.om.impl.builder.DataHandlerReaderUtils; import org.apache.axiom.om.impl.util.OMSerializerUtil; import org.apache.axiom.util.stax.XMLStreamWriterUtils; @@ -35,6 +38,7 @@ import javax.xml.stream.XMLStreamWriter; import java.io.IOException; +import java.io.OutputStream; import java.util.ArrayList; /** Class StreamingOMSerializer */ @@ -85,7 +89,29 @@ dataHandlerReader = DataHandlerReaderUtils.getDataHandlerReader(reader); dataHandlerWriter = XMLStreamWriterUtils.getDataHandlerWriter(writer); - serializeNode(reader, writer, startAtNext); + if (reader instanceof OMStAXWrapper) { + int event = reader.getEventType(); + if (event <= 0 || + event == XMLStreamReader.PROCESSING_INSTRUCTION || + event == XMLStreamReader.START_DOCUMENT) { + // Since we are serializing an entire document, + // enable the the optimized DataSource events. + // This will allow OMDataSource elements to be serialized + // directly without expansion. + if (log.isDebugEnabled()) { + log.debug("Enable OMDataSource events while serializing this document"); + } + ((OMStAXWrapper) reader).enableDataSourceEvents(true); + } + } + try { + serializeNode(reader, writer, startAtNext); + } finally { + if (reader instanceof OMStAXWrapper) { + ((OMStAXWrapper) reader).enableDataSourceEvents(false); + } + } + } /** @@ -112,15 +138,25 @@ boolean useCurrentEvent = !startAtNext; while (reader.hasNext() || useCurrentEvent) { - int event; + int event = 0; + OMDataSource ds = null; if (useCurrentEvent) { event = reader.getEventType(); useCurrentEvent = false; } else { - event = reader.next(); + event = reader.next(); } - switch (event) { + // If the reader is exposing a DataSourc + // for this event, then simply serialize the + // DataSource + if (reader instanceof OMStAXWrapper) { + ds = ((OMStAXWrapper) reader).getDataSource(); + } + if (ds != null) { + ds.serialize(writer); + } else { + switch (event) { case START_ELEMENT: serializeElement(reader, writer); depth++; @@ -160,6 +196,7 @@ } catch (Exception e) { //TODO: log exceptions } + } } if (depth == 0) { break; Added: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/resources/soap/noprettyprint.xml URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/resources/soap/noprettyprint.xml?rev=890433&view=auto ============================================================================== --- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/resources/soap/noprettyprint.xml (added) +++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/resources/soap/noprettyprint.xml Mon Dec 14 18:43:09 2009 @@ -0,0 +1 @@ +<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:usr="http://ws.apache.org/axis2/user"><soapenv:Header /><soapenv:Body><axis2:echoMyData xmlns:axis2="http://ws.apache.org/axis2"><data xsi:type="usr:myData">Hello World</data></axis2:echoMyData></soapenv:Body></soapenv:Envelope> \ No newline at end of file Modified: webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/om/impl/serializer/OMSerializerTest.java URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/om/impl/serializer/OMSerializerTest.java?rev=890433&r1=890432&r2=890433&view=diff ============================================================================== --- webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/om/impl/serializer/OMSerializerTest.java (original) +++ webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/om/impl/serializer/OMSerializerTest.java Mon Dec 14 18:43:09 2009 @@ -21,9 +21,12 @@ import org.apache.axiom.om.AbstractTestCase; import org.apache.axiom.om.OMAbstractFactory; +import org.apache.axiom.om.OMSourcedElement; import org.apache.axiom.om.OMXMLParserWrapper; import org.apache.axiom.om.OMConstants; import org.apache.axiom.om.TestConstants; +import org.apache.axiom.om.ds.custombuilder.ByteArrayCustomBuilder; +import org.apache.axiom.om.impl.builder.StAXBuilder; import org.apache.axiom.om.impl.builder.StAXOMBuilder; import org.apache.axiom.om.impl.llom.factory.OMXMLBuilderFactory; import org.apache.axiom.om.impl.serialize.StreamingOMSerializer; @@ -142,6 +145,166 @@ String outputString = new String(byteArrayOutputStream.toByteArray()); assertTrue(outputString != null && !"".equals(outputString) && outputString.length() > 1); } + + /** + * Scenario: + * A) Builder reads a soap message. + * B) The payload of the message is created by a customer builder + * C) The resulting OM is serialized (pulled) prior to completion of the intial read. + * D) The payload of the message should not be expanded into OM. + * + * Expansion of the message results in both a time and space penalty. + * @throws Exception + */ + public void testElementPullStreamAndOMExpansion() throws Exception { + // Create a reader sourced from a message containing an interesting payload + reader = StAXUtils.createXMLStreamReader(getTestResource("soap/OMElementTest.xml")); + + // Create a builder connected to the reader + StAXBuilder builder = OMXMLBuilderFactory.createStAXSOAPModelBuilder( + OMAbstractFactory.getSOAP11Factory(), + reader); + + // Create a custom builder to store the sub trees as a byte array instead of a full tree + ByteArrayCustomBuilder customBuilder = new ByteArrayCustomBuilder("utf-8"); + + // Register the custom builder on the builder so that they body payload is stored as bytes + builder.registerCustomBuilderForPayload(customBuilder); + + + // Create an output stream + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + writer = StAXUtils.createXMLStreamWriter(byteArrayOutputStream); + + // Now use StreamingOMSerializer to write the input stream to the output stream + SOAPEnvelope env = (SOAPEnvelope) builder.getDocumentElement(); + SOAPBody body = env.getBody(); + OMSourcedElement omse = (OMSourcedElement) body.getFirstElement(); + + StreamingOMSerializer serializer = new StreamingOMSerializer(); + serializer.serialize(env.getXMLStreamReaderWithoutCaching(), + writer); + writer.flush(); + + String outputString = new String(byteArrayOutputStream.toByteArray()); + assertTrue("Expected output was incorrect. Received:" + outputString, + outputString != null && !"".equals(outputString) && outputString.length() > 1); + assertTrue("Expected output was incorrect. Received:" + outputString, + outputString.contains("axis2:input")); + assertTrue("Expected output was incorrect. Received:" + outputString, + outputString.contains("This is some text")); + assertTrue("Expected output was incorrect. Received:" + outputString, + outputString.contains("Some Other Text")); + + assertTrue("Expectation is that an OMSourcedElement was created for the payload", + omse != null); + assertTrue("Expectation is that the OMSourcedElement was not expanded by serialization ", + !omse.isExpanded()); + } + + /** + * Scenario: + * A) Builder reads a soap message. + * B) The payload of the message is created by a customer builder + * C) The resulting OM is serialized (pulled) prior to completion of the intial read. + * D) The payload of the message should not be expanded into OM. + * + * Expansion of the message results in both a time and space penalty. + * @throws Exception + */ + public void testElementPullStreamAndOMExpansion2() throws Exception { + // Create a reader sourced from a message containing an interesting payload + reader = StAXUtils.createXMLStreamReader(getTestResource("soap/soapmessageWithXSI.xml")); + + // Create a builder connected to the reader + StAXBuilder builder = OMXMLBuilderFactory.createStAXSOAPModelBuilder( + OMAbstractFactory.getSOAP11Factory(), + reader); + + // Create a custom builder to store the sub trees as a byte array instead of a full tree + ByteArrayCustomBuilder customBuilder = new ByteArrayCustomBuilder("utf-8"); + + // Register the custom builder on the builder so that they body payload is stored as bytes + builder.registerCustomBuilderForPayload(customBuilder); + + + // Create an output stream + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + writer = StAXUtils.createXMLStreamWriter(byteArrayOutputStream); + + // Now use StreamingOMSerializer to write the input stream to the output stream + SOAPEnvelope env = (SOAPEnvelope) builder.getDocumentElement(); + SOAPBody body = env.getBody(); + OMSourcedElement omse = (OMSourcedElement) body.getFirstElement(); + + StreamingOMSerializer serializer = new StreamingOMSerializer(); + serializer.serialize(env.getXMLStreamReaderWithoutCaching(), + writer); + writer.flush(); + + String outputString = new String(byteArrayOutputStream.toByteArray()); + assertTrue("Expected output was incorrect. Received:" + outputString, + outputString != null && !"".equals(outputString) && outputString.length() > 1); + assertTrue("Expected output was incorrect. Received:" + outputString, + outputString.contains("Hello World")); + + assertTrue("Expectation is that an OMSourcedElement was created for the payload", + omse != null); + assertTrue("Expectation is that the OMSourcedElement was not expanded by serialization ", + !omse.isExpanded()); + } + + /** + * Scenario: + * A) Builder reads a soap message. + * B) The payload of the message is created by a customer builder + * C) The resulting OM is serialized (pulled) prior to completion of the intial read. + * D) The payload of the message should not be expanded into OM. + * + * Expansion of the message results in both a time and space penalty. + * @throws Exception + */ + public void testElementPullStreamAndOMExpansion3() throws Exception { + // Create a reader sourced from a message containing an interesting payload + reader = StAXUtils.createXMLStreamReader(getTestResource("soap/noprettyprint.xml")); + + // Create a builder connected to the reader + StAXBuilder builder = OMXMLBuilderFactory.createStAXSOAPModelBuilder( + OMAbstractFactory.getSOAP11Factory(), + reader); + + // Create a custom builder to store the sub trees as a byte array instead of a full tree + ByteArrayCustomBuilder customBuilder = new ByteArrayCustomBuilder("utf-8"); + + // Register the custom builder on the builder so that they body payload is stored as bytes + builder.registerCustomBuilderForPayload(customBuilder); + + + // Create an output stream + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + writer = StAXUtils.createXMLStreamWriter(byteArrayOutputStream); + + // Now use StreamingOMSerializer to write the input stream to the output stream + SOAPEnvelope env = (SOAPEnvelope) builder.getDocumentElement(); + SOAPBody body = env.getBody(); + OMSourcedElement omse = (OMSourcedElement) body.getFirstElement(); + + StreamingOMSerializer serializer = new StreamingOMSerializer(); + serializer.serialize(env.getXMLStreamReaderWithoutCaching(), + writer); + writer.flush(); + + String outputString = new String(byteArrayOutputStream.toByteArray()); + assertTrue("Expected output was incorrect. Received:" + outputString, + outputString != null && !"".equals(outputString) && outputString.length() > 1); + assertTrue("Expected output was incorrect. Received:" + outputString, + outputString.contains("Hello World")); + + assertTrue("Expectation is that an OMSourcedElement was created for the payload", + omse != null); + assertTrue("Expectation is that the OMSourcedElement was not expanded by serialization ", + !omse.isExpanded()); + } public void testDefaultNsSerialization() { try {