Merge "Bug 5708: Schemaless netconf mount point"
[netconf.git] / netconf / sal-netconf-connector / src / main / java / org / opendaylight / netconf / sal / connect / netconf / util / NetconfMessageTransformUtil.java
index a0622d719c41fa60a0ed1c4d5823dc5a9a0ef802..64cb14338f0b315814c7d4dee7867755b0a1faa2 100644 (file)
@@ -13,16 +13,26 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import java.io.IOException;
 import java.net.URI;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.util.AbstractMap;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.Date;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
 import javax.xml.transform.dom.DOMResult;
 import javax.xml.transform.dom.DOMSource;
+import org.opendaylight.controller.config.util.xml.DocumentedException;
+import org.opendaylight.controller.config.util.xml.XmlElement;
 import org.opendaylight.controller.config.util.xml.XmlUtil;
 import org.opendaylight.netconf.api.NetconfDocumentedException;
 import org.opendaylight.netconf.api.NetconfMessage;
+import org.opendaylight.netconf.notifications.NetconfNotification;
+import org.opendaylight.netconf.sal.connect.util.MessageCounter;
 import org.opendaylight.netconf.util.NetconfUtil;
 import org.opendaylight.netconf.util.messages.NetconfMessageUtil;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.edit.config.input.EditContent;
@@ -39,8 +49,11 @@ 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.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
 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.SchemaOrderedNormalizedNodeWriter;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
