summaryrefslogtreecommitdiffstats
path: root/v3po
diff options
context:
space:
mode:
Diffstat (limited to 'v3po')
-rw-r--r--v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java228
-rw-r--r--v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModificationDiff.java278
-rw-r--r--v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java4
-rw-r--r--v3po/data-impl/src/main/yang/data-impl.yang4
-rw-r--r--v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java367
-rw-r--r--v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java215
-rw-r--r--v3po/data-impl/src/test/resources/test-diff.yang17
-rw-r--r--v3po/features/pom.xml5
-rw-r--r--v3po/features/src/main/features/features.xml1
-rw-r--r--v3po/impl/src/main/config/default-config.xml15
-rw-r--r--v3po/impl/src/main/config/initializer-config.xml18
-rw-r--r--v3po/translate-api/pom.xml2
-rw-r--r--v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ChildWriter.java66
-rw-r--r--v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/DataObjectUpdate.java114
-rw-r--r--v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ModifiableWriterRegistry.java78
-rw-r--r--v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterFactory.java28
-rw-r--r--v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterRegistry.java131
-rw-r--r--v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterRegistryBuilder.java25
-rw-r--r--v3po/translate-api/src/main/yang/translate-api.yang11
-rw-r--r--v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/AbstractCompositeWriter.java234
-rw-r--r--v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/CompositeChildWriter.java125
-rw-r--r--v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/CompositeListWriter.java203
-rw-r--r--v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriter.java104
-rw-r--r--v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriter.java (renamed from v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/CompositeRootWriter.java)38
-rw-r--r--v3po/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriterTest.java83
-rw-r--r--v3po/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriterTest.java64
-rw-r--r--v3po/translate-utils/pom.xml5
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java37
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/CloseableWriterRegistry.java65
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/DelegatingWriterRegistry.java182
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterCustomizer.java49
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java24
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveAugmentWriterCustomizer.java50
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveChildWriterCustomizer.java59
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java334
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java225
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java85
-rw-r--r--v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModule.java2
-rw-r--r--v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModuleFactory.java7
-rw-r--r--v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModule.java21
-rw-r--r--v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModule.java17
-rw-r--r--v3po/translate-utils/src/main/yang/translate-utils.yang13
-rw-r--r--v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/util/DelegatingWriterRegistryTest.java189
-rw-r--r--v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilderTest.java156
-rw-r--r--v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryTest.java295
-rw-r--r--v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriterTest.java96
-rw-r--r--v3po/v3po2vpp/src/main/config/default-config.xml14
-rw-r--r--v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesHoneycombWriterModule.java213
-rw-r--r--v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/SubinterfaceAugmentationWriterFactory.java130
-rw-r--r--v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppHoneycombWriterModule.java82
-rw-r--r--v3po/v3po2vpp/src/main/yang/v3po2vpp.yang4
-rw-r--r--v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vpp/VppTest.java271
-rw-r--r--v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vpp/VppUtils.java63
-rw-r--r--v3po/vpp-translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/v3po/util/NamingContext.java4
54 files changed, 2782 insertions, 2368 deletions
diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java
index 91de1885e..77aa12aba 100644
--- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java
+++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java
@@ -16,38 +16,36 @@
package io.fd.honeycomb.v3po.data.impl;
-import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.util.concurrent.Futures.immediateCheckedFuture;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
-import com.google.common.collect.Sets;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.CheckedFuture;
import io.fd.honeycomb.v3po.data.DataModification;
import io.fd.honeycomb.v3po.data.ReadableDataManager;
import io.fd.honeycomb.v3po.translate.TranslationException;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
import io.fd.honeycomb.v3po.translate.util.write.TransactionMappingContext;
import io.fd.honeycomb.v3po.translate.util.write.TransactionWriteContext;
+import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate;
import io.fd.honeycomb.v3po.translate.write.WriteContext;
import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -110,6 +108,7 @@ public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager
@Override
protected void processCandidate(final DataTreeCandidate candidate)
throws TranslationException {
+
final DataTreeCandidateNode rootNode = candidate.getRootNode();
final YangInstanceIdentifier rootPath = candidate.getRootPath();
LOG.trace("ConfigDataTree.modify() rootPath={}, rootNode={}, dataBefore={}, dataAfter={}",
@@ -119,13 +118,12 @@ public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager
ModificationDiff.recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, rootNode);
LOG.debug("ConfigDataTree.modify() diff: {}", modificationDiff);
- final Map<InstanceIdentifier<?>, DataObject> nodesBefore = toBindingAware(modificationDiff.getModificationsBefore());
- LOG.debug("ConfigDataTree.modify() extracted nodesBefore={}", nodesBefore.keySet());
- final Map<InstanceIdentifier<?>, DataObject> nodesAfter = toBindingAware(modificationDiff.getModificationsAfter());
- LOG.debug("ConfigDataTree.modify() extracted nodesAfter={}", nodesAfter.keySet());
+ // Distinguish between updates (create + update) and deletes
+ final WriterRegistry.DataObjectUpdates baUpdates = toBindingAware(modificationDiff.getUpdates());
+ LOG.debug("ConfigDataTree.modify() extracted updates={}", baUpdates);
try (final WriteContext ctx = getTransactionWriteContext()) {
- writerRegistry.update(nodesBefore, nodesAfter, ctx);
+ writerRegistry.update(baUpdates, ctx);
final CheckedFuture<Void, TransactionCommitFailedException> contextUpdateResult =
((TransactionMappingContext) ctx.getMappingContext()).submit();
@@ -152,7 +150,7 @@ public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager
LOG.error(msg, e);
throw new TranslationException(msg, e);
} catch (TranslationException e) {
- LOG.error("Error while processing data change (before={}, after={})", nodesBefore, nodesAfter, e);
+ LOG.error("Error while processing data change (updates={})", baUpdates, e);
throw e;
}
}
@@ -167,177 +165,71 @@ public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager
return new TransactionWriteContext(serializer, beforeTx, afterTx, mappingContext);
}
- private Map<InstanceIdentifier<?>, DataObject> toBindingAware(final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> biNodes) {
+ private WriterRegistry.DataObjectUpdates toBindingAware(
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> biNodes) {
return ModifiableDataTreeDelegator.toBindingAware(biNodes, serializer);
}
}
@VisibleForTesting
- static Map<InstanceIdentifier<?>, DataObject> toBindingAware(final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> biNodes,
- final BindingNormalizedNodeSerializer serializer) {
- final HashMap<InstanceIdentifier<?>, DataObject> transformed = new HashMap<>(biNodes.size());
- for (Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> biEntry : biNodes.entrySet()) {
- final Map.Entry<InstanceIdentifier<?>, DataObject> baEntry = serializer.fromNormalizedNode(biEntry.getKey(), biEntry.getValue());
- if (baEntry != null) {
- transformed.put(baEntry.getKey(), baEntry.getValue());
+ static WriterRegistry.DataObjectUpdates toBindingAware(
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> biNodes,
+ final BindingNormalizedNodeSerializer serializer) {
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> dataObjectUpdates = HashMultimap.create();
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> dataObjectDeletes =
+ HashMultimap.create();
+
+ for (Map.Entry<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> biEntry : biNodes.entrySet()) {
+ final InstanceIdentifier<?> unkeyedIid =
+ RWUtils.makeIidWildcarded(serializer.fromYangInstanceIdentifier(biEntry.getKey()));
+
+ ModificationDiff.NormalizedNodeUpdate normalizedNodeUpdate = biEntry.getValue();
+ final DataObjectUpdate dataObjectUpdate = toDataObjectUpdate(normalizedNodeUpdate, serializer);
+ if (dataObjectUpdate != null) {
+ if (dataObjectUpdate instanceof DataObjectUpdate.DataObjectDelete) {
+ dataObjectDeletes.put(unkeyedIid, ((DataObjectUpdate.DataObjectDelete) dataObjectUpdate));
+ } else {
+ dataObjectUpdates.put(unkeyedIid, dataObjectUpdate);
+ }
}
}
- return transformed;
+ return new WriterRegistry.DataObjectUpdates(dataObjectUpdates, dataObjectDeletes);
}
- @VisibleForTesting
- static final class ModificationDiff {
-
- private static final ModificationDiff EMPTY_DIFF = new ModificationDiff(Collections.emptyMap(), Collections.emptyMap());
- private static final EnumSet LEAF_MODIFICATIONS = EnumSet.of(ModificationType.WRITE, ModificationType.DELETE);
-
- private final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> modificationsBefore;
- private final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> modificationsAfter;
+ @Nullable
+ private static DataObjectUpdate toDataObjectUpdate(
+ final ModificationDiff.NormalizedNodeUpdate normalizedNodeUpdate,
+ final BindingNormalizedNodeSerializer serializer) {
- private ModificationDiff(@Nonnull final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> modificationsBefore,
- @Nonnull final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> modificationsAfter) {
- this.modificationsBefore = modificationsBefore;
- this.modificationsAfter = modificationsAfter;
- }
-
- Map<YangInstanceIdentifier, NormalizedNode<?, ?>> getModificationsBefore() {
- return modificationsBefore;
- }
+ InstanceIdentifier<?> baId = serializer.fromYangInstanceIdentifier(normalizedNodeUpdate.getId());
+ checkNotNull(baId, "Unable to transform instance identifier: %s into BA", normalizedNodeUpdate.getId());
- Map<YangInstanceIdentifier, NormalizedNode<?, ?>> getModificationsAfter() {
- return modificationsAfter;
- }
-
- private ModificationDiff merge(final ModificationDiff other) {
- if (this == EMPTY_DIFF) {
- return other;
- }
+ DataObject dataObjectBefore = getDataObject(serializer,
+ normalizedNodeUpdate.getDataBefore(), normalizedNodeUpdate.getId());
+ DataObject dataObjectAfter =
+ getDataObject(serializer, normalizedNodeUpdate.getDataAfter(), normalizedNodeUpdate.getId());
- if (other == EMPTY_DIFF) {
- return this;
- }
-
- return new ModificationDiff(join(modificationsBefore, other.modificationsBefore),
- join(modificationsAfter, other.modificationsAfter));
- }
-
- private static Map<YangInstanceIdentifier, NormalizedNode<?, ?>> join(
- final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> mapOne,
- final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> mapTwo) {
- // Check unique modifications
- // TODO Probably not necessary to check
- final Sets.SetView<YangInstanceIdentifier> duplicates = Sets.intersection(mapOne.keySet(), mapTwo.keySet());
- checkArgument(duplicates.size() == 0, "Duplicates detected: %s. In maps: %s and %s", duplicates, mapOne, mapTwo);
- final HashMap<YangInstanceIdentifier, NormalizedNode<?, ?>> joined = new HashMap<>();
- joined.putAll(mapOne);
- joined.putAll(mapTwo);
- return joined;
- }
-
- private static ModificationDiff createFromBefore(YangInstanceIdentifier idBefore, DataTreeCandidateNode candidate) {
- return new ModificationDiff(
- Collections.singletonMap(idBefore, candidate.getDataBefore().get()),
- Collections.emptyMap());
- }
-
- private static ModificationDiff create(YangInstanceIdentifier id, DataTreeCandidateNode candidate) {
- return new ModificationDiff(
- Collections.singletonMap(id, candidate.getDataBefore().get()),
- Collections.singletonMap(id, candidate.getDataAfter().get()));
- }
-
- private static ModificationDiff createFromAfter(YangInstanceIdentifier idAfter, DataTreeCandidateNode candidate) {
- return new ModificationDiff(
- Collections.emptyMap(),
- Collections.singletonMap(idAfter, candidate.getDataAfter().get()));
- }
-
- /**
- * Produce a diff from a candidate node recursively.
- */
- @Nonnull
- static ModificationDiff recursivelyFromCandidate(@Nonnull final YangInstanceIdentifier yangIid,
- @Nonnull final DataTreeCandidateNode currentCandidate) {
- switch (currentCandidate.getModificationType()) {
- case APPEARED:
- case DISAPPEARED:
- case UNMODIFIED: {
- // (dis)appeared nodes are not important, no real data to process
- return ModificationDiff.EMPTY_DIFF;
- }
- case WRITE: {
- return currentCandidate.getDataBefore().isPresent()
- ? ModificationDiff.create(yangIid, currentCandidate)
- : ModificationDiff.createFromAfter(yangIid, currentCandidate);
- // TODO HONEYCOMB-94 process children recursively to get modifications for child nodes
- }
- case DELETE:
- return ModificationDiff.createFromBefore(yangIid, currentCandidate);
- case SUBTREE_MODIFIED: {
- // Modifications here are presented also for leaves. However that kind of granularity is not required
- // So if there's a modified leaf, mark current complex node also as modification
- java.util.Optional<Boolean> leavesModified = currentCandidate.getChildNodes().stream()
- .filter(ModificationDiff::isLeaf)
- // For some reason, we get modifications on unmodified list keys TODO debug and report ODL bug
- // and that messes up our modifications collection here, so we need to skip
- .filter(ModificationDiff::isModification)
- .map(child -> LEAF_MODIFICATIONS.contains(child.getModificationType()))
- .reduce((boolOne, boolTwo) -> boolOne || boolTwo);
-
- if (leavesModified.isPresent() && leavesModified.get()) {
- return ModificationDiff.create(yangIid, currentCandidate);
- // TODO HONEYCOMB-94 process children recursively to get modifications for child nodes even if current
- // was modified
- } else {
- // SUBTREE MODIFIED, no modification on current, but process children recursively
- return currentCandidate.getChildNodes().stream()
- // not interested in modifications to leaves
- .filter(child -> !isLeaf(child))
- .map(candidate -> recursivelyFromCandidate(yangIid.node(candidate.getIdentifier()), candidate))
- .reduce(ModificationDiff::merge)
- .orElse(EMPTY_DIFF);
- }
- }
- default:
- throw new IllegalStateException("Unknown modification type: "
- + currentCandidate.getModificationType() + ". Unsupported");
- }
- }
+ return dataObjectBefore == null && dataObjectAfter == null
+ ? null
+ : DataObjectUpdate.create(baId, dataObjectBefore, dataObjectAfter);
+ }
- /**
- * Check whether candidate.before and candidate.after is different. If not
- * return false.
- */
- private static boolean isModification(final DataTreeCandidateNode candidateNode) {
- if (candidateNode.getDataBefore().isPresent()) {
- if (candidateNode.getDataAfter().isPresent()) {
- return !candidateNode.getDataAfter().get().equals(candidateNode.getDataBefore().get());
- } else {
- return true;
- }
+ @Nullable
+ private static DataObject getDataObject(@Nonnull final BindingNormalizedNodeSerializer serializer,
+ @Nullable final NormalizedNode<?, ?> data,
+ @Nonnull final YangInstanceIdentifier id) {
+ DataObject dataObject = null;
+ if (data != null) {
+ final Map.Entry<InstanceIdentifier<?>, DataObject> dataObjectEntry =
+ serializer.fromNormalizedNode(id, data);
+ if (dataObjectEntry != null) {
+ dataObject = dataObjectEntry.getValue();
}
-
- // considering not a modification if data after is also null
- return candidateNode.getDataAfter().isPresent();
- }
-
- /**
- * Check whether candidate node is for a leaf type node.
- */
- private static boolean isLeaf(final DataTreeCandidateNode candidateNode) {
- // orNull intentional, some candidate nodes have both data after and data before null
- return candidateNode.getDataAfter().orNull() instanceof LeafNode<?>
- || candidateNode.getDataBefore().orNull() instanceof LeafNode<?>;
- }
-
- @Override
- public String toString() {
- return "ModificationDiff{"
- + "modificationsBefore=" + modificationsBefore
- + ", modificationsAfter=" + modificationsAfter
- + '}';
}
+ return dataObject;
}
+
}
diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModificationDiff.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModificationDiff.java
new file mode 100644
index 000000000..abc0062de
--- /dev/null
+++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModificationDiff.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MixinNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+
+/**
+ * Recursively collects and provides all unique and non-null modifications (modified normalized nodes).
+ */
+final class ModificationDiff {
+
+ private static final ModificationDiff EMPTY_DIFF = new ModificationDiff(Collections.emptyMap());
+ private static final EnumSet LEAF_MODIFICATIONS = EnumSet.of(ModificationType.WRITE, ModificationType.DELETE);
+
+ private final Map<YangInstanceIdentifier, NormalizedNodeUpdate> updates;
+
+ private ModificationDiff(@Nonnull Map<YangInstanceIdentifier, NormalizedNodeUpdate> updates) {
+ this.updates = updates;
+ }
+
+ /**
+ * Get processed modifications.
+ *
+ * @return mapped modifications, where key is keyed {@link YangInstanceIdentifier}.
+ */
+ Map<YangInstanceIdentifier, NormalizedNodeUpdate> getUpdates() {
+ return updates;
+ }
+
+ private ModificationDiff merge(final ModificationDiff other) {
+ if (this == EMPTY_DIFF) {
+ return other;
+ }
+
+ if (other == EMPTY_DIFF) {
+ return this;
+ }
+
+ return new ModificationDiff(join(updates, other.updates));
+ }
+
+ private static Map<YangInstanceIdentifier, NormalizedNodeUpdate> join(Map<YangInstanceIdentifier, NormalizedNodeUpdate> first,
+ Map<YangInstanceIdentifier, NormalizedNodeUpdate> second) {
+ final Map<YangInstanceIdentifier, NormalizedNodeUpdate> merged = new HashMap<>();
+ merged.putAll(first);
+ merged.putAll(second);
+ return merged;
+ }
+
+ private static ModificationDiff create(YangInstanceIdentifier id, DataTreeCandidateNode candidate) {
+ return new ModificationDiff(ImmutableMap.of(id, NormalizedNodeUpdate.create(id, candidate)));
+ }
+
+ /**
+ * Produce an aggregated diff from a candidate node recursively. MixinNodes are ignored as modifications and so
+ * are complex nodes which direct leaves were not modified.
+ */
+ @Nonnull
+ static ModificationDiff recursivelyFromCandidate(@Nonnull final YangInstanceIdentifier yangIid,
+ @Nonnull final DataTreeCandidateNode currentCandidate) {
+ // recursively process child nodes for exact modifications
+ return recursivelyChildrenFromCandidate(yangIid, currentCandidate)
+ // also add modification on current level, if elligible
+ .merge(isModification(currentCandidate)
+ ? ModificationDiff.create(yangIid, currentCandidate)
+ : EMPTY_DIFF);
+ }
+
+ /**
+ * Check whether current node was modified. {@link MixinNode}s are ignored
+ * and only nodes which direct leaves(or choices) are modified are considered a modification.
+ */
+ private static Boolean isModification(@Nonnull final DataTreeCandidateNode currentCandidate) {
+ // Mixin nodes are not considered modifications
+ if (isMixin(currentCandidate) && !isAugment(currentCandidate)) {
+ return false;
+ } else {
+ return isCurrentModified(currentCandidate);
+ }
+ }
+
+ private static Boolean isCurrentModified(final @Nonnull DataTreeCandidateNode currentCandidate) {
+ // Check if there are any modified leaves and if so, consider current node as modified
+ final Boolean directLeavesModified = currentCandidate.getChildNodes().stream()
+ .filter(ModificationDiff::isLeaf)
+ // For some reason, we get modifications on unmodified list keys TODO debug and report ODL bug
+ // and that messes up our modifications collection here, so we need to skip
+ .filter(ModificationDiff::isBeforeAndAfterDifferent)
+ .filter(child -> LEAF_MODIFICATIONS.contains(child.getModificationType()))
+ .findFirst()
+ .isPresent();
+
+ return directLeavesModified
+ // Also check choices (choices do not exist in BA world and if anything within a choice was modified,
+ // consider its parent as being modified)
+ || currentCandidate.getChildNodes().stream()
+ .filter(ModificationDiff::isChoice)
+ // Recursively check each choice if there was any change to it
+ .filter(ModificationDiff::isCurrentModified)
+ .findFirst()
+ .isPresent();
+ }
+
+ /**
+ * Process all non-leaf child nodes recursively, creating aggregated {@link ModificationDiff}.
+ */
+ private static ModificationDiff recursivelyChildrenFromCandidate(final @Nonnull YangInstanceIdentifier yangIid,
+ final @Nonnull DataTreeCandidateNode currentCandidate) {
+ // recursively process child nodes for specific modifications
+ return currentCandidate.getChildNodes().stream()
+ // not interested in modifications to leaves
+ .filter(child -> !isLeaf(child))
+ .map(candidate -> recursivelyFromCandidate(yangIid.node(candidate.getIdentifier()), candidate))
+ .reduce(ModificationDiff::merge)
+ .orElse(EMPTY_DIFF);
+ }
+
+ /**
+ * Check whether candidate.before and candidate.after is different. If not return false.
+ */
+ private static boolean isBeforeAndAfterDifferent(@Nonnull final DataTreeCandidateNode candidateNode) {
+ if (candidateNode.getDataBefore().isPresent()) {
+ return !candidateNode.getDataBefore().get().equals(candidateNode.getDataAfter().orNull());
+ }
+
+ // considering not a modification if data after is also null
+ return candidateNode.getDataAfter().isPresent();
+ }
+
+ /**
+ * Check whether candidate node is for a leaf type node.
+ */
+ private static boolean isLeaf(final DataTreeCandidateNode candidateNode) {
+ // orNull intentional, some candidate nodes have both data after and data before null
+ return candidateNode.getDataAfter().orNull() instanceof LeafNode<?>
+ || candidateNode.getDataBefore().orNull() instanceof LeafNode<?>;
+ }
+
+ /**
+ * Check whether candidate node is for a Mixin type node.
+ */
+ private static boolean isMixin(final DataTreeCandidateNode candidateNode) {
+ // orNull intentional, some candidate nodes have both data after and data before null
+ return candidateNode.getDataAfter().orNull() instanceof MixinNode
+ || candidateNode.getDataBefore().orNull() instanceof MixinNode;
+ }
+
+ /**
+ * Check whether candidate node is for an Augmentation type node.
+ */
+ private static boolean isAugment(final DataTreeCandidateNode candidateNode) {
+ // orNull intentional, some candidate nodes have both data after and data before null
+ return candidateNode.getDataAfter().orNull() instanceof AugmentationNode
+ || candidateNode.getDataBefore().orNull() instanceof AugmentationNode;
+ }
+
+ /**
+ * Check whether candidate node is for a Choice type node.
+ */
+ private static boolean isChoice(final DataTreeCandidateNode candidateNode) {
+ // orNull intentional, some candidate nodes have both data after and data before null
+ return candidateNode.getDataAfter().orNull() instanceof ChoiceNode
+ || candidateNode.getDataBefore().orNull() instanceof ChoiceNode;
+ }
+
+ @Override
+ public String toString() {
+ return "ModificationDiff{updates=" + updates + '}';
+ }
+
+ /**
+ * Update to a normalized node identifiable by its {@link YangInstanceIdentifier}.
+ */
+ static final class NormalizedNodeUpdate {
+
+ @Nonnull
+ private final YangInstanceIdentifier id;
+ @Nullable
+ private final NormalizedNode<?, ?> dataBefore;
+ @Nullable
+ private final NormalizedNode<?, ?> dataAfter;
+
+ private NormalizedNodeUpdate(@Nonnull final YangInstanceIdentifier id,
+ @Nullable final NormalizedNode<?, ?> dataBefore,
+ @Nullable final NormalizedNode<?, ?> dataAfter) {
+ this.id = checkNotNull(id);
+ this.dataAfter = dataAfter;
+ this.dataBefore = dataBefore;
+ }
+
+ @Nullable
+ public NormalizedNode<?, ?> getDataBefore() {
+ return dataBefore;
+ }
+
+ @Nullable
+ public NormalizedNode<?, ?> getDataAfter() {
+ return dataAfter;
+ }
+
+ @Nonnull
+ public YangInstanceIdentifier getId() {
+ return id;
+ }
+
+ static NormalizedNodeUpdate create(@Nonnull final YangInstanceIdentifier id,
+ @Nonnull final DataTreeCandidateNode candidate) {
+ return create(id, candidate.getDataBefore().orNull(), candidate.getDataAfter().orNull());
+ }
+
+ static NormalizedNodeUpdate create(@Nonnull final YangInstanceIdentifier id,
+ @Nullable final NormalizedNode<?, ?> dataBefore,
+ @Nullable final NormalizedNode<?, ?> dataAfter) {
+ checkArgument(!(dataBefore == null && dataAfter == null), "Both before and after data are null");
+ return new NormalizedNodeUpdate(id, dataBefore, dataAfter);
+ }
+
+ @Override
+ public boolean equals(final Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+
+ final NormalizedNodeUpdate that = (NormalizedNodeUpdate) other;
+
+ return id.equals(that.id);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "NormalizedNodeUpdate{" + "id=" + id
+ + ", dataBefore=" + dataBefore
+ + ", dataAfter=" + dataAfter
+ + '}';
+ }
+ }
+
+}
diff --git a/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java b/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java
index 3e871e09a..eabcdcbc8 100644
--- a/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java
+++ b/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java
@@ -38,8 +38,8 @@ public class ConfigDataTreeModule extends
public java.lang.AutoCloseable createInstance() {
LOG.debug("ConfigDataTreeModule.createInstance()");
return new CloseableConfigDataTree(
- new ModifiableDataTreeDelegator(getSerializerDependency(), getDataTreeDependency(), getWriterRegistryDependency(),
- getContextBindingBrokerDependency()));
+ new ModifiableDataTreeDelegator(getSerializerDependency(), getDataTreeDependency(),
+ getWriterRegistryBuilderDependency().build(), getContextBindingBrokerDependency()));
}
private static final class CloseableConfigDataTree implements ModifiableDataManager, AutoCloseable {
diff --git a/v3po/data-impl/src/main/yang/data-impl.yang b/v3po/data-impl/src/main/yang/data-impl.yang
index 0ea76cf0e..922846371 100644
--- a/v3po/data-impl/src/main/yang/data-impl.yang
+++ b/v3po/data-impl/src/main/yang/data-impl.yang
@@ -108,11 +108,11 @@ module data-impl {
}
}
- container writer-registry {
+ container writer-registry-builder {
uses config:service-ref {
refine type {
mandatory true;
- config:required-identity tapi:honeycomb-writer-registry;
+ config:required-identity tapi:honeycomb-writer-registry-builder;
}
}
}
diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java
index 086636de6..c2653661a 100644
--- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java
+++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java
@@ -16,27 +16,34 @@
package io.fd.honeycomb.v3po.data.impl;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyMap;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import com.google.common.base.Optional;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
import io.fd.honeycomb.v3po.data.DataModification;
import io.fd.honeycomb.v3po.translate.TranslationException;
+import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate;
import io.fd.honeycomb.v3po.translate.write.WriteContext;
import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
+import java.util.AbstractMap;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -46,18 +53,14 @@ import org.mockito.Mock;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Ethernet;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
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.MapNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
public class ModifiableDataTreeDelegatorTest {
@@ -65,112 +68,66 @@ public class ModifiableDataTreeDelegatorTest {
private WriterRegistry writer;
@Mock
private BindingNormalizedNodeSerializer serializer;
- @Mock
private DataTree dataTree;
@Mock
private org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification modification;
@Mock
private DataBroker contextBroker;
+ @Mock
+ private org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction tx;
private ModifiableDataTreeManager configDataTree;
+ static final InstanceIdentifier<?> DEFAULT_ID = InstanceIdentifier.create(DataObject.class);
+ static DataObject DEFAULT_DATA_OBJECT = mockDataObject("serialized", DataObject.class);
+
@Before
- public void setUp() {
+ public void setUp() throws Exception {
initMocks(this);
- configDataTree = new ModifiableDataTreeDelegator(serializer, dataTree, writer, contextBroker);
- }
-
- @Test
- public void testRead() throws Exception {
- final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot
- snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class);
- when(dataTree.takeSnapshot()).thenReturn(snapshot);
- when(snapshot.newModification()).thenReturn(modification);
+ dataTree = ModificationDiffTest.getDataTree();
+ when(contextBroker.newReadWriteTransaction()).thenReturn(tx);
+ when(tx.submit()).thenReturn(Futures.immediateCheckedFuture(null));
- final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class);
- final Optional node = mock(Optional.class);
- doReturn(node).when(modification).readNode(path);
+ when(serializer.fromYangInstanceIdentifier(any(YangInstanceIdentifier.class))).thenReturn(((InstanceIdentifier) DEFAULT_ID));
+ final Map.Entry<InstanceIdentifier<?>, DataObject> parsed = new AbstractMap.SimpleEntry<>(DEFAULT_ID, DEFAULT_DATA_OBJECT);
+ when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), any(NormalizedNode.class))).thenReturn(parsed);
- final DataModification dataTreeSnapshot = configDataTree.newModification();
- final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future =
- dataTreeSnapshot.read(path);
-
- verify(dataTree, times(2)).takeSnapshot();
- verify(modification).readNode(path);
-
- assertTrue(future.isDone());
- assertEquals(node, future.get());
+ configDataTree = new ModifiableDataTreeDelegator(serializer, dataTree, writer, contextBroker);
}
@Test
- public void testNewModification() throws Exception {
- final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot
- snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class);
- when(dataTree.takeSnapshot()).thenReturn(snapshot);
- when(snapshot.newModification()).thenReturn(modification);
-
- configDataTree.newModification();
- // Snapshot captured twice, so that original data could be provided to translation layer without any possible
- // modification
- verify(dataTree, times(2)).takeSnapshot();
- verify(snapshot, times(2)).newModification();
+ public void testRead() throws Exception {
+ final ContainerNode topContainer = ModificationDiffTest.getTopContainer("topContainer");
+ ModificationDiffTest.addNodeToTree(dataTree, topContainer, ModificationDiffTest.TOP_CONTAINER_ID);
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read =
+ configDataTree.read(ModificationDiffTest.TOP_CONTAINER_ID);
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read2 =
+ configDataTree.newModification().read(ModificationDiffTest.TOP_CONTAINER_ID);
+ final Optional<NormalizedNode<?, ?>> normalizedNodeOptional = read.get();
+ final Optional<NormalizedNode<?, ?>> normalizedNodeOptional2 = read2.get();
+
+ assertEquals(normalizedNodeOptional, normalizedNodeOptional2);
+ assertTrue(normalizedNodeOptional.isPresent());
+ assertEquals(topContainer, normalizedNodeOptional.get());
+ assertEquals(dataTree.takeSnapshot().readNode(ModificationDiffTest.TOP_CONTAINER_ID), normalizedNodeOptional);
}
@Test
public void testCommitSuccessful() throws Exception {
- final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot
- snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class);
- when(dataTree.takeSnapshot()).thenReturn(snapshot);
- when(snapshot.newModification()).thenReturn(modification);
- final DataTreeCandidate prepare = mock(DataTreeCandidate.class);
- doReturn(prepare).when(dataTree).prepare(modification);
-
- final org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction ctxTransaction = mock(
- org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction.class);
- doReturn(ctxTransaction).when(contextBroker).newReadWriteTransaction();
- doReturn(Futures.immediateCheckedFuture(null)).when(ctxTransaction).submit();
-
- final DataObject dataBefore = mockDataObject("before", Ethernet.class);
- final DataObject dataAfter = mockDataObject("after", Ethernet.class);
-
- // Prepare modification:
- final DataTreeCandidateNode rootNode = mockRootNode();
- doReturn(ModificationType.SUBTREE_MODIFIED).when(rootNode).getModificationType();
- doReturn(mock(YangInstanceIdentifier.PathArgument.class)).when(rootNode).getIdentifier();
- final DataTreeCandidateNode childNode = mock(DataTreeCandidateNode.class);
- doReturn(mock(YangInstanceIdentifier.PathArgument.class)).when(childNode).getIdentifier();
- doReturn(Collections.singleton(childNode)).when(rootNode).getChildNodes();
- doReturn(ModificationType.WRITE).when(childNode).getModificationType();
-
- // data before:
- final ContainerNode nodeBefore = mockContainerNode(dataBefore);
- when(childNode.getDataBefore()).thenReturn(Optional.fromNullable(nodeBefore));
- // data after:
- final ContainerNode nodeAfter = mockContainerNode(dataAfter);
- when(childNode.getDataAfter()).thenReturn(Optional.fromNullable(nodeAfter));
-
- // Run the test
- doReturn(rootNode).when(prepare).getRootNode();
+ final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue");
+
final DataModification dataModification = configDataTree.newModification();
+ dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList);
+ dataModification.validate();
dataModification.commit();
- // Verify all changes were processed:
- verify(writer).update(
- mapOf(dataBefore, Ethernet.class),
- mapOf(dataAfter, Ethernet.class),
- any(WriteContext.class));
-
- // Verify modification was validated
- verify(dataTree).validate(modification);
- // Verify context transaction was finished
- verify(ctxTransaction).submit();
- }
-
- private Map<InstanceIdentifier<?>, DataObject> mapOf(final DataObject dataBefore, final Class<Ethernet> type) {
- return eq(Collections.singletonMap(InstanceIdentifier.create(type),dataBefore));
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> map = HashMultimap.create();
+ map.put(DEFAULT_ID, DataObjectUpdate.create(DEFAULT_ID, DEFAULT_DATA_OBJECT, DEFAULT_DATA_OBJECT));
+ verify(writer).update(eq(new WriterRegistry.DataObjectUpdates(map, ImmutableMultimap.of())), any(WriteContext.class));
+ assertEquals(nestedList, dataTree.takeSnapshot().readNode(ModificationDiffTest.NESTED_LIST_ID).get());
}
- private DataObject mockDataObject(final String name, final Class<? extends DataObject> classToMock) {
+ private static DataObject mockDataObject(final String name, final Class<? extends DataObject> classToMock) {
final DataObject dataBefore = mock(classToMock, name);
doReturn(classToMock).when(dataBefore).getImplementedInterface();
return dataBefore;
@@ -178,171 +135,135 @@ public class ModifiableDataTreeDelegatorTest {
@Test
public void testCommitUndoSuccessful() throws Exception {
- final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot
- snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class);
- when(dataTree.takeSnapshot()).thenReturn(snapshot);
- when(snapshot.newModification()).thenReturn(modification);
- final DataTreeCandidate prepare = mock(DataTreeCandidate.class);
- doReturn(prepare).when(dataTree).prepare(modification);
-
- // Prepare data changes:
- final DataObject dataBefore = mockDataObject("before", Ethernet.class);
- final DataObject dataAfter = mockDataObject("after", Ethernet.class);
-
- final io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter reverter = mock(
- io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.class);
+ final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue");
// Fail on update:
+ final WriterRegistry.Reverter reverter = mock(WriterRegistry.Reverter.class);
final TranslationException failedOnUpdateException = new TranslationException("update failed");
- doThrow(new io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException(InstanceIdentifier.create(Ethernet.class), reverter,
- failedOnUpdateException)).when(writer).update(anyMap(), anyMap(), any(WriteContext.class));
-
- // Prepare modification:
- final DataTreeCandidateNode rootNode = mockRootNode();
- doReturn(ModificationType.SUBTREE_MODIFIED).when(rootNode).getModificationType();
- doReturn(mock(YangInstanceIdentifier.PathArgument.class)).when(rootNode).getIdentifier();
- final DataTreeCandidateNode childNode = mock(DataTreeCandidateNode.class);
- doReturn(mock(YangInstanceIdentifier.PathArgument.class)).when(childNode).getIdentifier();
- doReturn(Collections.singleton(childNode)).when(rootNode).getChildNodes();
- doReturn(ModificationType.WRITE).when(childNode).getModificationType();
-
- // data before:
- final ContainerNode nodeBefore = mockContainerNode(dataBefore);
- when(childNode.getDataBefore()).thenReturn(Optional.fromNullable(nodeBefore));
- // data after:
- final ContainerNode nodeAfter = mockContainerNode(dataAfter);
- when(childNode.getDataAfter()).thenReturn(Optional.fromNullable(nodeAfter));
-
- // Run the test
+ doThrow(new WriterRegistry.BulkUpdateException(Collections.singleton(DEFAULT_ID), reverter, failedOnUpdateException))
+ .when(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class));
+
try {
- doReturn(rootNode).when(prepare).getRootNode();
+ // Run the test
final DataModification dataModification = configDataTree.newModification();
+ dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList);
+ dataModification.validate();
dataModification.commit();
- } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException e) {
- verify(writer).update(anyMap(), anyMap(), any(WriteContext.class));
+ fail("WriterRegistry.BulkUpdateException was expected");
+ } catch (WriterRegistry.BulkUpdateException e) {
+ verify(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class));
+ assertThat(e.getFailedIds(), hasItem(DEFAULT_ID));
verify(reverter).revert();
assertEquals(failedOnUpdateException, e.getCause());
- return;
}
-
- fail("WriterRegistry.BulkUpdateException was expected");
}
@Test
public void testCommitUndoFailed() throws Exception {
- final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot
- snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class);
- when(dataTree.takeSnapshot()).thenReturn(snapshot);
- when(snapshot.newModification()).thenReturn(modification);
- final DataTreeCandidate prepare = mock(DataTreeCandidate.class);
- doReturn(prepare).when(dataTree).prepare(modification);
-
- // Prepare data changes:
- final DataObject dataBefore = mockDataObject("before", Ethernet.class);
- final DataObject dataAfter = mockDataObject("after", Ethernet.class);
-
- final io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter reverter = mock(
- io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.class);
+ final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue");
// Fail on update:
- doThrow(new io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException(InstanceIdentifier.create(Ethernet.class), reverter,
- new TranslationException("update failed"))).when(writer).update(anyMap(), anyMap(), any(WriteContext.class));
+ final WriterRegistry.Reverter reverter = mock(WriterRegistry.Reverter.class);
+ final TranslationException failedOnUpdateException = new TranslationException("update failed");
+ doThrow(new WriterRegistry.BulkUpdateException(Collections.singleton(DEFAULT_ID), reverter, failedOnUpdateException))
+ .when(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class));
// Fail on revert:
- final TranslationException failedOnRevertException = new TranslationException("update failed");
- final io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException revertFailedException =
- new io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException(Collections.<InstanceIdentifier<?>>emptyList(),
- failedOnRevertException);
- doThrow(revertFailedException).when(reverter).revert();
-
- // Prepare modification:
- final DataTreeCandidateNode rootNode = mockRootNode();
- doReturn(ModificationType.SUBTREE_MODIFIED).when(rootNode).getModificationType();
- doReturn(mock(YangInstanceIdentifier.PathArgument.class)).when(rootNode).getIdentifier();
- final DataTreeCandidateNode childNode = mock(DataTreeCandidateNode.class);
- doReturn(mock(YangInstanceIdentifier.PathArgument.class)).when(childNode).getIdentifier();
- doReturn(Collections.singleton(childNode)).when(rootNode).getChildNodes();
- doReturn(ModificationType.WRITE).when(childNode).getModificationType();
-
- // data before:
- final ContainerNode nodeBefore = mockContainerNode(dataBefore);
- when(childNode.getDataBefore()).thenReturn(Optional.fromNullable(nodeBefore));
- // data after:
- final ContainerNode nodeAfter = mockContainerNode(dataAfter);
- when(childNode.getDataAfter()).thenReturn(Optional.fromNullable(nodeAfter));
-
- // Run the test
+ final TranslationException failedOnRevertException = new TranslationException("revert failed");
+ doThrow(new WriterRegistry.Reverter.RevertFailedException(Collections.emptySet(), failedOnRevertException))
+ .when(reverter).revert();
+
try {
- doReturn(rootNode).when(prepare).getRootNode();
+ // Run the test
final DataModification dataModification = configDataTree.newModification();
+ dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList);
+ dataModification.validate();
dataModification.commit();
- } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException e) {
- verify(writer).update(anyMap(), anyMap(), any(WriteContext.class));
+ fail("WriterRegistry.Reverter.RevertFailedException was expected");
+ } catch (WriterRegistry.Reverter.RevertFailedException e) {
+ verify(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class));
verify(reverter).revert();
assertEquals(failedOnRevertException, e.getCause());
- return;
}
-
- fail("RevertFailedException was expected");
}
+ private abstract static class DataObject1 implements DataObject {}
+ private abstract static class DataObject2 implements DataObject {}
+ private abstract static class DataObject3 implements DataObject {}
+
@Test
- public void testChildrenFromNormalized() throws Exception {
- final BindingNormalizedNodeSerializer serializer = mock(BindingNormalizedNodeSerializer.class);
-
- final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> map = new HashMap<>();
-
- // init child1 (will not be serialized)
- final DataContainerChild child1 = mock(DataContainerChild.class);
- when(child1.getIdentifier()).thenReturn(mock(YangInstanceIdentifier.PathArgument.class));
- when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), eq(child1))).thenReturn(null);
- map.put(mock(YangInstanceIdentifier.class), child1);
-
- // init child 2 (will be serialized)
- final DataContainerChild child2 = mock(DataContainerChild.class);
- when(child2.getIdentifier()).thenReturn(mock(YangInstanceIdentifier.PathArgument.class));
-
- final Map.Entry entry = mock(Map.Entry.class);
- final InstanceIdentifier<?> id = mock(InstanceIdentifier.class);
- doReturn(id).when(entry).getKey();
- final DataObject data = mock(DataObject.class);
- doReturn(data).when(entry).getValue();
- when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), eq(child2))).thenReturn(entry);
- map.put(mock(YangInstanceIdentifier.class), child2);
-
- // run tested method
- final Map<InstanceIdentifier<?>, DataObject> baMap =
- ModifiableDataTreeDelegator.toBindingAware(map, serializer);
- assertEquals(1, baMap.size());
- assertEquals(data, baMap.get(id));
+ public void testToBindingAware() throws Exception {
+ when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), eq(null))).thenReturn(null);
+
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> biNodes = new HashMap<>();
+ // delete
+ final QName nn1 = QName.create("namespace", "nn1");
+ final YangInstanceIdentifier yid1 = mockYid(nn1);
+ final InstanceIdentifier iid1 = mockIid(yid1, DataObject1.class);
+ final NormalizedNode nn1B = mockNormalizedNode(nn1);
+ final DataObject1 do1B = mockDataObject(yid1, iid1, nn1B, DataObject1.class);
+ biNodes.put(yid1, ModificationDiff.NormalizedNodeUpdate.create(yid1, nn1B, null));
+
+ // create
+ final QName nn2 = QName.create("namespace", "nn1");
+ final YangInstanceIdentifier yid2 = mockYid(nn2);
+ final InstanceIdentifier iid2 = mockIid(yid2, DataObject2.class);;
+ final NormalizedNode nn2A = mockNormalizedNode(nn2);
+ final DataObject2 do2A = mockDataObject(yid2, iid2, nn2A, DataObject2.class);
+ biNodes.put(yid2, ModificationDiff.NormalizedNodeUpdate.create(yid2, null, nn2A));
+
+ // update
+ final QName nn3 = QName.create("namespace", "nn1");
+ final YangInstanceIdentifier yid3 = mockYid(nn3);
+ final InstanceIdentifier iid3 = mockIid(yid3, DataObject3.class);
+ final NormalizedNode nn3B = mockNormalizedNode(nn3);
+ final DataObject3 do3B = mockDataObject(yid3, iid3, nn3B, DataObject3.class);
+ final NormalizedNode nn3A = mockNormalizedNode(nn3);
+ final DataObject3 do3A = mockDataObject(yid3, iid3, nn3A, DataObject3.class);;
+ biNodes.put(yid3, ModificationDiff.NormalizedNodeUpdate.create(yid3, nn3B, nn3A));
+
+ final WriterRegistry.DataObjectUpdates dataObjectUpdates =
+ ModifiableDataTreeDelegator.toBindingAware(biNodes, serializer);
+
+ assertThat(dataObjectUpdates.getDeletes().size(), is(1));
+ assertThat(dataObjectUpdates.getDeletes().keySet(), hasItem(((InstanceIdentifier<?>) iid1)));
+ assertThat(dataObjectUpdates.getDeletes().values(), hasItem(
+ ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid1, do1B, null))));
+
+ assertThat(dataObjectUpdates.getUpdates().size(), is(2));
+ assertThat(dataObjectUpdates.getUpdates().keySet(), hasItems((InstanceIdentifier<?>) iid2, (InstanceIdentifier<?>) iid3));
+ assertThat(dataObjectUpdates.getUpdates().values(), hasItems(
+ DataObjectUpdate.create(iid2, null, do2A),
+ DataObjectUpdate.create(iid3, do3B, do3A)));
+
+ assertThat(dataObjectUpdates.getTypeIntersection().size(), is(3));
}
- private DataTreeCandidateNode mockRootNode() {
- final DataTreeCandidate candidate = mock(DataTreeCandidate.class);
- when(dataTree.prepare(modification)).thenReturn(candidate);
-
- final DataTreeCandidateNode rootNode = mock(DataTreeCandidateNode.class);
- when(candidate.getRootNode()).thenReturn(rootNode);
-
- return rootNode;
+ private <D extends DataObject> D mockDataObject(final YangInstanceIdentifier yid1,
+ final InstanceIdentifier iid1,
+ final NormalizedNode nn1B,
+ final Class<D> type) {
+ final D do1B = mock(type);
+ when(serializer.fromNormalizedNode(yid1, nn1B)).thenReturn(new AbstractMap.SimpleEntry<>(iid1, do1B));
+ return do1B;
}
- private ContainerNode mockContainerNode(DataObject modification) {
- final YangInstanceIdentifier.NodeIdentifier identifier =
- YangInstanceIdentifier.NodeIdentifier.create(QName.create("/"));
-
- final ContainerNode node = mock(ContainerNode.class);
- when(node.getIdentifier()).thenReturn(identifier);
-
- final Map.Entry entry = mock(Map.Entry.class);
- final Class<? extends DataObject> implementedInterface =
- (Class<? extends DataObject>) modification.getImplementedInterface();
- final InstanceIdentifier<?> id = InstanceIdentifier.create(implementedInterface);
+ private NormalizedNode mockNormalizedNode(final QName nn1) {
+ final NormalizedNode nn1B = mock(NormalizedNode.class);
+ when(nn1B.getNodeType()).thenReturn(nn1);
+ return nn1B;
+ }
- doReturn(id).when(entry).getKey();
- doReturn(modification).when(entry).getValue();
- doReturn(entry).when(serializer).fromNormalizedNode(any(YangInstanceIdentifier.class), eq(node));
+ private InstanceIdentifier mockIid(final YangInstanceIdentifier yid1,
+ final Class<? extends DataObject> type) {
+ final InstanceIdentifier iid1 = InstanceIdentifier.create(type);
+ when(serializer.fromYangInstanceIdentifier(yid1)).thenReturn(iid1);
+ return iid1;
+ }
- return node;
+ private YangInstanceIdentifier mockYid(final QName nn1) {
+ final YangInstanceIdentifier yid1 = mock(YangInstanceIdentifier.class);
+ when(yid1.getLastPathArgument()).thenReturn(new YangInstanceIdentifier.NodeIdentifier(nn1));
+ return yid1;
}
}
diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java
index 3475973d6..bc7582e93 100644
--- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java
+++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java
@@ -1,7 +1,8 @@
package io.fd.honeycomb.v3po.data.impl;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
import java.util.Map;
import org.junit.Test;
@@ -11,6 +12,8 @@ import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
@@ -28,15 +31,23 @@ import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceIm
public class ModificationDiffTest {
- private static final QName TOP_CONTAINER_QNAME =
+ static final QName TOP_CONTAINER_QNAME =
QName.create("urn:opendaylight:params:xml:ns:yang:test:diff", "2015-01-05", "top-container");
- private static final QName STRING_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "string");
- private static final QName NAME_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "name");
- private static final QName TEXT_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "text");
- private static final QName NESTED_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "nested-list");
- private static final QName DEEP_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "deep-list");
+ static final QName STRING_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "string");
+ static final QName NAME_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "name");
+ static final QName TEXT_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "text");
+ static final QName NESTED_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "nested-list");
+ static final QName DEEP_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "deep-list");
+
+ static final QName WITH_CHOICE_CONTAINER_QNAME =
+ QName.create("urn:opendaylight:params:xml:ns:yang:test:diff", "2015-01-05", "with-choice");
+ static final QName CHOICE_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "choice");
+ static final QName IN_CASE1_LEAF_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "in-case1");
+ static final QName IN_CASE2_LEAF_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "in-case2");
+
+ static final YangInstanceIdentifier TOP_CONTAINER_ID = YangInstanceIdentifier.of(TOP_CONTAINER_QNAME);
+ static final YangInstanceIdentifier NESTED_LIST_ID = TOP_CONTAINER_ID.node(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME));
- private static final YangInstanceIdentifier TOP_CONTAINER_ID = YangInstanceIdentifier.of(TOP_CONTAINER_QNAME);
@Test
public void testInitialWrite() throws Exception {
@@ -47,10 +58,33 @@ public class ModificationDiffTest {
dataTreeModification.write(TOP_CONTAINER_ID, topContainer);
final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = getModificationDiff(prepare);
+ final ModificationDiff modificationDiff = getModificationDiff(prepare);
- assertTrue(modificationDiff.getModificationsBefore().isEmpty());
- assertAfter(topContainer, TOP_CONTAINER_ID, modificationDiff);
+ assertThat(modificationDiff.getUpdates().size(), is(1));
+ assertThat(modificationDiff.getUpdates().values().size(), is(1));
+ assertUpdate(modificationDiff.getUpdates().values().iterator().next(), TOP_CONTAINER_ID, null, topContainer);
+ }
+
+ @Test
+ public void testInitialWriteForContainerWithChoice() throws Exception {
+ final TipProducingDataTree dataTree = getDataTree();
+ final DataTreeModification dataTreeModification = getModification(dataTree);
+ final ContainerNode containerWithChoice = Builders.containerBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(WITH_CHOICE_CONTAINER_QNAME))
+ .withChild(Builders.choiceBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CHOICE_QNAME))
+ .withChild(ImmutableNodes.leafNode(IN_CASE1_LEAF_QNAME, "withinCase1"))
+ .build())
+ .build();
+ final YangInstanceIdentifier WITH_CHOICE_CONTAINER_ID = YangInstanceIdentifier.of(WITH_CHOICE_CONTAINER_QNAME);
+ dataTreeModification.write(WITH_CHOICE_CONTAINER_ID, containerWithChoice);
+ final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
+
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+
+ assertThat(updates.size(), is(1));
+ assertUpdate(getNormalizedNodeUpdateForAfterType(updates, ContainerNode.class),
+ WITH_CHOICE_CONTAINER_ID, null, containerWithChoice);
}
private DataTreeModification getModification(final TipProducingDataTree dataTree) {
@@ -66,10 +100,9 @@ public class ModificationDiffTest {
dataTreeModification.write(TOP_CONTAINER_ID, topContainer);
final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = getModificationDiff(prepare);
+ final ModificationDiff modificationDiff = getModificationDiff(prepare);
- assertTrue(modificationDiff.getModificationsBefore().isEmpty());
- assertTrue(modificationDiff.getModificationsAfter().isEmpty());
+ assertThat(modificationDiff.getUpdates().size(), is(0));
}
private DataTreeCandidateTip prepareModification(final TipProducingDataTree dataTree,
@@ -83,60 +116,61 @@ public class ModificationDiffTest {
@Test
public void testUpdateWrite() throws Exception {
final TipProducingDataTree dataTree = getDataTree();
- final NormalizedNode<?, ?> topContainerBefore = addTopContainer(dataTree);
+ final ContainerNode topContainer = getTopContainer("string1");
+ addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID);
final DataTreeModification dataTreeModification = getModification(dataTree);
final NormalizedNode<?, ?> topContainerAfter = getTopContainer("string2");
dataTreeModification.write(TOP_CONTAINER_ID, topContainerAfter);
final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = getModificationDiff(prepare);
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
- assertBefore(topContainerBefore, TOP_CONTAINER_ID, modificationDiff);
- assertAfter(topContainerAfter, TOP_CONTAINER_ID, modificationDiff);
+ assertThat(updates.size(), is(1));
+ assertThat(updates.values().size(), is(1));
+ assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, topContainerAfter);
}
- private ModifiableDataTreeDelegator.ModificationDiff getModificationDiff(final DataTreeCandidateTip prepare) {
- return ModifiableDataTreeDelegator.ModificationDiff
- .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode());
+ private ModificationDiff getModificationDiff(final DataTreeCandidateTip prepare) {
+ return ModificationDiff.recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode());
}
@Test
public void testUpdateMerge() throws Exception {
final TipProducingDataTree dataTree = getDataTree();
- final NormalizedNode<?, ?> topContainerBefore = addTopContainer(dataTree);
+ final ContainerNode topContainer = getTopContainer("string1");
+ addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID);
final DataTreeModification dataTreeModification = getModification(dataTree);
final NormalizedNode<?, ?> topContainerAfter = getTopContainer("string2");
dataTreeModification.merge(TOP_CONTAINER_ID, topContainerAfter);
final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff =
- getModificationDiff(prepare);
-
- assertBefore(topContainerBefore, TOP_CONTAINER_ID, modificationDiff);
- assertAfter(topContainerAfter, TOP_CONTAINER_ID, modificationDiff);
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+ assertThat(updates.size(), is(1));
+ assertThat(updates.values().size(), is(1));
+ assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, topContainerAfter);
}
@Test
public void testUpdateDelete() throws Exception {
final TipProducingDataTree dataTree = getDataTree();
- final NormalizedNode<?, ?> topContainerBefore = addTopContainer(dataTree);
+ final ContainerNode topContainer = getTopContainer("string1");
+ addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID);
final DataTreeModification dataTreeModification = getModification(dataTree);
dataTreeModification.delete(TOP_CONTAINER_ID);
final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = getModificationDiff(prepare);
-
- assertBefore(topContainerBefore, TOP_CONTAINER_ID, modificationDiff);
- assertTrue(modificationDiff.getModificationsAfter().isEmpty());
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+ assertThat(updates.size(), is(1));
+ assertThat(updates.values().size(), is(1));
+ assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, null);
}
@Test
public void testWriteAndUpdateInnerList() throws Exception {
final TipProducingDataTree dataTree = getDataTree();
- addTopContainer(dataTree);
DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
@@ -146,15 +180,17 @@ public class ModificationDiffTest {
new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME));
final MapNode mapNode = getNestedList("name1", "text");
+ final YangInstanceIdentifier listEntryId = listId.node(mapNode.getValue().iterator().next().getIdentifier());
dataTreeModification.write(listId, mapNode);
dataTreeModification.ready();
dataTree.validate(dataTreeModification);
DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification);
- ModifiableDataTreeDelegator.ModificationDiff modificationDiff = getModificationDiff(prepare);
+ Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
- assertTrue(modificationDiff.getModificationsBefore().isEmpty());
- assertAfter(mapNode, listId, modificationDiff);
+ assertThat(updates.size(), is(1));
+ assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class),
+ listEntryId, null, mapNode.getValue().iterator().next());
// Commit so that update can be tested next
dataTree.commit(prepare);
@@ -171,9 +207,17 @@ public class ModificationDiffTest {
dataTree.validate(dataTreeModification);
prepare = dataTree.prepare(dataTreeModification);
- modificationDiff = getModificationDiff(prepare);
- assertBefore(mapNode.getValue().iterator().next(), listItemId, modificationDiff);
- assertAfter(mapEntryNode, listItemId, modificationDiff);
+ updates = getModificationDiff(prepare).getUpdates();
+ assertThat(updates.size(), is(1 /*Actual list entry*/));
+ }
+//
+ private void assertUpdate(final ModificationDiff.NormalizedNodeUpdate update,
+ final YangInstanceIdentifier idExpected,
+ final NormalizedNode<?, ?> beforeExpected,
+ final NormalizedNode<?, ?> afterExpected) {
+ assertThat(update.getId(), is(idExpected));
+ assertThat(update.getDataBefore(), is(beforeExpected));
+ assertThat(update.getDataAfter(), is(afterExpected));
}
@Test
@@ -192,25 +236,44 @@ public class ModificationDiffTest {
new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME));
final MapNode mapNode = getNestedList("name1", "text");
+ final YangInstanceIdentifier listEntryId = listId.node(mapNode.getValue().iterator().next().getIdentifier());
+
dataTreeModification.write(listId, mapNode);
final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = getModificationDiff(prepare);
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+
+ assertThat(updates.size(), is(2));
+ assertThat(updates.values().size(), is(2));
+ assertUpdate(getNormalizedNodeUpdateForAfterType(updates, ContainerNode.class), TOP_CONTAINER_ID, null,
+ Builders.containerBuilder(topContainer).withChild(mapNode).build());
+ assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class), listEntryId, null, mapNode.getValue().iterator().next());
+ // Assert that keys of the updates map are not wildcarded YID
+ assertThat(updates.keySet(), hasItems(
+ TOP_CONTAINER_ID,
+ listEntryId));
+ }
- assertTrue(modificationDiff.getModificationsBefore().isEmpty());
+ private ModificationDiff.NormalizedNodeUpdate getNormalizedNodeUpdateForAfterType(
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates,
+ final Class<? extends NormalizedNode<?, ?>> containerNodeClass) {
+ return updates.values().stream()
+ .filter(update -> containerNodeClass.isAssignableFrom(update.getDataAfter().getClass()))
+ .findFirst().get();
+ }
- // TODO HONEYCOMB-94 2 after modifications should appear, for top-container and nested-list entry
- assertAfter(Builders.containerBuilder(topContainer)
- .withChild(mapNode)
- .build(),
- TOP_CONTAINER_ID, modificationDiff);
+ private ModificationDiff.NormalizedNodeUpdate getNormalizedNodeUpdateForBeforeType(
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates,
+ final Class<? extends NormalizedNode<?, ?>> containerNodeClass) {
+ return updates.values().stream()
+ .filter(update -> containerNodeClass.isAssignableFrom(update.getDataBefore().getClass()))
+ .findFirst().get();
}
@Test
public void testWriteDeepList() throws Exception {
final TipProducingDataTree dataTree = getDataTree();
- addTopContainer(dataTree);
DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
@@ -250,7 +313,8 @@ public class ModificationDiffTest {
.withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, "name1")).build());
dataTreeModification.merge(
deepListId,
- Builders.mapBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME))
+ Builders.mapBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME))
.build());
dataTreeModification.merge(
deepListEntryId,
@@ -261,16 +325,14 @@ public class ModificationDiffTest {
prepare = dataTree.prepare(dataTreeModification);
dataTree.commit(prepare);
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = getModificationDiff(prepare);
-
- assertTrue(modificationDiff.getModificationsBefore().isEmpty());
- assertAfter(getDeepList("name1"), deepListId, modificationDiff);
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+ assertThat(updates.size(), is(1));
+ assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class), deepListEntryId, null, deepListEntry);
}
@Test
public void testDeleteInnerListItem() throws Exception {
final TipProducingDataTree dataTree = getDataTree();
- addTopContainer(dataTree);
DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
@@ -298,58 +360,37 @@ public class ModificationDiffTest {
dataTree.validate(dataTreeModification);
prepare = dataTree.prepare(dataTreeModification);
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = getModificationDiff(prepare);
-
- assertBefore(mapNode.getValue().iterator().next(), listItemId, modificationDiff);
- assertTrue(modificationDiff.getModificationsAfter().isEmpty());
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+ assertThat(updates.size(), is(1));
+ assertUpdate(getNormalizedNodeUpdateForBeforeType(updates, MapEntryNode.class), listItemId, mapNode.getValue().iterator().next(), null);
}
- private NormalizedNode<?, ?> addTopContainer(final TipProducingDataTree dataTree)
+ static void addNodeToTree(final DataTree dataTree, final NormalizedNode<?, ?> node,
+ final YangInstanceIdentifier id)
throws DataValidationFailedException {
DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
- final NormalizedNode<?, ?> topContainerBefore = getTopContainer("string1");
- dataTreeModification.write(TOP_CONTAINER_ID, topContainerBefore);
+ dataTreeModification.write(id, node);
dataTreeModification.ready();
dataTree.validate(dataTreeModification);
- DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification);
+ DataTreeCandidate prepare = dataTree.prepare(dataTreeModification);
dataTree.commit(prepare);
- return topContainerBefore;
- }
-
- private void assertAfter(final NormalizedNode<?, ?> topContainer, final YangInstanceIdentifier TOP_CONTAINER_ID,
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff) {
- assertModification(topContainer, TOP_CONTAINER_ID, modificationDiff.getModificationsAfter());
- }
-
- private void assertModification(final NormalizedNode<?, ?> topContainer,
- final YangInstanceIdentifier TOP_CONTAINER_ID,
- final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> modificationMap) {
- assertEquals(1, modificationMap.keySet().size());
- assertEquals(TOP_CONTAINER_ID, modificationMap.keySet().iterator().next());
- assertEquals(topContainer, modificationMap.values().iterator().next());
- }
-
- private void assertBefore(final NormalizedNode<?, ?> topContainerBefore,
- final YangInstanceIdentifier TOP_CONTAINER_ID,
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff) {
- assertModification(topContainerBefore, TOP_CONTAINER_ID, modificationDiff.getModificationsBefore());
}
- private TipProducingDataTree getDataTree() throws ReactorException {
+ static TipProducingDataTree getDataTree() throws ReactorException {
final TipProducingDataTree dataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
dataTree.setSchemaContext(getSchemaCtx());
return dataTree;
}
- private ContainerNode getTopContainer(final String stringValue) {
+ static ContainerNode getTopContainer(final String stringValue) {
return Builders.containerBuilder()
.withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME))
.withChild(ImmutableNodes.leafNode(STRING_LEAF_QNAME, stringValue))
.build();
}
- private MapNode getNestedList(final String listItemName, final String text) {
+ static MapNode getNestedList(final String listItemName, final String text) {
return Builders.mapBuilder()
.withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME))
.withChild(
@@ -378,9 +419,9 @@ public class ModificationDiffTest {
.build();
}
- private SchemaContext getSchemaCtx() throws ReactorException {
+ private static SchemaContext getSchemaCtx() throws ReactorException {
final CrossSourceStatementReactor.BuildAction buildAction = YangInferencePipeline.RFC6020_REACTOR.newBuild();
- buildAction.addSource(new YangStatementSourceImpl(getClass().getResourceAsStream("/test-diff.yang")));
+ buildAction.addSource(new YangStatementSourceImpl(ModificationDiffTest.class.getResourceAsStream("/test-diff.yang")));
return buildAction.buildEffective();
}
} \ No newline at end of file
diff --git a/v3po/data-impl/src/test/resources/test-diff.yang b/v3po/data-impl/src/test/resources/test-diff.yang
index 7e8721f00..5cccc8718 100644
--- a/v3po/data-impl/src/test/resources/test-diff.yang
+++ b/v3po/data-impl/src/test/resources/test-diff.yang
@@ -34,4 +34,21 @@ module test-diff {
}
}
+ container with-choice {
+
+ choice choice {
+ case case1 {
+ leaf in-case1 {
+ type string;
+ }
+ }
+
+ case case2 {
+ leaf in-case2 {
+ type string;
+ }
+ }
+ }
+ }
+
}
diff --git a/v3po/features/pom.xml b/v3po/features/pom.xml
index 43b5e83bc..2929e5d87 100644
--- a/v3po/features/pom.xml
+++ b/v3po/features/pom.xml
@@ -226,6 +226,11 @@
<version>${project.version}</version>
</dependency>
<dependency>
+ <groupId>org.jgrapht</groupId>
+ <artifactId>jgrapht-core</artifactId>
+ <version>0.9.2</version>
+ </dependency>
+ <dependency>
<groupId>${project.groupId}</groupId>
<artifactId>vpp-translate-utils</artifactId>
<version>${project.version}</version>
diff --git a/v3po/features/src/main/features/features.xml b/v3po/features/src/main/features/features.xml
index 89c64726c..8419fbebd 100644
--- a/v3po/features/src/main/features/features.xml
+++ b/v3po/features/src/main/features/features.xml
@@ -38,6 +38,7 @@
<bundle>mvn:io.fd.honeycomb.v3po/v3po-impl/${project.version}</bundle>
<bundle>mvn:io.fd.honeycomb.v3po/translate-api/${project.version}</bundle>
<bundle>mvn:io.fd.honeycomb.v3po/translate-spi/${project.version}</bundle>
+ <bundle>mvn:org.jgrapht/jgrapht-core/{{VERSION}}</bundle>
<bundle>mvn:io.fd.honeycomb.v3po/translate-utils/${project.version}</bundle>
<bundle>mvn:io.fd.honeycomb.v3po/vpp-translate-utils/${project.version}</bundle>
<bundle>mvn:io.fd.honeycomb.v3po/data-api/${project.version}</bundle>
diff --git a/v3po/impl/src/main/config/default-config.xml b/v3po/impl/src/main/config/default-config.xml
index 00c586d12..cc6c7c6e2 100644
--- a/v3po/impl/src/main/config/default-config.xml
+++ b/v3po/impl/src/main/config/default-config.xml
@@ -83,10 +83,10 @@
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-dom-mapping-service</type>
<name>runtime-mapping-singleton</name>
</serializer>
- <writer-registry>
- <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-registry</type>
- <name>write-registry</name>
- </writer-registry>
+ <writer-registry-builder>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-registry-builder</type>
+ <name>write-registry-builder</name>
+ </writer-registry-builder>
<context-binding-broker>
<type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
<name>honeycomb-context-binding-data-broker</name>
@@ -200,6 +200,13 @@
<provider>/modules/module[type='delegating-writer-registry'][name='write-registry']</provider>
</instance>
</service>
+ <service>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-registry-builder</type>
+ <instance>
+ <name>write-registry-builder</name>
+ <provider>/modules/module[type='delegating-writer-registry'][name='write-registry']</provider>
+ </instance>
+ </service>
<service>
<type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:data-tree</type>
diff --git a/v3po/impl/src/main/config/initializer-config.xml b/v3po/impl/src/main/config/initializer-config.xml
index cb98a2a89..dd2ff2b1e 100644
--- a/v3po/impl/src/main/config/initializer-config.xml
+++ b/v3po/impl/src/main/config/initializer-config.xml
@@ -34,8 +34,8 @@
<!-- Config initialization -->
<!-- Empty registry which does not pass data to VPP -->
<module>
- <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:utils">prefix:noop-writer-registry</type>
- <name>noop-writer-registry</name>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:utils">prefix:noop-writer-registry-builder</type>
+ <name>noop-writer-registry-builder</name>
</module>
<!-- Config data tree which does not pass data to translation layer (uses noop-write-registry) -->
<module>
@@ -50,10 +50,10 @@
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-dom-mapping-service</type>
<name>runtime-mapping-singleton</name>
</serializer>
- <writer-registry>
- <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-registry</type>
- <name>noop-writer-registry</name>
- </writer-registry>
+ <writer-registry-builder>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-registry-builder</type>
+ <name>noop-writer-registry-builder</name>
+ </writer-registry-builder>
<context-binding-broker>
<type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
<name>honeycomb-context-binding-data-broker</name>
@@ -166,10 +166,10 @@
<services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
<!-- Config initialization -->
<service>
- <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-registry</type>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-registry-builder</type>
<instance>
- <name>noop-writer-registry</name>
- <provider>/modules/module[type='noop-writer-registry'][name='noop-writer-registry']</provider>
+ <name>noop-writer-registry-builder</name>
+ <provider>/modules/module[type='noop-writer-registry-builder'][name='noop-writer-registry-builder']</provider>
</instance>
</service>
<service>
diff --git a/v3po/translate-api/pom.xml b/v3po/translate-api/pom.xml
index 8e244fa8d..1b08e85e8 100644
--- a/v3po/translate-api/pom.xml
+++ b/v3po/translate-api/pom.xml
@@ -19,7 +19,7 @@
<groupId>io.fd.honeycomb.common</groupId>
<artifactId>impl-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
- <relativePath>../../common/api-parent</relativePath>
+ <relativePath>../../common/impl-parent</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ChildWriter.java b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ChildWriter.java
deleted file mode 100644
index b38f26983..000000000
--- a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ChildWriter.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.translate.write;
-
-import com.google.common.annotations.Beta;
-import javax.annotation.Nonnull;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-/**
- * Child writer allowing its parent to pass the builder object
- *
- * @param <D> Specific DataObject derived type, that is handled by this writer
- */
-@Beta
-public interface ChildWriter<D extends DataObject> extends Writer<D> {
-
- /**
- * Extract data object managed by this writer from parent data and perform write.
- *
- * @param parentId Id of parent node
- * @param parentDataAfter Parent data from modification to extract data object from
- * @param ctx Write context for current modification
- */
- void writeChild(@Nonnull final InstanceIdentifier<? extends DataObject> parentId,
- @Nonnull final DataObject parentDataAfter,
- @Nonnull final WriteContext ctx) throws WriteFailedException;
-
- /**
- * Extract data object managed by this writer(if necessary) from parent data and perform delete.
- *
- * @param parentId Id of parent node
- * @param parentDataBefore Parent data before modification to extract data object from
- * @param ctx Write context for current modification
- */
- void deleteChild(@Nonnull final InstanceIdentifier<? extends DataObject> parentId,
- @Nonnull final DataObject parentDataBefore,
- @Nonnull final WriteContext ctx) throws WriteFailedException;
-
- /**
- * Extract data object managed by this writer(if necessary) from parent data and perform delete.
- *
- * @param parentId Id of parent node
- * @param parentDataBefore Parent data before modification to extract data object from
- * @param parentDataAfter Parent data from modification to extract data object from
- * @param ctx Write context for current modification
- */
- void updateChild(@Nonnull final InstanceIdentifier<? extends DataObject> parentId,
- @Nonnull final DataObject parentDataBefore,
- @Nonnull final DataObject parentDataAfter,
- @Nonnull final WriteContext ctx) throws WriteFailedException;
-}
diff --git a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/DataObjectUpdate.java b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/DataObjectUpdate.java
new file mode 100644
index 000000000..0d891ecba
--- /dev/null
+++ b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/DataObjectUpdate.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.write;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Simple wrapper for BA id + data before and after state. Does not allow both before and after to be null.
+ */
+public class DataObjectUpdate {
+
+ @Nonnull
+ private final InstanceIdentifier<?> id;
+ @Nullable
+ private final DataObject dataBefore;
+ @Nullable
+ private final DataObject dataAfter;
+
+ private DataObjectUpdate(@Nonnull final InstanceIdentifier<?> id,
+ @Nullable final DataObject dataBefore,
+ @Nullable final DataObject dataAfter) {
+ this.id = checkNotNull(id);
+ this.dataAfter = dataAfter;
+ this.dataBefore = dataBefore;
+ }
+
+ public DataObject getDataBefore() {
+ return dataBefore;
+ }
+
+ public DataObject getDataAfter() {
+ return dataAfter;
+ }
+
+ public InstanceIdentifier<?> getId() {
+ return id;
+ }
+
+ public static DataObjectUpdate create(@Nonnull final InstanceIdentifier<?> id,
+ @Nullable final DataObject dataBefore,
+ @Nullable final DataObject dataAfter) {
+ checkArgument(!(dataBefore == null && dataAfter == null), "Both before and after data are null");
+ if (dataBefore != null) {
+ checkArgument(id.getTargetType().isAssignableFrom(dataBefore.getClass()));
+ }
+ if (dataAfter != null) {
+ checkArgument(id.getTargetType().isAssignableFrom(dataAfter.getClass()));
+ }
+
+ return dataAfter == null
+ ? new DataObjectDelete(id, dataBefore)
+ : new DataObjectUpdate(id, dataBefore, dataAfter);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final DataObjectUpdate that = (DataObjectUpdate) o;
+
+ return id.equals(that.id);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "DataObjectUpdate{" + "id=" + id
+ + ", dataBefore=" + dataBefore
+ + ", dataAfter=" + dataAfter
+ + '}';
+ }
+
+ public DataObjectUpdate reverse() {
+ return DataObjectUpdate.create(id, dataAfter, dataBefore);
+ }
+
+ public static class DataObjectDelete extends DataObjectUpdate {
+
+ private DataObjectDelete(@Nonnull final InstanceIdentifier<?> id,
+ @Nullable final DataObject dataBefore) {
+ super(id, dataBefore, null);
+ }
+ }
+}
diff --git a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ModifiableWriterRegistry.java b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ModifiableWriterRegistry.java
new file mode 100644
index 000000000..71ecbb806
--- /dev/null
+++ b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ModifiableWriterRegistry.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.write;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Mutable registry that allows adding new writers.
+ */
+@Beta
+public interface ModifiableWriterRegistry {
+
+ /**
+ * Add a writer responsible for writing only a single complex node.
+ */
+ ModifiableWriterRegistry addWriter(@Nonnull Writer<? extends DataObject> writer);
+
+ /**
+ * Add a writer responsible for writing multiple complex nodes within a subtree its responsible for.
+ * Identifiers for subtree nodes handled by a single writer have to be relative from {@link DataObject} that
+ * represents subtree root.
+ */
+ ModifiableWriterRegistry addSubtreeWriter(@Nonnull Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull Writer<? extends DataObject> writer);
+
+ /**
+ * Add a writer and make sure it will be executed before writer identifier by relatedType is executed.
+ */
+ ModifiableWriterRegistry addWriterBefore(@Nonnull Writer<? extends DataObject> writer,
+ @Nonnull InstanceIdentifier<?> relatedType);
+
+ ModifiableWriterRegistry addSubtreeWriterBefore(@Nonnull Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull Writer<? extends DataObject> writer,
+ @Nonnull InstanceIdentifier<?> relatedType);
+
+ ModifiableWriterRegistry addWriterBefore(@Nonnull Writer<? extends DataObject> writer,
+ @Nonnull Collection<InstanceIdentifier<?>> relatedTypes);
+
+ ModifiableWriterRegistry addSubtreeWriterBefore(@Nonnull Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull Writer<? extends DataObject> writer,
+ @Nonnull Collection<InstanceIdentifier<?>> relatedTypes);
+
+ /**
+ * Add a writer and make sure it will be executed after writer identifier by relatedType is executed.
+ */
+ ModifiableWriterRegistry addWriterAfter(@Nonnull Writer<? extends DataObject> writer,
+ @Nonnull InstanceIdentifier<?> relatedType);
+
+ ModifiableWriterRegistry addSubtreeWriterAfter(@Nonnull Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull Writer<? extends DataObject> writer,
+ @Nonnull InstanceIdentifier<?> relatedType);
+
+ ModifiableWriterRegistry addWriterAfter(@Nonnull Writer<? extends DataObject> writer,
+ @Nonnull Collection<InstanceIdentifier<?>> relatedTypes);
+
+ ModifiableWriterRegistry addSubtreeWriterAfter(@Nonnull Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull Writer<? extends DataObject> writer,
+ @Nonnull Collection<InstanceIdentifier<?>> relatedTypes);
+}
diff --git a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterFactory.java b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterFactory.java
new file mode 100644
index 000000000..4287964db
--- /dev/null
+++ b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterFactory.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.write;
+
+import com.google.common.annotations.Beta;
+
+@Beta
+public interface WriterFactory {
+
+ /**
+ * Initialize 1 or more writers and add them to provided registry.
+ */
+ void init(ModifiableWriterRegistry registry);
+}
diff --git a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterRegistry.java b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterRegistry.java
index d30f06d13..64735017f 100644
--- a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterRegistry.java
+++ b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterRegistry.java
@@ -19,47 +19,131 @@ package io.fd.honeycomb.v3po.translate.write;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.Beta;
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
import io.fd.honeycomb.v3po.translate.TranslationException;
-import java.util.List;
-import java.util.Map;
+import java.util.Set;
import javax.annotation.Nonnull;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
/**
- * Special {@link Writer} capable of performing bulk updates
+ * Special {@link Writer} capable of performing bulk updates.
*/
@Beta
public interface WriterRegistry extends Writer<DataObject> {
/**
- * Performs bulk update
+ * Performs bulk update.
*
* @throws BulkUpdateException in case bulk update fails
- * @throws TranslationException in case some other error occurs while processing update request
+ * @throws TranslationException in case some other error occurs while processing update request
*/
- void update(@Nonnull final Map<InstanceIdentifier<?>, DataObject> dataBefore,
- @Nonnull final Map<InstanceIdentifier<?>, DataObject> dataAfter,
- @Nonnull final WriteContext ctx) throws TranslationException;
+ void update(@Nonnull DataObjectUpdates updates,
+ @Nonnull WriteContext ctx) throws TranslationException;
+
+ /**
+ * Simple DTO containing updates for {@link WriterRegistry}. Currently only deletes and updates (create + update)
+ * are distinguished.
+ */
+ @Beta
+ final class DataObjectUpdates {
+
+ private final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates;
+ private final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> deletes;
+
+ /**
+ * Create new instance.
+ *
+ * @param updates All updates indexed by their unkeyed {@link InstanceIdentifier}
+ * @param deletes All deletes indexed by their unkeyed {@link InstanceIdentifier}
+ */
+ public DataObjectUpdates(@Nonnull final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates,
+ @Nonnull final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> deletes) {
+ this.deletes = deletes;
+ this.updates = updates;
+ }
+
+ public Multimap<InstanceIdentifier<?>, DataObjectUpdate> getUpdates() {
+ return updates;
+ }
+
+ public Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> getDeletes() {
+ return deletes;
+ }
+
+ public boolean isEmpty() {
+ return updates.isEmpty() && deletes.isEmpty();
+ }
+
+ @Override
+ public String toString() {
+ return "DataObjectUpdates{" + "updates=" + updates + ", deletes=" + deletes + '}';
+ }
+
+ /**
+ * Get a {@link Set} containing all update types from both updates as well as deletes.
+ */
+ public Set<InstanceIdentifier<?>> getTypeIntersection() {
+ return Sets.union(deletes.keySet(), updates.keySet());
+ }
+
+ /**
+ * Check whether there is only a single type of data object to be updated.
+ *
+ * @return true if there is only a single type of updates (update + delete)
+ */
+ public boolean containsOnlySingleType() {
+ return getTypeIntersection().size() == 1;
+ }
+
+ @Override
+ public boolean equals(final Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+
+ final DataObjectUpdates that = (DataObjectUpdates) other;
+
+ if (!updates.equals(that.updates)) {
+ return false;
+ }
+ return deletes.equals(that.deletes);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = updates.hashCode();
+ result = 31 * result + deletes.hashCode();
+ return result;
+ }
+
+ }
/**
* Thrown when bulk update failed.
*/
@Beta
- class BulkUpdateException extends WriteFailedException {
+ class BulkUpdateException extends TranslationException {
private final Reverter reverter;
+ private final Set<InstanceIdentifier<?>> failedIds;
/**
* Constructs an BulkUpdateException.
- *
- * @param failedId instance identifier of the data object that caused bulk update to fail.
- * @param cause the cause of bulk update failure
+ * @param failedIds instance identifiers of the data objects that were not processed during bulk update.
+ * @param cause the cause of bulk update failure
*/
- public BulkUpdateException(@Nonnull final InstanceIdentifier<?> failedId, @Nonnull final Reverter reverter,
- final Throwable cause) {
- super(failedId, "Bulk update failed at " + failedId, cause);
+ public BulkUpdateException(@Nonnull final Set<InstanceIdentifier<?>> failedIds,
+ @Nonnull final Reverter reverter,
+ @Nonnull final Throwable cause) {
+ super("Bulk update failed at: " + failedIds, cause);
+ this.failedIds = failedIds;
this.reverter = checkNotNull(reverter, "reverter should not be null");
}
@@ -72,10 +156,13 @@ public interface WriterRegistry extends Writer<DataObject> {
reverter.revert();
}
+ public Set<InstanceIdentifier<?>> getFailedIds() {
+ return failedIds;
+ }
}
/**
- * Abstraction over revert mechanism in case of a bulk update failure
+ * Abstraction over revert mechanism in case of a bulk update failure.
*/
@Beta
interface Reverter {
@@ -95,19 +182,19 @@ public interface WriterRegistry extends Writer<DataObject> {
class RevertFailedException extends TranslationException {
// TODO change to list of VppDataModifications to make debugging easier
- private final List<InstanceIdentifier<?>> notRevertedChanges;
+ private final Set<InstanceIdentifier<?>> notRevertedChanges;
/**
- * Constructs an RevertFailedException with the list of changes that were not reverted.
+ * Constructs a RevertFailedException with the list of changes that were not reverted.
*
* @param notRevertedChanges list of changes that were not reverted
* @param cause the cause of revert failure
*/
- public RevertFailedException(@Nonnull final List<InstanceIdentifier<?>> notRevertedChanges,
+ public RevertFailedException(@Nonnull final Set<InstanceIdentifier<?>> notRevertedChanges,
final Throwable cause) {
super(cause);
checkNotNull(notRevertedChanges, "notRevertedChanges should not be null");
- this.notRevertedChanges = ImmutableList.copyOf(notRevertedChanges);
+ this.notRevertedChanges = ImmutableSet.copyOf(notRevertedChanges);
}
/**
@@ -116,7 +203,7 @@ public interface WriterRegistry extends Writer<DataObject> {
* @return list of changes that were not reverted
*/
@Nonnull
- public List<InstanceIdentifier<?>> getNotRevertedChanges() {
+ public Set<InstanceIdentifier<?>> getNotRevertedChanges() {
return notRevertedChanges;
}
}
diff --git a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterRegistryBuilder.java b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterRegistryBuilder.java
new file mode 100644
index 000000000..55ef66ec6
--- /dev/null
+++ b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterRegistryBuilder.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.write;
+
+/**
+ * Builder for writer registries.
+ */
+public interface WriterRegistryBuilder {
+
+ WriterRegistry build();
+}
diff --git a/v3po/translate-api/src/main/yang/translate-api.yang b/v3po/translate-api/src/main/yang/translate-api.yang
index 4a8093ab8..414ee20e1 100644
--- a/v3po/translate-api/src/main/yang/translate-api.yang
+++ b/v3po/translate-api/src/main/yang/translate-api.yang
@@ -24,14 +24,19 @@ module translate-api {
config:java-class io.fd.honeycomb.v3po.translate.read.ReaderRegistry;
}
- identity honeycomb-writer {
+ identity honeycomb-writer-factory {
base "config:service-type";
- config:java-class io.fd.honeycomb.v3po.translate.write.Writer;
+ config:java-class io.fd.honeycomb.v3po.translate.write.WriterFactory;
}
identity honeycomb-writer-registry {
base "config:service-type";
- config:java-class io.fd.honeycomb.v3po.translate.write.WriterRegistry;
+ config:java-class io.fd.honeycomb.v3po.translate.write.ModifiableWriterRegistry;
+ }
+
+ identity honeycomb-writer-registry-builder {
+ base "config:service-type";
+ config:java-class io.fd.honeycomb.v3po.translate.write.WriterRegistryBuilder;
}
identity honeycomb-mapping-context {
diff --git a/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/AbstractCompositeWriter.java b/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/AbstractCompositeWriter.java
index 0212c086b..5f1391c74 100644
--- a/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/AbstractCompositeWriter.java
+++ b/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/AbstractCompositeWriter.java
@@ -18,82 +18,33 @@ package io.fd.honeycomb.v3po.translate.impl.write;
import static com.google.common.base.Preconditions.checkArgument;
-import com.google.common.base.Optional;
-import com.google.common.collect.Lists;
-import io.fd.honeycomb.v3po.translate.impl.TraversalType;
-import io.fd.honeycomb.v3po.translate.util.RWUtils;
-import io.fd.honeycomb.v3po.translate.write.ChildWriter;
import io.fd.honeycomb.v3po.translate.write.WriteContext;
import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
import io.fd.honeycomb.v3po.translate.write.Writer;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import org.opendaylight.yangtools.yang.binding.Augmentation;
-import org.opendaylight.yangtools.yang.binding.ChildOf;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public abstract class AbstractCompositeWriter<D extends DataObject> implements Writer<D> {
+abstract class AbstractCompositeWriter<D extends DataObject> implements Writer<D> {
private static final Logger LOG = LoggerFactory.getLogger(AbstractCompositeWriter.class);
- private final Map<Class<? extends DataObject>, ChildWriter<? extends ChildOf<D>>> childWriters;
- private final Map<Class<? extends DataObject>, ChildWriter<? extends Augmentation<D>>> augWriters;
private final InstanceIdentifier<D> instanceIdentifier;
- private TraversalType traversalType;
- public AbstractCompositeWriter(final Class<D> type,
- final List<ChildWriter<? extends ChildOf<D>>> childWriters,
- final List<ChildWriter<? extends Augmentation<D>>> augWriters,
- final TraversalType traversalType) {
- this.traversalType = traversalType;
- this.instanceIdentifier = InstanceIdentifier.create(type);
- this.childWriters = RWUtils.uniqueLinkedIndex(childWriters, RWUtils.MANAGER_CLASS_FUNCTION);
- this.augWriters = RWUtils.uniqueLinkedIndex(augWriters, RWUtils.MANAGER_CLASS_AUG_FUNCTION);
+ AbstractCompositeWriter(final InstanceIdentifier<D> type) {
+ this.instanceIdentifier = type;
}
protected void writeCurrent(final InstanceIdentifier<D> id, final D data, final WriteContext ctx)
throws WriteFailedException {
LOG.debug("{}: Writing current: {} data: {}", this, id, data);
-
- switch (traversalType) {
- case PREORDER: {
- LOG.trace("{}: Writing current attributes", this);
- writeCurrentAttributes(id, data, ctx);
- writeChildren(id, data, ctx);
- break;
- }
- case POSTORDER: {
- writeChildren(id, data, ctx);
- LOG.trace("{}: Writing current attributes", this);
- writeCurrentAttributes(id, data, ctx);
- break;
- }
- }
-
+ writeCurrentAttributes(id, data, ctx);
LOG.debug("{}: Current node written successfully", this);
}
- private void writeChildren(final InstanceIdentifier<D> id, final D data, final WriteContext ctx)
- throws WriteFailedException {
- for (ChildWriter<? extends ChildOf<D>> child : childWriters.values()) {
- LOG.debug("{}: Writing child in: {}", this, child);
- child.writeChild(id, data, ctx);
- }
-
- for (ChildWriter<? extends Augmentation<D>> child : augWriters.values()) {
- LOG.debug("{}: Writing augment in: {}", this, child);
- child.writeChild(id, data, ctx);
- }
- }
-
protected void updateCurrent(final InstanceIdentifier<D> id, final D dataBefore, final D dataAfter,
final WriteContext ctx) throws WriteFailedException {
LOG.debug("{}: Updating current: {} dataBefore: {}, datAfter: {}", this, id, dataBefore, dataAfter);
@@ -103,69 +54,14 @@ public abstract class AbstractCompositeWriter<D extends DataObject> implements W
// No change, ignore
return;
}
-
- switch (traversalType) {
- case PREORDER: {
- LOG.trace("{}: Updating current attributes", this);
- updateCurrentAttributes(id, dataBefore, dataAfter, ctx);
- updateChildren(id, dataBefore, dataAfter, ctx);
- break;
- }
- case POSTORDER: {
- updateChildren(id, dataBefore, dataAfter, ctx);
- LOG.trace("{}: Updating current attributes", this);
- updateCurrentAttributes(id, dataBefore, dataAfter, ctx);
- break;
- }
- }
-
+ updateCurrentAttributes(id, dataBefore, dataAfter, ctx);
LOG.debug("{}: Current node updated successfully", this);
}
- private void updateChildren(final InstanceIdentifier<D> id, final D dataBefore, final D dataAfter,
- final WriteContext ctx) throws WriteFailedException {
- for (ChildWriter<? extends ChildOf<D>> child : childWriters.values()) {
- LOG.debug("{}: Updating child in: {}", this, child);
- child.updateChild(id, dataBefore, dataAfter, ctx);
- }
-
- for (ChildWriter<? extends Augmentation<D>> child : augWriters.values()) {
- LOG.debug("{}: Updating augment in: {}", this, child);
- child.updateChild(id, dataBefore, dataAfter, ctx);
- }
- }
-
protected void deleteCurrent(final InstanceIdentifier<D> id, final D dataBefore, final WriteContext ctx)
throws WriteFailedException {
LOG.debug("{}: Deleting current: {} dataBefore: {}", this, id, dataBefore);
-
- switch (traversalType) {
- case PREORDER: {
- deleteChildren(id, dataBefore, ctx);
- LOG.trace("{}: Deleting current attributes", this);
- deleteCurrentAttributes(id, dataBefore, ctx);
- break;
- }
- case POSTORDER: {
- LOG.trace("{}: Deleting current attributes", this);
- deleteCurrentAttributes(id, dataBefore, ctx);
- deleteChildren(id, dataBefore, ctx);
- break;
- }
- }
- }
-
- private void deleteChildren(final InstanceIdentifier<D> id, final D dataBefore, final WriteContext ctx)
- throws WriteFailedException {
- for (ChildWriter<? extends Augmentation<D>> child : reverseCollection(augWriters.values())) {
- LOG.debug("{}: Deleting augment in: {}", this, child);
- child.deleteChild(id, dataBefore, ctx);
- }
-
- for (ChildWriter<? extends ChildOf<D>> child : reverseCollection(childWriters.values())) {
- LOG.debug("{}: Deleting child in: {}", this, child);
- child.deleteChild(id, dataBefore, ctx);
- }
+ deleteCurrentAttributes(id, dataBefore, ctx);
}
@SuppressWarnings("unchecked")
@@ -177,28 +73,20 @@ public abstract class AbstractCompositeWriter<D extends DataObject> implements W
LOG.debug("{}: Updating : {}", this, id);
LOG.trace("{}: Updating : {}, from: {} to: {}", this, id, dataBefore, dataAfter);
- if (idPointsToCurrent(id)) {
- if(isWrite(dataBefore, dataAfter)) {
- writeCurrent((InstanceIdentifier<D>) id, castToManaged(dataAfter), ctx);
- } else if(isDelete(dataBefore, dataAfter)) {
- deleteCurrent((InstanceIdentifier<D>) id, castToManaged(dataBefore), ctx);
- } else {
- checkArgument(dataBefore != null && dataAfter != null, "No data to process");
- updateCurrent((InstanceIdentifier<D>) id, castToManaged(dataBefore), castToManaged(dataAfter), ctx);
- }
+ checkArgument(idPointsToCurrent(id), "Cannot handle data: %s. Only: %s can be handled by writer: %s",
+ id, getManagedDataObjectType(), this);
+
+ if (isWrite(dataBefore, dataAfter)) {
+ writeCurrent((InstanceIdentifier<D>) id, castToManaged(dataAfter), ctx);
+ } else if (isDelete(dataBefore, dataAfter)) {
+ deleteCurrent((InstanceIdentifier<D>) id, castToManaged(dataBefore), ctx);
} else {
- if (isWrite(dataBefore, dataAfter)) {
- writeSubtree(id, dataAfter, ctx);
- } else if (isDelete(dataBefore, dataAfter)) {
- deleteSubtree(id, dataBefore, ctx);
- } else {
- checkArgument(dataBefore != null && dataAfter != null, "No data to process");
- updateSubtree(id, dataBefore, dataAfter, ctx);
- }
+ checkArgument(dataBefore != null && dataAfter != null, "No data to process");
+ updateCurrent((InstanceIdentifier<D>) id, castToManaged(dataBefore), castToManaged(dataAfter), ctx);
}
}
- private void checkDataType(final @Nullable DataObject dataAfter) {
+ private void checkDataType(@Nonnull final DataObject dataAfter) {
checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(dataAfter.getClass()));
}
@@ -217,100 +105,10 @@ public abstract class AbstractCompositeWriter<D extends DataObject> implements W
return dataAfter == null && dataBefore != null;
}
- private void writeSubtree(final InstanceIdentifier<? extends DataObject> id,
- final DataObject dataAfter, final WriteContext ctx) throws WriteFailedException {
- LOG.debug("{}: Writing subtree: {}", this, id);
-
- final Writer<? extends ChildOf<D>> writer = getNextChildWriter(id);
- final Writer<? extends Augmentation<D>> augWriter = getNextAgumentationWriter(id);
-
- if (writer != null) {
- LOG.debug("{}: Writing subtree: {} in: {}", this, id, writer);
- writer.update(id, null, dataAfter, ctx);
- } else if (augWriter != null) {
- LOG.debug("{}: Updating augmented subtree: {} in: {}", this, id, augWriter);
- augWriter.update(id, null, dataAfter, ctx);
- } else {
- // If there's no dedicated writer, use write current
- // But we need current data after to do so
- final InstanceIdentifier<D> currentId = RWUtils.cutId(id, getManagedDataObjectType());
- Optional<D> currentDataAfter = ctx.readAfter(currentId);
- LOG.debug("{}: Dedicated subtree writer missing for: {}. Writing current.", this,
- RWUtils.getNextId(id, getManagedDataObjectType()).getType(), currentDataAfter);
- writeCurrent(currentId, castToManaged(currentDataAfter.get()), ctx);
- }
- }
-
private boolean idPointsToCurrent(final @Nonnull InstanceIdentifier<? extends DataObject> id) {
return id.getTargetType().equals(getManagedDataObjectType().getTargetType());
}
- private void deleteSubtree(final InstanceIdentifier<? extends DataObject> id,
- final DataObject dataBefore, final WriteContext ctx) throws WriteFailedException {
- LOG.debug("{}: Deleting subtree: {}", this, id);
-
- final Writer<? extends ChildOf<D>> writer = getNextChildWriter(id);
- final Writer<? extends Augmentation<D>> augWriter = getNextAgumentationWriter(id);
-
- if (writer != null) {
- LOG.debug("{}: Deleting subtree: {} in: {}", this, id, writer);
- writer.update(id, dataBefore, null, ctx);
- } else if (augWriter != null) {
- LOG.debug("{}: Updating augmented subtree: {} in: {}", this, id, augWriter);
- augWriter.update(id, dataBefore, null, ctx);
- } else {
- updateSubtreeFromCurrent(id, ctx);
- }
- }
-
- @SuppressWarnings("unchecked")
- private void updateSubtreeFromCurrent(final InstanceIdentifier<? extends DataObject> id, final WriteContext ctx)
- throws WriteFailedException {
- final InstanceIdentifier<D> currentId = RWUtils.cutId(id, getManagedDataObjectType());
- Optional<D> currentDataBefore = ctx.readBefore(currentId);
- Optional<D> currentDataAfter = ctx.readAfter(currentId);
- LOG.debug("{}: Dedicated subtree writer missing for: {}. Updating current without subtree", this,
- RWUtils.getNextId(id, getManagedDataObjectType()).getType(), currentDataAfter);
- updateCurrent((InstanceIdentifier<D>) id, castToManaged(currentDataBefore.orNull()),
- castToManaged(currentDataAfter.orNull()), ctx);
- }
-
- private void updateSubtree(final InstanceIdentifier<? extends DataObject> id,
- final DataObject dataBefore,
- final DataObject dataAfter,
- final WriteContext ctx) throws WriteFailedException {
- LOG.debug("{}: Updating subtree: {}", this, id);
- final Writer<? extends ChildOf<D>> writer = getNextChildWriter(id);
- final Writer<? extends Augmentation<D>> augWriter = getNextAgumentationWriter(id);
-
- if (writer != null) {
- LOG.debug("{}: Updating subtree: {} in: {}", this, id, writer);
- writer.update(id, dataBefore, dataAfter, ctx);
- } else if (augWriter != null) {
- LOG.debug("{}: Updating augmented subtree: {} in: {}", this, id, augWriter);
- augWriter.update(id, dataBefore, dataAfter, ctx);
- } else {
- updateSubtreeFromCurrent(id, ctx);
- }
- }
-
- private Writer<? extends ChildOf<D>> getNextChildWriter(final InstanceIdentifier<? extends DataObject> id) {
- final Class<? extends DataObject> next = RWUtils.getNextId(id, getManagedDataObjectType()).getType();
- return childWriters.get(next);
- }
-
- private Writer<? extends Augmentation<D>> getNextAgumentationWriter(final InstanceIdentifier<? extends DataObject> id) {
- final Class<? extends DataObject> next = RWUtils.getNextId(id, getManagedDataObjectType()).getType();
- return augWriters.get(next);
- }
-
- private static <T> List<T> reverseCollection(final Collection<T> original) {
- // TODO find a better reverse mechanism (probably a different collection for child writers is necessary)
- final ArrayList<T> list = Lists.newArrayList(original);
- Collections.reverse(list);
- return list;
- }
-
protected abstract void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
@Nonnull final D data,
@Nonnull final WriteContext ctx) throws WriteFailedException;
diff --git a/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/CompositeChildWriter.java b/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/CompositeChildWriter.java
deleted file mode 100644
index 6e0841deb..000000000
--- a/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/CompositeChildWriter.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.translate.impl.write;
-
-import com.google.common.base.Optional;
-import io.fd.honeycomb.v3po.translate.impl.TraversalType;
-import io.fd.honeycomb.v3po.translate.write.ChildWriter;
-import io.fd.honeycomb.v3po.translate.write.WriteContext;
-import io.fd.honeycomb.v3po.translate.util.RWUtils;
-import io.fd.honeycomb.v3po.translate.spi.write.ChildWriterCustomizer;
-import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
-import java.util.List;
-import javax.annotation.Nonnull;
-import org.opendaylight.yangtools.yang.binding.Augmentation;
-import org.opendaylight.yangtools.yang.binding.ChildOf;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-public class CompositeChildWriter<D extends DataObject> extends AbstractCompositeWriter<D>
- implements ChildWriter<D> {
-
- private final ChildWriterCustomizer<D> customizer;
-
- public CompositeChildWriter(@Nonnull final Class<D> type,
- @Nonnull final List<ChildWriter<? extends ChildOf<D>>> childWriters,
- @Nonnull final List<ChildWriter<? extends Augmentation<D>>> augWriters,
- @Nonnull final ChildWriterCustomizer<D> customizer) {
- this(type, childWriters, augWriters, customizer, TraversalType.PREORDER);
- }
-
-
- public CompositeChildWriter(@Nonnull final Class<D> type,
- @Nonnull final List<ChildWriter<? extends ChildOf<D>>> childWriters,
- @Nonnull final List<ChildWriter<? extends Augmentation<D>>> augWriters,
- @Nonnull final ChildWriterCustomizer<D> customizer,
- @Nonnull final TraversalType traversalType) {
- super(type, childWriters, augWriters, traversalType);
- this.customizer = customizer;
- }
-
- public CompositeChildWriter(@Nonnull final Class<D> type,
- @Nonnull final List<ChildWriter<? extends ChildOf<D>>> childWriters,
- @Nonnull final ChildWriterCustomizer<D> customizer) {
- this(type, childWriters, RWUtils.<D>emptyAugWriterList(), customizer);
- }
-
- public CompositeChildWriter(@Nonnull final Class<D> type,
- @Nonnull final ChildWriterCustomizer<D> customizer) {
- this(type, RWUtils.<D>emptyChildWriterList(), RWUtils.<D>emptyAugWriterList(), customizer);
- }
-
- @Override
- protected void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D data,
- @Nonnull final WriteContext ctx) throws WriteFailedException {
- customizer.writeCurrentAttributes(id, data, ctx);
- }
-
- @Override
- protected void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
- @Nonnull WriteContext ctx) throws WriteFailedException {
- customizer.deleteCurrentAttributes(id, dataBefore, ctx);
- }
-
- @Override
- protected void updateCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
- @Nonnull final D dataAfter, @Nonnull WriteContext ctx)
- throws WriteFailedException {
- customizer.updateCurrentAttributes(id, dataBefore, dataAfter, ctx);
- }
-
- @Override
- public void writeChild(@Nonnull final InstanceIdentifier<? extends DataObject> parentId,
- @Nonnull final DataObject parentData, @Nonnull WriteContext ctx)
- throws WriteFailedException {
- final InstanceIdentifier<D> currentId = RWUtils.appendTypeToId(parentId, getManagedDataObjectType());
- final Optional<D> currentData = customizer.extract(currentId, parentData);
- if(currentData.isPresent()) {
- writeCurrent(currentId, currentData.get(), ctx);
- }
- }
-
- @Override
- public void deleteChild(@Nonnull final InstanceIdentifier<? extends DataObject> parentId,
- @Nonnull final DataObject parentDataBefore,
- @Nonnull final WriteContext ctx) throws WriteFailedException {
- final InstanceIdentifier<D> currentId = RWUtils.appendTypeToId(parentId, getManagedDataObjectType());
- final Optional<D> dataBefore = customizer.extract(currentId, parentDataBefore);
- if(dataBefore.isPresent()) {
- deleteCurrent(currentId, dataBefore.get(), ctx);
- }
- }
-
- @Override
- public void updateChild(@Nonnull final InstanceIdentifier<? extends DataObject> parentId,
- @Nonnull final DataObject parentDataBefore, @Nonnull final DataObject parentDataAfter,
- @Nonnull final WriteContext ctx) throws WriteFailedException {
- final InstanceIdentifier<D> currentId = RWUtils.appendTypeToId(parentId, getManagedDataObjectType());
- final Optional<D> before = customizer.extract(currentId, parentDataBefore);
- final Optional<D> after = customizer.extract(currentId, parentDataAfter);
-
- if(before.isPresent()) {
- if(after.isPresent()) {
- updateCurrent(currentId, before.get(), after.get(), ctx);
- } else {
- deleteCurrent(currentId, before.get(), ctx);
- }
- } else if (after.isPresent()){
- writeCurrent(currentId, after.get(), ctx);
- }
- }
-}
diff --git a/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/CompositeListWriter.java b/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/CompositeListWriter.java
deleted file mode 100644
index fe9e8d5e3..000000000
--- a/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/CompositeListWriter.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.translate.impl.write;
-
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import io.fd.honeycomb.v3po.translate.impl.TraversalType;
-import io.fd.honeycomb.v3po.translate.spi.write.ListWriterCustomizer;
-import io.fd.honeycomb.v3po.translate.util.RWUtils;
-import io.fd.honeycomb.v3po.translate.write.ChildWriter;
-import io.fd.honeycomb.v3po.translate.write.WriteContext;
-import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import javax.annotation.Nonnull;
-import org.opendaylight.yangtools.yang.binding.Augmentation;
-import org.opendaylight.yangtools.yang.binding.ChildOf;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.Identifiable;
-import org.opendaylight.yangtools.yang.binding.Identifier;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-public class CompositeListWriter<D extends DataObject & Identifiable<K>, K extends Identifier<D>> extends
- AbstractCompositeWriter<D>
- implements ChildWriter<D> {
-
- private static final Function<DataObject, Object> INDEX_FUNCTION = input -> input instanceof Identifiable<?>
- ? ((Identifiable<?>) input).getKey()
- : input;
-
-
- private final ListWriterCustomizer<D, K> customizer;
-
- public CompositeListWriter(@Nonnull final Class<D> type,
- @Nonnull final List<ChildWriter<? extends ChildOf<D>>> childWriters,
- @Nonnull final List<ChildWriter<? extends Augmentation<D>>> augWriters,
- @Nonnull final ListWriterCustomizer<D, K> customizer) {
- this(type, childWriters, augWriters, customizer, TraversalType.PREORDER);
- }
-
- public CompositeListWriter(@Nonnull final Class<D> type,
- @Nonnull final List<ChildWriter<? extends ChildOf<D>>> childWriters,
- @Nonnull final List<ChildWriter<? extends Augmentation<D>>> augWriters,
- @Nonnull final ListWriterCustomizer<D, K> customizer,
- @Nonnull final TraversalType traversalType) {
- super(type, childWriters, augWriters, traversalType);
- this.customizer = customizer;
- }
-
- public CompositeListWriter(@Nonnull final Class<D> type,
- @Nonnull final List<ChildWriter<? extends ChildOf<D>>> childWriters,
- @Nonnull final ListWriterCustomizer<D, K> customizer) {
- this(type, childWriters, RWUtils.<D>emptyAugWriterList(), customizer);
- }
-
- public CompositeListWriter(@Nonnull final Class<D> type,
- @Nonnull final ListWriterCustomizer<D, K> customizer) {
- this(type, RWUtils.<D>emptyChildWriterList(), RWUtils.<D>emptyAugWriterList(), customizer);
-
- }
-
- @Override
- protected void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D data,
- @Nonnull final WriteContext ctx) throws WriteFailedException {
- customizer.writeCurrentAttributes(id, data, ctx);
- }
-
- @Override
- protected void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
- @Nonnull final WriteContext ctx) throws WriteFailedException {
- customizer.deleteCurrentAttributes(id, dataBefore, ctx);
- }
-
- @Override
- protected void updateCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
- @Nonnull final D dataAfter, @Nonnull final WriteContext ctx)
- throws WriteFailedException {
- customizer.updateCurrentAttributes(id, dataBefore, dataAfter, ctx);
- }
-
- @Override
- public void writeChild(@Nonnull final InstanceIdentifier<? extends DataObject> parentId,
- @Nonnull final DataObject parentData,
- @Nonnull final WriteContext ctx) throws WriteFailedException {
- final InstanceIdentifier<D> currentId = RWUtils.appendTypeToId(parentId, getManagedDataObjectType());
- final Optional<List<D>> currentData = customizer.extract(currentId, parentData);
- if (currentData.isPresent()) {
- for (D entry : currentData.get()) {
- writeCurrent(currentId, entry, ctx);
- }
- }
- }
-
- @Override
- public void deleteChild(@Nonnull final InstanceIdentifier<? extends DataObject> parentId,
- @Nonnull final DataObject parentDataBefore,
- @Nonnull final WriteContext ctx) throws WriteFailedException {
- final InstanceIdentifier<D> currentId = RWUtils.appendTypeToId(parentId, getManagedDataObjectType());
- final Optional<List<D>> dataBefore = customizer.extract(currentId, parentDataBefore);
- if (dataBefore.isPresent()) {
- for (D entry : dataBefore.get()) {
- deleteCurrent(currentId, entry, ctx);
- }
- }
- }
-
- private Map<Object, D> listOfIdentifiableToMap(Optional<List<D>> list) {
- if (list.isPresent()) {
- return Maps.uniqueIndex(list.get(), INDEX_FUNCTION);
- } else {
- return Collections.emptyMap();
- }
-
- }
-
- @Override
- public void updateChild(@Nonnull final InstanceIdentifier<? extends DataObject> parentId,
- @Nonnull final DataObject parentDataBefore, @Nonnull final DataObject parentDataAfter,
- @Nonnull final WriteContext ctx) throws WriteFailedException {
- final InstanceIdentifier<D> currentId = RWUtils.appendTypeToId(parentId, getManagedDataObjectType());
- final Map<Object, D> dataBefore = listOfIdentifiableToMap(customizer.extract(currentId, parentDataBefore));
- final Map<Object, D> dataAfter = listOfIdentifiableToMap(customizer.extract(currentId, parentDataAfter));
-
- // The order of delete/write/update operations can have side-effects for devices like VPP
- // TODO make it configurable
-
- // First perform delete:
- for (Object deletedNodeKey : Sets.difference(dataBefore.keySet(), dataAfter.keySet())) {
- final D deleted = dataBefore.get(deletedNodeKey);
- deleteCurrent(currentId, deleted, ctx);
- }
-
- // Then write/update:
- for (Map.Entry<Object, D> after : dataAfter.entrySet()) {
- final D before = dataBefore.get(after.getKey());
- if(before == null) {
- writeCurrent(currentId, after.getValue(), ctx);
- } else {
- updateCurrent(currentId, before, after.getValue(), ctx);
- }
- }
-
- }
-
- @Override
- protected void writeCurrent(final InstanceIdentifier<D> id, final D data, final WriteContext ctx)
- throws WriteFailedException {
- // Make sure the key is present
- if(isWildcarded(id)) {
- super.writeCurrent(getSpecificId(id, data), data, ctx);
- } else {
- super.writeCurrent(id, data, ctx);
- }
- }
-
- @Override
- protected void updateCurrent(final InstanceIdentifier<D> id, final D dataBefore, final D dataAfter,
- final WriteContext ctx) throws WriteFailedException {
- // Make sure the key is present
- if(isWildcarded(id)) {
- super.updateCurrent(getSpecificId(id, dataBefore), dataBefore, dataAfter, ctx);
- } else {
- super.updateCurrent(id, dataBefore, dataAfter, ctx);
- }
- }
-
- @Override
- protected void deleteCurrent(final InstanceIdentifier<D> id, final D dataBefore, final WriteContext ctx)
- throws WriteFailedException {
- // Make sure the key is present
- if(isWildcarded(id)) {
- super.deleteCurrent(getSpecificId(id, dataBefore), dataBefore, ctx);
- } else {
- super.deleteCurrent(id, dataBefore, ctx);
- }
- }
-
- private boolean isWildcarded(final InstanceIdentifier<D> id) {
- return id.firstIdentifierOf(getManagedDataObjectType().getTargetType()).isWildcarded();
- }
-
- private InstanceIdentifier<D> getSpecificId(final InstanceIdentifier<D> currentId, final D current) {
- return RWUtils.replaceLastInId(currentId,
- new InstanceIdentifier.IdentifiableItem<>(currentId.getTargetType(), current.getKey()));
- }
-}
diff --git a/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriter.java b/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriter.java
new file mode 100644
index 000000000..b61fb51f7
--- /dev/null
+++ b/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriter.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.impl.write;
+
+import io.fd.honeycomb.v3po.translate.spi.write.ListWriterCustomizer;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
+import io.fd.honeycomb.v3po.translate.write.Writer;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Special writer handling updates for nodes of type list.
+ */
+public final class GenericListWriter<D extends DataObject & Identifiable<K>, K extends Identifier<D>> extends
+ AbstractCompositeWriter<D> implements Writer<D> {
+
+ private final ListWriterCustomizer<D, K> customizer;
+
+ public GenericListWriter(@Nonnull final InstanceIdentifier<D> type,
+ @Nonnull final ListWriterCustomizer<D, K> customizer) {
+ super(type);
+ this.customizer = customizer;
+ }
+
+ @Override
+ protected void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D data,
+ @Nonnull final WriteContext ctx) throws WriteFailedException {
+ customizer.writeCurrentAttributes(id, data, ctx);
+ }
+
+ @Override
+ protected void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
+ @Nonnull final WriteContext ctx) throws WriteFailedException {
+ customizer.deleteCurrentAttributes(id, dataBefore, ctx);
+ }
+
+ @Override
+ protected void updateCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
+ @Nonnull final D dataAfter, @Nonnull final WriteContext ctx)
+ throws WriteFailedException {
+ customizer.updateCurrentAttributes(id, dataBefore, dataAfter, ctx);
+ }
+
+ @Override
+ protected void writeCurrent(final InstanceIdentifier<D> id, final D data, final WriteContext ctx)
+ throws WriteFailedException {
+ // Make sure the key is present
+ if (isWildcarded(id)) {
+ super.writeCurrent(getSpecificId(id, data), data, ctx);
+ } else {
+ super.writeCurrent(id, data, ctx);
+ }
+ }
+
+ @Override
+ protected void updateCurrent(final InstanceIdentifier<D> id, final D dataBefore, final D dataAfter,
+ final WriteContext ctx) throws WriteFailedException {
+ // Make sure the key is present
+ if (isWildcarded(id)) {
+ super.updateCurrent(getSpecificId(id, dataBefore), dataBefore, dataAfter, ctx);
+ } else {
+ super.updateCurrent(id, dataBefore, dataAfter, ctx);
+ }
+ }
+
+ @Override
+ protected void deleteCurrent(final InstanceIdentifier<D> id, final D dataBefore, final WriteContext ctx)
+ throws WriteFailedException {
+ // Make sure the key is present
+ if (isWildcarded(id)) {
+ super.deleteCurrent(getSpecificId(id, dataBefore), dataBefore, ctx);
+ } else {
+ super.deleteCurrent(id, dataBefore, ctx);
+ }
+ }
+
+ private boolean isWildcarded(final InstanceIdentifier<D> id) {
+ return id.firstIdentifierOf(getManagedDataObjectType().getTargetType()).isWildcarded();
+ }
+
+ private InstanceIdentifier<D> getSpecificId(final InstanceIdentifier<D> currentId, final D current) {
+ return RWUtils.replaceLastInId(currentId,
+ new InstanceIdentifier.IdentifiableItem<>(currentId.getTargetType(), current.getKey()));
+ }
+}
diff --git a/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/CompositeRootWriter.java b/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriter.java
index 6f46359ff..6ca80ca37 100644
--- a/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/CompositeRootWriter.java
+++ b/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriter.java
@@ -16,50 +16,26 @@
package io.fd.honeycomb.v3po.translate.impl.write;
-import io.fd.honeycomb.v3po.translate.impl.TraversalType;
import io.fd.honeycomb.v3po.translate.spi.write.RootWriterCustomizer;
-import io.fd.honeycomb.v3po.translate.util.RWUtils;
-import io.fd.honeycomb.v3po.translate.write.ChildWriter;
import io.fd.honeycomb.v3po.translate.write.WriteContext;
import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
-import java.util.List;
import javax.annotation.Nonnull;
-import org.opendaylight.yangtools.yang.binding.Augmentation;
-import org.opendaylight.yangtools.yang.binding.ChildOf;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-public class CompositeRootWriter<D extends DataObject> extends AbstractCompositeWriter<D> {
+/**
+ * Special writer handling updates for any complex nodes.
+ */
+public final class GenericWriter<D extends DataObject> extends AbstractCompositeWriter<D> {
private final RootWriterCustomizer<D> customizer;
- public CompositeRootWriter(@Nonnull final Class<D> type,
- @Nonnull final List<ChildWriter<? extends ChildOf<D>>> childWriters,
- @Nonnull final List<ChildWriter<? extends Augmentation<D>>> augWriters,
- @Nonnull final RootWriterCustomizer<D> customizer) {
- this(type, childWriters, augWriters, customizer, TraversalType.PREORDER);
- }
-
- public CompositeRootWriter(@Nonnull final Class<D> type,
- @Nonnull final List<ChildWriter<? extends ChildOf<D>>> childWriters,
- @Nonnull final List<ChildWriter<? extends Augmentation<D>>> augWriters,
- @Nonnull final RootWriterCustomizer<D> customizer,
- @Nonnull final TraversalType traversalType) {
- super(type, childWriters, augWriters, traversalType);
+ public GenericWriter(@Nonnull final InstanceIdentifier<D> type,
+ @Nonnull final RootWriterCustomizer<D> customizer) {
+ super(type);
this.customizer = customizer;
}
- public CompositeRootWriter(@Nonnull final Class<D> type,
- @Nonnull final List<ChildWriter<? extends ChildOf<D>>> childWriters,
- @Nonnull final RootWriterCustomizer<D> customizer) {
- this(type, childWriters, RWUtils.<D>emptyAugWriterList(), customizer);
- }
-
- public CompositeRootWriter(@Nonnull final Class<D> type,
- @Nonnull final RootWriterCustomizer<D> customizer) {
- this(type, RWUtils.<D>emptyChildWriterList(), RWUtils.<D>emptyAugWriterList(), customizer);
- }
-
@Override
protected void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D data,
@Nonnull final WriteContext ctx) throws WriteFailedException {
diff --git a/v3po/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriterTest.java b/v3po/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriterTest.java
new file mode 100644
index 000000000..54a7466e1
--- /dev/null
+++ b/v3po/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriterTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.impl.write;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import io.fd.honeycomb.v3po.translate.spi.write.ListWriterCustomizer;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class GenericListWriterTest {
+
+ private static final InstanceIdentifier<IdentifiableDataObject>
+ DATA_OBJECT_INSTANCE_IDENTIFIER = InstanceIdentifier.create(IdentifiableDataObject.class);
+ @Mock
+ private ListWriterCustomizer<IdentifiableDataObject, DataObjectIdentifier> customizer;
+ @Mock
+ private WriteContext ctx;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ final GenericListWriter<IdentifiableDataObject, DataObjectIdentifier> writer =
+ new GenericListWriter<>(DATA_OBJECT_INSTANCE_IDENTIFIER, customizer);
+
+ final IdentifiableDataObject before = mock(IdentifiableDataObject.class);
+ final DataObjectIdentifier beforeKey = mock(DataObjectIdentifier.class);
+ when(before.getKey()).thenReturn(beforeKey);
+ final IdentifiableDataObject after = mock(IdentifiableDataObject.class);
+ final DataObjectIdentifier keyAfter = mock(DataObjectIdentifier.class);
+ when(after.getKey()).thenReturn(keyAfter);
+
+ assertEquals(DATA_OBJECT_INSTANCE_IDENTIFIER, writer.getManagedDataObjectType());
+
+ final InstanceIdentifier<IdentifiableDataObject> keyedIdBefore =
+ (InstanceIdentifier<IdentifiableDataObject>) InstanceIdentifier.create(Collections
+ .singleton(new InstanceIdentifier.IdentifiableItem<>(IdentifiableDataObject.class, beforeKey)));
+ final InstanceIdentifier<IdentifiableDataObject> keyedIdAfter =
+ (InstanceIdentifier<IdentifiableDataObject>) InstanceIdentifier.create(Collections
+ .singleton(new InstanceIdentifier.IdentifiableItem<>(IdentifiableDataObject.class, keyAfter)));
+
+ writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, before, after, ctx);
+ verify(customizer).updateCurrentAttributes(keyedIdBefore, before, after, ctx);
+
+ writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, before, null, ctx);
+ verify(customizer).deleteCurrentAttributes(keyedIdBefore, before, ctx);
+
+ writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, null, after, ctx);
+ verify(customizer).writeCurrentAttributes(keyedIdAfter, after, ctx);
+ }
+
+ private abstract static class IdentifiableDataObject implements DataObject, Identifiable<DataObjectIdentifier> {}
+ private abstract static class DataObjectIdentifier implements Identifier<IdentifiableDataObject> {}
+} \ No newline at end of file
diff --git a/v3po/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriterTest.java b/v3po/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriterTest.java
new file mode 100644
index 000000000..919072bcb
--- /dev/null
+++ b/v3po/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriterTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.impl.write;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import io.fd.honeycomb.v3po.translate.spi.write.RootWriterCustomizer;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class GenericWriterTest {
+
+ private static final InstanceIdentifier<DataObject>
+ DATA_OBJECT_INSTANCE_IDENTIFIER = InstanceIdentifier.create(DataObject.class);
+ @Mock
+ private RootWriterCustomizer<DataObject> customizer;
+ @Mock
+ private WriteContext ctx;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ final GenericWriter<DataObject> writer =
+ new GenericWriter<>(DATA_OBJECT_INSTANCE_IDENTIFIER, customizer);
+
+ final DataObject before = mock(DataObject.class);
+ final DataObject after = mock(DataObject.class);
+
+ assertEquals(DATA_OBJECT_INSTANCE_IDENTIFIER, writer.getManagedDataObjectType());
+ writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, before, after, ctx);
+ verify(customizer).updateCurrentAttributes(DATA_OBJECT_INSTANCE_IDENTIFIER, before, after, ctx);
+
+ writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, before, null, ctx);
+ verify(customizer).deleteCurrentAttributes(DATA_OBJECT_INSTANCE_IDENTIFIER, before, ctx);
+
+ writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, null, after, ctx);
+ verify(customizer).writeCurrentAttributes(DATA_OBJECT_INSTANCE_IDENTIFIER, after, ctx);
+ }
+} \ No newline at end of file
diff --git a/v3po/translate-utils/pom.xml b/v3po/translate-utils/pom.xml
index 86a11bdba..e4197cf0e 100644
--- a/v3po/translate-utils/pom.xml
+++ b/v3po/translate-utils/pom.xml
@@ -70,6 +70,11 @@
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-data-codec-gson</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.jgrapht</groupId>
+ <artifactId>jgrapht-core</artifactId>
+ <version>0.9.2</version>
+ </dependency>
<!-- Testing Dependencies -->
<dependency>
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java
index b712e159c..55ae9ecf0 100644
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java
@@ -23,13 +23,13 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import io.fd.honeycomb.v3po.translate.SubtreeManager;
import io.fd.honeycomb.v3po.translate.read.ChildReader;
-import io.fd.honeycomb.v3po.translate.write.ChildWriter;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collector;
import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
import javax.annotation.Nonnull;
import org.opendaylight.yangtools.yang.binding.Augmentation;
import org.opendaylight.yangtools.yang.binding.ChildOf;
@@ -79,18 +79,10 @@ public final class RWUtils {
return Collections.emptyList();
}
- public static <T> List<ChildWriter<? extends ChildOf<T>>> emptyChildWriterList() {
- return Collections.emptyList();
- }
-
public static <T> List<ChildReader<? extends Augmentation<T>>> emptyAugReaderList() {
return Collections.emptyList();
}
- public static <T> List<ChildWriter<? extends Augmentation<T>>> emptyAugWriterList() {
- return Collections.emptyList();
- }
-
public static <T> List<ChildReader<? extends Augmentation<T>>> singletonAugReaderList(
ChildReader<? extends Augmentation<T>> item) {
return Collections.<ChildReader<? extends Augmentation<T>>>singletonList(item);
@@ -101,16 +93,6 @@ public final class RWUtils {
return Collections.<ChildReader<? extends ChildOf<T>>>singletonList(item);
}
- public static <T> List<ChildWriter<? extends ChildOf<T>>> singletonChildWriterList(
- ChildWriter<? extends ChildOf<T>> item) {
- return Collections.<ChildWriter<? extends ChildOf<T>>>singletonList(item);
- }
-
- public static <T> List<ChildWriter<? extends Augmentation<T>>> singletonAugWriterList(
- ChildWriter<? extends Augmentation<T>> item) {
- return Collections.<ChildWriter<? extends Augmentation<T>>>singletonList(item);
- }
-
/**
* Replace last item in ID with a provided IdentifiableItem of the same type
*/
@@ -197,4 +179,21 @@ public final class RWUtils {
return (InstanceIdentifier<D>) InstanceIdentifier.create(Iterables.concat(
parentId.getPathArguments(), Collections.singleton(t)));
}
+
+ /**
+ * Transform a keyed instance identifier into a wildcarded one.
+ */
+ public static InstanceIdentifier<?> makeIidWildcarded(final InstanceIdentifier<?> id) {
+ final List<InstanceIdentifier.PathArgument> transformedPathArguments =
+ StreamSupport.stream(id.getPathArguments().spliterator(), false)
+ .map(RWUtils::cleanPathArgumentFromKeys)
+ .collect(Collectors.toList());
+ return InstanceIdentifier.create(transformedPathArguments);
+ }
+
+ private static InstanceIdentifier.PathArgument cleanPathArgumentFromKeys(final InstanceIdentifier.PathArgument pathArgument) {
+ return pathArgument instanceof InstanceIdentifier.IdentifiableItem<?, ?>
+ ? new InstanceIdentifier.Item<>(pathArgument.getType())
+ : pathArgument;
+ }
}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/CloseableWriterRegistry.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/CloseableWriterRegistry.java
deleted file mode 100644
index cd53a4f8b..000000000
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/CloseableWriterRegistry.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.translate.util.write;
-
-import io.fd.honeycomb.v3po.translate.TranslationException;
-import io.fd.honeycomb.v3po.translate.write.WriteContext;
-import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
-import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
-import java.util.Map;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-/**
- * WriterRegistry wrapper providing AutoCloseable interface.
- */
-public final class CloseableWriterRegistry implements WriterRegistry, AutoCloseable {
- private final WriterRegistry writerRegistry;
-
- public CloseableWriterRegistry( final WriterRegistry writerRegistry) {
- this.writerRegistry = writerRegistry;
- }
-
- @Override
- public void update(
- @Nonnull final Map<InstanceIdentifier<?>, DataObject> nodesBefore,
- @Nonnull final Map<InstanceIdentifier<?>, DataObject> nodesAfter,
- @Nonnull final WriteContext ctx) throws TranslationException {
- writerRegistry.update(nodesBefore, nodesAfter, ctx);
- }
-
- @Override
- public void update(
- @Nonnull final InstanceIdentifier<? extends DataObject> id,
- @Nullable final DataObject dataBefore, @Nullable final DataObject dataAfter,
- @Nonnull final WriteContext ctx) throws WriteFailedException {
- writerRegistry.update(id, dataBefore, dataAfter, ctx);
- }
-
- @Nonnull
- @Override
- public InstanceIdentifier<DataObject> getManagedDataObjectType() {
- return writerRegistry.getManagedDataObjectType();
- }
-
- @Override
- public void close() throws Exception {
- // NOOP
- }
-} \ No newline at end of file
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/DelegatingWriterRegistry.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/DelegatingWriterRegistry.java
deleted file mode 100644
index 061d3fa4a..000000000
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/DelegatingWriterRegistry.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.translate.util.write;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
-import io.fd.honeycomb.v3po.translate.util.RWUtils;
-import io.fd.honeycomb.v3po.translate.write.WriteContext;
-import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
-import io.fd.honeycomb.v3po.translate.write.Writer;
-import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Simple writer registry able to perform and aggregated write (ROOT write) on top of all provided writers. Also able to
- * delegate a specific write to one of the delegate writers.
- *
- * This could serve as a utility to hold & hide all available writers in upper layers.
- */
-public final class DelegatingWriterRegistry implements WriterRegistry {
-
- private static final Logger LOG = LoggerFactory.getLogger(DelegatingWriterRegistry.class);
-
- private final Map<Class<? extends DataObject>, Writer<? extends DataObject>> rootWriters;
-
- /**
- * Create new {@link DelegatingWriterRegistry}
- *
- * @param rootWriters List of delegate writers
- */
- public DelegatingWriterRegistry(@Nonnull final List<Writer<? extends DataObject>> rootWriters) {
- this.rootWriters = RWUtils.uniqueLinkedIndex(checkNotNull(rootWriters), RWUtils.MANAGER_CLASS_FUNCTION);
- }
-
- /**
- * @throws UnsupportedOperationException This getter is not supported for writer registry since it does not manage a
- * specific node type
- */
- @Nonnull
- @Override
- public InstanceIdentifier<DataObject> getManagedDataObjectType() {
- throw new UnsupportedOperationException("Root registry has no type");
- }
-
- @Override
- public void update(@Nonnull final InstanceIdentifier<? extends DataObject> id,
- @Nullable final DataObject dataBefore,
- @Nullable final DataObject dataAfter,
- @Nonnull final WriteContext ctx) throws WriteFailedException {
- final InstanceIdentifier.PathArgument first = checkNotNull(
- Iterables.getFirst(id.getPathArguments(), null), "Empty id");
- final Writer<? extends DataObject> writer = rootWriters.get(first.getType());
- checkNotNull(writer,
- "Unable to write %s. Missing writer. Current writers for: %s", id, rootWriters.keySet());
- writer.update(id, dataBefore, dataAfter, ctx);
- }
-
- @Override
- public void update(@Nonnull final Map<InstanceIdentifier<?>, DataObject> nodesBefore,
- @Nonnull final Map<InstanceIdentifier<?>, DataObject> nodesAfter,
- @Nonnull final WriteContext ctx) throws WriteFailedException {
-
- Multimap<InstanceIdentifier<?>, InstanceIdentifier<?>> rootIdToNestedIds = HashMultimap.create();
- try {
- checkAllWritersPresent(nodesBefore, rootIdToNestedIds);
- checkAllWritersPresent(nodesAfter, rootIdToNestedIds);
- } catch (IllegalArgumentException e) {
- LOG.warn("Unable to process update", e);
- throw e;
- }
-
- final List<InstanceIdentifier<?>> processedNodes = Lists.newArrayList();
-
- for (Map.Entry<Class<? extends DataObject>, Writer<? extends DataObject>> rootWriterEntry : rootWriters
- .entrySet()) {
-
- final InstanceIdentifier<? extends DataObject> id = rootWriterEntry.getValue().getManagedDataObjectType();
- // FIXME !! this is not ideal, we are not handling nested updates in expected order
- // Root writers are invoked in order they were registered, but nested updates are not, since they are
- // iterated here.
- //
- for (InstanceIdentifier<?> specificInstanceIdentifier : rootIdToNestedIds.get(id)) {
- final DataObject dataBefore = nodesBefore.get(specificInstanceIdentifier);
- final DataObject dataAfter = nodesAfter.get(specificInstanceIdentifier);
-
- // No change to current writer
- if (dataBefore == null && dataAfter == null) {
- continue;
- }
-
- try {
- LOG.debug("ChangesProcessor.applyChanges() processing dataBefore={}, dataAfter={}", dataBefore, dataAfter);
- update(specificInstanceIdentifier, dataBefore, dataAfter, ctx);
- processedNodes.add(id);
- } catch (Exception e) {
- LOG.error("Error while processing data change of: {} (before={}, after={})", id, dataBefore, dataAfter, e);
- throw new BulkUpdateException(id, new ReverterImpl(this, processedNodes, nodesBefore, nodesAfter, ctx), e);
- }
- }
- }
- }
-
- private void checkAllWritersPresent(final @Nonnull Map<InstanceIdentifier<?>, DataObject> nodesBefore,
- final @Nonnull Multimap<InstanceIdentifier<?>, InstanceIdentifier<?>> rootIdToNestedIds) {
- for (final InstanceIdentifier<?> changeId : nodesBefore.keySet()) {
- final InstanceIdentifier.PathArgument first = Iterables.getFirst(changeId.getPathArguments(), null);
- checkNotNull(first, "Empty identifier detected");
- final InstanceIdentifier<? extends DataObject> rootId = InstanceIdentifier.create(first.getType());
- checkArgument(rootWriters.keySet().contains(first.getType()),
- "Unable to handle change. Missing dedicated writer for: %s", first.getType());
- rootIdToNestedIds.put(rootId, changeId);
- }
- }
-
- private static final class ReverterImpl implements Reverter {
- private final WriterRegistry delegatingWriterRegistry;
- private final List<InstanceIdentifier<?>> processedNodes;
- private final Map<InstanceIdentifier<?>, DataObject> nodesBefore;
- private final Map<InstanceIdentifier<?>, DataObject> nodesAfter;
- private final WriteContext ctx;
-
- ReverterImpl(final WriterRegistry delegatingWriterRegistry,
- final List<InstanceIdentifier<?>> processedNodes,
- final Map<InstanceIdentifier<?>, DataObject> nodesBefore,
- final Map<InstanceIdentifier<?>, DataObject> nodesAfter, final WriteContext ctx) {
- this.delegatingWriterRegistry = delegatingWriterRegistry;
- this.processedNodes = processedNodes;
- this.nodesBefore = nodesBefore;
- this.nodesAfter = nodesAfter;
- this.ctx = ctx;
- }
-
- @Override
- public void revert() throws RevertFailedException {
- final LinkedList<InstanceIdentifier<?>> notReverted = new LinkedList<>(processedNodes);
-
- while (notReverted.size() > 0) {
- final InstanceIdentifier<?> node = notReverted.peekLast();
- LOG.debug("ChangesProcessor.revertChanges() processing node={}", node);
-
- final DataObject dataBefore = nodesBefore.get(node);
- final DataObject dataAfter = nodesAfter.get(node);
-
- // revert a change by invoking writer with reordered arguments
- try {
- delegatingWriterRegistry.update(node, dataAfter, dataBefore, ctx);
- notReverted.removeLast(); // change was successfully reverted
- } catch (Exception e) {
- throw new RevertFailedException(notReverted, e);
- }
-
- }
- }
- }
-}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterCustomizer.java
deleted file mode 100644
index 9f9c9f53e..000000000
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterCustomizer.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.translate.util.write;
-
-import io.fd.honeycomb.v3po.translate.spi.write.RootWriterCustomizer;
-import io.fd.honeycomb.v3po.translate.write.WriteContext;
-import javax.annotation.Nonnull;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-/**
- * Customizer not performing any changes on current level. Suitable for nodes that don't have any leaves and all of
- * its child nodes are managed by dedicated writers
- */
-public class NoopWriterCustomizer<D extends DataObject> implements RootWriterCustomizer<D> {
-
- @Override
- public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataAfter,
- @Nonnull final WriteContext ctx) {
-
- }
-
- @Override
- public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
- @Nonnull final D dataAfter,
- @Nonnull final WriteContext ctx) {
-
- }
-
- @Override
- public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
- @Nonnull final WriteContext ctx) {
-
- }
-}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java
index 866854212..236ad8917 100644
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java
@@ -20,7 +20,6 @@ import io.fd.honeycomb.v3po.translate.TranslationException;
import io.fd.honeycomb.v3po.translate.write.WriteContext;
import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
-import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -32,19 +31,11 @@ import org.slf4j.LoggerFactory;
* Empty registry that does not perform any changes. Can be used in data layer, if we want to disable passing data to
* translation layer.
*/
-public class NoopWriterRegistry implements WriterRegistry {
+public class NoopWriterRegistry implements WriterRegistry, AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(NoopWriterRegistry.class);
@Override
- public void update(@Nonnull final Map<InstanceIdentifier<?>, DataObject> dataBefore,
- @Nonnull final Map<InstanceIdentifier<?>, DataObject> dataAfter, @Nonnull final WriteContext ctx)
- throws TranslationException {
- LOG.trace("NoopWriterRegistry.update dataBefore{}, dataAfter={], ctx={}", dataBefore, dataAfter, ctx);
- // NOOP
- }
-
- @Override
public void update(@Nonnull final InstanceIdentifier<? extends DataObject> id,
@Nullable final DataObject dataBefore, @Nullable final DataObject dataAfter,
@Nonnull final WriteContext ctx) throws WriteFailedException {
@@ -53,9 +44,20 @@ public class NoopWriterRegistry implements WriterRegistry {
// NOOP
}
+ @Override
+ public void update(@Nonnull final DataObjectUpdates updates,
+ @Nonnull final WriteContext ctx) throws TranslationException {
+ // NOOP
+ }
+
@Nonnull
@Override
public InstanceIdentifier<DataObject> getManagedDataObjectType() {
- throw new UnsupportedOperationException("Root registry has no type");
+ throw new UnsupportedOperationException("Noop registry has no type");
+ }
+
+ @Override
+ public void close() throws Exception {
+ // NOOP
}
}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveAugmentWriterCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveAugmentWriterCustomizer.java
deleted file mode 100644
index 6d29214b3..000000000
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveAugmentWriterCustomizer.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.translate.util.write;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-
-import com.google.common.base.Optional;
-import io.fd.honeycomb.v3po.translate.spi.write.ChildWriterCustomizer;
-import javax.annotation.Nonnull;
-import org.opendaylight.yangtools.yang.binding.Augmentable;
-import org.opendaylight.yangtools.yang.binding.Augmentation;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-/**
- * Might be slow !
- */
-public class ReflexiveAugmentWriterCustomizer<C extends DataObject> extends NoopWriterCustomizer<C> implements
- ChildWriterCustomizer<C> {
-
- @Nonnull
- @Override
- @SuppressWarnings("unchecked")
- public Optional<C> extract(@Nonnull final InstanceIdentifier<C> currentId, @Nonnull final DataObject parentData) {
- checkArgument(parentData instanceof Augmentable<?>, "Not augmnatable parent object: %s", parentData);
- final Class<C> currentType = currentId.getTargetType();
- final Augmentation<?> augmentation = ((Augmentable) parentData).getAugmentation(currentType);
- if(augmentation == null) {
- return Optional.absent();
- } else {
- checkState(currentType.isAssignableFrom(augmentation.getClass()));
- return Optional.of(currentType.cast(augmentation));
- }
- }
-}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveChildWriterCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveChildWriterCustomizer.java
deleted file mode 100644
index 79cdf62c3..000000000
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveChildWriterCustomizer.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.translate.util.write;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Iterables;
-import io.fd.honeycomb.v3po.translate.util.ReflectionUtils;
-import io.fd.honeycomb.v3po.translate.spi.write.ChildWriterCustomizer;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Collections;
-import javax.annotation.Nonnull;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-/**
- * Might be slow !
- */
-public class ReflexiveChildWriterCustomizer<C extends DataObject> extends NoopWriterCustomizer<C> implements
- ChildWriterCustomizer<C> {
-
- @Nonnull
- @Override
- @SuppressWarnings("unchecked")
- public Optional<C> extract(@Nonnull final InstanceIdentifier<C> currentId, @Nonnull final DataObject parentData) {
- final Class<C> currentType = currentId.getTargetType();
- final Optional<Method> method = ReflectionUtils.findMethodReflex(getParentType(currentId),
- "get" + currentType.getSimpleName(), Collections.<Class<?>>emptyList(), currentType);
-
- Preconditions.checkArgument(method.isPresent(), "Unable to get %s from %s", currentType, parentData);
-
- try {
- return method.isPresent()
- ? Optional.fromNullable((C) method.get().invoke(parentData))
- : Optional.absent();
- } catch (IllegalAccessException | InvocationTargetException e) {
- throw new IllegalArgumentException("Unable to get " + currentType + " from " + parentData, e);
- }
- }
-
- private Class<? extends DataObject> getParentType(final @Nonnull InstanceIdentifier<C> currentId) {
- return Iterables.get(currentId.getPathArguments(), Iterables.size(currentId.getPathArguments()) - 2).getType();
- }
-}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java
new file mode 100644
index 000000000..79d8eb88d
--- /dev/null
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.write.registry;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import io.fd.honeycomb.v3po.translate.TranslationException;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
+import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
+import io.fd.honeycomb.v3po.translate.write.Writer;
+import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Flat writer registry, delegating updates to writers in the order writers were submitted.
+ */
+@ThreadSafe
+final class FlatWriterRegistry implements WriterRegistry {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FlatWriterRegistry.class);
+
+ // All types handled by writers directly or as children
+ private final ImmutableSet<InstanceIdentifier<?>> handledTypes;
+
+ private final Set<InstanceIdentifier<?>> writersOrderReversed;
+ private final Set<InstanceIdentifier<?>> writersOrder;
+ private final Map<InstanceIdentifier<?>, Writer<?>> writers;
+
+ /**
+ * Create flat registry instance.
+ *
+ * @param writers immutable, ordered map of writers to use to process updates. Order of the writers has to be
+ * one in which create and update operations should be handled. Deletes will be handled in reversed
+ * order. All deletes are handled before handling all the updates.
+ */
+ FlatWriterRegistry(@Nonnull final ImmutableMap<InstanceIdentifier<?>, Writer<?>> writers) {
+ this.writers = writers;
+ this.writersOrderReversed = Sets.newLinkedHashSet(Lists.reverse(Lists.newArrayList(writers.keySet())));
+ this.writersOrder = writers.keySet();
+ this.handledTypes = getAllHandledTypes(writers);
+ }
+
+ private static ImmutableSet<InstanceIdentifier<?>> getAllHandledTypes(
+ @Nonnull final ImmutableMap<InstanceIdentifier<?>, Writer<?>> writers) {
+ final ImmutableSet.Builder<InstanceIdentifier<?>> handledTypesBuilder = ImmutableSet.builder();
+ for (Map.Entry<InstanceIdentifier<?>, Writer<?>> writerEntry : writers.entrySet()) {
+ final InstanceIdentifier<?> writerType = writerEntry.getKey();
+ final Writer<?> writer = writerEntry.getValue();
+ handledTypesBuilder.add(writerType);
+ if (writer instanceof SubtreeWriter) {
+ handledTypesBuilder.addAll(((SubtreeWriter<?>) writer).getHandledChildTypes());
+ }
+ }
+ return handledTypesBuilder.build();
+ }
+
+ @Override
+ public void update(@Nonnull final InstanceIdentifier<? extends DataObject> id,
+ @Nullable final DataObject dataBefore,
+ @Nullable final DataObject dataAfter,
+ @Nonnull final WriteContext ctx) throws WriteFailedException {
+ singleUpdate(ImmutableMultimap.of(
+ RWUtils.makeIidWildcarded(id), DataObjectUpdate.create(id, dataBefore, dataAfter)), ctx);
+ }
+
+ @Override
+ public void update(@Nonnull final DataObjectUpdates updates,
+ @Nonnull final WriteContext ctx) throws TranslationException {
+ if (updates.isEmpty()) {
+ return;
+ }
+
+ // Optimization
+ if (updates.containsOnlySingleType()) {
+ // First process delete
+ singleUpdate(updates.getDeletes(), ctx);
+ // Next is update
+ singleUpdate(updates.getUpdates(), ctx);
+ } else {
+ // First process deletes
+ bulkUpdate(updates.getDeletes(), ctx, true, writersOrderReversed);
+ // Next are updates
+ bulkUpdate(updates.getUpdates(), ctx, true, writersOrder);
+ }
+
+ LOG.debug("Update successful for types: {}", updates.getTypeIntersection());
+ LOG.trace("Update successful for: {}", updates);
+ }
+
+ private void singleUpdate(@Nonnull final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates,
+ @Nonnull final WriteContext ctx) throws WriteFailedException {
+ if (updates.isEmpty()) {
+ return;
+ }
+
+ final InstanceIdentifier<?> singleType = updates.keySet().iterator().next();
+ LOG.debug("Performing single type update for: {}", singleType);
+ Collection<? extends DataObjectUpdate> singleTypeUpdates = updates.get(singleType);
+ Writer<?> writer = getWriter(singleType);
+
+ if (writer == null) {
+ // This node must be handled by a subtree writer, find it and call it or else fail
+ checkArgument(handledTypes.contains(singleType), "Unable to process update. Missing writers for: %s",
+ singleType);
+ writer = getSubtreeWriterResponsible(singleType);
+ singleTypeUpdates = getParentDataObjectUpdate(ctx, updates, writer);
+ }
+
+ LOG.trace("Performing single type update with writer: {}", writer);
+ for (DataObjectUpdate singleUpdate : singleTypeUpdates) {
+ writer.update(singleUpdate.getId(), singleUpdate.getDataBefore(), singleUpdate.getDataAfter(), ctx);
+ }
+ }
+
+ private Writer<?> getSubtreeWriterResponsible(final InstanceIdentifier<?> singleType) {
+ final Writer<?> writer;// This is slow ( minor TODO-perf )
+ writer = writers.values().stream()
+ .filter(w -> w instanceof SubtreeWriter)
+ .filter(w -> ((SubtreeWriter<?>) w).getHandledChildTypes().contains(singleType))
+ .findFirst()
+ .get();
+ return writer;
+ }
+
+ private Collection<DataObjectUpdate> getParentDataObjectUpdate(final WriteContext ctx,
+ final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates,
+ final Writer<?> writer) {
+ // Now read data for subtree reader root, but first keyed ID is needed and that ID can be cut from updates
+ InstanceIdentifier<?> firstAffectedChildId = ((SubtreeWriter<?>) writer).getHandledChildTypes().stream()
+ .filter(updates::containsKey)
+ .map(unkeyedId -> updates.get(unkeyedId))
+ .flatMap(doUpdates -> doUpdates.stream())
+ .map(DataObjectUpdate::getId)
+ .findFirst()
+ .get();
+
+ final InstanceIdentifier<?> parentKeyedId =
+ RWUtils.cutId(firstAffectedChildId, writer.getManagedDataObjectType());
+
+ final Optional<? extends DataObject> parentBefore = ctx.readBefore(parentKeyedId);
+ final Optional<? extends DataObject> parentAfter = ctx.readAfter(parentKeyedId);
+ return Collections.singleton(
+ DataObjectUpdate.create(parentKeyedId, parentBefore.orNull(), parentAfter.orNull()));
+ }
+
+ private void bulkUpdate(@Nonnull final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates,
+ @Nonnull final WriteContext ctx,
+ final boolean attemptRevert,
+ @Nonnull final Set<InstanceIdentifier<?>> writersOrder) throws BulkUpdateException {
+ if (updates.isEmpty()) {
+ return;
+ }
+
+ LOG.debug("Performing bulk update with revert attempt: {} for: {}", attemptRevert, updates.keySet());
+
+ // Check that all updates can be handled
+ checkAllTypesCanBeHandled(updates);
+
+ // Capture all changes successfully processed in case revert is needed
+ final Set<InstanceIdentifier<?>> processedNodes = new HashSet<>();
+
+ // Iterate over all writers and call update if there are any related updates
+ for (InstanceIdentifier<?> writerType : writersOrder) {
+ Collection<? extends DataObjectUpdate> writersData = updates.get(writerType);
+ final Writer<?> writer = getWriter(writerType);
+
+ if (writersData.isEmpty()) {
+ // If there are no data for current writer, but it is a SubtreeWriter and there are updates to
+ // its children, still invoke it with its root data
+ if (writer instanceof SubtreeWriter<?> && isAffected(((SubtreeWriter<?>) writer), updates)) {
+ // Provide parent data for SubtreeWriter for further processing
+ writersData = getParentDataObjectUpdate(ctx, updates, writer);
+ } else {
+ // Skipping unaffected writer
+ // Alternative to this would be modification sort according to the order of writers
+ continue;
+ }
+ }
+
+ LOG.debug("Performing update for: {}", writerType);
+ LOG.trace("Performing update with writer: {}", writer);
+
+ for (DataObjectUpdate singleUpdate : writersData) {
+ try {
+ writer.update(singleUpdate.getId(), singleUpdate.getDataBefore(), singleUpdate.getDataAfter(), ctx);
+ processedNodes.add(singleUpdate.getId());
+ LOG.trace("Update successful for type: {}", writerType);
+ LOG.debug("Update successful for: {}", singleUpdate);
+ } catch (Exception e) {
+ LOG.error("Error while processing data change of: {} (updates={})", writerType, writersData, e);
+
+ final Reverter reverter = attemptRevert
+ ? new ReverterImpl(processedNodes, updates, writersOrder, ctx)
+ : () -> {}; // NOOP reverter
+
+ // Find out which changes left unprocessed
+ final Set<InstanceIdentifier<?>> unprocessedChanges = updates.values().stream()
+ .map(DataObjectUpdate::getId)
+ .filter(id -> !processedNodes.contains(id))
+ .collect(Collectors.toSet());
+ throw new BulkUpdateException(unprocessedChanges, reverter, e);
+ }
+ }
+ }
+ }
+
+ private void checkAllTypesCanBeHandled(
+ @Nonnull final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates) {
+ if (!handledTypes.containsAll(updates.keySet())) {
+ final Sets.SetView<InstanceIdentifier<?>> missingWriters = Sets.difference(updates.keySet(), handledTypes);
+ LOG.warn("Unable to process update. Missing writers for: {}", missingWriters);
+ throw new IllegalArgumentException("Unable to process update. Missing writers for: " + missingWriters);
+ }
+ }
+
+ /**
+ * Check whether {@link SubtreeWriter} is affected by the updates.
+ *
+ * @return true if there are any updates to SubtreeWriter's child nodes (those marked by SubtreeWriter
+ * as being taken care of)
+ * */
+ private static boolean isAffected(final SubtreeWriter<?> writer,
+ final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates) {
+ return !Sets.intersection(writer.getHandledChildTypes(), updates.keySet()).isEmpty();
+ }
+
+ private Writer<?> getWriter(@Nonnull final InstanceIdentifier<?> singleType) {
+ final Writer<?> writer = writers.get(singleType);
+ checkNotNull(writer,
+ "Unable to write %s. Missing writer. Current writers for: %s", singleType, writers.keySet());
+ return writer;
+ }
+
+ @Nonnull
+ @Override
+ public InstanceIdentifier<DataObject> getManagedDataObjectType() {
+ throw new UnsupportedOperationException("Registry has no managed type");
+ }
+
+ // FIXME unit test
+ private final class ReverterImpl implements Reverter {
+
+ private final Collection<InstanceIdentifier<?>> processedNodes;
+ private final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates;
+ private final Set<InstanceIdentifier<?>> revertDeleteOrder;
+ private final WriteContext ctx;
+
+ ReverterImpl(final Collection<InstanceIdentifier<?>> processedNodes,
+ final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates,
+ final Set<InstanceIdentifier<?>> writersOrderOriginal,
+ final WriteContext ctx) {
+ this.processedNodes = processedNodes;
+ this.updates = updates;
+ // Use opposite ordering when executing revert
+ this.revertDeleteOrder = writersOrderOriginal == FlatWriterRegistry.this.writersOrder
+ ? FlatWriterRegistry.this.writersOrderReversed
+ : FlatWriterRegistry.this.writersOrder;
+ this.ctx = ctx;
+ }
+
+ @Override
+ public void revert() throws RevertFailedException {
+ Multimap<InstanceIdentifier<?>, DataObjectUpdate> updatesToRevert =
+ filterAndRevertProcessed(updates, processedNodes);
+
+ LOG.info("Attempting revert for changes: {}", updatesToRevert);
+ try {
+ // Perform reversed bulk update without revert attempt
+ bulkUpdate(updatesToRevert, ctx, true, revertDeleteOrder);
+ LOG.info("Revert successful");
+ } catch (BulkUpdateException e) {
+ LOG.error("Revert failed", e);
+ throw new RevertFailedException(e.getFailedIds(), e);
+ }
+ }
+
+ /**
+ * Create new updates map, but only keep already processed changes. Switching before and after data for each
+ * update.
+ */
+ private Multimap<InstanceIdentifier<?>, DataObjectUpdate> filterAndRevertProcessed(
+ final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates,
+ final Collection<InstanceIdentifier<?>> processedNodes) {
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> filtered = HashMultimap.create();
+ for (InstanceIdentifier<?> processedNode : processedNodes) {
+ final InstanceIdentifier<?> wildcardedIid = RWUtils.makeIidWildcarded(processedNode);
+ if (updates.containsKey(wildcardedIid)) {
+ updates.get(wildcardedIid).stream()
+ .filter(dataObjectUpdate -> processedNode.contains(dataObjectUpdate.getId()))
+ .forEach(dataObjectUpdate -> filtered.put(processedNode, dataObjectUpdate.reverse()));
+ }
+ }
+ return filtered;
+ }
+ }
+
+}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java
new file mode 100644
index 000000000..f5d218f55
--- /dev/null
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.write.registry;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
+import io.fd.honeycomb.v3po.translate.write.ModifiableWriterRegistry;
+import io.fd.honeycomb.v3po.translate.write.Writer;
+import io.fd.honeycomb.v3po.translate.write.WriterRegistryBuilder;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.NotThreadSafe;
+import org.jgrapht.experimental.dag.DirectedAcyclicGraph;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Builder for {@link FlatWriterRegistry} allowing users to specify inter-writer relationships.
+ */
+@NotThreadSafe
+public final class FlatWriterRegistryBuilder implements ModifiableWriterRegistry, WriterRegistryBuilder, AutoCloseable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FlatWriterRegistryBuilder.class);
+
+ // Using directed acyclic graph to represent the ordering relationships between writers
+ private final DirectedAcyclicGraph<InstanceIdentifier<?>, WriterRelation>
+ writersRelations = new DirectedAcyclicGraph<>((sourceVertex, targetVertex) -> new WriterRelation());
+ private final Map<InstanceIdentifier<?>, Writer<?>> writersMap = new HashMap<>();
+
+ /**
+ * AddWriter without any special relationship to any other type.
+ */
+ @Override
+ public FlatWriterRegistryBuilder addWriter(@Nonnull final Writer<? extends DataObject> writer) {
+ // Make IID wildcarded just in case
+ // + the way InstanceIdentifier.create + equals work for Identifiable items is unexpected, meaning updates would
+ // not be matched to writers in registry
+ final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType());
+ checkWriterNotPresentYet(targetType);
+ writersRelations.addVertex(targetType);
+ writersMap.put(targetType, writer);
+ return this;
+ }
+
+ /**
+ * AddWriter without any special relationship to any other type.
+ */
+ @Override
+ public FlatWriterRegistryBuilder addSubtreeWriter(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final Writer<? extends DataObject> writer) {
+ addWriter(SubtreeWriter.createForWriter(handledChildren, writer));
+ return this;
+ }
+
+ private void checkWriterNotPresentYet(final InstanceIdentifier<?> targetType) {
+ Preconditions.checkArgument(!writersMap.containsKey(targetType),
+ "Writer for type: %s already present: %s", targetType, writersMap.get(targetType));
+ }
+
+ /**
+ * Add writer with relationship: to be executed before writer handling relatedType.
+ */
+ @Override
+ public FlatWriterRegistryBuilder addWriterBefore(@Nonnull final Writer<? extends DataObject> writer,
+ @Nonnull final InstanceIdentifier<?> relatedType) {
+ final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType());
+ final InstanceIdentifier<?> wildcardedRelatedType = RWUtils.makeIidWildcarded(relatedType);
+ checkWriterNotPresentYet(targetType);
+ writersRelations.addVertex(targetType);
+ writersRelations.addVertex(wildcardedRelatedType);
+ addEdge(targetType, wildcardedRelatedType);
+ writersMap.put(targetType, writer);
+ return this;
+ }
+
+ @Override
+ public FlatWriterRegistryBuilder addSubtreeWriterBefore(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final Writer<? extends DataObject> writer,
+ @Nonnull final InstanceIdentifier<?> relatedType) {
+ return addWriterBefore(SubtreeWriter.createForWriter(handledChildren, writer), relatedType);
+ }
+
+ @Override
+ public FlatWriterRegistryBuilder addWriterBefore(@Nonnull final Writer<? extends DataObject> writer,
+ @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
+ final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType());
+ checkWriterNotPresentYet(targetType);
+ writersRelations.addVertex(targetType);
+ relatedTypes.stream()
+ .map(RWUtils::makeIidWildcarded)
+ .forEach(writersRelations::addVertex);
+ relatedTypes.stream()
+ .map(RWUtils::makeIidWildcarded)
+ .forEach(type -> addEdge(targetType, type));
+ writersMap.put(targetType, writer);
+ return this;
+ }
+
+ @Override
+ public FlatWriterRegistryBuilder addSubtreeWriterBefore(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final Writer<? extends DataObject> writer,
+ @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
+ return addWriterBefore(SubtreeWriter.createForWriter(handledChildren, writer), relatedTypes);
+ }
+
+ /**
+ * Add writer with relationship: to be executed after writer handling relatedType.
+ */
+ @Override
+ public FlatWriterRegistryBuilder addWriterAfter(@Nonnull final Writer<? extends DataObject> writer,
+ @Nonnull final InstanceIdentifier<?> relatedType) {
+ final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType());
+ final InstanceIdentifier<?> wildcardedRelatedType = RWUtils.makeIidWildcarded(relatedType);
+ checkWriterNotPresentYet(targetType);
+ writersRelations.addVertex(targetType);
+ writersRelations.addVertex(wildcardedRelatedType);
+ // set edge to indicate before relationship, just reversed
+ addEdge(wildcardedRelatedType, targetType);
+ writersMap.put(targetType, writer);
+ return this;
+ }
+
+ @Override
+ public FlatWriterRegistryBuilder addSubtreeWriterAfter(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final Writer<? extends DataObject> writer,
+ @Nonnull final InstanceIdentifier<?> relatedType) {
+ return addWriterAfter(SubtreeWriter.createForWriter(handledChildren, writer), relatedType);
+ }
+
+ @Override
+ public FlatWriterRegistryBuilder addWriterAfter(@Nonnull final Writer<? extends DataObject> writer,
+ @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
+ final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType());
+ checkWriterNotPresentYet(targetType);
+ writersRelations.addVertex(targetType);
+ relatedTypes.stream()
+ .map(RWUtils::makeIidWildcarded)
+ .forEach(writersRelations::addVertex);
+ // set edge to indicate before relationship, just reversed
+ relatedTypes.stream()
+ .map(RWUtils::makeIidWildcarded)
+ .forEach(type -> addEdge(type, targetType));
+ writersMap.put(targetType, writer);
+ return this;
+ }
+
+ @Override
+ public FlatWriterRegistryBuilder addSubtreeWriterAfter(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final Writer<? extends DataObject> writer,
+ @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
+ return addWriterAfter(SubtreeWriter.createForWriter(handledChildren, writer), relatedTypes);
+ }
+
+
+ private void addEdge(final InstanceIdentifier<?> targetType,
+ final InstanceIdentifier<?> relatedType) {
+ try {
+ writersRelations.addDagEdge(targetType, relatedType);
+ } catch (DirectedAcyclicGraph.CycleFoundException e) {
+ throw new IllegalArgumentException(String.format(
+ "Unable to add writer with relation: %s -> %s. Loop detected", targetType, relatedType), e);
+ }
+ }
+
+ /**
+ * Create FlatWriterRegistry with writers ordered according to submitted relationships.
+ */
+ @Override
+ public FlatWriterRegistry build() {
+ final ImmutableMap<InstanceIdentifier<?>, Writer<?>> mappedWriters = getMappedWriters();
+ LOG.debug("Building writer registry with writers: {}",
+ mappedWriters.keySet().stream()
+ .map(InstanceIdentifier::getTargetType)
+ .map(Class::getSimpleName)
+ .collect(Collectors.joining(", ")));
+ LOG.trace("Building writer registry with writers: {}", mappedWriters);
+ return new FlatWriterRegistry(mappedWriters);
+ }
+
+ @VisibleForTesting
+ ImmutableMap<InstanceIdentifier<?>, Writer<?>> getMappedWriters() {
+ final ImmutableMap.Builder<InstanceIdentifier<?>, Writer<?>> builder = ImmutableMap.builder();
+ // Iterate writer types according to their relationships from graph
+ writersRelations.iterator()
+ .forEachRemaining(writerType -> {
+ // There might be types stored just for relationship sake, no real writer, ignoring those
+ if (writersMap.containsKey(writerType)) {
+ builder.put(writerType, writersMap.get(writerType));
+ }
+ });
+ return builder.build();
+ }
+
+ @Override
+ public void close() throws Exception {
+ writersMap.clear();
+ writersRelations.removeAllEdges(writersRelations.edgeSet());
+ writersRelations.removeAllVertices(writersRelations.vertexSet());
+ }
+
+ // Represents edges in graph
+ private static final class WriterRelation {}
+}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java
new file mode 100644
index 000000000..e395b29da
--- /dev/null
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.write.registry;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.Iterables;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
+import io.fd.honeycomb.v3po.translate.write.Writer;
+import java.util.HashSet;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Simple writer delegate for subtree writers (writers handling also children nodes) providing a list of all the
+ * children nodes being handled.
+ */
+final class SubtreeWriter<D extends DataObject> implements Writer<D> {
+
+ private final Writer<D> delegate;
+ private final Set<InstanceIdentifier<?>> handledChildTypes = new HashSet<>();
+
+ private SubtreeWriter(final Writer<D> delegate, Set<InstanceIdentifier<?>> handledTypes) {
+ this.delegate = delegate;
+ for (InstanceIdentifier<?> handledType : handledTypes) {
+ // Iid has to start with writer's handled root type
+ checkArgument(delegate.getManagedDataObjectType().getTargetType().equals(
+ handledType.getPathArguments().iterator().next().getType()),
+ "Handled node from subtree has to be identified by an instance identifier starting from: %s."
+ + "Instance identifier was: %s", getManagedDataObjectType().getTargetType(), handledType);
+ checkArgument(Iterables.size(handledType.getPathArguments()) > 1,
+ "Handled node from subtree identifier too short: %s", handledType);
+ handledChildTypes.add(InstanceIdentifier.create(Iterables.concat(
+ getManagedDataObjectType().getPathArguments(), Iterables.skip(handledType.getPathArguments(), 1))));
+ }
+ }
+
+ /**
+ * Return set of types also handled by this writer. All of the types are children of the type managed by this
+ * writer excluding the type of this writer.
+ */
+ Set<InstanceIdentifier<?>> getHandledChildTypes() {
+ return handledChildTypes;
+ }
+
+ @Override
+ public void update(
+ @Nonnull final InstanceIdentifier<? extends DataObject> id,
+ @Nullable final DataObject dataBefore,
+ @Nullable final DataObject dataAfter, @Nonnull final WriteContext ctx) throws WriteFailedException {
+ delegate.update(id, dataBefore, dataAfter, ctx);
+ }
+
+ @Override
+ @Nonnull
+ public InstanceIdentifier<D> getManagedDataObjectType() {
+ return delegate.getManagedDataObjectType();
+ }
+
+ /**
+ * Wrap a writer as a subtree writer.
+ */
+ static Writer<?> createForWriter(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final Writer<? extends DataObject> writer) {
+ return new SubtreeWriter<>(writer, handledChildren);
+ }
+}
diff --git a/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModule.java b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModule.java
index 0eb5062d5..fccd6b1c2 100644
--- a/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModule.java
+++ b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModule.java
@@ -42,6 +42,8 @@ public class DelegatingReaderRegistryModule extends org.opendaylight.yang.gen.v1
return new CloseableReaderRegistry(new DelegatingReaderRegistry(rootReadersDependency));
}
+
+
// TODO move to translate-utils
private static final class CloseableReaderRegistry implements ReaderRegistry, AutoCloseable {
private final DelegatingReaderRegistry delegatingReaderRegistry;
diff --git a/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModuleFactory.java b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModuleFactory.java
index 214ab0f90..24d6c50b8 100644
--- a/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModuleFactory.java
+++ b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModuleFactory.java
@@ -8,6 +8,13 @@
* Do not modify this file unless it is present under src/main directory
*/
package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406;
+
+import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
+
public class DelegatingReaderRegistryModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractDelegatingReaderRegistryModuleFactory {
+ @Override
+ public DelegatingReaderRegistryModule handleChangedClass(final DynamicMBeanWithInstance old) throws Exception {
+ return super.handleChangedClass(old);
+ }
}
diff --git a/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModule.java b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModule.java
index 0266ca900..7eadde80e 100644
--- a/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModule.java
+++ b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModule.java
@@ -1,12 +1,6 @@
package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406;
-import com.google.common.base.Function;
-import com.google.common.collect.Lists;
-import io.fd.honeycomb.v3po.translate.util.write.CloseableWriterRegistry;
-import io.fd.honeycomb.v3po.translate.util.write.DelegatingWriterRegistry;
-import io.fd.honeycomb.v3po.translate.write.Writer;
-import java.util.List;
-import org.opendaylight.yangtools.yang.binding.DataObject;
+import io.fd.honeycomb.v3po.translate.util.write.registry.FlatWriterRegistryBuilder;
public class DelegatingWriterRegistryModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractDelegatingWriterRegistryModule {
public DelegatingWriterRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
@@ -24,16 +18,9 @@ public class DelegatingWriterRegistryModule extends org.opendaylight.yang.gen.v1
@Override
public java.lang.AutoCloseable createInstance() {
- final List<Writer<? extends DataObject>> rootReadersDependency = Lists.transform(getRootWritersDependency(),
- new Function<Writer, Writer<? extends DataObject>>() {
-
- @SuppressWarnings("unchecked")
- @Override
- public Writer<? extends DataObject> apply(final Writer input) {
- return input;
- }
- });
- return new CloseableWriterRegistry(new DelegatingWriterRegistry(rootReadersDependency));
+ final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder();
+ getWriterFactoryDependency().forEach(writerFactory -> writerFactory.init(flatWriterRegistryBuilder));
+ return flatWriterRegistryBuilder;
}
}
diff --git a/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModule.java b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModule.java
index 16c8af359..fedd069f1 100644
--- a/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModule.java
+++ b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModule.java
@@ -1,7 +1,8 @@
package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406;
-import io.fd.honeycomb.v3po.translate.util.write.*;
import io.fd.honeycomb.v3po.translate.util.write.NoopWriterRegistry;
+import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
+import io.fd.honeycomb.v3po.translate.write.WriterRegistryBuilder;
public class NoopWriterRegistryModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractNoopWriterRegistryModule {
public NoopWriterRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
@@ -19,7 +20,19 @@ public class NoopWriterRegistryModule extends org.opendaylight.yang.gen.v1.urn.h
@Override
public java.lang.AutoCloseable createInstance() {
- return new CloseableWriterRegistry(new NoopWriterRegistry());
+ return new NoopWriterRegistryBuilder();
}
+ private static final class NoopWriterRegistryBuilder implements AutoCloseable, WriterRegistryBuilder {
+
+ @Override
+ public WriterRegistry build() {
+ return new NoopWriterRegistry();
+ }
+
+ @Override
+ public void close() throws Exception {
+ // Noop
+ }
+ }
}
diff --git a/v3po/translate-utils/src/main/yang/translate-utils.yang b/v3po/translate-utils/src/main/yang/translate-utils.yang
index 1219648c3..2c00bb548 100644
--- a/v3po/translate-utils/src/main/yang/translate-utils.yang
+++ b/v3po/translate-utils/src/main/yang/translate-utils.yang
@@ -40,6 +40,7 @@ module translate-utils {
identity delegating-writer-registry {
base config:module-type;
config:provided-service tapi:honeycomb-writer-registry;
+ config:provided-service tapi:honeycomb-writer-registry-builder;
config:java-name-prefix DelegatingWriterRegistry;
}
@@ -47,11 +48,11 @@ module translate-utils {
case delegating-writer-registry {
when "/config:modules/config:module/config:type = 'delegating-writer-registry'";
- list root-writers {
+ list writer-factory {
uses config:service-ref {
refine type {
mandatory true;
- config:required-identity tapi:honeycomb-writer;
+ config:required-identity tapi:honeycomb-writer-factory;
}
}
}
@@ -59,15 +60,15 @@ module translate-utils {
}
}
- identity noop-writer-registry {
+ identity noop-writer-registry-builder {
base config:module-type;
- config:provided-service tapi:honeycomb-writer-registry;
+ config:provided-service tapi:honeycomb-writer-registry-builder;
config:java-name-prefix NoopWriterRegistry;
}
augment "/config:modules/config:module/config:configuration" {
- case noop-writer-registry {
- when "/config:modules/config:module/config:type = 'noop-writer-registry'";
+ case noop-writer-registry-builder {
+ when "/config:modules/config:module/config:type = 'noop-writer-registry-builder'";
}
}
diff --git a/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/util/DelegatingWriterRegistryTest.java b/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/util/DelegatingWriterRegistryTest.java
deleted file mode 100644
index f51e49db5..000000000
--- a/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/util/DelegatingWriterRegistryTest.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.translate.impl.write.util;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import io.fd.honeycomb.v3po.translate.util.write.DelegatingWriterRegistry;
-import io.fd.honeycomb.v3po.translate.write.WriteContext;
-import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
-import io.fd.honeycomb.v3po.translate.write.Writer;
-import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mockito;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.Vpp;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppState;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-public class DelegatingWriterRegistryTest {
-
- private final InstanceIdentifier<Vpp> vppId;
- private final InstanceIdentifier<VppState> vppStateId;
- private final InstanceIdentifier<Interfaces> interfaceId;
-
- private WriteContext ctx;
- private Writer<Vpp> writer;
- private Writer<VppState> vppStateWriter;
- private Writer<Interfaces> interfacesWriter;
-
- private DelegatingWriterRegistry registry;
-
- public DelegatingWriterRegistryTest() {
- vppId = InstanceIdentifier.create(Vpp.class);
- vppStateId = InstanceIdentifier.create(VppState.class);
- interfaceId = InstanceIdentifier.create(Interfaces.class);
- }
-
- @SuppressWarnings("unchecked")
- private <D extends DataObject> Writer<D> mockWriter(Class<D> clazz) {
- final Writer<D> mock = (Writer<D>) Mockito.mock(Writer.class);
- doReturn(InstanceIdentifier.create(clazz)).when(mock).getManagedDataObjectType();
- return mock;
- }
-
- private DataObject mockDataObject(final String name, final Class<? extends DataObject> classToMock) {
- final DataObject dataBefore = mock(classToMock, name);
- doReturn(classToMock).when(dataBefore).getImplementedInterface();
- return dataBefore;
- }
-
- @SuppressWarnings("unchecked")
- private static Map<InstanceIdentifier<?>, DataObject> asMap(DataObject... objects) {
- final Map<InstanceIdentifier<?>, DataObject> map = new HashMap<>();
- for (DataObject object : objects) {
- final Class<? extends DataObject> implementedInterface =
- (Class<? extends DataObject>) object.getImplementedInterface();
- final InstanceIdentifier<?> id = InstanceIdentifier.create(implementedInterface);
- map.put(id, object);
- }
- return map;
- }
-
- @Before
- public void setUp() {
- ctx = mock(WriteContext.class);
- writer = mockWriter(Vpp.class);
- vppStateWriter = mockWriter(VppState.class);
- interfacesWriter = mockWriter(Interfaces.class);
-
- final List<Writer<? extends DataObject>> writers = new ArrayList<>();
- writers.add(writer);
- writers.add(vppStateWriter);
- writers.add(interfacesWriter);
-
- registry = new DelegatingWriterRegistry(writers);
- }
-
- @Test(expected = UnsupportedOperationException.class)
- public void testGetManagedDataObjectType() {
- registry.getManagedDataObjectType();
- }
-
- @Test
- public void testBulkUpdateRevert() throws Exception {
- // Prepare data changes:
- final DataObject dataBefore1 = mockDataObject("Vpp before", Vpp.class);
- final DataObject dataAfter1 = mockDataObject("Vpp after", Vpp.class);
-
- final DataObject dataBefore2 = mockDataObject("VppState before", VppState.class);
- final DataObject dataAfter2 = mockDataObject("VppState after", VppState.class);
-
- // Fail on update
- Mockito.doThrow(new WriteFailedException(InstanceIdentifier.create(Vpp.class), "vpp failed"))
- .when(vppStateWriter).update(vppStateId, dataBefore2, dataAfter2, ctx);
-
- // Run the test
- try {
- registry.update(asMap(dataBefore1, dataBefore2), asMap(dataAfter1, dataAfter2), ctx);
- } catch (WriterRegistry.BulkUpdateException e) {
- // Check second update failed
- assertEquals(vppStateId, e.getFailedId());
- verify(writer).update(vppId, dataBefore1, dataAfter1, ctx);
- verify(vppStateWriter).update(vppStateId, dataBefore2, dataAfter2, ctx);
-
- // Try to revert changes
- e.revertChanges();
-
- // Check revert was successful
- verify(writer).update(vppId, dataAfter1, dataBefore1, ctx);
- verify(vppStateWriter, never()).update(vppStateId, dataAfter2, dataBefore2, ctx);
-
- return;
- }
- fail("BulkUpdateException expected");
- }
-
- @Test
- public void testBulkUpdateRevertFail() throws Exception {
- // Prepare data changes:
- final DataObject dataBefore1 = mockDataObject("Vpp before", Vpp.class);
- final DataObject dataAfter1 = mockDataObject("Vpp after", Vpp.class);
-
- final DataObject dataBefore2 = mockDataObject("VppState before", VppState.class);
- final DataObject dataAfter2 = mockDataObject("VppState after", VppState.class);
-
- final DataObject dataBefore3 = mockDataObject("Interfaces before", Interfaces.class);
- final DataObject dataAfter3 = mockDataObject("Interfaces after", Interfaces.class);
-
- // Fail on the third update
- doThrow(new WriteFailedException(InstanceIdentifier.create(Vpp.class), "vpp failed")).when(interfacesWriter)
- .update(interfaceId, dataBefore3, dataAfter3, ctx);
-
- // Fail on the second revert
- doThrow(new WriteFailedException(InstanceIdentifier.create(Vpp.class), "vpp failed")).when(writer)
- .update(vppId, dataAfter1, dataBefore1, ctx);
-
- // Run the test
- try {
- registry.update(asMap(dataBefore1, dataBefore2, dataBefore3), asMap(dataAfter1, dataAfter2, dataAfter3), ctx);
- } catch (WriterRegistry.BulkUpdateException e) {
- // Check third update failed
- assertEquals(interfaceId, e.getFailedId());
- verify(writer).update(vppId, dataBefore1, dataAfter1, ctx);
- verify(vppStateWriter).update(vppStateId, dataBefore2, dataAfter2, ctx);
- verify(interfacesWriter).update(interfaceId, dataBefore3, dataAfter3, ctx);
-
- // Try to revert changes
- try {
- e.revertChanges();
- } catch (WriterRegistry.Reverter.RevertFailedException e2) {
- // Check second revert failed
- assertEquals(Collections.singletonList(vppId), e2.getNotRevertedChanges());
- verify(writer).update(vppId, dataAfter1, dataBefore1, ctx);
- verify(vppStateWriter).update(vppStateId, dataAfter2, dataBefore2, ctx);
- verify(interfacesWriter, never()).update(interfaceId, dataAfter3, dataBefore3, ctx);
- return;
- }
- fail("WriterRegistry.Revert.RevertFailedException expected");
- }
- fail("BulkUpdateException expected");
- }
-} \ No newline at end of file
diff --git a/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilderTest.java b/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilderTest.java
new file mode 100644
index 000000000..ec407b685
--- /dev/null
+++ b/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilderTest.java
@@ -0,0 +1,156 @@
+package io.fd.honeycomb.v3po.translate.util.write.registry;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import io.fd.honeycomb.v3po.translate.write.Writer;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class FlatWriterRegistryBuilderTest {
+
+
+ @Test
+ public void testRelationsBefore() throws Exception {
+ final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder();
+ /*
+ 1 -> 2 -> 3
+ -> 4
+ */
+ flatWriterRegistryBuilder.addWriter(mockWriter(DataObject3.class));
+ flatWriterRegistryBuilder.addWriter(mockWriter(DataObject4.class));
+ flatWriterRegistryBuilder.addWriterBefore(mockWriter(DataObject2.class),
+ Lists.newArrayList(DataObject3.IID, DataObject4.IID));
+ flatWriterRegistryBuilder.addWriterBefore(mockWriter(DataObject1.class), DataObject2.IID);
+ final ImmutableMap<InstanceIdentifier<?>, Writer<?>> mappedWriters =
+ flatWriterRegistryBuilder.getMappedWriters();
+
+ final ArrayList<InstanceIdentifier<?>> typesInList = Lists.newArrayList(mappedWriters.keySet());
+ assertEquals(DataObject1.IID, typesInList.get(0));
+ assertEquals(DataObject2.IID, typesInList.get(1));
+ assertThat(typesInList.get(2), anyOf(equalTo(DataObject3.IID), equalTo(DataObject4.IID)));
+ assertThat(typesInList.get(3), anyOf(equalTo(DataObject3.IID), equalTo(DataObject4.IID)));
+ }
+
+ @Test
+ public void testRelationsAfter() throws Exception {
+ final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder();
+ /*
+ 1 -> 2 -> 3
+ -> 4
+ */
+ flatWriterRegistryBuilder.addWriter(mockWriter(DataObject1.class));
+ flatWriterRegistryBuilder.addWriterAfter(mockWriter(DataObject2.class), DataObject1.IID);
+ flatWriterRegistryBuilder.addWriterAfter(mockWriter(DataObject3.class), DataObject2.IID);
+ flatWriterRegistryBuilder.addWriterAfter(mockWriter(DataObject4.class), DataObject2.IID);
+ final ImmutableMap<InstanceIdentifier<?>, Writer<?>> mappedWriters =
+ flatWriterRegistryBuilder.getMappedWriters();
+
+ final List<InstanceIdentifier<?>> typesInList = Lists.newArrayList(mappedWriters.keySet());
+ assertEquals(DataObject1.IID, typesInList.get(0));
+ assertEquals(DataObject2.IID, typesInList.get(1));
+ assertThat(typesInList.get(2), anyOf(equalTo(DataObject3.IID), equalTo(DataObject4.IID)));
+ assertThat(typesInList.get(3), anyOf(equalTo(DataObject3.IID), equalTo(DataObject4.IID)));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testRelationsLoop() throws Exception {
+ final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder();
+ /*
+ 1 -> 2 -> 1
+ */
+ flatWriterRegistryBuilder.addWriter(mockWriter(DataObject1.class));
+ flatWriterRegistryBuilder.addWriterAfter(mockWriter(DataObject2.class), DataObject1.IID);
+ flatWriterRegistryBuilder.addWriterAfter(mockWriter(DataObject1.class), DataObject2.IID);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testAddWriterTwice() throws Exception {
+ final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder();
+ flatWriterRegistryBuilder.addWriter(mockWriter(DataObject1.class));
+ flatWriterRegistryBuilder.addWriter(mockWriter(DataObject1.class));
+ }
+
+ @Test
+ public void testAddSubtreeWriter() throws Exception {
+ final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder();
+ flatWriterRegistryBuilder.addSubtreeWriter(
+ Sets.newHashSet(DataObject4.DataObject5.IID,
+ DataObject4.DataObject5.IID),
+ mockWriter(DataObject4.class));
+ final ImmutableMap<InstanceIdentifier<?>, Writer<?>> mappedWriters =
+ flatWriterRegistryBuilder.getMappedWriters();
+ final ArrayList<InstanceIdentifier<?>> typesInList = Lists.newArrayList(mappedWriters.keySet());
+
+ assertEquals(DataObject4.IID, typesInList.get(0));
+ assertEquals(1, typesInList.size());
+ }
+
+ @Test
+ public void testCreateSubtreeWriter() throws Exception {
+ final Writer<?> forWriter = SubtreeWriter.createForWriter(Sets.newHashSet(
+ DataObject4.DataObject5.IID,
+ DataObject4.DataObject5.DataObject51.IID,
+ DataObject4.DataObject6.IID),
+ mockWriter(DataObject4.class));
+ assertThat(forWriter, instanceOf(SubtreeWriter.class));
+ assertThat(((SubtreeWriter<?>) forWriter).getHandledChildTypes().size(), is(3));
+ assertThat(((SubtreeWriter<?>) forWriter).getHandledChildTypes(), hasItems(DataObject4.DataObject5.IID,
+ DataObject4.DataObject6.IID, DataObject4.DataObject5.DataObject51.IID));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateInvalidSubtreeWriter() throws Exception {
+ SubtreeWriter.createForWriter(Sets.newHashSet(
+ InstanceIdentifier.create(DataObject3.class).child(DataObject3.DataObject31.class)),
+ mockWriter(DataObject4.class));
+ }
+
+ @SuppressWarnings("unchecked")
+ private Writer<? extends DataObject> mockWriter(final Class<? extends DataObject> doClass)
+ throws NoSuchFieldException, IllegalAccessException {
+ final Writer mock = mock(Writer.class);
+ when(mock.getManagedDataObjectType()).thenReturn((InstanceIdentifier<?>) doClass.getDeclaredField("IID").get(null));
+ return mock;
+ }
+
+ private abstract static class DataObject1 implements DataObject {
+ static InstanceIdentifier<DataObject1> IID = InstanceIdentifier.create(DataObject1.class);
+ }
+ private abstract static class DataObject2 implements DataObject {
+ static InstanceIdentifier<DataObject2> IID = InstanceIdentifier.create(DataObject2.class);
+ }
+ private abstract static class DataObject3 implements DataObject {
+ static InstanceIdentifier<DataObject3> IID = InstanceIdentifier.create(DataObject3.class);
+ private abstract static class DataObject31 implements DataObject, ChildOf<DataObject3> {
+ static InstanceIdentifier<DataObject31> IID = DataObject3.IID.child(DataObject31.class);
+ }
+ }
+ private abstract static class DataObject4 implements DataObject {
+ static InstanceIdentifier<DataObject4> IID = InstanceIdentifier.create(DataObject4.class);
+ private abstract static class DataObject5 implements DataObject, ChildOf<DataObject4> {
+ static InstanceIdentifier<DataObject5> IID = DataObject4.IID.child(DataObject5.class);
+ private abstract static class DataObject51 implements DataObject, ChildOf<DataObject5> {
+ static InstanceIdentifier<DataObject51> IID = DataObject5.IID.child(DataObject51.class);
+ }
+ }
+ private abstract static class DataObject6 implements DataObject, ChildOf<DataObject4> {
+ static InstanceIdentifier<DataObject6> IID = DataObject4.IID.child(DataObject6.class);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryTest.java b/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryTest.java
new file mode 100644
index 000000000..7fb5779f3
--- /dev/null
+++ b/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryTest.java
@@ -0,0 +1,295 @@
+package io.fd.honeycomb.v3po.translate.util.write.registry;
+
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.Writer;
+import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class FlatWriterRegistryTest {
+
+ @Mock
+ private Writer<DataObject1> writer1;
+ @Mock
+ private Writer<DataObject2> writer2;
+ @Mock
+ private Writer<DataObject3> writer3;
+ @Mock
+ private WriteContext ctx;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(writer1.getManagedDataObjectType()).thenReturn(DataObject1.IID);
+ when(writer2.getManagedDataObjectType()).thenReturn(DataObject2.IID);
+ when(writer3.getManagedDataObjectType()).thenReturn(DataObject3.IID);
+ }
+
+ @Test
+ public void testSingleUpdate() throws Exception {
+ final FlatWriterRegistry flatWriterRegistry =
+ new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1));
+
+ final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
+ final DataObject1 before = mock(DataObject1.class);
+ final DataObject1 after = mock(DataObject1.class);
+ flatWriterRegistry.update(iid, before, after, ctx);
+
+ verify(writer1).update(iid, before, after, ctx);
+ }
+
+ @Test
+ public void testMultipleUpdatesForSingleWriter() throws Exception {
+ final FlatWriterRegistry flatWriterRegistry =
+ new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
+ final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
+ final InstanceIdentifier<DataObject1> iid2 = InstanceIdentifier.create(DataObject1.class);
+ final DataObject1 dataObject = mock(DataObject1.class);
+ updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject));
+ updates.put(DataObject1.IID, DataObjectUpdate.create(iid2, dataObject, dataObject));
+ flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
+
+ verify(writer1).update(iid, dataObject, dataObject, ctx);
+ verify(writer1).update(iid2, dataObject, dataObject, ctx);
+ // Invoked when registry is being created
+ verifyNoMoreInteractions(writer1);
+ verifyZeroInteractions(writer2);
+ }
+
+ @Test
+ public void testMultipleUpdatesForMultipleWriters() throws Exception {
+ final FlatWriterRegistry flatWriterRegistry =
+ new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
+ final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
+ final DataObject1 dataObject = mock(DataObject1.class);
+ updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject));
+ final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
+ final DataObject2 dataObject2 = mock(DataObject2.class);
+ updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, dataObject2, dataObject2));
+ flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
+
+ final InOrder inOrder = inOrder(writer1, writer2);
+ inOrder.verify(writer1).update(iid, dataObject, dataObject, ctx);
+ inOrder.verify(writer2).update(iid2, dataObject2, dataObject2, ctx);
+
+ verifyNoMoreInteractions(writer1);
+ verifyNoMoreInteractions(writer2);
+ }
+
+ @Test
+ public void testMultipleDeletesForMultipleWriters() throws Exception {
+ final FlatWriterRegistry flatWriterRegistry =
+ new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> deletes = HashMultimap.create();
+ final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
+ final DataObject1 dataObject = mock(DataObject1.class);
+ deletes.put(DataObject1.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid, dataObject, null)));
+ final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
+ final DataObject2 dataObject2 = mock(DataObject2.class);
+ deletes.put(DataObject2.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid2, dataObject2, null)));
+ flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(ImmutableMultimap.of(), deletes), ctx);
+
+ final InOrder inOrder = inOrder(writer1, writer2);
+ // Reversed order of invocation, first writer2 and then writer1
+ inOrder.verify(writer2).update(iid2, dataObject2, null, ctx);
+ inOrder.verify(writer1).update(iid, dataObject, null, ctx);
+
+ verifyNoMoreInteractions(writer1);
+ verifyNoMoreInteractions(writer2);
+ }
+
+ @Test
+ public void testMultipleUpdatesAndDeletesForMultipleWriters() throws Exception {
+ final FlatWriterRegistry flatWriterRegistry =
+ new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> deletes = HashMultimap.create();
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
+ final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
+ final DataObject1 dataObject = mock(DataObject1.class);
+ // Writer 1 delete
+ deletes.put(DataObject1.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid, dataObject, null)));
+ // Writer 1 update
+ updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject));
+ final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
+ final DataObject2 dataObject2 = mock(DataObject2.class);
+ // Writer 2 delete
+ deletes.put(DataObject2.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid2, dataObject2, null)));
+ // Writer 2 update
+ updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, dataObject2, dataObject2));
+ flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, deletes), ctx);
+
+ final InOrder inOrder = inOrder(writer1, writer2);
+ // Reversed order of invocation, first writer2 and then writer1 for deletes
+ inOrder.verify(writer2).update(iid2, dataObject2, null, ctx);
+ inOrder.verify(writer1).update(iid, dataObject, null, ctx);
+ // Then also updates are processed
+ inOrder.verify(writer1).update(iid, dataObject, dataObject, ctx);
+ inOrder.verify(writer2).update(iid2, dataObject2, dataObject2, ctx);
+
+ verifyNoMoreInteractions(writer1);
+ verifyNoMoreInteractions(writer2);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testMultipleUpdatesOneMissing() throws Exception {
+ final FlatWriterRegistry flatWriterRegistry =
+ new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1));
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
+ addUpdate(updates, DataObject1.class);
+ addUpdate(updates, DataObject2.class);
+ flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
+ }
+
+ @Test
+ public void testMultipleUpdatesOneFailing() throws Exception {
+ final FlatWriterRegistry flatWriterRegistry =
+ new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
+
+ // Writer1 always fails
+ doThrow(new RuntimeException()).when(writer1)
+ .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
+ addUpdate(updates, DataObject1.class);
+ addUpdate(updates, DataObject2.class);
+
+ try {
+ flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
+ fail("Bulk update should have failed on writer1");
+ } catch (WriterRegistry.BulkUpdateException e) {
+ assertThat(e.getFailedIds().size(), is(2));
+ assertThat(e.getFailedIds(), hasItem(InstanceIdentifier.create(DataObject2.class)));
+ assertThat(e.getFailedIds(), hasItem(InstanceIdentifier.create(DataObject1.class)));
+ }
+ }
+
+ @Test
+ public void testMultipleUpdatesOneFailingThenRevertWithSuccess() throws Exception {
+ final FlatWriterRegistry flatWriterRegistry =
+ new FlatWriterRegistry(
+ ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2, DataObject3.IID, writer3));
+
+ // Writer1 always fails
+ doThrow(new RuntimeException()).when(writer3)
+ .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
+ addUpdate(updates, DataObject1.class);
+ addUpdate(updates, DataObject3.class);
+ final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
+ final DataObject2 before2 = mock(DataObject2.class);
+ final DataObject2 after2 = mock(DataObject2.class);
+ updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, before2, after2));
+
+ try {
+ flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
+ fail("Bulk update should have failed on writer1");
+ } catch (WriterRegistry.BulkUpdateException e) {
+ assertThat(e.getFailedIds().size(), is(1));
+
+ final InOrder inOrder = inOrder(writer1, writer2, writer3);
+ inOrder.verify(writer1)
+ .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
+ inOrder.verify(writer2)
+ .update(iid2, before2, after2, ctx);
+ inOrder.verify(writer3)
+ .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
+
+ e.revertChanges();
+ // Revert changes. Successful updates are iterated in reverse
+ inOrder.verify(writer2)
+ .update(iid2, after2, before2, ctx);
+ inOrder.verify(writer1)
+ .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
+ verifyNoMoreInteractions(writer3);
+ }
+ }
+
+ @Test
+ public void testMultipleUpdatesOneFailingThenRevertWithFail() throws Exception {
+ final FlatWriterRegistry flatWriterRegistry =
+ new FlatWriterRegistry(
+ ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2, DataObject3.IID, writer3));
+
+ // Writer1 always fails
+ doThrow(new RuntimeException()).when(writer3)
+ .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
+ addUpdate(updates, DataObject1.class);
+ addUpdate(updates, DataObject2.class);
+ addUpdate(updates, DataObject3.class);
+
+ try {
+ flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
+ fail("Bulk update should have failed on writer1");
+ } catch (WriterRegistry.BulkUpdateException e) {
+ // Writer1 always fails from now
+ doThrow(new RuntimeException()).when(writer1)
+ .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
+ try {
+ e.revertChanges();
+ } catch (WriterRegistry.Reverter.RevertFailedException e1) {
+ assertThat(e1.getNotRevertedChanges().size(), is(1));
+ assertThat(e1.getNotRevertedChanges(), hasItem(InstanceIdentifier.create(DataObject1.class)));
+ }
+ }
+ }
+
+ private <D extends DataObject> void addUpdate(final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates,
+ final Class<D> type) throws Exception {
+ final InstanceIdentifier<D> iid = (InstanceIdentifier<D>) type.getDeclaredField("IID").get(null);
+ updates.put(iid, DataObjectUpdate.create(iid, mock(type), mock(type)));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testSingleUpdateMissingWriter() throws Exception {
+ final FlatWriterRegistry flatWriterRegistry =
+ new FlatWriterRegistry(ImmutableMap.of());
+
+ final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
+ final DataObject1 before = mock(DataObject1.class);
+ final DataObject1 after = mock(DataObject1.class);
+ flatWriterRegistry.update(iid, before, after, ctx);
+ }
+
+ private abstract static class DataObject1 implements DataObject {
+ static final InstanceIdentifier<DataObject1> IID = InstanceIdentifier.create(DataObject1.class);
+ }
+ private abstract static class DataObject2 implements DataObject {
+ static final InstanceIdentifier<DataObject2> IID = InstanceIdentifier.create(DataObject2.class);
+ }
+ private abstract static class DataObject3 implements DataObject {
+ static final InstanceIdentifier<DataObject3> IID = InstanceIdentifier.create(DataObject3.class);
+ }
+} \ No newline at end of file
diff --git a/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriterTest.java b/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriterTest.java
new file mode 100644
index 000000000..b7dcadc73
--- /dev/null
+++ b/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriterTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.write.registry;
+
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.Sets;
+import io.fd.honeycomb.v3po.translate.write.Writer;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class SubtreeWriterTest {
+
+ @Mock
+ Writer<DataObject1> writer;
+ @Mock
+ Writer<DataObject1.DataObject11> writer11;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(writer.getManagedDataObjectType()).thenReturn(DataObject1.IID);
+ when(writer11.getManagedDataObjectType()).thenReturn(DataObject1.DataObject11.IID);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSubtreeWriterCreationFail() throws Exception {
+ // The subtree node identified by IID.c(DataObject.class) is not a child of writer.getManagedDataObjectType
+ SubtreeWriter.createForWriter(Collections.singleton(InstanceIdentifier.create(DataObject.class)), writer);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSubtreeWriterCreationFailInvalidIid() throws Exception {
+ // The subtree node identified by IID.c(DataObject.class) is not a child of writer.getManagedDataObjectType
+ SubtreeWriter.createForWriter(Collections.singleton(DataObject1.IID), writer);
+ }
+
+ @Test
+ public void testSubtreeWriterCreation() throws Exception {
+ final SubtreeWriter<?> forWriter = (SubtreeWriter<?>) SubtreeWriter.createForWriter(Sets.newHashSet(
+ DataObject1.DataObject11.IID,
+ DataObject1.DataObject11.DataObject111.IID,
+ DataObject1.DataObject12.IID),
+ writer);
+
+ assertEquals(writer.getManagedDataObjectType(), forWriter.getManagedDataObjectType());
+ assertEquals(3, forWriter.getHandledChildTypes().size());
+ }
+
+ @Test
+ public void testSubtreeWriterHandledTypes() throws Exception {
+ final SubtreeWriter<?> forWriter = (SubtreeWriter<?>) SubtreeWriter.createForWriter(Sets.newHashSet(
+ DataObject1.DataObject11.DataObject111.IID),
+ writer);
+
+ assertEquals(writer.getManagedDataObjectType(), forWriter.getManagedDataObjectType());
+ assertEquals(1, forWriter.getHandledChildTypes().size());
+ assertThat(forWriter.getHandledChildTypes(), hasItem(DataObject1.DataObject11.DataObject111.IID));
+ }
+
+ private abstract static class DataObject1 implements DataObject {
+ static InstanceIdentifier<DataObject1> IID = InstanceIdentifier.create(DataObject1.class);
+ private abstract static class DataObject11 implements DataObject, ChildOf<DataObject1> {
+ static InstanceIdentifier<DataObject11> IID = DataObject1.IID.child(DataObject11.class);
+ private abstract static class DataObject111 implements DataObject, ChildOf<DataObject11> {
+ static InstanceIdentifier<DataObject111> IID = DataObject11.IID.child(DataObject111.class);
+ }
+ }
+ private abstract static class DataObject12 implements DataObject, ChildOf<DataObject1> {
+ static InstanceIdentifier<DataObject12> IID = DataObject1.IID.child(DataObject12.class);
+ }
+ }
+} \ No newline at end of file
diff --git a/v3po/v3po2vpp/src/main/config/default-config.xml b/v3po/v3po2vpp/src/main/config/default-config.xml
index 1ee177553..6b487a13f 100644
--- a/v3po/v3po2vpp/src/main/config/default-config.xml
+++ b/v3po/v3po2vpp/src/main/config/default-config.xml
@@ -162,14 +162,14 @@
<module>
<type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:utils">prefix:delegating-writer-registry</type>
<name>write-registry</name>
- <root-writers>
- <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer</type>
+ <writer-factory>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-factory</type>
<name>vpp-honeycomb-writer</name>
- </root-writers>
- <root-writers>
- <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer</type>
+ </writer-factory>
+ <writer-factory>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-factory</type>
<name>interfaces-honeycomb-writer</name>
- </root-writers>
+ </writer-factory>
</module>
</modules>
@@ -205,7 +205,7 @@
</instance>
</service>
<service>
- <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer</type>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-factory</type>
<instance>
<name>vpp-honeycomb-writer</name>
<provider>/modules/module[type='vpp-honeycomb-writer'][name='vpp-honeycomb-writer']
diff --git a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesHoneycombWriterModule.java b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesHoneycombWriterModule.java
index d87370a8b..8cc740a48 100644
--- a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesHoneycombWriterModule.java
+++ b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesHoneycombWriterModule.java
@@ -1,21 +1,24 @@
package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import io.fd.honeycomb.v3po.translate.impl.TraversalType;
-import io.fd.honeycomb.v3po.translate.impl.write.CompositeChildWriter;
-import io.fd.honeycomb.v3po.translate.impl.write.CompositeListWriter;
-import io.fd.honeycomb.v3po.translate.impl.write.CompositeRootWriter;
-import io.fd.honeycomb.v3po.translate.util.RWUtils;
-import io.fd.honeycomb.v3po.translate.util.write.CloseableWriter;
-import io.fd.honeycomb.v3po.translate.util.write.NoopWriterCustomizer;
-import io.fd.honeycomb.v3po.translate.util.write.ReflexiveAugmentWriterCustomizer;
-import io.fd.honeycomb.v3po.translate.v3po.interfaces.*;
+import com.google.common.collect.Sets;
+import io.fd.honeycomb.v3po.translate.impl.write.GenericListWriter;
+import io.fd.honeycomb.v3po.translate.impl.write.GenericWriter;
+import io.fd.honeycomb.v3po.translate.v3po.interfaces.EthernetCustomizer;
+import io.fd.honeycomb.v3po.translate.v3po.interfaces.InterfaceCustomizer;
+import io.fd.honeycomb.v3po.translate.v3po.interfaces.L2Customizer;
+import io.fd.honeycomb.v3po.translate.v3po.interfaces.RoutingCustomizer;
+import io.fd.honeycomb.v3po.translate.v3po.interfaces.TapCustomizer;
+import io.fd.honeycomb.v3po.translate.v3po.interfaces.VhostUserCustomizer;
+import io.fd.honeycomb.v3po.translate.v3po.interfaces.VxlanCustomizer;
+import io.fd.honeycomb.v3po.translate.v3po.interfaces.VxlanGpeCustomizer;
import io.fd.honeycomb.v3po.translate.v3po.interfaces.ip.Ipv4AddressCustomizer;
import io.fd.honeycomb.v3po.translate.v3po.interfaces.ip.Ipv4Customizer;
import io.fd.honeycomb.v3po.translate.v3po.interfaces.ip.Ipv4NeighbourCustomizer;
import io.fd.honeycomb.v3po.translate.v3po.interfaces.ip.Ipv6Customizer;
-import io.fd.honeycomb.v3po.translate.write.ChildWriter;
+import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext;
+import io.fd.honeycomb.v3po.translate.write.ModifiableWriterRegistry;
+import io.fd.honeycomb.v3po.translate.write.WriterFactory;
+import java.util.Set;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface1;
@@ -24,15 +27,18 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev14061
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.Address;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.Neighbor;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentation;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.*;
-import org.opendaylight.yangtools.yang.binding.Augmentation;
-import org.opendaylight.yangtools.yang.binding.ChildOf;
-
-import java.util.ArrayList;
-import java.util.List;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Ethernet;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Routing;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Tap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VhostUser;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Vxlan;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VxlanGpe;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.openvpp.jvpp.future.FutureJVpp;
public class InterfacesHoneycombWriterModule extends
- org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406.AbstractInterfacesHoneycombWriterModule {
+ org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406.AbstractInterfacesHoneycombWriterModule {
public InterfacesHoneycombWriterModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
super(identifier, dependencyResolver);
@@ -52,91 +58,98 @@ public class InterfacesHoneycombWriterModule extends
@Override
public java.lang.AutoCloseable createInstance() {
-
- final List<ChildWriter<? extends Augmentation<Interface>>> ifcAugmentations = Lists.newArrayList();
- ifcAugmentations.add(getVppIfcAugmentationWriter());
- ifcAugmentations.add(getInterface1AugmentationWriter());
- ifcAugmentations.add(
- SubinterfaceAugmentationWriterFactory.createInstance(getVppJvppIfcDependency(), getInterfaceContextDependency(),
- getBridgeDomainContextDependency()));
-
- final ChildWriter<Interface> interfaceWriter = new CompositeListWriter<>(Interface.class,
- RWUtils.emptyChildWriterList(),
- ifcAugmentations,
- new InterfaceCustomizer(getVppJvppIfcDependency(), getInterfaceContextDependency()),
- // It's important that this customizer is handled in a postorder way, because you first have to handle child nodes
- // e.g. Vxlan before setting other interface or vppInterfaceAugmentation leaves
- TraversalType.POSTORDER);
-
- final List<ChildWriter<? extends ChildOf<Interfaces>>> childWriters = new ArrayList<>();
- childWriters.add(interfaceWriter);
-
- // FIXME if we just return the root writer and cfg subsystem takes care to set it into reader registry,
- // we loose the ordering information for root writers
- // Or can we rely to the order in which readers are configured ?
- return new CloseableWriter<>(new CompositeRootWriter<>(Interfaces.class,
- childWriters, new NoopWriterCustomizer<>()));
+ return new InterfacesWriterFactory(getVppJvppIfcDependency(),
+ getBridgeDomainContextDependency(),
+ getInterfaceContextDependency());
}
- private ChildWriter<? extends Augmentation<Interface>> getInterface1AugmentationWriter() {
-
- final ChildWriter<Neighbor> neighborWriter = new CompositeListWriter<>(Neighbor.class,
- new Ipv4NeighbourCustomizer(getVppJvppIfcDependency(),getInterfaceContextDependency()));
-
- final ChildWriter<Address> addressWriter = new CompositeListWriter<>(Address.class,
- new Ipv4AddressCustomizer(getVppJvppIfcDependency(), getInterfaceContextDependency()));
- final ChildWriter<Ipv4> ipv4Writer = new CompositeChildWriter<>(Ipv4.class,
- ImmutableList.of(neighborWriter,addressWriter),
- new Ipv4Customizer(getVppJvppIfcDependency(),getInterfaceContextDependency()));
- final ChildWriter<Ipv6> ipv6Writer = new CompositeChildWriter<>(Ipv6.class,
- new Ipv6Customizer(getVppJvppIfcDependency()));
+ private static class InterfacesWriterFactory implements WriterFactory, AutoCloseable {
+
+ private final FutureJVpp jvpp;
+ private final NamingContext bdContext;
+ private final NamingContext ifcContext;
+
+ InterfacesWriterFactory(final FutureJVpp vppJvppIfcDependency,
+ final NamingContext bridgeDomainContextDependency,
+ final NamingContext interfaceContextDependency) {
+ this.jvpp = vppJvppIfcDependency;
+ this.bdContext = bridgeDomainContextDependency;
+ this.ifcContext = interfaceContextDependency;
+ }
+
+ @Override
+ public void close() throws Exception {
+ // unregister is not supported in ModifiableWriterRegistry (not really needed though)
+ }
+
+ @Override
+ public void init(final ModifiableWriterRegistry registry) {
+ // Interfaces
+ // Interface =
+ final InstanceIdentifier<Interface> ifcId = InstanceIdentifier.create(Interfaces.class).child(Interface.class);
+ registry.addWriter(new GenericListWriter<>(ifcId, new InterfaceCustomizer(jvpp, ifcContext)));
+ // VppInterfaceAugmentation
+ addVppInterfaceAgmentationWriters(ifcId, registry);
+ // Interface1 (ietf-ip augmentation)
+ addInterface1AugmentationWriters(ifcId, registry);
+ // SubinterfaceAugmentation TODO make dedicated module for subIfc writer factory
+ new SubinterfaceAugmentationWriterFactory(ifcId, jvpp, ifcContext, bdContext).init(registry);
+ }
+
+ private void addInterface1AugmentationWriters(final InstanceIdentifier<Interface> ifcId,
+ final ModifiableWriterRegistry registry) {
+ final InstanceIdentifier<Interface1> ifc1AugId = ifcId.augmentation(Interface1.class);
+ // Ipv6(after interface) TODO unfinished customizer =
+ registry.addWriterAfter(new GenericWriter<>(ifc1AugId.child(Ipv6.class), new Ipv6Customizer(jvpp)),
+ ifcId);
+ // Ipv4(after interface)
+ final InstanceIdentifier<Ipv4> ipv4Id = ifc1AugId.child(Ipv4.class);
+ registry.addWriterAfter(new GenericWriter<>(ipv4Id, new Ipv4Customizer(jvpp, ifcContext)),
+ ifcId);
+ // Address(after Ipv4) =
+ final InstanceIdentifier<Address> ipv4AddressId = ipv4Id.child(Address.class);
+ registry.addWriterAfter(new GenericListWriter<>(ipv4AddressId, new Ipv4AddressCustomizer(jvpp, ifcContext)),
+ ipv4Id);
+ // Neighbor(after ipv4Address)
+ registry.addWriterAfter(new GenericListWriter<>(ipv4Id.child(Neighbor.class), new Ipv4NeighbourCustomizer(jvpp, ifcContext)),
+ ipv4AddressId);
+ }
+
+ private void addVppInterfaceAgmentationWriters(final InstanceIdentifier<Interface> ifcId,
+ final ModifiableWriterRegistry registry) {
+ final InstanceIdentifier<VppInterfaceAugmentation> vppIfcAugId = ifcId.augmentation(VppInterfaceAugmentation.class);
+ // VhostUser(Needs to be executed before Interface customizer) =
+ final InstanceIdentifier<VhostUser> vhostId = vppIfcAugId.child(VhostUser.class);
+ registry.addWriterBefore(new GenericWriter<>(vhostId, new VhostUserCustomizer(jvpp, ifcContext)),
+ ifcId);
+ // Vxlan(Needs to be executed before Interface customizer) =
+ final InstanceIdentifier<Vxlan> vxlanId = vppIfcAugId.child(Vxlan.class);
+ registry.addWriterBefore(new GenericWriter<>(vxlanId, new VxlanCustomizer(jvpp, ifcContext)),
+ ifcId);
+ // VxlanGpe(Needs to be executed before Interface customizer) =
+ final InstanceIdentifier<VxlanGpe> vxlanGpeId = vppIfcAugId.child(VxlanGpe.class);
+ registry.addWriterBefore(new GenericWriter<>(vxlanGpeId, new VxlanGpeCustomizer(jvpp, ifcContext)),
+ ifcId);
+ // Tap(Needs to be executed before Interface customizer) =
+ final InstanceIdentifier<Tap> tapId = vppIfcAugId.child(Tap.class);
+ registry.addWriterBefore(new GenericWriter<>(tapId, new TapCustomizer(jvpp, ifcContext)),
+ ifcId);
+
+ final Set<InstanceIdentifier<?>> specificIfcTypes = Sets.newHashSet(vhostId, vxlanGpeId, vxlanGpeId, tapId);
+
+ // Ethernet(No dependency, customizer not finished TODO) =
+ registry.addWriter(new GenericWriter<>(vppIfcAugId.child(Ethernet.class), new EthernetCustomizer(jvpp)));
+ // Routing(Execute only after specific interface customizers) =
+ registry.addWriterAfter(
+ new GenericWriter<>(vppIfcAugId.child(Routing.class), new RoutingCustomizer(jvpp, ifcContext)),
+ specificIfcTypes);
+ // Routing(Execute only after specific interface customizers) =
+ registry.addWriterAfter(
+ new GenericWriter<>(vppIfcAugId.child(L2.class), new L2Customizer(jvpp, ifcContext, bdContext)),
+ specificIfcTypes);
+ }
- final List<ChildWriter<? extends ChildOf<Interface1>>> interface1ChildWriters = Lists.newArrayList();
- interface1ChildWriters.add(ipv4Writer);
- interface1ChildWriters.add(ipv6Writer);
-
- return new CompositeChildWriter<>(Interface1.class,
- interface1ChildWriters, new ReflexiveAugmentWriterCustomizer<>());
}
- private ChildWriter<VppInterfaceAugmentation> getVppIfcAugmentationWriter() {
-
- final ChildWriter<Ethernet> ethernetWriter = new CompositeChildWriter<>(Ethernet.class,
- new EthernetCustomizer(getVppJvppIfcDependency()));
-
- final ChildWriter<Routing> routingWriter = new CompositeChildWriter<>(Routing.class,
- new RoutingCustomizer(getVppJvppIfcDependency(), getInterfaceContextDependency()));
-
- final ChildWriter<Vxlan> vxlanWriter = new CompositeChildWriter<>(Vxlan.class,
- new VxlanCustomizer(getVppJvppIfcDependency(), getInterfaceContextDependency()));
-
- final ChildWriter<VxlanGpe> vxlanGpeWriter = new CompositeChildWriter<>(VxlanGpe.class,
- new VxlanGpeCustomizer(getVppJvppIfcDependency(), getInterfaceContextDependency()));
-
- final ChildWriter<VhostUser> vhostUserWriter = new CompositeChildWriter<>(VhostUser.class,
- new VhostUserCustomizer(getVppJvppIfcDependency(), getInterfaceContextDependency()));
-
- final ChildWriter<Tap> tapWriter = new CompositeChildWriter<>(Tap.class,
- new TapCustomizer(getVppJvppIfcDependency(), getInterfaceContextDependency()));
-
- final ChildWriter<L2> l2Writer = new CompositeChildWriter<>(L2.class,
- new L2Customizer(getVppJvppIfcDependency(), getInterfaceContextDependency(),
- getBridgeDomainContextDependency())
- );
-
- final List<ChildWriter<? extends ChildOf<VppInterfaceAugmentation>>> vppIfcChildWriters = Lists.newArrayList();
- vppIfcChildWriters.add(vhostUserWriter);
- vppIfcChildWriters.add(vxlanWriter);
- vppIfcChildWriters.add(vxlanGpeWriter);
- vppIfcChildWriters.add(tapWriter);
- vppIfcChildWriters.add(ethernetWriter);
- vppIfcChildWriters.add(l2Writer);
- vppIfcChildWriters.add(routingWriter);
-
- return new CompositeChildWriter<>(VppInterfaceAugmentation.class,
- vppIfcChildWriters,
- RWUtils.emptyAugWriterList(),
- new ReflexiveAugmentWriterCustomizer<>());
- }
}
diff --git a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/SubinterfaceAugmentationWriterFactory.java b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/SubinterfaceAugmentationWriterFactory.java
index f24dbf7e0..589ba92be 100644
--- a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/SubinterfaceAugmentationWriterFactory.java
+++ b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/SubinterfaceAugmentationWriterFactory.java
@@ -16,90 +16,86 @@
package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406;
-import static io.fd.honeycomb.v3po.translate.util.RWUtils.singletonChildWriterList;
-
-import io.fd.honeycomb.v3po.translate.impl.write.CompositeChildWriter;
-import io.fd.honeycomb.v3po.translate.impl.write.CompositeListWriter;
-import io.fd.honeycomb.v3po.translate.util.RWUtils;
-import io.fd.honeycomb.v3po.translate.util.write.ReflexiveAugmentWriterCustomizer;
-import io.fd.honeycomb.v3po.translate.util.write.ReflexiveChildWriterCustomizer;
+import com.google.common.collect.Sets;
+import io.fd.honeycomb.v3po.translate.impl.write.GenericListWriter;
+import io.fd.honeycomb.v3po.translate.impl.write.GenericWriter;
import io.fd.honeycomb.v3po.translate.v3po.interfaces.RewriteCustomizer;
import io.fd.honeycomb.v3po.translate.v3po.interfaces.SubInterfaceCustomizer;
import io.fd.honeycomb.v3po.translate.v3po.interfaces.SubInterfaceL2Customizer;
import io.fd.honeycomb.v3po.translate.v3po.interfaces.ip.SubInterfaceIpv4AddressCustomizer;
import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext;
-import io.fd.honeycomb.v3po.translate.write.ChildWriter;
-import java.util.ArrayList;
-import java.util.List;
-import javax.annotation.Nonnull;
+import io.fd.honeycomb.v3po.translate.write.ModifiableWriterRegistry;
+import io.fd.honeycomb.v3po.translate.write.WriterFactory;
+import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.dot1q.tag.or.any.Dot1qTag;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.SubinterfaceAugmentation;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.interfaces._interface.SubInterfaces;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.interfaces._interface.sub.interfaces.SubInterface;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.interfaces._interface.sub.interfaces.SubInterfaceKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.base.attributes.L2;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.match.attributes.match.type.vlan.tagged.VlanTagged;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.base.attributes.Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.base.attributes.Tags;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.base.attributes.l2.Rewrite;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.base.attributes.tags.Tag;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.ip4.attributes.Ipv4;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.ip4.attributes.ipv4.Address;
-import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.tag.rewrite.PushTags;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.openvpp.jvpp.future.FutureJVpp;
-final class SubinterfaceAugmentationWriterFactory {
-
- private SubinterfaceAugmentationWriterFactory() {
- }
-
- private static ChildWriter<Ipv4> getIp4Writer(
- @Nonnull final FutureJVpp futureJvpp, @Nonnull final NamingContext interfaceContext) {
+final class SubinterfaceAugmentationWriterFactory implements WriterFactory {
- final ChildWriter<Address> addressWriter = new CompositeListWriter<>(
- Address.class,
- new SubInterfaceIpv4AddressCustomizer(futureJvpp, interfaceContext));
+ private final InstanceIdentifier<Interface> ifcId;
+ private final FutureJVpp jvpp;
+ private final NamingContext ifcContext;
+ private final NamingContext bdContext;
- return new CompositeChildWriter<>(
- Ipv4.class,
- RWUtils.singletonChildWriterList(addressWriter),
- new ReflexiveChildWriterCustomizer<>());
+ public SubinterfaceAugmentationWriterFactory(
+ final InstanceIdentifier<Interface> ifcId, final FutureJVpp jvpp,
+ final NamingContext ifcContext, final NamingContext bdContext) {
+ this.ifcId = ifcId;
+ this.jvpp = jvpp;
+ this.ifcContext = ifcContext;
+ this.bdContext = bdContext;
}
- private static ChildWriter<L2> getL2Writer(
- @Nonnull final FutureJVpp futureJvpp, @Nonnull final NamingContext interfaceContext,
- @Nonnull final NamingContext bridgeDomainContext) {
-
- final ChildWriter<? extends ChildOf<L2>> rewriteWriter =
- new CompositeChildWriter<>(Rewrite.class, new RewriteCustomizer(futureJvpp, interfaceContext));
-
- return new CompositeChildWriter<>(
- L2.class,
- singletonChildWriterList(rewriteWriter),
- new SubInterfaceL2Customizer(futureJvpp, interfaceContext, bridgeDomainContext)
- );
- }
-
- static ChildWriter<SubinterfaceAugmentation> createInstance(
- @Nonnull final FutureJVpp futureJvpp, @Nonnull final NamingContext interfaceContext,
- @Nonnull final NamingContext bridgeDomainContext) {
- final List<ChildWriter<? extends ChildOf<SubInterface>>> childWriters = new ArrayList<>();
-
- // TODO L2 is ChildOf<SubInterfaceBaseAttributes>, but SubInterface extends SubInterfaceBaseAttributes
- // If we use containers inside groupings, we need to cast and lose static type checking.
- // Can we get rid of the cast?
- childWriters.add((ChildWriter) getL2Writer(futureJvpp, interfaceContext, bridgeDomainContext));
- childWriters.add((ChildWriter) getIp4Writer(futureJvpp, interfaceContext));
-
- final CompositeListWriter<SubInterface, SubInterfaceKey> subInterfaceWriter = new CompositeListWriter<>(
- SubInterface.class,
- childWriters,
- new SubInterfaceCustomizer(futureJvpp, interfaceContext));
-
- final ChildWriter<SubInterfaces> subInterfacesWriter = new CompositeChildWriter<>(
- SubInterfaces.class,
- singletonChildWriterList(subInterfaceWriter),
- new ReflexiveChildWriterCustomizer<>());
+ @Override
+ public void init(final ModifiableWriterRegistry registry) {
+ final InstanceIdentifier<SubinterfaceAugmentation> subIfcAugId =
+ ifcId.augmentation(SubinterfaceAugmentation.class);
+ // Subinterfaces
+ // Subinterface(Handle only after all interface related stuff gets processed) =
+ final InstanceIdentifier<SubInterface> subIfcId = subIfcAugId.child(SubInterfaces.class).child(SubInterface.class);
+ registry.addSubtreeWriterAfter(
+ // TODO this customizer covers quite a lot of complex child nodes (maybe refactor ?)
+ Sets.newHashSet(
+ InstanceIdentifier.create(SubInterface.class).child(Tags.class),
+ InstanceIdentifier.create(SubInterface.class).child(Tags.class).child(Tag.class),
+ InstanceIdentifier.create(SubInterface.class).child(Tags.class).child(Tag.class).child(
+ Dot1qTag.class),
+ InstanceIdentifier.create(SubInterface.class).child(Match.class),
+ InstanceIdentifier.create(SubInterface.class).child(Match.class).child(VlanTagged.class)),
+ new GenericListWriter<>(subIfcId, new SubInterfaceCustomizer(jvpp, ifcContext)),
+ ifcId);
+ // L2 =
+ final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.base.attributes.L2>
+ l2Id = subIfcId.child(
+ org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.base.attributes.L2.class);
+ registry.addWriterAfter(new GenericWriter<>(l2Id, new SubInterfaceL2Customizer(jvpp, ifcContext, bdContext)),
+ subIfcId);
+ // Rewrite(also handles pushTags + pushTags/dot1qtag) =
+ final InstanceIdentifier<Rewrite> rewriteId = l2Id.child(Rewrite.class);
+ registry.addSubtreeWriterAfter(
+ Sets.newHashSet(
+ InstanceIdentifier.create(Rewrite.class).child(PushTags.class),
+ InstanceIdentifier.create(Rewrite.class).child(PushTags.class)
+ .child(org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev150626.dot1q.tag.Dot1qTag.class)),
+ new GenericWriter<>(rewriteId, new RewriteCustomizer(jvpp, ifcContext)),
+ l2Id);
+ // Ipv4(handled after L2 and L2/rewrite is done) =
+ final InstanceIdentifier<Address> ipv4SubifcAddressId = subIfcId.child(Ipv4.class).child(Address.class);
+ registry.addWriterAfter(new GenericListWriter<>(ipv4SubifcAddressId,
+ new SubInterfaceIpv4AddressCustomizer(jvpp, ifcContext)),
+ rewriteId);
- return new CompositeChildWriter<>(
- SubinterfaceAugmentation.class,
- singletonChildWriterList(subInterfacesWriter),
- RWUtils.emptyAugWriterList(),
- new ReflexiveAugmentWriterCustomizer<>());
}
}
diff --git a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppHoneycombWriterModule.java b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppHoneycombWriterModule.java
index 6bf3da104..d9a542bde 100644
--- a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppHoneycombWriterModule.java
+++ b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppHoneycombWriterModule.java
@@ -1,25 +1,18 @@
package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406;
-import io.fd.honeycomb.v3po.translate.impl.write.CompositeChildWriter;
-import io.fd.honeycomb.v3po.translate.impl.write.CompositeListWriter;
-import io.fd.honeycomb.v3po.translate.impl.write.CompositeRootWriter;
-import io.fd.honeycomb.v3po.translate.util.RWUtils;
-import io.fd.honeycomb.v3po.translate.util.write.CloseableWriter;
-import io.fd.honeycomb.v3po.translate.util.write.NoopWriterCustomizer;
-import io.fd.honeycomb.v3po.translate.util.write.ReflexiveChildWriterCustomizer;
+import io.fd.honeycomb.v3po.translate.impl.write.GenericListWriter;
+import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext;
import io.fd.honeycomb.v3po.translate.v3po.vpp.BridgeDomainCustomizer;
import io.fd.honeycomb.v3po.translate.v3po.vpp.L2FibEntryCustomizer;
-import io.fd.honeycomb.v3po.translate.write.ChildWriter;
-import java.util.ArrayList;
-import java.util.List;
+import io.fd.honeycomb.v3po.translate.write.ModifiableWriterRegistry;
+import io.fd.honeycomb.v3po.translate.write.WriterFactory;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.Vpp;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.fib.attributes.L2FibTable;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.fib.attributes.l2.fib.table.L2FibEntry;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.fib.attributes.l2.fib.table.L2FibEntryKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.BridgeDomains;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomain;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomainKey;
-import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.openvpp.jvpp.future.FutureJVpp;
public class VppHoneycombWriterModule extends
org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406.AbstractVppHoneycombWriterModule {
@@ -42,38 +35,45 @@ public class VppHoneycombWriterModule extends
@Override
public java.lang.AutoCloseable createInstance() {
- final CompositeListWriter<BridgeDomain, BridgeDomainKey> bridgeDomainWriter = new CompositeListWriter<>(
- BridgeDomain.class,
- RWUtils.singletonChildWriterList(l2FibTableWriter()),
- new BridgeDomainCustomizer(getVppJvppWriterDependency(), getBridgeDomainContextVppDependency()));
-
- final ChildWriter<BridgeDomains> bridgeDomainsWriter = new CompositeChildWriter<>(
- BridgeDomains.class,
- RWUtils.singletonChildWriterList(bridgeDomainWriter),
- new ReflexiveChildWriterCustomizer<>());
-
- final List<ChildWriter<? extends ChildOf<Vpp>>> childWriters = new ArrayList<>();
- childWriters.add(bridgeDomainsWriter);
-
- return new CloseableWriter<>(new CompositeRootWriter<>(
- Vpp.class,
- childWriters,
- new NoopWriterCustomizer<>()));
+ return new VppHoneycombWriterFactory(
+ getVppJvppWriterDependency(),
+ getBridgeDomainContextVppDependency(),
+ getInterfaceContextVppDependency());
}
- private ChildWriter l2FibTableWriter() {
- final CompositeListWriter<L2FibEntry, L2FibEntryKey> l2FibEntryWriter =
- new CompositeListWriter<>(L2FibEntry.class,
- new L2FibEntryCustomizer(getVppJvppWriterDependency(),
- getBridgeDomainContextVppDependency(), getInterfaceContextVppDependency()));
+ private static final class VppHoneycombWriterFactory implements WriterFactory, AutoCloseable {
- final ChildWriter<L2FibTable> l2FibTableWriter = new CompositeChildWriter<>(
- L2FibTable.class,
- RWUtils.singletonChildWriterList(l2FibEntryWriter),
- new ReflexiveChildWriterCustomizer<>());
+ private final FutureJVpp jvpp;
+ private final NamingContext bdContext;
+ private final NamingContext ifcContext;
- return l2FibTableWriter;
- }
+ VppHoneycombWriterFactory(final FutureJVpp vppJvppWriterDependency,
+ final NamingContext bridgeDomainContextVppDependency,
+ final NamingContext interfaceContextVppDependency) {
+ this.jvpp = vppJvppWriterDependency;
+ this.bdContext = bridgeDomainContextVppDependency;
+ this.ifcContext = interfaceContextVppDependency;
+ }
+ @Override
+ public void close() throws Exception {
+ // unregister is not supported in ModifiableWriterRegistry (not really needed though)
+ }
+ @Override
+ public void init(final ModifiableWriterRegistry registry) {
+ // Vpp has no handlers
+ // BridgeDomains has no handlers
+ // BridgeDomain =
+ final InstanceIdentifier<BridgeDomain> bdId =
+ InstanceIdentifier.create(Vpp.class).child(BridgeDomains.class).child(BridgeDomain.class);
+ registry.addWriter(new GenericListWriter<>(bdId, new BridgeDomainCustomizer(jvpp, bdContext)));
+ // L2FibTable has no handlers
+ // L2FibEntry(handled after BridgeDomain) =
+ final InstanceIdentifier<L2FibEntry> l2FibEntryId = bdId.child(L2FibTable.class).child(L2FibEntry.class);
+ registry.addWriterAfter(
+ new GenericListWriter<>(l2FibEntryId, new L2FibEntryCustomizer(jvpp, bdContext, ifcContext)),
+ bdId);
+ }
+ }
}
diff --git a/v3po/v3po2vpp/src/main/yang/v3po2vpp.yang b/v3po/v3po2vpp/src/main/yang/v3po2vpp.yang
index cf89cbd51..8a5527248 100644
--- a/v3po/v3po2vpp/src/main/yang/v3po2vpp.yang
+++ b/v3po/v3po2vpp/src/main/yang/v3po2vpp.yang
@@ -149,7 +149,7 @@ module v3po2vpp {
identity vpp-honeycomb-writer {
base config:module-type;
- config:provided-service tapi:honeycomb-writer;
+ config:provided-service tapi:honeycomb-writer-factory;
}
augment "/config:modules/config:module/config:configuration" {
@@ -187,7 +187,7 @@ module v3po2vpp {
identity interfaces-honeycomb-writer {
base config:module-type;
- config:provided-service tapi:honeycomb-writer;
+ config:provided-service tapi:honeycomb-writer-factory;
}
augment "/config:modules/config:module/config:configuration" {
diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vpp/VppTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vpp/VppTest.java
deleted file mode 100644
index e713c623d..000000000
--- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vpp/VppTest.java
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.translate.v3po.vpp;
-
-import static io.fd.honeycomb.v3po.translate.v3po.test.ContextTestUtils.getMapping;
-import static io.fd.honeycomb.v3po.translate.v3po.test.ContextTestUtils.getMappingIid;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-import com.google.common.base.Optional;
-import com.google.common.collect.Iterators;
-import com.google.common.collect.Lists;
-import io.fd.honeycomb.v3po.translate.MappingContext;
-import io.fd.honeycomb.v3po.translate.ModificationCache;
-import io.fd.honeycomb.v3po.translate.impl.write.CompositeRootWriter;
-import io.fd.honeycomb.v3po.translate.util.write.DelegatingWriterRegistry;
-import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext;
-import io.fd.honeycomb.v3po.translate.write.WriteContext;
-import io.fd.honeycomb.v3po.translate.write.Writer;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
-import java.util.concurrent.ExecutionException;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.Mappings;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.Vpp;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.BridgeDomains;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.BridgeDomainsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomain;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomainBuilder;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.openvpp.jvpp.VppInvocationException;
-import org.openvpp.jvpp.dto.BridgeDomainAddDel;
-import org.openvpp.jvpp.dto.BridgeDomainAddDelReply;
-import org.openvpp.jvpp.future.FutureJVpp;
-
-public class VppTest {
-
- private static final byte ADD_OR_UPDATE_BD = 1;
- private static final byte ZERO = 0;
- private static final String BD_NAME = "bdn1";
- private static final String BD_CONTEXT_NAME = "test-instance";
-
- @Mock
- private FutureJVpp api;
- @Mock
- private WriteContext ctx;
- @Mock
- private MappingContext mappingContext;
-
- private DelegatingWriterRegistry rootRegistry;
- private CompositeRootWriter<Vpp> vppWriter;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- NamingContext bdContext = new NamingContext("generatedBdName", BD_CONTEXT_NAME);
- final ModificationCache toBeReturned = new ModificationCache();
- doReturn(toBeReturned).when(ctx).getModificationCache();
- doReturn(mappingContext).when(ctx).getMappingContext();
-
- vppWriter = VppUtils.getVppWriter(api, bdContext);
- rootRegistry = new DelegatingWriterRegistry(
- Collections.<Writer<? extends DataObject>>singletonList(vppWriter));
- }
-
- private BridgeDomains getBridgeDomains(String... name) {
- final List<BridgeDomain> bdmns = Lists.newArrayList();
- for (String s : name) {
- bdmns.add(new BridgeDomainBuilder()
- .setName(s)
- .setArpTermination(false)
- .setFlood(true)
- .setForward(false)
- .setLearn(true)
- .build());
- }
- return new BridgeDomainsBuilder()
- .setBridgeDomain(bdmns)
- .build();
- }
-
- private void whenBridgeDomainAddDelThenSuccess()
- throws ExecutionException, VppInvocationException, InterruptedException {
- final CompletionStage<BridgeDomainAddDelReply> replyCs = mock(CompletionStage.class);
- final CompletableFuture<BridgeDomainAddDelReply> replyFuture = mock(CompletableFuture.class);
- when(replyCs.toCompletableFuture()).thenReturn(replyFuture);
- final BridgeDomainAddDelReply reply = new BridgeDomainAddDelReply();
- when(replyFuture.get()).thenReturn(reply);
- when(api.bridgeDomainAddDel(any(BridgeDomainAddDel.class))).thenReturn(replyCs);
- }
-
- private void verifyBridgeDomainAddDel(final BridgeDomain bd, final int bdId) throws VppInvocationException {
- final byte arpTerm = BridgeDomainTestUtils.booleanToByte(bd.isArpTermination());
- final byte flood = BridgeDomainTestUtils.booleanToByte(bd.isFlood());
- final byte forward = BridgeDomainTestUtils.booleanToByte(bd.isForward());
- final byte learn = BridgeDomainTestUtils.booleanToByte(bd.isLearn());
- final byte uuf = BridgeDomainTestUtils.booleanToByte(bd.isUnknownUnicastFlood());
-
- // TODO adding equals methods for jvpp DTOs would make ArgumentCaptor usage obsolete
- ArgumentCaptor<BridgeDomainAddDel> argumentCaptor = ArgumentCaptor.forClass(BridgeDomainAddDel.class);
- verify(api).bridgeDomainAddDel(argumentCaptor.capture());
- final BridgeDomainAddDel actual = argumentCaptor.getValue();
- assertEquals(arpTerm, actual.arpTerm);
- assertEquals(flood, actual.flood);
- assertEquals(forward, actual.forward);
- assertEquals(learn, actual.learn);
- assertEquals(uuf, actual.uuFlood);
- assertEquals(ADD_OR_UPDATE_BD, actual.isAdd);
- assertEquals(bdId, actual.bdId);
- }
-
- private void verifyBridgeDomainDeleteWasInvoked(final int bdId) throws VppInvocationException {
- ArgumentCaptor<BridgeDomainAddDel> argumentCaptor = ArgumentCaptor.forClass(BridgeDomainAddDel.class);
- verify(api).bridgeDomainAddDel(argumentCaptor.capture());
- final BridgeDomainAddDel actual = argumentCaptor.getValue();
- assertEquals(bdId, actual.bdId);
- assertEquals(ZERO, actual.arpTerm);
- assertEquals(ZERO, actual.flood);
- assertEquals(ZERO, actual.forward);
- assertEquals(ZERO, actual.learn);
- assertEquals(ZERO, actual.uuFlood);
- assertEquals(ZERO, actual.isAdd);
- }
-
- @Test
- public void writeVppUsingRootRegistry() throws Exception {
- final int bdId = 1;
- final BridgeDomains bdn1 = getBridgeDomains(BD_NAME);
- whenBridgeDomainAddDelThenSuccess();
-
- // Returning no Mappings for "test-instance" makes bdContext.containsName() return false
- doReturn(Optional.absent()).when(mappingContext)
- .read(getMappingIid(BD_NAME, BD_CONTEXT_NAME).firstIdentifierOf(Mappings.class));
- // Make bdContext.containsIndex() return false
- doReturn(Optional.absent()).when(mappingContext)
- .read(getMappingIid(BD_NAME, BD_CONTEXT_NAME));
-
- rootRegistry.update(
- InstanceIdentifier.create(Vpp.class),
- null,
- new VppBuilder().setBridgeDomains(bdn1).build(),
- ctx);
-
- verifyBridgeDomainAddDel(Iterators.getOnlyElement(bdn1.getBridgeDomain().iterator()), bdId);
- }
-
- @Test
- public void writeVppUsingVppWriter() throws Exception {
- final int bdId = 1;
- final BridgeDomains bdn1 = getBridgeDomains(BD_NAME);
- whenBridgeDomainAddDelThenSuccess();
-
- // Returning no Mappings for "test-instance" makes bdContext.containsName() return false
- doReturn(Optional.absent()).when(mappingContext)
- .read(getMappingIid(BD_NAME, BD_CONTEXT_NAME).firstIdentifierOf(Mappings.class));
- // Make bdContext.containsIndex() return false
- doReturn(Optional.absent()).when(mappingContext)
- .read(getMappingIid(BD_NAME, BD_CONTEXT_NAME));
-
- vppWriter.update(InstanceIdentifier.create(Vpp.class),
- null,
- new VppBuilder().setBridgeDomains(bdn1).build(),
- ctx);
-
- verifyBridgeDomainAddDel(Iterators.getOnlyElement(bdn1.getBridgeDomain().iterator()), bdId);
- verify(mappingContext).put(getMappingIid(BD_NAME, BD_CONTEXT_NAME), getMapping(BD_NAME, 1).get());
- }
-
- @Test
- public void writeVppFromRoot() throws Exception {
- final BridgeDomains bdn1 = getBridgeDomains(BD_NAME);
- final int bdId = 1;
- final Vpp vpp = new VppBuilder().setBridgeDomains(bdn1).build();
- whenBridgeDomainAddDelThenSuccess();
-
- // Returning no Mappings for "test-instance" makes bdContext.containsName() return false
- doReturn(Optional.absent()).when(mappingContext)
- .read(getMappingIid(BD_NAME, BD_CONTEXT_NAME).firstIdentifierOf(Mappings.class));
- // Make bdContext.containsIndex() return false
- doReturn(Optional.absent()).when(mappingContext)
- .read(getMappingIid(BD_NAME, BD_CONTEXT_NAME));
-
- rootRegistry.update(Collections.emptyMap(),
- Collections.<InstanceIdentifier<?>, DataObject>singletonMap(InstanceIdentifier.create(Vpp.class),
- vpp), ctx);
-
- verifyBridgeDomainAddDel(Iterators.getOnlyElement(bdn1.getBridgeDomain().iterator()), bdId);
- }
-
- @Test
- public void deleteVpp() throws Exception {
- final BridgeDomains bdn1 = getBridgeDomains(BD_NAME);
- final int bdId = 1;
- whenBridgeDomainAddDelThenSuccess();
- doReturn(getMapping(BD_NAME, bdId)).when(mappingContext).read(getMappingIid(BD_NAME, BD_CONTEXT_NAME));
-
- rootRegistry.update(
- InstanceIdentifier.create(Vpp.class),
- new VppBuilder().setBridgeDomains(bdn1).build(),
- null,
- ctx);
-
- verifyBridgeDomainDeleteWasInvoked(bdId);
- }
-
- @Test
- public void updateVppNoActualChange() throws Exception {
- rootRegistry.update(
- InstanceIdentifier.create(Vpp.class),
- new VppBuilder().setBridgeDomains(getBridgeDomains(BD_NAME)).build(),
- new VppBuilder().setBridgeDomains(getBridgeDomains(BD_NAME)).build(),
- ctx);
-
- verifyZeroInteractions(api);
- }
-
- @Test
- public void writeUpdate() throws Exception {
- final int bdn1Id = 1;
- doReturn(getMapping(BD_NAME, bdn1Id)).when(mappingContext).read(getMappingIid(BD_NAME, BD_CONTEXT_NAME));
-
- final BridgeDomains domainsBefore = getBridgeDomains(BD_NAME);
- final BridgeDomain bdn1Before = domainsBefore.getBridgeDomain().get(0);
-
- final BridgeDomain bdn1After = new BridgeDomainBuilder(bdn1Before).setFlood(!bdn1Before.isFlood()).build();
- final BridgeDomains domainsAfter = new BridgeDomainsBuilder()
- .setBridgeDomain(Collections.singletonList(bdn1After))
- .build();
-
- whenBridgeDomainAddDelThenSuccess();
-
- rootRegistry.update(
- InstanceIdentifier.create(Vpp.class),
- new VppBuilder().setBridgeDomains(domainsBefore).build(),
- new VppBuilder().setBridgeDomains(domainsAfter).build(),
- ctx);
-
- // bdn1 is created with negated flood value
- verifyBridgeDomainAddDel(bdn1After, bdn1Id);
- }
-
- // TODO test unkeyed list
- // TODO test update of a child without dedicated writer
-} \ No newline at end of file
diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vpp/VppUtils.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vpp/VppUtils.java
deleted file mode 100644
index 4b3eb5adc..000000000
--- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vpp/VppUtils.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.translate.v3po.vpp;
-
-import io.fd.honeycomb.v3po.translate.impl.write.CompositeChildWriter;
-import io.fd.honeycomb.v3po.translate.impl.write.CompositeListWriter;
-import io.fd.honeycomb.v3po.translate.impl.write.CompositeRootWriter;
-import io.fd.honeycomb.v3po.translate.util.RWUtils;
-import io.fd.honeycomb.v3po.translate.util.write.NoopWriterCustomizer;
-import io.fd.honeycomb.v3po.translate.util.write.ReflexiveChildWriterCustomizer;
-import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext;
-import io.fd.honeycomb.v3po.translate.write.ChildWriter;
-import java.util.ArrayList;
-import java.util.List;
-import javax.annotation.Nonnull;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.Vpp;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.BridgeDomains;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomain;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomainKey;
-import org.opendaylight.yangtools.yang.binding.ChildOf;
-import org.openvpp.jvpp.future.FutureJVpp;
-
-final class VppUtils {
-
- public VppUtils() {}
-
- /**
- * Create root Vpp writer with all its children wired
- */
- static CompositeRootWriter<Vpp> getVppWriter(@Nonnull final FutureJVpp vppApi, final NamingContext bdContext) {
-
- final CompositeListWriter<BridgeDomain, BridgeDomainKey> bridgeDomainWriter = new CompositeListWriter<>(
- BridgeDomain.class,
- new BridgeDomainCustomizer(vppApi, bdContext));
-
- final ChildWriter<BridgeDomains> bridgeDomainsReader = new CompositeChildWriter<>(
- BridgeDomains.class,
- RWUtils.singletonChildWriterList(bridgeDomainWriter),
- new ReflexiveChildWriterCustomizer<BridgeDomains>());
-
- final List<ChildWriter<? extends ChildOf<Vpp>>> childWriters = new ArrayList<>();
- childWriters.add(bridgeDomainsReader);
-
- return new CompositeRootWriter<>(
- Vpp.class,
- childWriters,
- new NoopWriterCustomizer<Vpp>());
- }
-}
diff --git a/v3po/vpp-translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/v3po/util/NamingContext.java b/v3po/vpp-translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/v3po/util/NamingContext.java
index dc77106fb..f6e0e6d48 100644
--- a/v3po/vpp-translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/v3po/util/NamingContext.java
+++ b/v3po/vpp-translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/v3po/util/NamingContext.java
@@ -75,10 +75,6 @@ public final class NamingContext implements AutoCloseable {
public synchronized String getName(final int index, @Nonnull final MappingContext mappingContext) {
if (!containsName(index, mappingContext)) {
final String artificialName = getArtificialName(index);
- LOG.info("Assigning artificial name: {} for index: {}", artificialName, index);
- for (StackTraceElement stackTraceElement : Thread.currentThread().getStackTrace()) {
- LOG.error("{}", stackTraceElement.toString());
- }
addName(index, artificialName, mappingContext);
}