X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;ds=sidebyside;f=netconf%2Fnetconf-util%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Futil%2FNetconfUtil.java;h=2bf085609283c0ec2d058c8eb2b176a0f374a28c;hb=197acb99bed8c585e26091eafb761481533718a0;hp=0e8b9aeec4592b0f98dc5a4d719db0e44933c7c4;hpb=9b80e63527cf2a9844d4a6b2e743c45169d19777;p=netconf.git diff --git a/netconf/netconf-util/src/main/java/org/opendaylight/netconf/util/NetconfUtil.java b/netconf/netconf-util/src/main/java/org/opendaylight/netconf/util/NetconfUtil.java index 0e8b9aeec4..2bf0856092 100644 --- a/netconf/netconf-util/src/main/java/org/opendaylight/netconf/util/NetconfUtil.java +++ b/netconf/netconf-util/src/main/java/org/opendaylight/netconf/util/NetconfUtil.java @@ -7,63 +7,146 @@ */ package org.opendaylight.netconf.util; -import com.google.common.base.Preconditions; +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.collect.ImmutableMap; import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Iterator; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.dom.DOMResult; -import org.opendaylight.controller.config.util.xml.DocumentedException; -import org.opendaylight.controller.config.util.xml.XmlElement; -import org.opendaylight.controller.config.util.xml.XmlMappingConstants; -import org.opendaylight.controller.config.util.xml.XmlUtil; +import javax.xml.transform.dom.DOMSource; +import org.eclipse.jdt.annotation.Nullable; +import org.opendaylight.netconf.api.DocumentedException; +import org.opendaylight.netconf.api.xml.XmlElement; import org.opendaylight.netconf.api.xml.XmlNetconfConstants; +import org.opendaylight.netconf.api.xml.XmlUtil; +import org.opendaylight.yangtools.rcf8528.data.util.EmptyMountPointContext; +import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadata; +import org.opendaylight.yangtools.rfc7952.data.util.NormalizedMetadataWriter; +import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; 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.api.schema.stream.NormalizedNodeWriter; -import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.codec.xml.XMLStreamNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.codec.xml.XmlCodecFactory; +import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaPath; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; +import org.xml.sax.SAXException; public final class NetconfUtil { + /** + * Shim interface to handle differences around namespace handling between various XMLStreamWriter implementations. + * Specifically: + * + * + *

+ * Due to this we perform a quick test for behavior and decide the appropriate strategy. + */ + @FunctionalInterface + private interface NamespaceSetter { + void initializeNamespace(XMLStreamWriter writer) throws XMLStreamException; + + static NamespaceSetter forFactory(final XMLOutputFactory xmlFactory) { + final String netconfNamespace = NETCONF_QNAME.getNamespace().toString(); + final AnyXmlNamespaceContext namespaceContext = new AnyXmlNamespaceContext(ImmutableMap.of( + "op", netconfNamespace)); + + try { + final XMLStreamWriter testWriter = xmlFactory.createXMLStreamWriter(new DOMResult( + XmlUtil.newDocument())); + testWriter.setNamespaceContext(namespaceContext); + } catch (final UnsupportedOperationException e) { + // This happens with JDK's DOM writer, which we may be using + LOG.warn("Unable to set namespace context, falling back to setPrefix()", e); + return writer -> writer.setPrefix("op", netconfNamespace); + } catch (XMLStreamException e) { + throw new ExceptionInInitializerError(e); + } + + // Success, we can use setNamespaceContext() + return writer -> writer.setNamespaceContext(namespaceContext); + } + } private static final Logger LOG = LoggerFactory.getLogger(NetconfUtil.class); + + // FIXME: document what exactly this QName means, as it is not referring to a tangible node nor the ietf-module. + // FIXME: what is this contract saying? + // - is it saying all data is going to be interpreted with this root? + // - is this saying we are following a specific interface contract (i.e. do we have schema mounts?) + // - is it also inferring some abilities w.r.t. RFC8342? + public static final QName NETCONF_QNAME = QName.create(QNameModule.create(SchemaContext.NAME.getNamespace(), + Revision.of("2011-06-01")), "netconf").intern(); + // FIXME: is this the device-bound revision? + public static final QName NETCONF_DATA_QNAME = QName.create(NETCONF_QNAME, "data").intern(); + public static final XMLOutputFactory XML_FACTORY; static { - XML_FACTORY = XMLOutputFactory.newFactory(); - XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, false); + final XMLOutputFactory f = XMLOutputFactory.newFactory(); + // FIXME: not repairing namespaces is probably common, this should be availabe as common XML constant. + f.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, false); + XML_FACTORY = f; } - private NetconfUtil() {} + private static final NamespaceSetter XML_NAMESPACE_SETTER = NamespaceSetter.forFactory(XML_FACTORY); + + private NetconfUtil() { + // No-op + } - public static Document checkIsMessageOk(Document response) throws DocumentedException { - XmlElement element = XmlElement.fromDomDocument(response); - Preconditions.checkState(element.getName().equals(XmlMappingConstants.RPC_REPLY_KEY)); - element = element.getOnlyChildElement(); - if (element.getName().equals(XmlNetconfConstants.OK)) { + public static Document checkIsMessageOk(final Document response) throws DocumentedException { + final XmlElement docElement = XmlElement.fromDomDocument(response); + // FIXME: we should throw DocumentedException here + checkState(XmlNetconfConstants.RPC_REPLY_KEY.equals(docElement.getName())); + final XmlElement element = docElement.getOnlyChildElement(); + if (XmlNetconfConstants.OK.equals(element.getName())) { return response; } + LOG.warn("Can not load last configuration. Operation failed."); + // FIXME: we should be throwing a DocumentedException here throw new IllegalStateException("Can not load last configuration. Operation failed: " + XmlUtil.toString(response)); } - public static void writeNormalizedNode(final NormalizedNode normalized, final DOMResult result, final SchemaPath schemaPath, final SchemaContext context) + @SuppressWarnings("checkstyle:IllegalCatch") + public static void writeNormalizedNode(final NormalizedNode normalized, final DOMResult result, + final SchemaPath schemaPath, final SchemaContext context) throws IOException, XMLStreamException { final XMLStreamWriter writer = XML_FACTORY.createXMLStreamWriter(result); try ( - final NormalizedNodeStreamWriter normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, context, schemaPath); - final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter) + NormalizedNodeStreamWriter normalizedNodeStreamWriter = + XMLStreamNormalizedNodeStreamWriter.create(writer, context, schemaPath); + NormalizedNodeWriter normalizedNodeWriter = + NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter) ) { normalizedNodeWriter.write(normalized); normalizedNodeWriter.flush(); } finally { try { - if(writer != null) { + if (writer != null) { writer.close(); } } catch (final Exception e) { @@ -71,4 +154,78 @@ public final class NetconfUtil { } } } + + @SuppressWarnings("checkstyle:IllegalCatch") + public static void writeNormalizedNode(final NormalizedNode normalized, + final @Nullable NormalizedMetadata metadata, + final DOMResult result, final SchemaPath schemaPath, + final SchemaContext context) throws IOException, XMLStreamException { + if (metadata == null) { + writeNormalizedNode(normalized, result, schemaPath, context); + return; + } + + final XMLStreamWriter writer = XML_FACTORY.createXMLStreamWriter(result); + XML_NAMESPACE_SETTER.initializeNamespace(writer); + try ( + NormalizedNodeStreamWriter normalizedNodeStreamWriter = + XMLStreamNormalizedNodeStreamWriter.create(writer, context, schemaPath); + NormalizedMetadataWriter normalizedNodeWriter = + NormalizedMetadataWriter.forStreamWriter(normalizedNodeStreamWriter) + ) { + normalizedNodeWriter.write(normalized, metadata); + normalizedNodeWriter.flush(); + } finally { + try { + if (writer != null) { + writer.close(); + } + } catch (final Exception e) { + LOG.warn("Unable to close resource properly", e); + } + } + } + + public static void writeFilter(final YangInstanceIdentifier query, final DOMResult result, + final SchemaPath schemaPath, final SchemaContext context) throws IOException, XMLStreamException { + if (query.isEmpty()) { + // No query at all + return; + } + + final XMLStreamWriter xmlWriter = XML_FACTORY.createXMLStreamWriter(result); + try { + try (NormalizedNodeStreamWriter writer = + XMLStreamNormalizedNodeStreamWriter.create(xmlWriter, context, schemaPath)) { + final Iterator it = query.getPathArguments().iterator(); + final PathArgument first = it.next(); + StreamingContext.fromSchemaAndQNameChecked(context, first.getNodeType()).streamToWriter(writer, first, + it); + } + } finally { + xmlWriter.close(); + } + } + + public static NormalizedNodeResult transformDOMSourceToNormalizedNode(final MountPointContext mountContext, + final DOMSource value) throws XMLStreamException, URISyntaxException, IOException, SAXException { + final NormalizedNodeResult resultHolder = new NormalizedNodeResult(); + final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder); + final XmlCodecFactory codecs = XmlCodecFactory.create(mountContext); + + // FIXME: we probably need to propagate MountPointContext here and not just the child nodes + final ContainerSchemaNode dataRead = new NodeContainerProxy(NETCONF_DATA_QNAME, + mountContext.getSchemaContext().getChildNodes()); + try (XmlParserStream xmlParserStream = XmlParserStream.create(writer, codecs, dataRead)) { + xmlParserStream.traverse(value); + } + return resultHolder; + } + + + // FIXME: document this interface contract. Does it support RFC8528/RFC8542? How? + public static NormalizedNodeResult transformDOMSourceToNormalizedNode(final EffectiveModelContext schemaContext, + final DOMSource value) throws XMLStreamException, URISyntaxException, IOException, SAXException { + return transformDOMSourceToNormalizedNode(new EmptyMountPointContext(schemaContext), value); + } }