Bug 6910: Fix anyxml node streaming 47/46747/3
authorTom Pantelis <tpanteli@brocade.com>
Mon, 10 Oct 2016 20:57:32 +0000 (16:57 -0400)
committerTom Pantelis <tpanteli@brocade.com>
Tue, 11 Oct 2016 15:58:20 +0000 (15:58 +0000)
On output, changed AbstractNormalizedNodeDataOutput to transform the
DOMSource to a result String that is serialized to the stream. On input,
modified NormalizedNodeInputStreamReader to parse the XML string into
a org.w3c.dom.Node and create a DOMSource.

Change-Id: Ib10822c4444331351cf7f25e1f26d981f7d41dc7
Signed-off-by: Tom Pantelis <tpanteli@brocade.com>
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/AbstractNormalizedNodeDataOutput.java
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeInputStreamReader.java
opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeStreamReaderWriterTest.java

index 5512f3171601d96f1710e8048f3c19993369d0bc..3dc8f1c591d37e18027cc1d886c5f6b9b5acafd4 100644 (file)
@@ -11,10 +11,16 @@ import com.google.common.base.Preconditions;
 import java.io.DataOutput;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.io.StringWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.Collection;
 import java.util.Map;
 import java.util.Set;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
@@ -273,11 +279,17 @@ abstract class AbstractNormalizedNodeDataOutput implements NormalizedNodeDataOut
     @Override
     public void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException, IllegalArgumentException {
         Preconditions.checkNotNull(name, "Node identifier should not be null");
-        LOG.debug("Writing a new xml node");
+        LOG.debug("Writing any xml node");
 
         startNode(name.getNodeType(), NodeTypes.ANY_XML_NODE);
 
-        writeObject(value);
+        try {
+            StreamResult xmlOutput = new StreamResult(new StringWriter());
+            TransformerFactory.newInstance().newTransformer().transform((DOMSource)value, xmlOutput);
+            writeObject(xmlOutput.getWriter().toString());
+        } catch (TransformerException | TransformerFactoryConfigurationError e) {
+            throw new IOException("Error writing anyXml", e);
+        }
     }
 
     @Override
index 118b4c0debdced86d69a2ea2d24bc6529c8b5f63..8a114893e66b7c0bca923af23f2c69323e1925d8 100644 (file)
@@ -12,6 +12,7 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
 import java.io.DataInput;
 import java.io.IOException;
+import java.io.StringReader;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.nio.charset.StandardCharsets;
@@ -21,6 +22,8 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.transform.dom.DOMSource;
 import org.opendaylight.controller.cluster.datastore.node.utils.QNameFactory;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -38,6 +41,9 @@ import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNo
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
 
 /**
  * NormalizedNodeInputStreamReader reads the byte stream and constructs the normalized node including its children nodes.
@@ -178,7 +184,7 @@ public class NormalizedNodeInputStreamReader implements NormalizedNodeDataInput
 
             case NodeTypes.ANY_XML_NODE :
                 LOG.debug("Read xml node");
-                return Builders.anyXmlBuilder().withValue((DOMSource) readObject()).build();
+                return Builders.anyXmlBuilder().withNodeIdentifier(identifier).withValue(readDOMSource()).build();
 
             case NodeTypes.MAP_NODE :
                 LOG.debug("Read map node {}", identifier);
@@ -227,6 +233,19 @@ public class NormalizedNodeInputStreamReader implements NormalizedNodeDataInput
         }
     }
 
+    private DOMSource readDOMSource() throws IOException {
+        String xml = readObject().toString();
+        try {
+            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+            factory.setNamespaceAware(true);
+            Element node = factory.newDocumentBuilder().parse(
+                    new InputSource(new StringReader(xml))).getDocumentElement();
+            return new DOMSource(node);
+        } catch (SAXException | ParserConfigurationException e) {
+            throw new IOException("Error parsing XML: " + xml, e);
+        }
+    }
+
     private QName readQName() throws IOException {
         // Read in the same sequence of writing
         String localName = readCodedString();
index 9803df937a721ce3d41d6b9bf12941852601e3ed..e2ce60e007cd1b46cbcb72dc9722c4dcd7095815 100644 (file)
@@ -8,9 +8,20 @@
 
 package org.opendaylight.controller.cluster.datastore.node.utils.stream;
 
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.base.Optional;
 import com.google.common.io.ByteStreams;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
 import org.apache.commons.lang.SerializationUtils;
 import org.junit.Assert;
 import org.junit.Test;
@@ -20,14 +31,21 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
 
 public class NormalizedNodeStreamReaderWriterTest {
 
@@ -79,7 +97,6 @@ public class NormalizedNodeStreamReaderWriterTest {
         LeafSetEntryNode<Object> entry3 = ImmutableLeafSetEntryNodeBuilder.create().withNodeIdentifier(
                 new NodeWithValue<>(TestModel.BINARY_LEAF_LIST_QNAME, null)).withValue(null).build();
 
-
         return TestModel.createBaseTestContainerBuilder().
                 withChild(ImmutableLeafSetNodeBuilder.create().withNodeIdentifier(
                         new NodeIdentifier(TestModel.BINARY_LEAF_LIST_QNAME)).
@@ -170,6 +187,48 @@ public class NormalizedNodeStreamReaderWriterTest {
 
     }
 
+    @Test
+    public void testAnyXmlStreaming() throws Exception {
+        String xml = "<foo xmlns=\"http://www.w3.org/TR/html4/\" x=\"123\"><bar>one</bar><bar>two</bar></foo>";
+        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware(true);
+
+        Node xmlNode = factory.newDocumentBuilder().parse(
+                new InputSource(new StringReader(xml))).getDocumentElement();
+
+        assertEquals("http://www.w3.org/TR/html4/", xmlNode.getNamespaceURI());
+
+        NormalizedNode<?, ?> anyXmlContainer = ImmutableContainerNodeBuilder.create().withNodeIdentifier(
+                new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME)).withChild(
+                        Builders.anyXmlBuilder().withNodeIdentifier(new NodeIdentifier(TestModel.ANY_XML_QNAME))
+                            .withValue(new DOMSource(xmlNode)).build()).build();
+
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        NormalizedNodeOutputStreamWriter writer = new NormalizedNodeOutputStreamWriter(
+            ByteStreams.newDataOutput(byteArrayOutputStream));
+
+        writer.writeNormalizedNode(anyXmlContainer);
+
+        NormalizedNodeInputStreamReader reader = new NormalizedNodeInputStreamReader(
+                ByteStreams.newDataInput(byteArrayOutputStream.toByteArray()));
+
+        ContainerNode deserialized = (ContainerNode)reader.readNormalizedNode();
+
+        Optional<DataContainerChild<? extends PathArgument, ?>> child =
+                deserialized.getChild(new NodeIdentifier(TestModel.ANY_XML_QNAME));
+        assertEquals("AnyXml child present", true, child.isPresent());
+
+        StreamResult xmlOutput = new StreamResult(new StringWriter());
+        Transformer transformer = TransformerFactory.newInstance().newTransformer();
+        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+        transformer.transform(((AnyXmlNode)child.get()).getValue(), xmlOutput);
+
+        assertEquals("XML", xml, xmlOutput.getWriter().toString());
+        assertEquals("http://www.w3.org/TR/html4/", ((AnyXmlNode)child.get()).getValue().getNode().getNamespaceURI());
+
+        writer.close();
+    }
+
     private static String largeString(final int pow){
         String s = "X";
         for(int i=0;i<pow;i++){