X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=netconf%2Fnetconf-util%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Futil%2FNetconfUtil.java;h=867c782776c36f1cd372dcade4449e7ef4359d15;hb=e91aa63a54778bca132a22056bbc084bd7d9e1ae;hp=134aefecc23e4ef19b6ccb85117d249c2870cda4;hpb=65e3ef6c3f9697a80fbdd7654fcce97cc3cd207e;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 134aefecc2..867c782776 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,72 +7,413 @@ */ package org.opendaylight.netconf.util; -import com.google.common.base.Preconditions; +import static com.google.common.base.Preconditions.checkState; + import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; 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.rfc7952.data.api.NormalizedMetadata; +import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension; +import org.opendaylight.yangtools.rfc7952.data.util.NormalizedMetadataWriter; +import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext; +import org.opendaylight.yangtools.rfc8528.data.util.EmptyMountPointContext; +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.NodeIdentifierWithPredicates; +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.codec.xml.XMLStreamNormalizedNodeStreamWriter; +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.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.w3c.dom.Element; +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(Map.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.debug("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(final 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)) {
+ 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));
}
- @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 (
- NormalizedNodeStreamWriter normalizedNodeStreamWriter =
- XMLStreamNormalizedNodeStreamWriter.create(writer, context, schemaPath);
- NormalizedNodeWriter normalizedNodeWriter =
- NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter)
- ) {
- normalizedNodeWriter.write(normalized);
- normalizedNodeWriter.flush();
+ /**
+ * Write {@code normalized} data into {@link DOMResult}.
+ *
+ * @param normalized data to be written
+ * @param result DOM result holder
+ * @param schemaPath schema path of the parent node
+ * @param context mountpoint schema context
+ * @throws IOException when failed to write data into {@link NormalizedNodeStreamWriter}
+ * @throws XMLStreamException when failed to serialize data into XML document
+ */
+ public static void writeNormalizedNode(final NormalizedNode normalized, final DOMResult result,
+ final SchemaPath schemaPath, final EffectiveModelContext context) throws IOException, XMLStreamException {
+ final XMLStreamWriter xmlWriter = XML_FACTORY.createXMLStreamWriter(result);
+ try (var streamWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter, context, schemaPath);
+ var writer = NormalizedNodeWriter.forStreamWriter(streamWriter)) {
+ writer.write(normalized);
+ writer.flush();
} finally {
- try {
- if (writer != null) {
- writer.close();
- }
- } catch (final Exception e) {
- LOG.warn("Unable to close resource properly", e);
+ xmlWriter.close();
+ }
+ }
+
+ /**
+ * Write {@code normalized} data along with corresponding {@code metadata} into {@link DOMResult}.
+ *
+ * @param normalized data to be written
+ * @param metadata metadata to be written
+ * @param result DOM result holder
+ * @param schemaPath schema path of the parent node
+ * @param context mountpoint schema context
+ * @throws IOException when failed to write data into {@link NormalizedNodeStreamWriter}
+ * @throws XMLStreamException when failed to serialize data into XML document
+ */
+ public static void writeNormalizedNode(final NormalizedNode normalized, final @Nullable NormalizedMetadata metadata,
+ final DOMResult result, final SchemaPath schemaPath, final EffectiveModelContext context)
+ throws IOException, XMLStreamException {
+ if (metadata == null) {
+ writeNormalizedNode(normalized, result, schemaPath, context);
+ return;
+ }
+
+ final XMLStreamWriter xmlWriter = XML_FACTORY.createXMLStreamWriter(result);
+ XML_NAMESPACE_SETTER.initializeNamespace(xmlWriter);
+ try (var streamWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter, context, schemaPath);
+ var writer = NormalizedMetadataWriter.forStreamWriter(streamWriter)) {
+ writer.write(normalized, metadata);
+ writer.flush();
+ } finally {
+ xmlWriter.close();
+ }
+ }
+
+ /**
+ * Write data specified by {@link YangInstanceIdentifier} into {@link DOMResult}.
+ *
+ * @param query path to the root node
+ * @param result DOM result holder
+ * @param schemaPath schema path of the parent node
+ * @param context mountpoint schema context
+ * @throws IOException when failed to write data into {@link NormalizedNodeStreamWriter}
+ * @throws XMLStreamException when failed to serialize data into XML document
+ */
+ public static void writeNormalizedNode(final YangInstanceIdentifier query, final DOMResult result,
+ final SchemaPath schemaPath, final EffectiveModelContext context) throws IOException, XMLStreamException {
+ final XMLStreamWriter xmlWriter = XML_FACTORY.createXMLStreamWriter(result);
+ XML_NAMESPACE_SETTER.initializeNamespace(xmlWriter);
+ try (var streamWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter, context, schemaPath);
+ var writer = new EmptyListXmlWriter(streamWriter, xmlWriter)) {
+ final Iterator