From: Robert Varga Date: Tue, 9 Jul 2019 10:57:04 +0000 (+0200) Subject: Retrofit mount-point awareness into yang-data-util X-Git-Tag: v3.0.3~11 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=e3287e24f631008fdcd591a0e706bcd9254c401e;p=yangtools.git Retrofit mount-point awareness into yang-data-util {Container,ListEntry}NodeDataWithSchema can potentially contain out-of-band mount point data, which needs intermediate processing once all data has been acquired. We introduce AbstractMountPointDataWithSchema as a common superclass, which optionally holds MountPointData. MountPointData contains enough information to resolve the data nodes in the context of a schema context supplied either statically or dynamically (based on the mount data). JIRA: YANGTOOLS-1007 Change-Id: I08ba4dcf3aaf758f12b42e625d9d17a774d07825 Signed-off-by: Robert Varga --- diff --git a/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointStreamWriter.java b/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointStreamWriter.java index 5c020ad485..c2fbe802a4 100644 --- a/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointStreamWriter.java +++ b/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointStreamWriter.java @@ -12,7 +12,9 @@ import java.util.Optional; import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.rfc8528.model.api.MountPointSchemaResolver; import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriterExtension; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; /** * A {@link NormalizedNodeStreamWriterExtension} exposed by stream writers which can handle mount point data, notably @@ -30,4 +32,13 @@ public interface MountPointStreamWriter extends NormalizedNodeStreamWriterExtens * @throws NullPointerException if label is null */ Optional findMountPoint(@NonNull QName label); + + /** + * Start a new mount point with a specific root context. + * + * @param label Mount point label + * @param mountContext SchemaContext associated with the context + * @return A new NormalizedNodeStreamWriter, or empty if the mount point data should be ignored + */ + Optional startMountPoint(@NonNull QName label, @NonNull SchemaContext mountContext); } diff --git a/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/StaticMountPointSchemaResolver.java b/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/StaticMountPointSchemaResolver.java index 8831c498b3..88715ab177 100644 --- a/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/StaticMountPointSchemaResolver.java +++ b/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/StaticMountPointSchemaResolver.java @@ -20,5 +20,6 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; @Beta public interface StaticMountPointSchemaResolver extends MountPointSchemaResolver, SchemaContextProvider { @Override + // FIXME: remove this override when SchemaContextProvider's method has sane semantics. @NonNull SchemaContext getSchemaContext(); } diff --git a/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/YangLibraryConstants.java b/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/YangLibraryConstants.java index 25392c7d48..98af1e6c90 100644 --- a/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/YangLibraryConstants.java +++ b/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/YangLibraryConstants.java @@ -10,7 +10,11 @@ package org.opendaylight.yangtools.rfc8528.model.api; import static java.util.Objects.requireNonNull; import com.google.common.annotations.Beta; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; import java.net.URI; +import java.util.Arrays; +import java.util.Optional; import org.eclipse.jdt.annotation.NonNullByDefault; /** @@ -42,17 +46,22 @@ public final class YangLibraryConstants { public static final String MODULE_NAME = "ietf-yang-library"; /** - * Top-level containers which hold YANG Library information. + * Top-level containers which hold YANG Library information, ordered by descending preference, with more modern + * and/or preferred entries first. */ public enum ContainerName { + // Note: order this enum from most-preferred to least-preferred name /** - * Container in RFC7895 (pre-NMDA) YANG Library. + * Container in RFC8525 (NMDA) YANG Library. */ - RFC7895("modules-state"), + RFC8525("yang-library"), /** - * Container in RFC8525 (NMDA) YANG Library. + * Container in RFC7895 (pre-NMDA) YANG Library. */ - RFC8525("yang-library"); + RFC7895("modules-state"); + + private static final ImmutableMap NAME_TO_ENUM = Maps.uniqueIndex( + Arrays.asList(values()), ContainerName::getLocalName); private final String localName; @@ -63,6 +72,10 @@ public final class YangLibraryConstants { public String getLocalName() { return localName; } + + public static Optional forLocalName(final String localName) { + return Optional.ofNullable(NAME_TO_ENUM.get(requireNonNull(localName))); + } } private YangLibraryConstants() { diff --git a/yang/yang-data-util/pom.xml b/yang/yang-data-util/pom.xml index 93da76e874..08a67b249e 100644 --- a/yang/yang-data-util/pom.xml +++ b/yang/yang-data-util/pom.xml @@ -30,6 +30,10 @@ org.opendaylight.yangtools rfc7952-data-api + + org.opendaylight.yangtools + rfc8528-data-api + org.opendaylight.yangtools yang-model-api diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractMountPointDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractMountPointDataWithSchema.java new file mode 100644 index 0000000000..e8eb9a4c78 --- /dev/null +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractMountPointDataWithSchema.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.data.util; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.annotations.Beta; +import java.io.IOException; +import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadataStreamWriter; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; + +/** + * A {@link CompositeNodeDataWithSchema} which can hold mount-point data. This data is manipulated through + * {@link #getMountPointData(QName)}. + */ +@Beta +public abstract class AbstractMountPointDataWithSchema + extends CompositeNodeDataWithSchema { + private MountPointData mountedData; + + AbstractMountPointDataWithSchema(final T schema) { + super(schema); + } + + @Override + public void write(final NormalizedNodeStreamWriter writer, final NormalizedMetadataStreamWriter metaWriter) + throws IOException { + super.write(writer, metaWriter); + if (mountedData != null) { + mountedData.write(writer); + } + } + + public final MountPointData getMountPointData(final QName label) { + if (mountedData != null) { + final QName existing = mountedData.getIdentifier(); + checkState(label.equals(existing), "Mismatched mount label {}, already have {}", label, existing); + } else { + mountedData = new MountPointData(label); + } + return mountedData; + } +} diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerNodeDataWithSchema.java index feb41b446d..ca0bb2a668 100644 --- a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerNodeDataWithSchema.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerNodeDataWithSchema.java @@ -19,7 +19,7 @@ import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; *

* Represents a YANG container node. */ -public class ContainerNodeDataWithSchema extends CompositeNodeDataWithSchema { +public class ContainerNodeDataWithSchema extends AbstractMountPointDataWithSchema { public ContainerNodeDataWithSchema(final ContainerSchemaNode schema) { super(schema); diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListEntryNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListEntryNodeDataWithSchema.java index 930d74eb06..483315f286 100644 --- a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListEntryNodeDataWithSchema.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListEntryNodeDataWithSchema.java @@ -29,7 +29,7 @@ import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; *

* Represents a YANG list entry node. */ -public abstract class ListEntryNodeDataWithSchema extends CompositeNodeDataWithSchema { +public abstract class ListEntryNodeDataWithSchema extends AbstractMountPointDataWithSchema { private static final class Keyed extends ListEntryNodeDataWithSchema { private final Map> keyValues = new HashMap<>(); // This template results in Maps in schema definition order diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/MountPointChild.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/MountPointChild.java new file mode 100644 index 0000000000..6bee66f0a9 --- /dev/null +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/MountPointChild.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 PANTHEON.tech s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.data.util; + +import com.google.common.annotations.Beta; +import java.io.IOException; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizableAnydata; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +/** + * A raw child of {@link MountPointData}. This is similar in functionality to {@link NormalizableAnydata}, but + * rather than normalizing, the data is fed into a combination of a SchemaContext and NormalizedNodeStreamWriter. + */ +@Beta +@NonNullByDefault +public interface MountPointChild { + /** + * Stream this child into a writer, with the help of a SchemaContext. + * + * @param writer Writer to emit the child into + * @param schemaContext SchemaContext for normalization purposes + * @throws IOException if an underlying error occurs + * @throws NullPointerException if any of the arguments is null + */ + void writeTo(NormalizedNodeStreamWriter writer, SchemaContext schemaContext) throws IOException; + + /** + * Normalized this child to a particular SchemaContext. + * + * @param schemaContext SchemaContext for normalization purposes + * @return A NormalizedNode representation of this child + * @throws IOException if an underlying error occurs + * @throws NullPointerException if any of the arguments is null + */ + NormalizedNode normalizeTo(SchemaContext schemaContext) throws IOException; +} diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/MountPointData.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/MountPointData.java new file mode 100644 index 0000000000..52892ad823 --- /dev/null +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/MountPointData.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2019 PANTHEON.tech s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.data.util; + +import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; + +import com.google.common.annotations.Beta; +import java.io.IOException; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.yangtools.concepts.AbstractIdentifiable; +import org.opendaylight.yangtools.rfc8528.data.api.DynamicMountPointSchemaResolver; +import org.opendaylight.yangtools.rfc8528.data.api.MountPointStreamWriter; +import org.opendaylight.yangtools.rfc8528.model.api.MountPointSchemaResolver; +import org.opendaylight.yangtools.rfc8528.model.api.StaticMountPointSchemaResolver; +import org.opendaylight.yangtools.rfc8528.model.api.YangLibraryConstants.ContainerName; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.parser.api.YangParserException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * YANG Schema Mount-supported data attached to either a {@code list} item or a {@code container}. + */ +@Beta +public final class MountPointData extends AbstractIdentifiable { + private static final Logger LOG = LoggerFactory.getLogger(MountPointData.class); + + private final Map yangLib = new EnumMap<>(ContainerName.class); + private final List children = new ArrayList<>(); + + MountPointData(final QName label) { + super(label); + } + + public void setContainer(final @NonNull ContainerName containerName, final @NonNull MountPointChild data) { + final MountPointChild prev = yangLib.putIfAbsent(containerName, requireNonNull(data)); + checkState(prev == null, "Attempted to duplicate container %s data %s with %s", containerName, prev, data); + addChild(data); + } + + public void addChild(final @NonNull MountPointChild data) { + children.add(requireNonNull(data)); + } + + void write(final @NonNull NormalizedNodeStreamWriter writer) throws IOException { + final MountPointStreamWriter mountWriter = writer.getExtensions().getInstance(MountPointStreamWriter.class); + if (mountWriter == null) { + LOG.debug("Writer {} does not support mount points, ignoring data in {}", writer, getIdentifier()); + return; + } + + final Optional optResolver = mountWriter.findMountPoint(getIdentifier()); + if (!optResolver.isPresent()) { + LOG.debug("Mount point for {} is not present, ignoring it", getIdentifier()); + return; + } + + final MountPointSchemaResolver resolver = optResolver.get(); + if (resolver instanceof StaticMountPointSchemaResolver) { + writeStatic(mountWriter, ((StaticMountPointSchemaResolver) resolver).getSchemaContext()); + } else if (resolver instanceof DynamicMountPointSchemaResolver) { + writeDynamic(mountWriter, (DynamicMountPointSchemaResolver) resolver); + } else { + throw new IOException("Unhandled resolver " + resolver); + } + } + + private void writeDynamic(final @NonNull MountPointStreamWriter mountWriter, + final DynamicMountPointSchemaResolver resolver) throws IOException { + for (Entry entry : yangLib.entrySet()) { + final Optional optContext = resolver.findContainerContext(entry.getKey()); + if (!optContext.isPresent()) { + LOG.debug("YANG Library context for mount point {} container {} not found", getIdentifier(), + entry.getKey()); + continue; + } + + final NormalizedNode data = entry.getValue().normalizeTo(optContext.get()); + if (!(data instanceof ContainerNode)) { + throw new IOException("Invalid non-container " + data); + } + + final SchemaContext context; + try { + context = resolver.assembleSchemaContext((ContainerNode) data); + } catch (YangParserException e) { + throw new IOException("Failed to assemble context for " + data, e); + } + + writeStatic(mountWriter, context); + return; + } + + LOG.warn("Failed to create a dynamic context for mount point {}, ignoring its data", getIdentifier()); + } + + private void writeStatic(final @NonNull MountPointStreamWriter mountWriter, + final @NonNull SchemaContext schemaContext) throws IOException { + final Optional optWriter = mountWriter.startMountPoint(getIdentifier(), + schemaContext); + if (!optWriter.isPresent()) { + LOG.debug("Ignoring mount point {} data due to writer decision", getIdentifier()); + return; + } + + try (NormalizedNodeStreamWriter writer = optWriter.get()) { + for (MountPointChild child : children) { + child.writeTo(writer, schemaContext); + } + } + } +}