@@ -53,8 +66,9 @@ import org.w3c.dom.Element;
 
 public class NetconfMessageTransformUtil {
 
-    private static final Logger LOG= LoggerFactory.getLogger(NetconfMessageTransformUtil.class);
+    private static final Logger LOG = LoggerFactory.getLogger(NetconfMessageTransformUtil.class);
 
+    public static final String MESSAGE_ID_PREFIX = "m";
     public static final String MESSAGE_ID_ATTR = "message-id";
 
     public static final QName CREATE_SUBSCRIPTION_RPC_QNAME = QName.cachedReference(QName.create(CreateSubscriptionInput.QNAME, "create-subscription"));
@@ -264,7 +278,7 @@ public class NetconfMessageTransformUtil {
         return Builders.containerBuilder().withNodeIdentifier(toId(name)).withValue(ImmutableList.copyOf(node)).build();
     }
 
-    public static DataContainerChild<?, ?> createEditConfigStructure(final SchemaContext ctx, final YangInstanceIdentifier dataPath,
+    public static AnyXmlNode createEditConfigAnyxml(final SchemaContext ctx, final YangInstanceIdentifier dataPath,
                                                                      final Optional<ModifyAction> operation, final Optional<NormalizedNode<?, ?>> lastChildOverride) {
         final NormalizedNode<?, ?> configContent;
 
@@ -287,12 +301,107 @@ public class NetconfMessageTransformUtil {
         }
         final DOMSource value = new DOMSource(element);
 
-        return Builders.choiceBuilder().withNodeIdentifier(toId(EditContent.QNAME)).withChild(
-                Builders.anyXmlBuilder().withNodeIdentifier(toId(NETCONF_CONFIG_QNAME)).withValue(value).build()).build();
+        return Builders.anyXmlBuilder().withNodeIdentifier(toId(NETCONF_CONFIG_QNAME)).withValue(value).build();
+    }
+
+    public static DataContainerChild<?, ?> createEditConfigStructure(final SchemaContext ctx, final YangInstanceIdentifier dataPath,
+                                                                     final Optional<ModifyAction> operation, final Optional<NormalizedNode<?, ?>> lastChildOverride) {
+        return Builders.choiceBuilder().withNodeIdentifier(toId(EditContent.QNAME))
+                .withChild(createEditConfigAnyxml(ctx, dataPath, operation, lastChildOverride)).build();
     }
 
     public static SchemaPath toPath(final QName rpc) {
         return SchemaPath.create(true, rpc);
     }
 
+    private static final ThreadLocal<SimpleDateFormat> EVENT_TIME_FORMAT = new ThreadLocal<SimpleDateFormat>() {
+        @Override
+        protected SimpleDateFormat initialValue() {
+
+            final SimpleDateFormat withMillis = new SimpleDateFormat(
+                    NetconfNotification.RFC3339_DATE_FORMAT_WITH_MILLIS_BLUEPRINT);
+
+            return new SimpleDateFormat(NetconfNotification.RFC3339_DATE_FORMAT_BLUEPRINT) {
+                private static final long serialVersionUID = 1L;
+
+                @Override public Date parse(final String source) throws ParseException {
+                    try {
+                        return super.parse(source);
+                    } catch (ParseException e) {
+                        // In case of failure, try to parse with milliseconds
+                        return withMillis.parse(source);
+                    }
+                }
+            };
+        }
+
+        @Override
+        public void set(final SimpleDateFormat value) {
+            throw new UnsupportedOperationException();
+        }
+    };
+
+    public static Map.Entry<Date, XmlElement> stripNotification(final NetconfMessage message) {
+        final XmlElement xmlElement = XmlElement.fromDomDocument(message.getDocument());
+        final List<XmlElement> childElements = xmlElement.getChildElements();
+        Preconditions.checkArgument(childElements.size() == 2, "Unable to parse notification %s, unexpected format.\nExpected 2 childElements," +
+                " actual childElements size is %s", message, childElements.size());
+
+        final XmlElement eventTimeElement;
+        final XmlElement notificationElement;
+
+        if (childElements.get(0).getName().equals(EVENT_TIME)) {
+            eventTimeElement = childElements.get(0);
+            notificationElement = childElements.get(1);
+        }
+        else if(childElements.get(1).getName().equals(EVENT_TIME)) {
+            eventTimeElement = childElements.get(1);
+            notificationElement = childElements.get(0);
+        } else {
+            throw new IllegalArgumentException("Notification payload does not contain " + EVENT_TIME + " " + message);
+        }
+
+        try {
+            return new AbstractMap.SimpleEntry<>(EVENT_TIME_FORMAT.get().parse(eventTimeElement.getTextContent()), notificationElement);
+        } catch (DocumentedException e) {
+            throw new IllegalArgumentException("Notification payload does not contain " + EVENT_TIME + " " + message);
+        } catch (ParseException e) {
+            LOG.warn("Unable to parse event time from {}. Setting time to {}", eventTimeElement, NetconfNotification.UNKNOWN_EVENT_TIME, e);
+            return new AbstractMap.SimpleEntry<>(NetconfNotification.UNKNOWN_EVENT_TIME, notificationElement);
+        }
+    }
+
+    public static DOMResult prepareDomResultForRpcRequest(final QName rpcQName, final MessageCounter counter) {
+        final Document document = XmlUtil.newDocument();
+        final Element rpcNS =
+                document.createElementNS(NETCONF_RPC_QNAME.getNamespace().toString(), NETCONF_RPC_QNAME.getLocalName());
+        // set msg id
+        rpcNS.setAttribute(MESSAGE_ID_ATTR, counter.getNewMessageId(MESSAGE_ID_PREFIX));
+        final Element elementNS = document.createElementNS(rpcQName.getNamespace().toString(), rpcQName.getLocalName());
+        rpcNS.appendChild(elementNS);
+        document.appendChild(rpcNS);
+        return new DOMResult(elementNS);
+    }
+
+    public static void writeNormalizedRpc(final ContainerNode normalized, final DOMResult result,
+                                          final SchemaPath schemaPath, final SchemaContext baseNetconfCtx) throws IOException, XMLStreamException {
+        final XMLStreamWriter writer = NetconfUtil.XML_FACTORY.createXMLStreamWriter(result);
+        try {
+            try (final NormalizedNodeStreamWriter normalizedNodeStreamWriter =
+                    XMLStreamNormalizedNodeStreamWriter.create(writer, baseNetconfCtx, schemaPath)) {
+                try (final SchemaOrderedNormalizedNodeWriter normalizedNodeWriter =
+                        new SchemaOrderedNormalizedNodeWriter(normalizedNodeStreamWriter, baseNetconfCtx, schemaPath)) {
+                    Collection<DataContainerChild<?, ?>> value = normalized.getValue();
+                    normalizedNodeWriter.write(value);
+                    normalizedNodeWriter.flush();
+                }
+            }
+        } finally {
+            try {
+                writer.close();
+            } catch (final Exception e) {
+               LOG.warn("Unable to close resource properly", e);
+            }
+        }
+    }
 }