summaryrefslogtreecommitdiffstats
path: root/v3po
diff options
context:
space:
mode:
authorMaros Marsalek <mmarsale@cisco.com>2016-04-12 10:13:02 +0200
committerMaros Marsalek <mmarsale@cisco.com>2016-04-12 10:13:02 +0200
commit680ae1b1425ae7113382a758146197915faa9c5a (patch)
tree763a7baf48e20ff3f0ec6154da7751d2fa07b9f0 /v3po
parent3bfb62a82fd41327701a41d6ac6fae2cbab451e8 (diff)
HONEYCOMB-9: Exception handling for VPP APIs
Change-Id: Ic71a2ac3d01e88cb38596a24a12a7bf8ebf54da5 Signed-off-by: Marek Gradzki <mgradzki@cisco.com> Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
Diffstat (limited to 'v3po')
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppConfigDataTree.java12
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTree.java66
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppReaderRegistry.java7
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppWriterRegistry.java2
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/ReadFailedException.java60
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/VppApiInvocationException.java2
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/VppException.java10
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ChildVppReader.java4
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ListVppReader.java4
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ReaderRegistry.java9
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/VppReader.java5
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/AbstractCompositeVppReader.java13
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/CompositeChildVppReader.java6
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/CompositeListVppReader.java30
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/CompositeRootVppReader.java4
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/spi/RootVppReaderCustomizer.java15
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/util/DelegatingReaderRegistry.java23
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/VppWriter.java10
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/WriterRegistry.java93
-rw-r--r--v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/DelegatingWriterRegistry.java64
-rw-r--r--v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VPPConfigDataTreeTest.java84
-rw-r--r--v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTreeTest.java79
-rw-r--r--v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans/ReadFailedExceptionTest.java53
-rw-r--r--v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans/w/util/DelegatingWriterRegistryTest.java189
24 files changed, 663 insertions, 181 deletions
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppConfigDataTree.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppConfigDataTree.java
index f14bec3aa..9f34fcbd1 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppConfigDataTree.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppConfigDataTree.java
@@ -118,15 +118,13 @@ public final class VppConfigDataTree implements VppDataTree {
try {
e.revertChanges();
LOG.info("Changes successfully reverted");
- } catch (VppException | RuntimeException e2) {
- LOG.error("Failed to revert successful changes", e2);
+ } catch (WriterRegistry.Reverter.RevertFailedException revertFailedException) {
+ // fail with failed revert
+ LOG.error("Failed to revert successful changes", revertFailedException);
+ throw revertFailedException;
}
- // rethrow as we can't do anything more about it
- // FIXME we need to throw a different kind of exception here to differentiate between:
- // fail with success revert
- // fail with failed revert (this one needs to contain IDs of changes that were not reverted)
- throw e;
+ throw e; // fail with success revert
} catch (VppException e) {
LOG.error("Error while processing data change (before={}, after={})", nodesBefore, nodesAfter, e);
throw e;
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTree.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTree.java
index 3fddd108c..d0acd05a8 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTree.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTree.java
@@ -26,12 +26,12 @@ import com.google.common.collect.Collections2;
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.impl.trans.ReadFailedException;
import io.fd.honeycomb.v3po.impl.trans.r.ReaderRegistry;
import java.util.Collection;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@@ -52,7 +52,6 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
/**
* ReadableVppDataTree implementation for operational data.
*/
@@ -79,34 +78,44 @@ public final class VppOperationalDataTree implements ReadableVppDataTree {
}
@Override
- public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
+ public CheckedFuture<Optional<NormalizedNode<?, ?>>,
+ org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> read(
@Nonnull final YangInstanceIdentifier yangInstanceIdentifier) {
- if (checkNotNull(yangInstanceIdentifier).equals(YangInstanceIdentifier.EMPTY)) {
- LOG.debug("VppOperationalDataProxy.read(), yangInstanceIdentifier=ROOT");
- return Futures.immediateCheckedFuture(Optional.<NormalizedNode<?, ?>>of(readRoot()));
+ try {
+ if (checkNotNull(yangInstanceIdentifier).equals(YangInstanceIdentifier.EMPTY)) {
+ return Futures.immediateCheckedFuture(readRoot());
+ } else {
+ return Futures.immediateCheckedFuture(readNode(yangInstanceIdentifier));
+ }
+ } catch (ReadFailedException e) {
+ return Futures.immediateFailedCheckedFuture(
+ new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException(
+ "Failed to read VPP data", e));
}
+ }
- LOG.debug("VppOperationalDataProxy.read(), yangInstanceIdentifier={}", yangInstanceIdentifier);
+ private Optional<NormalizedNode<?, ?>> readNode(final YangInstanceIdentifier yangInstanceIdentifier)
+ throws ReadFailedException {
+ LOG.debug("VppOperationalDataTree.readNode(), yangInstanceIdentifier={}", yangInstanceIdentifier);
final InstanceIdentifier<?> path = serializer.fromYangInstanceIdentifier(yangInstanceIdentifier);
checkNotNull(path, "Invalid instance identifier %s. Cannot create BA equivalent.", yangInstanceIdentifier);
- LOG.debug("VppOperationalDataProxy.read(), path={}", path);
+ LOG.debug("VppOperationalDataTree.readNode(), path={}", path);
- final Optional<? extends DataObject> dataObject = readerRegistry.read(path);
+ final Optional<? extends DataObject> dataObject;
+ dataObject = readerRegistry.read(path);
if (dataObject.isPresent()) {
final NormalizedNode<?, ?> value = toNormalizedNodeFunction(path).apply(dataObject.get());
- return Futures.immediateCheckedFuture(Optional.<NormalizedNode<?, ?>>fromNullable(value));
+ return Optional.<NormalizedNode<?, ?>>fromNullable(value);
+ } else {
+ return Optional.absent();
}
-
- return Futures.immediateCheckedFuture(Optional.<NormalizedNode<?, ?>>absent());
}
- private DataSchemaNode getSchemaNode(final @Nonnull YangInstanceIdentifier yangInstanceIdentifier) {
- return globalContext.getDataChildByName(yangInstanceIdentifier.getLastPathArgument().getNodeType());
- }
+ private Optional<NormalizedNode<?, ?>> readRoot() throws ReadFailedException {
+ LOG.debug("VppOperationalDataTree.readRoot()");
- private NormalizedNode<?, ?> readRoot() {
final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> dataNodeBuilder =
Builders.containerBuilder()
.withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(SchemaContext.NAME));
@@ -121,16 +130,17 @@ public final class VppOperationalDataTree implements ReadableVppDataTree {
dataNodeBuilder.withChild((DataContainerChild<?, ?>) node);
}
- return dataNodeBuilder.build();
+ return Optional.<NormalizedNode<?, ?>>of(dataNodeBuilder.build());
}
private NormalizedNode<?, ?> wrapDataObjects(final YangInstanceIdentifier yangInstanceIdentifier,
- final InstanceIdentifier<? extends DataObject> instanceIdentifier,
- final Collection<? extends DataObject> dataObjects) {
+ final InstanceIdentifier<? extends DataObject> instanceIdentifier,
+ final Collection<? extends DataObject> dataObjects) {
final Collection<NormalizedNode<?, ?>> normalizedRootElements = Collections2
.transform(dataObjects, toNormalizedNodeFunction(instanceIdentifier));
- final DataSchemaNode schemaNode = getSchemaNode(yangInstanceIdentifier);
+ final DataSchemaNode schemaNode =
+ globalContext.getDataChildByName(yangInstanceIdentifier.getLastPathArgument().getNodeType());
if (schemaNode instanceof ListSchemaNode) {
// In case of a list, wrap all the values in a Mixin parent node
final ListSchemaNode listSchema = (ListSchemaNode) schemaNode;
@@ -142,19 +152,19 @@ public final class VppOperationalDataTree implements ReadableVppDataTree {
}
private static DataContainerChild<?, ?> wrapListIntoMixinNode(
- final Collection<NormalizedNode<?, ?>> normalizedRootElements, final ListSchemaNode listSchema) {
+ final Collection<NormalizedNode<?, ?>> normalizedRootElements, final ListSchemaNode listSchema) {
if (listSchema.getKeyDefinition().isEmpty()) {
final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> listBuilder =
- Builders.unkeyedListBuilder();
+ Builders.unkeyedListBuilder();
for (NormalizedNode<?, ?> normalizedRootElement : normalizedRootElements) {
listBuilder.withChild((UnkeyedListEntryNode) normalizedRootElement);
}
return listBuilder.build();
} else {
final CollectionNodeBuilder<MapEntryNode, ? extends MapNode> listBuilder =
- listSchema.isUserOrdered()
- ? Builders.orderedMapBuilder()
- : Builders.mapBuilder();
+ listSchema.isUserOrdered()
+ ? Builders.orderedMapBuilder()
+ : Builders.mapBuilder();
for (NormalizedNode<?, ?> normalizedRootElement : normalizedRootElements) {
listBuilder.withChild((MapEntryNode) normalizedRootElement);
@@ -168,11 +178,11 @@ public final class VppOperationalDataTree implements ReadableVppDataTree {
return new Function<DataObject, NormalizedNode<?, ?>>() {
@Override
public NormalizedNode<?, ?> apply(@Nullable final DataObject dataObject) {
- LOG.trace("VppOperationalDataProxy.toNormalizedNode(), path={}, dataObject={}", path, dataObject);
+ LOG.trace("VppOperationalDataTree.toNormalizedNode(), path={}, dataObject={}", path, dataObject);
final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry =
- serializer.toNormalizedNode(path, dataObject);
+ serializer.toNormalizedNode(path, dataObject);
- LOG.trace("VppOperationalDataProxy.toNormalizedNode(), normalizedNodeEntry={}", entry);
+ LOG.trace("VppOperationalDataTree.toNormalizedNode(), normalizedNodeEntry={}", entry);
return entry.getValue();
}
};
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppReaderRegistry.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppReaderRegistry.java
index df7b6568c..72d17b7e2 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppReaderRegistry.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppReaderRegistry.java
@@ -18,6 +18,7 @@ package io.fd.honeycomb.v3po.impl.data;
import com.google.common.base.Optional;
import com.google.common.collect.Multimap;
+import io.fd.honeycomb.v3po.impl.trans.ReadFailedException;
import io.fd.honeycomb.v3po.impl.trans.r.ChildVppReader;
import io.fd.honeycomb.v3po.impl.trans.r.ReaderRegistry;
import io.fd.honeycomb.v3po.impl.trans.r.VppReader;
@@ -95,13 +96,15 @@ public class VppReaderRegistry implements ReaderRegistry {
@Nonnull
@Override
- public Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> readAll() {
+ public Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> readAll()
+ throws io.fd.honeycomb.v3po.impl.trans.ReadFailedException {
return reader.readAll();
}
@Nonnull
@Override
- public Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id) {
+ public Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id)
+ throws ReadFailedException {
return reader.read(id);
}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppWriterRegistry.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppWriterRegistry.java
index 83186c2de..defcca47e 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppWriterRegistry.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppWriterRegistry.java
@@ -98,7 +98,7 @@ public class VppWriterRegistry implements WriterRegistry {
public void update(@Nonnull final Map<InstanceIdentifier<?>, DataObject> dataBefore,
@Nonnull final Map<InstanceIdentifier<?>, DataObject> dataAfter,
@Nonnull final WriteContext ctx)
- throws VppException, BulkUpdateException {
+ throws VppException {
writer.update(dataBefore, dataAfter, ctx);
}
}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/ReadFailedException.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/ReadFailedException.java
new file mode 100644
index 000000000..4da8b0e73
--- /dev/null
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/ReadFailedException.java
@@ -0,0 +1,60 @@
+/*
+ * 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.impl.trans;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Thrown when Vpp reader or customizer is not able to read data for the given id.
+ */
+public class ReadFailedException extends VppException {
+
+ private final InstanceIdentifier<?> failedId;
+
+ /**
+ * Constructs an ReadFailedException given data id and exception cause.
+ *
+ * @param failedId instance identifier of the data object that could not be read
+ * @param cause the cause of read failure
+ */
+ public ReadFailedException(@Nonnull final InstanceIdentifier<?> failedId, final Throwable cause) {
+ super("Failed to read " + failedId, cause);
+ this.failedId = checkNotNull(failedId, "failedId should not be null");
+ }
+
+ /**
+ * Constructs an ReadFailedException given data id.
+ *
+ * @param failedId instance identifier of the data object that could not be read
+ */
+ public ReadFailedException(@Nonnull final InstanceIdentifier<?> failedId) {
+ this(failedId, null);
+ }
+
+ /**
+ * Returns id of the data object that could not be read.
+ *
+ * @return data object instance identifier
+ */
+ @Nonnull
+ public InstanceIdentifier<?> getFailedId() {
+ return failedId;
+ }
+}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/VppApiInvocationException.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/VppApiInvocationException.java
index 0bb7c2b19..042c627ef 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/VppApiInvocationException.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/VppApiInvocationException.java
@@ -21,7 +21,7 @@ import com.google.common.base.Preconditions;
import javax.annotation.Nonnull;
/**
- * Throws when Vpp jAPI method invocation failed.
+ * Thrown when Vpp jAPI method invocation failed.
*/
@Beta
public class VppApiInvocationException extends VppException {
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/VppException.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/VppException.java
index aa18ae705..aadeaa996 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/VppException.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/VppException.java
@@ -19,7 +19,7 @@ package io.fd.honeycomb.v3po.impl.trans;
import com.google.common.annotations.Beta;
/**
- * Base exception for Vpp writers
+ * Base exception for Vpp translation layer
*/
@Beta
public class VppException extends Exception {
@@ -28,11 +28,11 @@ public class VppException extends Exception {
super(s);
}
- public VppException(final String s, final Throwable throwable) {
- super(s, throwable);
+ public VppException(final String s, final Throwable cause) {
+ super(s, cause);
}
- public VppException(final Throwable throwable) {
- super(throwable);
+ public VppException(final Throwable cause) {
+ super(cause);
}
}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ChildVppReader.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ChildVppReader.java
index 7f4d6fc4b..aad3080d4 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ChildVppReader.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ChildVppReader.java
@@ -17,6 +17,7 @@
package io.fd.honeycomb.v3po.impl.trans.r;
import com.google.common.annotations.Beta;
+import io.fd.honeycomb.v3po.impl.trans.ReadFailedException;
import javax.annotation.Nonnull;
import org.opendaylight.yangtools.concepts.Builder;
import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -38,9 +39,10 @@ public interface ChildVppReader<C extends DataObject> extends VppReader<C> {
* determine the exact position within more complex subtrees.
* @param parentBuilder Builder of parent DataObject. Objects read on this level (if any) must be placed into the
* parent builder.
+ * @throws ReadFailedException if read was unsuccessful
*/
void read(@Nonnull final InstanceIdentifier<? extends DataObject> id,
- @Nonnull final Builder<? extends DataObject> parentBuilder);
+ @Nonnull final Builder<? extends DataObject> parentBuilder) throws ReadFailedException;
}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ListVppReader.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ListVppReader.java
index 8a7f29cb3..ce392c6bc 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ListVppReader.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ListVppReader.java
@@ -17,6 +17,7 @@
package io.fd.honeycomb.v3po.impl.trans.r;
import com.google.common.annotations.Beta;
+import io.fd.honeycomb.v3po.impl.trans.ReadFailedException;
import java.util.List;
import javax.annotation.Nonnull;
import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -38,7 +39,8 @@ public interface ListVppReader<D extends DataObject & Identifiable<K>, K extends
* @param id Wildcarded identifier of list managed by this reader
*
* @return List of all entries in this list
+ * @throws ReadFailedException if read was unsuccessful
*/
@Nonnull
- List<D> readList(@Nonnull final InstanceIdentifier<D> id);
+ List<D> readList(@Nonnull final InstanceIdentifier<D> id) throws ReadFailedException;
}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ReaderRegistry.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ReaderRegistry.java
index 8c592a699..6a9937679 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ReaderRegistry.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ReaderRegistry.java
@@ -18,6 +18,7 @@ package io.fd.honeycomb.v3po.impl.trans.r;
import com.google.common.annotations.Beta;
import com.google.common.collect.Multimap;
+import io.fd.honeycomb.v3po.impl.trans.ReadFailedException;
import javax.annotation.Nonnull;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@@ -29,11 +30,13 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
public interface ReaderRegistry extends VppReader<DataObject> {
/**
- * Performs read on all registered root readers and merges the results into a Multimap.
- * Keys represent identifiers for root DataObjects from the data tree modeled by YANG.
+ * Performs read on all registered root readers and merges the results into a Multimap. Keys represent identifiers
+ * for root DataObjects from the data tree modeled by YANG.
*
* @return multimap that preserves deterministic iteration order across non-distinct key values
+ * @throws ReadFailedException if read was unsuccessful
*/
@Nonnull
- Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> readAll();
+ Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> readAll()
+ throws ReadFailedException;
}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/VppReader.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/VppReader.java
index 1cc76a3a4..02189e42d 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/VppReader.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/VppReader.java
@@ -18,6 +18,7 @@ package io.fd.honeycomb.v3po.impl.trans.r;
import com.google.common.annotations.Beta;
import com.google.common.base.Optional;
+import io.fd.honeycomb.v3po.impl.trans.ReadFailedException;
import io.fd.honeycomb.v3po.impl.trans.SubtreeManager;
import javax.annotation.Nonnull;
import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -42,8 +43,10 @@ public interface VppReader<D extends DataObject> extends SubtreeManager<D> {
* identifiers pointing below node managed by this reader, it's reader's responsibility to filter out the
* right node or to delegate the read to a child reader.
* @return List of DataObjects identified by id. If the ID points to a single node, it will be wrapped in a list
+ * @throws ReadFailedException if read was unsuccessful
*/
@Nonnull
- Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id);
+ Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id) throws
+ ReadFailedException;
}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/AbstractCompositeVppReader.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/AbstractCompositeVppReader.java
index 61f318b35..061cfc9f3 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/AbstractCompositeVppReader.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/AbstractCompositeVppReader.java
@@ -22,6 +22,7 @@ import com.google.common.annotations.Beta;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
+import io.fd.honeycomb.v3po.impl.trans.ReadFailedException;
import io.fd.honeycomb.v3po.impl.trans.r.ChildVppReader;
import io.fd.honeycomb.v3po.impl.trans.r.VppReader;
import io.fd.honeycomb.v3po.impl.trans.util.ReflectionUtils;
@@ -68,7 +69,8 @@ abstract class AbstractCompositeVppReader<D extends DataObject, B extends Builde
/**
* @param id {@link InstanceIdentifier} pointing to current node. In case of keyed list, key must be present.
*/
- protected Optional<D> readCurrent(final InstanceIdentifier<D> id) {
+ protected Optional<D> readCurrent(final InstanceIdentifier<D> id) throws
+ ReadFailedException {
LOG.debug("{}: Reading current: {}", this, id);
final B builder = getBuilder(id);
// Cache empty value to determine if anything has changed later TODO cache in a field
@@ -101,7 +103,8 @@ abstract class AbstractCompositeVppReader<D extends DataObject, B extends Builde
@Nonnull
@Override
@SuppressWarnings("unchecked")
- public Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id) {
+ public Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id)
+ throws ReadFailedException {
LOG.trace("{}: Reading : {}", this, id);
if (id.getTargetType().equals(getManagedDataObjectType().getTargetType())) {
return readCurrent((InstanceIdentifier<D>) id);
@@ -110,7 +113,8 @@ abstract class AbstractCompositeVppReader<D extends DataObject, B extends Builde
}
}
- private Optional<? extends DataObject> readSubtree(final InstanceIdentifier<? extends DataObject> id) {
+ private Optional<? extends DataObject> readSubtree(final InstanceIdentifier<? extends DataObject> id)
+ throws ReadFailedException {
LOG.debug("{}: Reading subtree: {}", this, id);
final Class<? extends DataObject> next = VppRWUtils.getNextId(id, getManagedDataObjectType()).getType();
final ChildVppReader<? extends ChildOf<D>> vppReader = childReaders.get(next);
@@ -139,7 +143,8 @@ abstract class AbstractCompositeVppReader<D extends DataObject, B extends Builde
* @param id {@link InstanceIdentifier} pointing to current node. In case of keyed list, key must be present.
* @param builder Builder object for current node where the read attributes must be placed
*/
- protected abstract void readCurrentAttributes(final InstanceIdentifier<D> id, B builder);
+ protected abstract void readCurrentAttributes(final InstanceIdentifier<D> id, B builder) throws
+ ReadFailedException;
/**
* Return new instance of a builder object for current node
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/CompositeChildVppReader.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/CompositeChildVppReader.java
index a64a72bc4..f18a5b38a 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/CompositeChildVppReader.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/CompositeChildVppReader.java
@@ -18,6 +18,7 @@ package io.fd.honeycomb.v3po.impl.trans.r.impl;
import com.google.common.annotations.Beta;
import com.google.common.base.Optional;
+import io.fd.honeycomb.v3po.impl.trans.ReadFailedException;
import io.fd.honeycomb.v3po.impl.trans.r.ChildVppReader;
import io.fd.honeycomb.v3po.impl.trans.r.impl.spi.ChildVppReaderCustomizer;
import io.fd.honeycomb.v3po.impl.trans.util.VppRWUtils;
@@ -78,7 +79,7 @@ public final class CompositeChildVppReader<C extends DataObject, B extends Build
@Override
public final void read(@Nonnull final InstanceIdentifier<? extends DataObject> parentId,
- @Nonnull final Builder<? extends DataObject> parentBuilder) {
+ @Nonnull final Builder<? extends DataObject> parentBuilder) throws ReadFailedException {
final Optional<C> read = readCurrent(VppRWUtils.appendTypeToId(parentId, getManagedDataObjectType()));
if(read.isPresent()) {
@@ -87,7 +88,8 @@ public final class CompositeChildVppReader<C extends DataObject, B extends Build
}
@Override
- protected void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id, @Nonnull final B builder) {
+ protected void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id, @Nonnull final B builder)
+ throws ReadFailedException {
customizer.readCurrentAttributes(id, builder);
}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/CompositeListVppReader.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/CompositeListVppReader.java
index 7e3d8452e..a9ca3e788 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/CompositeListVppReader.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/CompositeListVppReader.java
@@ -20,6 +20,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.annotations.Beta;
import com.google.common.base.Optional;
+import io.fd.honeycomb.v3po.impl.trans.ReadFailedException;
import io.fd.honeycomb.v3po.impl.trans.r.ChildVppReader;
import io.fd.honeycomb.v3po.impl.trans.r.ListVppReader;
import io.fd.honeycomb.v3po.impl.trans.r.impl.spi.ListVppReaderCustomizer;
@@ -39,17 +40,16 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Composite implementation of {@link ChildVppReader} able to place the read result into
- * parent builder object intended for list node type.
+ * Composite implementation of {@link ChildVppReader} able to place the read result into parent builder object intended
+ * for list node type.
*
- * This reader checks if the IDs are wildcarded in which case it performs read of all
- * list entries. In case the ID has a key, it reads only the specified value.
+ * This reader checks if the IDs are wildcarded in which case it performs read of all list entries. In case the ID has a
+ * key, it reads only the specified value.
*/
@Beta
@ThreadSafe
public final class CompositeListVppReader<C extends DataObject & Identifiable<K>, K extends Identifier<C>, B extends Builder<C>>
- extends AbstractCompositeVppReader<C, B> implements ChildVppReader<C>, ListVppReader<C, K>
-{
+ extends AbstractCompositeVppReader<C, B> implements ChildVppReader<C>, ListVppReader<C, K> {
private static final Logger LOG = LoggerFactory.getLogger(CompositeListVppReader.class);
@@ -59,10 +59,9 @@ public final class CompositeListVppReader<C extends DataObject & Identifiable<K>
* Create new {@link CompositeListVppReader}
*
* @param managedDataObjectType Class object for managed data type. Must come from a list node type.
- * @param childReaders Child nodes(container, list) readers
- * @param augReaders Child augmentations readers
- * @param customizer Customizer instance to customize this generic reader
- *
+ * @param childReaders Child nodes(container, list) readers
+ * @param augReaders Child augmentations readers
+ * @param customizer Customizer instance to customize this generic reader
*/
public CompositeListVppReader(@Nonnull final Class<C> managedDataObjectType,
@Nonnull final List<ChildVppReader<? extends ChildOf<C>>> childReaders,
@@ -87,12 +86,12 @@ public final class CompositeListVppReader<C extends DataObject & Identifiable<K>
public CompositeListVppReader(@Nonnull final Class<C> managedDataObjectType,
@Nonnull final ListVppReaderCustomizer<C, K, B> customizer) {
this(managedDataObjectType, VppRWUtils.<C>emptyChildReaderList(), VppRWUtils.<C>emptyAugReaderList(),
- customizer);
+ customizer);
}
@Override
public void read(@Nonnull final InstanceIdentifier<? extends DataObject> id,
- @Nonnull final Builder<? extends DataObject> parentBuilder) {
+ @Nonnull final Builder<? extends DataObject> parentBuilder) throws ReadFailedException {
// Create ID pointing to current node
final InstanceIdentifier<C> currentId = VppRWUtils.appendTypeToId(id, getManagedDataObjectType());
// Read all, since current ID is definitely wildcarded
@@ -102,7 +101,7 @@ public final class CompositeListVppReader<C extends DataObject & Identifiable<K>
@Override
@Nonnull
- public List<C> readList(@Nonnull final InstanceIdentifier<C> id) {
+ public List<C> readList(@Nonnull final InstanceIdentifier<C> id) throws ReadFailedException {
LOG.trace("{}: Reading all list entries", this);
final List<K> allIds = customizer.getAllIds(id);
LOG.debug("{}: Reading list entries for: {}", this, allIds);
@@ -110,7 +109,7 @@ public final class CompositeListVppReader<C extends DataObject & Identifiable<K>
final ArrayList<C> allEntries = new ArrayList<>(allIds.size());
for (K key : allIds) {
final InstanceIdentifier.IdentifiableItem<C, K> currentBdItem =
- VppRWUtils.getCurrentIdItem(id, key);
+ VppRWUtils.getCurrentIdItem(id, key);
final InstanceIdentifier<C> keyedId = VppRWUtils.replaceLastInId(id, currentBdItem);
final Optional<C> read = readCurrent(keyedId);
final DataObject singleItem = read.get();
@@ -121,7 +120,8 @@ public final class CompositeListVppReader<C extends DataObject & Identifiable<K>
}
@Override
- protected void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id, @Nonnull final B builder) {
+ protected void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id, @Nonnull final B builder)
+ throws ReadFailedException {
customizer.readCurrentAttributes(id, builder);
}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/CompositeRootVppReader.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/CompositeRootVppReader.java
index c87600b26..d5d82e7dd 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/CompositeRootVppReader.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/CompositeRootVppReader.java
@@ -17,6 +17,7 @@
package io.fd.honeycomb.v3po.impl.trans.r.impl;
import com.google.common.annotations.Beta;
+import io.fd.honeycomb.v3po.impl.trans.ReadFailedException;
import io.fd.honeycomb.v3po.impl.trans.r.ChildVppReader;
import io.fd.honeycomb.v3po.impl.trans.r.VppReader;
import io.fd.honeycomb.v3po.impl.trans.r.impl.spi.RootVppReaderCustomizer;
@@ -76,7 +77,8 @@ public final class CompositeRootVppReader<C extends DataObject, B extends Builde
}
@Override
- protected void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id, @Nonnull final B builder) {
+ protected void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id, @Nonnull final B builder)
+ throws ReadFailedException {
customizer.readCurrentAttributes(id, builder);
}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/spi/RootVppReaderCustomizer.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/spi/RootVppReaderCustomizer.java
index a09ed0488..299e94367 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/spi/RootVppReaderCustomizer.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/impl/spi/RootVppReaderCustomizer.java
@@ -17,6 +17,7 @@
package io.fd.honeycomb.v3po.impl.trans.r.impl.spi;
import com.google.common.annotations.Beta;
+import io.fd.honeycomb.v3po.impl.trans.ReadFailedException;
import javax.annotation.Nonnull;
import org.opendaylight.yangtools.concepts.Builder;
import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -31,16 +32,20 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@Beta
public interface RootVppReaderCustomizer<C extends DataObject, B extends Builder<C>> {
- // TODO add (un)checked, well defined exception here to indicate issues in the customizer
-
/**
- * Create new builder that will be used to build read value
+ * Creates new builder that will be used to build read value.
*/
@Nonnull
B getBuilder(@Nonnull final InstanceIdentifier<C> id);
+
/**
- * Add current data (identified by id) to the provided builder
+ * Adds current data (identified by id) to the provided builder.
+ *
+ * @param id id of current data object
+ * @param builder builder for creating read value
+ * @throws ReadFailedException if read was unsuccessful
*/
- void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id, @Nonnull final B builder);
+ void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id, @Nonnull final B builder) throws
+ ReadFailedException;
}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/util/DelegatingReaderRegistry.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/util/DelegatingReaderRegistry.java
index 0777425b2..20524073e 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/util/DelegatingReaderRegistry.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/util/DelegatingReaderRegistry.java
@@ -22,6 +22,7 @@ import com.google.common.base.Optional;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Multimap;
+import io.fd.honeycomb.v3po.impl.trans.ReadFailedException;
import io.fd.honeycomb.v3po.impl.trans.r.ListVppReader;
import io.fd.honeycomb.v3po.impl.trans.r.ReaderRegistry;
import io.fd.honeycomb.v3po.impl.trans.r.VppReader;
@@ -36,8 +37,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Simple reader registry able to perform and aggregated read (ROOT read) on top of all
- * provided readers. Also able to delegate a specific read to one of the delegate readers.
+ * Simple reader registry able to perform and aggregated read (ROOT read) on top of all provided readers. Also able to
+ * delegate a specific read to one of the delegate readers.
*
* This could serve as a utility to hold & hide all available readers in upper layers.
*/
@@ -58,7 +59,8 @@ public final class DelegatingReaderRegistry implements ReaderRegistry {
@Override
@Nonnull
- public Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> readAll() {
+ public Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> readAll()
+ throws ReadFailedException {
LOG.debug("Reading from all delegates: {}", this);
LOG.trace("Reading from all delegates: {}", rootReaders.values());
@@ -66,15 +68,15 @@ public final class DelegatingReaderRegistry implements ReaderRegistry {
for (VppReader<? extends DataObject> rootReader : rootReaders.values()) {
LOG.debug("Reading from delegate: {}", rootReader);
- if(rootReader instanceof ListVppReader) {
+ if (rootReader instanceof ListVppReader) {
final List<? extends DataObject> listEntries =
- ((ListVppReader) rootReader).readList(rootReader.getManagedDataObjectType());
- if(!listEntries.isEmpty()) {
+ ((ListVppReader) rootReader).readList(rootReader.getManagedDataObjectType());
+ if (!listEntries.isEmpty()) {
objects.putAll(rootReader.getManagedDataObjectType(), listEntries);
}
} else {
final Optional<? extends DataObject> read = rootReader.read(rootReader.getManagedDataObjectType());
- if(read.isPresent()) {
+ if (read.isPresent()) {
objects.putAll(rootReader.getManagedDataObjectType(), Collections.singletonList(read.get()));
}
}
@@ -85,12 +87,13 @@ public final class DelegatingReaderRegistry implements ReaderRegistry {
@Nonnull
@Override
- public Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id) {
+ public Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id)
+ throws ReadFailedException {
final InstanceIdentifier.PathArgument first = checkNotNull(
- Iterables.getFirst(id.getPathArguments(), null), "Empty id");
+ Iterables.getFirst(id.getPathArguments(), null), "Empty id");
final VppReader<? extends DataObject> vppReader = rootReaders.get(first.getType());
checkNotNull(vppReader,
- "Unable to read %s. Missing reader. Current readers for: %s", id, rootReaders.keySet());
+ "Unable to read %s. Missing reader. Current readers for: %s", id, rootReaders.keySet());
LOG.debug("Reading from delegate: {}", vppReader);
return vppReader.read(id);
}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/VppWriter.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/VppWriter.java
index f8a49a271..d338fafa2 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/VppWriter.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/VppWriter.java
@@ -25,7 +25,8 @@ import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
/**
- * Base VPP writer, responsible for translation between DataObjects and VPP APIs. Handling all update operations(create, update, delete)
+ * Base VPP writer, responsible for translation between DataObjects and VPP APIs. Handling all update operations(create,
+ * update, delete)
*
* @param <D> Specific DataObject derived type, that is handled by this writer
*/
@@ -35,10 +36,11 @@ public interface VppWriter<D extends DataObject> extends SubtreeManager<D> {
/**
* Handle update operation. U from CRUD.
*
- * @param id Identifier(from root) of data being written
+ * @param id Identifier(from root) of data being written
* @param dataBefore Old data
- * @param dataAfter New, updated data
- * @param ctx Write context enabling writer to get information about candidate data as well as current data
+ * @param dataAfter New, updated data
+ * @param ctx Write context enabling writer to get information about candidate data as well as current data
+ * @throws VppException if update failed
*/
void update(@Nonnull final InstanceIdentifier<? extends DataObject> id,
@Nullable final DataObject dataBefore,
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/WriterRegistry.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/WriterRegistry.java
index 4b09ff29e..26c294b30 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/WriterRegistry.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/WriterRegistry.java
@@ -16,8 +16,12 @@
package io.fd.honeycomb.v3po.impl.trans.w;
+import static com.google.common.base.Preconditions.checkNotNull;
+
import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
import io.fd.honeycomb.v3po.impl.trans.VppException;
+import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -33,23 +37,51 @@ public interface WriterRegistry extends VppWriter<DataObject> {
* Performs bulk update
*
* @throws BulkUpdateException in case bulk update fails
+ * @throws VppException 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 VppException, BulkUpdateException;
+ @Nonnull final WriteContext ctx) throws VppException;
+ /**
+ * Thrown when bulk update failed.
+ */
@Beta
- public class BulkUpdateException extends VppException {
+ class BulkUpdateException extends VppException {
- private final Revert runnable;
+ private final Reverter reverter;
+ private final InstanceIdentifier<?> failedId; // TODO change to VppDataModification
+
+ /**
+ * 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
+ */
+ public BulkUpdateException(@Nonnull final InstanceIdentifier<?> failedId, @Nonnull final Reverter reverter,
+ final Throwable cause) {
+ super("Bulk update failed at " + failedId, cause);
+ this.failedId = checkNotNull(failedId, "failedId should not be null");
+ this.reverter = checkNotNull(reverter, "reverter should not be null");
+ }
- public BulkUpdateException(final InstanceIdentifier<?> id, final RuntimeException e, final Revert runnable) {
- super("Bulk edit failed at " + id, e);
- this.runnable = runnable;
+ /**
+ * Reverts changes that were successfully applied during bulk update before failure occurred.
+ *
+ * @throws Reverter.RevertFailedException if revert fails
+ */
+ public void revertChanges() throws Reverter.RevertFailedException {
+ reverter.revert();
}
- public void revertChanges() throws VppException {
- runnable.revert();
+ /**
+ * Returns instance identifier of the data object that caused bulk update to fail.
+ *
+ * @return data object's instance identifier
+ */
+ @Nonnull
+ public InstanceIdentifier<?> getFailedId() {
+ return failedId;
}
}
@@ -57,8 +89,47 @@ public interface WriterRegistry extends VppWriter<DataObject> {
* Abstraction over revert mechanism in cast of a bulk update failure
*/
@Beta
- public interface Revert {
+ interface Reverter {
- public void revert() throws VppException;
+ /**
+ * Reverts changes that were successfully applied during bulk update before failure occurred. Changes are
+ * reverted in reverse order they were applied.
+ *
+ * @throws RevertFailedException if not all of applied changes were successfully reverted
+ */
+ void revert() throws RevertFailedException;
+
+ /**
+ * Thrown when some of the changes applied during bulk update were not reverted.
+ */
+ @Beta
+ class RevertFailedException extends VppException {
+
+ // TODO change to list of VppDataModifications to make debugging easier
+ private final List<InstanceIdentifier<?>> notRevertedChanges;
+
+ /**
+ * Constructs an 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,
+ final Throwable cause) {
+ super(cause);
+ checkNotNull(notRevertedChanges, "notRevertedChanges should not be null");
+ this.notRevertedChanges = ImmutableList.copyOf(notRevertedChanges);
+ }
+
+ /**
+ * Returns the list of changes that were not reverted.
+ *
+ * @return list of changes that were not reverted
+ */
+ @Nonnull
+ public List<InstanceIdentifier<?>> getNotRevertedChanges() {
+ return notRevertedChanges;
+ }
+ }
}
-}
+} \ No newline at end of file
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/DelegatingWriterRegistry.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/DelegatingWriterRegistry.java
index d20e69a8b..cc1188e17 100644
--- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/DelegatingWriterRegistry.java
+++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/DelegatingWriterRegistry.java
@@ -29,8 +29,8 @@ import io.fd.honeycomb.v3po.impl.trans.util.VppRWUtils;
import io.fd.honeycomb.v3po.impl.trans.w.VppWriter;
import io.fd.honeycomb.v3po.impl.trans.w.WriteContext;
import io.fd.honeycomb.v3po.impl.trans.w.WriterRegistry;
+import java.util.LinkedList;
import java.util.List;
-import java.util.ListIterator;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -40,8 +40,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Simple writer registry able to perform and aggregated read (ROOT write) on top of all
- * provided writers. Also able to delegate a specific read to one of the delegate writers.
+ * Simple writer registry able to perform and aggregated read (ROOT write) on top of all provided writers. Also able to
+ * delegate a specific read to one of the delegate writers.
*
* This could serve as a utility to hold & hide all available writers in upper layers.
*/
@@ -50,12 +50,12 @@ public final class DelegatingWriterRegistry implements WriterRegistry {
private static final Logger LOG = LoggerFactory.getLogger(DelegatingWriterRegistry.class);
private static final Function<InstanceIdentifier<?>, Class<? extends DataObject>> ID_TO_CLASS =
- new Function<InstanceIdentifier<?>, Class<? extends DataObject>>() {
- @Override
- public Class<? extends DataObject> apply(final InstanceIdentifier<?> input) {
- return input.getTargetType();
- }
- };
+ new Function<InstanceIdentifier<?>, Class<? extends DataObject>>() {
+ @Override
+ public Class<? extends DataObject> apply(final InstanceIdentifier<?> input) {
+ return input.getTargetType();
+ }
+ };
private final Map<Class<? extends DataObject>, VppWriter<? extends DataObject>> rootWriters;
@@ -84,10 +84,10 @@ public final class DelegatingWriterRegistry implements WriterRegistry {
@Nullable final DataObject dataAfter,
@Nonnull final WriteContext ctx) throws VppException {
final InstanceIdentifier.PathArgument first = checkNotNull(
- Iterables.getFirst(id.getPathArguments(), null), "Empty id");
+ Iterables.getFirst(id.getPathArguments(), null), "Empty id");
final VppWriter<? extends DataObject> vppWriter = rootWriters.get(first.getType());
checkNotNull(vppWriter,
- "Unable to write %s. Missing writer. Current writers for: %s", id, rootWriters.keySet());
+ "Unable to write %s. Missing writer. Current writers for: %s", id, rootWriters.keySet());
vppWriter.update(id, dataBefore, dataAfter, ctx);
}
@@ -101,7 +101,7 @@ public final class DelegatingWriterRegistry implements WriterRegistry {
final List<InstanceIdentifier<?>> processedNodes = Lists.newArrayList();
for (Map.Entry<Class<? extends DataObject>, VppWriter<? extends DataObject>> rootWriterEntry : rootWriters
- .entrySet()) {
+ .entrySet()) {
final InstanceIdentifier<? extends DataObject> id = rootWriterEntry.getValue().getManagedDataObjectType();
@@ -109,7 +109,7 @@ public final class DelegatingWriterRegistry implements WriterRegistry {
final DataObject dataAfter = nodesAfter.get(id);
// No change to current writer
- if(dataBefore == null && dataAfter == null) {
+ if (dataBefore == null && dataAfter == null) {
continue;
}
@@ -118,31 +118,32 @@ public final class DelegatingWriterRegistry implements WriterRegistry {
try {
update(id, dataBefore, dataAfter, ctx);
processedNodes.add(id);
- } catch (RuntimeException e) {
- LOG.error("Error while processing data change of: {} (before={}, after={})", id, dataBefore, dataAfter, e);
- throw new BulkUpdateException(id, e, new RevertImpl(this, processedNodes, nodesBefore, nodesAfter, ctx));
+ } 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) {
checkArgument(rootWriters.keySet().containsAll(Collections2.transform(nodesBefore.keySet(), ID_TO_CLASS)),
- "Unable to handle all changes. Missing dedicated writers for: %s",
- Sets.difference(nodesBefore.keySet(), rootWriters.keySet()));
+ "Unable to handle all changes. Missing dedicated writers for: %s",
+ Sets.difference(nodesBefore.keySet(), rootWriters.keySet()));
}
- private static final class RevertImpl implements Revert {
+ 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;
- public RevertImpl(final WriterRegistry delegatingWriterRegistry,
- final List<InstanceIdentifier<?>> processedNodes,
- final Map<InstanceIdentifier<?>, DataObject> nodesBefore,
- final Map<InstanceIdentifier<?>, DataObject> nodesAfter, 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;
@@ -151,12 +152,11 @@ public final class DelegatingWriterRegistry implements WriterRegistry {
}
@Override
- public void revert() throws VppException {
-
- final ListIterator<InstanceIdentifier<?>> iterator = processedNodes.listIterator(processedNodes.size());
+ public void revert() throws RevertFailedException {
+ final LinkedList<InstanceIdentifier<?>> notReverted = new LinkedList<>(processedNodes);
- while (iterator.hasPrevious()) {
- final InstanceIdentifier<?> node = iterator.previous();
+ while (notReverted.size() > 0) {
+ final InstanceIdentifier<?> node = notReverted.peekLast();
LOG.debug("ChangesProcessor.revertChanges() processing node={}", node);
final DataObject dataBefore = nodesBefore.get(node);
@@ -165,9 +165,11 @@ public final class DelegatingWriterRegistry implements WriterRegistry {
// revert a change by invoking writer with reordered arguments
try {
delegatingWriterRegistry.update(node, dataAfter, dataBefore, ctx);
- } catch (RuntimeException e) {
- throw new RuntimeException();
+ notReverted.removeLast(); // change was successfully reverted
+ } catch (Exception e) {
+ throw new RevertFailedException(notReverted, e);
}
+
}
}
}
diff --git a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VPPConfigDataTreeTest.java b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VPPConfigDataTreeTest.java
index 963df735b..8719a5356 100644
--- a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VPPConfigDataTreeTest.java
+++ b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VPPConfigDataTreeTest.java
@@ -44,8 +44,6 @@ import org.mockito.Mock;
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.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.Vxlan;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.QName;
@@ -58,7 +56,6 @@ 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.DataTreeModification;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
public class VPPConfigDataTreeTest {
@@ -89,7 +86,8 @@ public class VPPConfigDataTreeTest {
doReturn(node).when(snapshot).readNode(path);
final VppDataTreeSnapshot vppDataTreeSnapshot = proxy.takeSnapshot();
- final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future = vppDataTreeSnapshot.read(path);
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future =
+ vppDataTreeSnapshot.read(path);
verify(dataTree).takeSnapshot();
verify(snapshot).readNode(path);
@@ -132,9 +130,9 @@ public class VPPConfigDataTreeTest {
// Verify all changes were processed:
verify(vppWriter).update(
- mapOf(dataBefore, Ethernet.class),
- mapOf(dataAfter, Ethernet.class),
- any(WriteContext.class));
+ mapOf(dataBefore, Ethernet.class),
+ mapOf(dataAfter, Ethernet.class),
+ any(WriteContext.class));
// Verify modification was validated
verify(dataTree).validate(modification);
@@ -142,7 +140,8 @@ public class VPPConfigDataTreeTest {
private Map<InstanceIdentifier<?>, DataObject> mapOf(final DataObject dataBefore, final Class<Ethernet> type) {
return eq(
- Collections.<InstanceIdentifier<?>, DataObject>singletonMap(InstanceIdentifier.create(type), dataBefore));
+ Collections.<InstanceIdentifier<?>, DataObject>singletonMap(InstanceIdentifier.create(type),
+ dataBefore));
}
private DataObject mockDataObject(final String name, final Class<? extends DataObject> classToMock) {
@@ -154,79 +153,77 @@ public class VPPConfigDataTreeTest {
@Test
public void testCommitUndoSuccessful() throws Exception {
// Prepare data changes:
- final DataObject dataBefore1 = mockDataObject("before", Ethernet.class);
- final DataObject dataAfter1 = mockDataObject("after", Ethernet.class);
-
- final DataObject dataBefore2 = mockDataObject("before", Vxlan.class);
- final DataObject dataAfter2 = mockDataObject("after", Vxlan.class);
+ final DataObject dataBefore = mockDataObject("before", Ethernet.class);
+ final DataObject dataAfter = mockDataObject("after", Ethernet.class);
- final DataObject dataBefore3 = mockDataObject("before", L2.class);
- final DataObject dataAfter3 = mockDataObject("after", L2.class);
+ final WriterRegistry.Reverter reverter = mock(WriterRegistry.Reverter.class);
- // reject third applied change
- final WriterRegistry.Revert revert = mock(WriterRegistry.Revert.class);
- doThrow(new WriterRegistry.BulkUpdateException(InstanceIdentifier.create(L2.class), new RuntimeException(),
- revert)).when(vppWriter).update(anyMap(), anyMap(), any(WriteContext.class));
+ // Fail on update:
+ final VppException failedOnUpdateException = new VppException("update failed");
+ doThrow(new WriterRegistry.BulkUpdateException(InstanceIdentifier.create(Ethernet.class), reverter,
+ failedOnUpdateException)).when(vppWriter).update(anyMap(), anyMap(), any(WriteContext.class));
// Prepare modification:
final DataTreeCandidateNode rootNode = mockRootNode();
// data before:
- final ContainerNode nodeBefore = mockContainerNode(dataBefore1, dataBefore2, dataBefore3);
+ final ContainerNode nodeBefore = mockContainerNode(dataBefore);
when(rootNode.getDataBefore()).thenReturn(Optional.<NormalizedNode<?, ?>>fromNullable(nodeBefore));
// data after:
- final ContainerNode nodeAfter = mockContainerNode(dataAfter1, dataAfter2, dataAfter3);
+ final ContainerNode nodeAfter = mockContainerNode(dataAfter);
when(rootNode.getDataAfter()).thenReturn(Optional.<NormalizedNode<?, ?>>fromNullable(nodeAfter));
// Run the test
try {
proxy.commit(modification);
- } catch (DataValidationFailedException | VppException e) {
+ } catch (WriterRegistry.BulkUpdateException e) {
verify(vppWriter).update(anyMap(), anyMap(), any(WriteContext.class));
- verify(revert).revert();
+ verify(reverter).revert();
+ assertEquals(failedOnUpdateException, e.getCause());
return;
}
- fail("DataValidationFailedException was expected");
+ fail("WriterRegistry.BulkUpdateException was expected");
}
@Test
public void testCommitUndoFailed() throws Exception {
// Prepare data changes:
- final DataObject dataBefore1 = mockDataObject("before", Ethernet.class);
- final DataObject dataAfter1 = mockDataObject("after", Ethernet.class);
+ final DataObject dataBefore = mockDataObject("before", Ethernet.class);
+ final DataObject dataAfter = mockDataObject("after", Ethernet.class);
- final DataObject dataBefore2 = mockDataObject("before", Vxlan.class);
- final DataObject dataAfter2 = mockDataObject("after", Vxlan.class);
+ final WriterRegistry.Reverter reverter = mock(WriterRegistry.Reverter.class);
- final DataObject dataBefore3 = mockDataObject("before", L2.class);
- final DataObject dataAfter3 = mockDataObject("after", L2.class);
+ // Fail on update:
+ doThrow(new WriterRegistry.BulkUpdateException(InstanceIdentifier.create(Ethernet.class), reverter,
+ new VppException("update failed"))).when(vppWriter).update(anyMap(), anyMap(), any(WriteContext.class));
- // reject third applied change
- final WriterRegistry.Revert revert = mock(WriterRegistry.Revert.class);
- doThrow(new RuntimeException("revert failed")).when(revert).revert();
- doThrow(new WriterRegistry.BulkUpdateException(InstanceIdentifier.create(L2.class), new RuntimeException(),
- revert)).when(vppWriter).update(anyMap(), anyMap(), any(WriteContext.class));
+ // Fail on revert:
+ final VppException failedOnRevertException = new VppException("update failed");
+ final WriterRegistry.Reverter.RevertFailedException revertFailedException =
+ new WriterRegistry.Reverter.RevertFailedException(Collections.<InstanceIdentifier<?>>emptyList(),
+ failedOnRevertException);
+ doThrow(revertFailedException).when(reverter).revert();
// Prepare modification:
final DataTreeCandidateNode rootNode = mockRootNode();
// data before:
- final ContainerNode nodeBefore = mockContainerNode(dataBefore1, dataBefore2, dataBefore3);
+ final ContainerNode nodeBefore = mockContainerNode(dataBefore);
when(rootNode.getDataBefore()).thenReturn(Optional.<NormalizedNode<?, ?>>fromNullable(nodeBefore));
// data after:
- final ContainerNode nodeAfter = mockContainerNode(dataAfter1, dataAfter2, dataAfter3);
+ final ContainerNode nodeAfter = mockContainerNode(dataAfter);
when(rootNode.getDataAfter()).thenReturn(Optional.<NormalizedNode<?, ?>>fromNullable(nodeAfter));
// Run the test
try {
proxy.commit(modification);
- } catch (DataValidationFailedException | VppException e) {
- // FIXME the behavior with successful and failed revert is the same from outside world
+ } catch (WriterRegistry.Reverter.RevertFailedException e) {
verify(vppWriter).update(anyMap(), anyMap(), any(WriteContext.class));
- verify(revert).revert();
+ verify(reverter).revert();
+ assertEquals(failedOnRevertException, e.getCause());
return;
}
- fail("DataValidationFailedException was expected");
+ fail("RevertFailedException was expected");
}
private DataTreeCandidateNode mockRootNode() {
@@ -256,9 +253,9 @@ public class VPPConfigDataTreeTest {
when(child.getIdentifier()).thenReturn(mock(YangInstanceIdentifier.PathArgument.class));
list.add(child);
- final Map.Entry entry = mock(Map.Entry.class);
+ final Map.Entry entry = mock(Map.Entry.class);
final Class<? extends DataObject> implementedInterface =
- (Class<? extends DataObject>) modification.getImplementedInterface();
+ (Class<? extends DataObject>) modification.getImplementedInterface();
final InstanceIdentifier<?> id = InstanceIdentifier.create(implementedInterface);
doReturn(id).when(entry).getKey();
@@ -267,5 +264,4 @@ public class VPPConfigDataTreeTest {
}
return node;
}
-
}
diff --git a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTreeTest.java b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTreeTest.java
index f4b2faa3e..8939f8f52 100644
--- a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTreeTest.java
+++ b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTreeTest.java
@@ -16,16 +16,22 @@
package io.fd.honeycomb.v3po.impl.data;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
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.Iterables;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.CheckedFuture;
import io.fd.honeycomb.v3po.impl.trans.r.ReaderRegistry;
import java.util.Map;
@@ -34,10 +40,12 @@ import org.junit.Test;
import org.mockito.Mock;
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.VppState;
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.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
@@ -65,16 +73,16 @@ public class VppOperationalDataTreeTest {
public void setUp() {
initMocks(this);
operationalData = new VppOperationalDataTree(serializer, globalContext, reader);
+ doReturn(schemaNode).when(globalContext).getDataChildByName(any(QName.class));
}
@Test
- public void testRead() throws Exception {
+ public void testReadNode() throws Exception {
final YangInstanceIdentifier yangId = mock(YangInstanceIdentifier.class);
final YangInstanceIdentifier.PathArgument pArg = mock(YangInstanceIdentifier.PathArgument.class);
doReturn(pArg).when(yangId).getLastPathArgument();
doReturn(QName.create("namespace", "2012-12-12", "local")).when(pArg).getNodeType();
- doReturn(schemaNode).when(globalContext).getDataChildByName(any(QName.class));
doReturn(id).when(serializer).fromYangInstanceIdentifier(yangId);
final DataObject dataObject = mock(DataObject.class);
@@ -91,6 +99,69 @@ public class VppOperationalDataTreeTest {
final Optional<NormalizedNode<?, ?>> result = future.get();
assertTrue(result.isPresent());
assertEquals(expectedValue, result.get());
+ }
+
+ @Test
+ public void testReadNonExistingNode() throws Exception {
+ final YangInstanceIdentifier yangId = mock(YangInstanceIdentifier.class);
+ doReturn(id).when(serializer).fromYangInstanceIdentifier(yangId);
+ doReturn(Optional.absent()).when(reader).read(id);
+
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future = operationalData.read(yangId);
+
+ verify(serializer).fromYangInstanceIdentifier(yangId);
+ verify(reader).read(id);
+ final Optional<NormalizedNode<?, ?>> result = future.get();
+ assertFalse(result.isPresent());
+ }
+
+ @Test
+ public void testReadFailed() throws Exception{
+ doThrow(io.fd.honeycomb.v3po.impl.trans.ReadFailedException.class).when(reader).readAll();
+
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future =
+ operationalData.read( YangInstanceIdentifier.EMPTY);
+
+ try {
+ future.checkedGet();
+ } catch (ReadFailedException e) {
+ assertTrue(e.getCause() instanceof io.fd.honeycomb.v3po.impl.trans.ReadFailedException);
+ return;
+ }
+ fail("ReadFailedException was expected");
+ }
+
+ @Test
+ public void testReadRootWithOneNonListElement() throws Exception {
+ // Prepare data
+ final InstanceIdentifier<VppState> vppStateII = InstanceIdentifier.create(VppState.class);
+ final VppState vppState = mock(VppState.class);
+ Multimap<InstanceIdentifier<?>, DataObject> dataObjects = LinkedListMultimap.create();
+ dataObjects.put(vppStateII, vppState);
+ doReturn(dataObjects).when(reader).readAll();
+
+ // Init serializer
+ final YangInstanceIdentifier vppYangId = YangInstanceIdentifier.builder().node(VppState.QNAME).build();
+ when(serializer.toYangInstanceIdentifier(vppStateII)).thenReturn(vppYangId);
+ when(serializer.toNormalizedNode(vppStateII, vppState)).thenReturn(entry);
+ final DataContainerChild<?, ?> vppStateContainer = mock(DataContainerChild.class);
+ doReturn(vppStateContainer).when(entry).getValue();
+ doReturn(vppYangId.getLastPathArgument()).when(vppStateContainer).getIdentifier();
+
+ // Read root
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future =
+ operationalData.read(YangInstanceIdentifier.EMPTY);
+
+ verify(reader).readAll();
+ verify(serializer).toYangInstanceIdentifier(vppStateII);
+ verify(serializer).toNormalizedNode(vppStateII, vppState);
+
+ // Check the result is an ContainerNode with only one child
+ final Optional<NormalizedNode<?, ?>> result = future.get();
+ assertTrue(result.isPresent());
+ final ContainerNode rootNode = (ContainerNode) result.get();
+ assertEquals(SchemaContext.NAME, rootNode.getIdentifier().getNodeType());
+ assertEquals(vppStateContainer, Iterables.getOnlyElement(rootNode.getValue()));
}
} \ No newline at end of file
diff --git a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans/ReadFailedExceptionTest.java b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans/ReadFailedExceptionTest.java
new file mode 100644
index 000000000..b815434a8
--- /dev/null
+++ b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans/ReadFailedExceptionTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.impl.trans;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.bridge.domains.BridgeDomain;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.bridge.domains.bridge.domain.Interface;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class ReadFailedExceptionTest {
+
+ @Test
+ public void testInstantiation() {
+ final InstanceIdentifier<BridgeDomain> id = InstanceIdentifier.create(BridgeDomain.class);
+ ReadFailedException e = new ReadFailedException(id);
+ assertEquals(id, e.getFailedId());
+ assertNull(e.getCause());
+ assertTrue(e.getMessage().contains(id.toString()));
+ }
+
+ @Test
+ public void testInstantiationWithCause() {
+ final InstanceIdentifier<Interface> id = InstanceIdentifier.create(Interface.class);
+ final RuntimeException cause = new RuntimeException();
+ ReadFailedException e = new ReadFailedException(id, cause);
+ assertEquals(id, e.getFailedId());
+ assertEquals(cause, e.getCause());
+ assertTrue(e.getMessage().contains(id.toString()));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testInstantiationFailed() {
+ new ReadFailedException(null);
+ }
+} \ No newline at end of file
diff --git a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans/w/util/DelegatingWriterRegistryTest.java b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans/w/util/DelegatingWriterRegistryTest.java
new file mode 100644
index 000000000..26f63f40f
--- /dev/null
+++ b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans/w/util/DelegatingWriterRegistryTest.java
@@ -0,0 +1,189 @@
+/*
+ * 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.impl.trans.w.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.impl.trans.VppException;
+import io.fd.honeycomb.v3po.impl.trans.w.VppWriter;
+import io.fd.honeycomb.v3po.impl.trans.w.WriteContext;
+import io.fd.honeycomb.v3po.impl.trans.w.WriterRegistry;
+import io.fd.honeycomb.v3po.impl.trans.w.impl.CompositeRootVppWriter;
+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 CompositeRootVppWriter<Vpp> vppWriter;
+ private CompositeRootVppWriter<VppState> vppStateWriter;
+ private CompositeRootVppWriter<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> CompositeRootVppWriter<D> mockWriter(Class<D> clazz) {
+ final CompositeRootVppWriter<D> mock = (CompositeRootVppWriter<D>) Mockito.mock(CompositeRootVppWriter.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);
+ vppWriter = mockWriter(Vpp.class);
+ vppStateWriter = mockWriter(VppState.class);
+ interfacesWriter = mockWriter(Interfaces.class);
+
+ final List<VppWriter<? extends DataObject>> writers = new ArrayList<>();
+ writers.add(vppWriter);
+ 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
+ doThrow(new VppException("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(vppWriter).update(vppId, dataBefore1, dataAfter1, ctx);
+ verify(vppStateWriter).update(vppStateId, dataBefore2, dataAfter2, ctx);
+
+ // Try to revert changes
+ e.revertChanges();
+
+ // Check revert was successful
+ verify(vppWriter).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 VppException("vpp failed")).when(interfacesWriter)
+ .update(interfaceId, dataBefore3, dataAfter3, ctx);
+
+ // Fail on the second revert
+ doThrow(new VppException("vpp failed again")).when(vppWriter)
+ .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(vppWriter).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(vppWriter).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