From 5947a575539402344e450fd34b03f555b84523be Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Tue, 12 Apr 2016 10:13:02 +0200 Subject: HONEYCOMB-9: Exception handling for VPP APIs Change-Id: Ic71a2ac3d01e88cb38596a24a12a7bf8ebf54da5 Signed-off-by: Marek Gradzki Signed-off-by: Maros Marsalek --- .../v3po/impl/data/VppConfigDataTree.java | 12 ++- .../v3po/impl/data/VppOperationalDataTree.java | 66 ++++++++------- .../v3po/impl/data/VppReaderRegistry.java | 7 +- .../v3po/impl/data/VppWriterRegistry.java | 2 +- .../v3po/impl/trans/ReadFailedException.java | 60 ++++++++++++++ .../v3po/impl/trans/VppApiInvocationException.java | 2 +- .../fd/honeycomb/v3po/impl/trans/VppException.java | 10 +-- .../v3po/impl/trans/r/ChildVppReader.java | 4 +- .../honeycomb/v3po/impl/trans/r/ListVppReader.java | 4 +- .../v3po/impl/trans/r/ReaderRegistry.java | 9 ++- .../fd/honeycomb/v3po/impl/trans/r/VppReader.java | 5 +- .../trans/r/impl/AbstractCompositeVppReader.java | 13 ++- .../impl/trans/r/impl/CompositeChildVppReader.java | 6 +- .../impl/trans/r/impl/CompositeListVppReader.java | 30 +++---- .../impl/trans/r/impl/CompositeRootVppReader.java | 4 +- .../trans/r/impl/spi/RootVppReaderCustomizer.java | 15 ++-- .../trans/r/util/DelegatingReaderRegistry.java | 23 +++--- .../fd/honeycomb/v3po/impl/trans/w/VppWriter.java | 10 ++- .../v3po/impl/trans/w/WriterRegistry.java | 93 +++++++++++++++++++--- .../trans/w/util/DelegatingWriterRegistry.java | 64 +++++++-------- 20 files changed, 306 insertions(+), 133 deletions(-) create mode 100644 v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/ReadFailedException.java (limited to 'v3po/impl/src/main/java/io/fd') 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>, ReadFailedException> read( + public CheckedFuture>, + 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.>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> 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 dataObject = readerRegistry.read(path); + final Optional dataObject; + dataObject = readerRegistry.read(path); if (dataObject.isPresent()) { final NormalizedNode value = toNormalizedNodeFunction(path).apply(dataObject.get()); - return Futures.immediateCheckedFuture(Optional.>fromNullable(value)); + return Optional.>fromNullable(value); + } else { + return Optional.absent(); } - - return Futures.immediateCheckedFuture(Optional.>absent()); } - private DataSchemaNode getSchemaNode(final @Nonnull YangInstanceIdentifier yangInstanceIdentifier) { - return globalContext.getDataChildByName(yangInstanceIdentifier.getLastPathArgument().getNodeType()); - } + private Optional> readRoot() throws ReadFailedException { + LOG.debug("VppOperationalDataTree.readRoot()"); - private NormalizedNode readRoot() { final DataContainerNodeAttrBuilder 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.>of(dataNodeBuilder.build()); } private NormalizedNode wrapDataObjects(final YangInstanceIdentifier yangInstanceIdentifier, - final InstanceIdentifier instanceIdentifier, - final Collection dataObjects) { + final InstanceIdentifier instanceIdentifier, + final Collection dataObjects) { final Collection> 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> normalizedRootElements, final ListSchemaNode listSchema) { + final Collection> normalizedRootElements, final ListSchemaNode listSchema) { if (listSchema.getKeyDefinition().isEmpty()) { final CollectionNodeBuilder listBuilder = - Builders.unkeyedListBuilder(); + Builders.unkeyedListBuilder(); for (NormalizedNode normalizedRootElement : normalizedRootElements) { listBuilder.withChild((UnkeyedListEntryNode) normalizedRootElement); } return listBuilder.build(); } else { final CollectionNodeBuilder 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>() { @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> 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, ? extends DataObject> readAll() { + public Multimap, ? extends DataObject> readAll() + throws io.fd.honeycomb.v3po.impl.trans.ReadFailedException { return reader.readAll(); } @Nonnull @Override - public Optional read(@Nonnull final InstanceIdentifier id) { + public Optional read(@Nonnull final InstanceIdentifier 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, DataObject> dataBefore, @Nonnull final Map, 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 extends VppReader { * 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 id, - @Nonnull final Builder parentBuilder); + @Nonnull final Builder 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, 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 readList(@Nonnull final InstanceIdentifier id); + List readList(@Nonnull final InstanceIdentifier 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 { /** - * 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, ? extends DataObject> readAll(); + Multimap, ? 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 extends SubtreeManager { * 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 read(@Nonnull final InstanceIdentifier id); + Optional read(@Nonnull final InstanceIdentifier 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 readCurrent(final InstanceIdentifier id) { + protected Optional readCurrent(final InstanceIdentifier 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 read(@Nonnull final InstanceIdentifier id) { + public Optional read(@Nonnull final InstanceIdentifier id) + throws ReadFailedException { LOG.trace("{}: Reading : {}", this, id); if (id.getTargetType().equals(getManagedDataObjectType().getTargetType())) { return readCurrent((InstanceIdentifier) id); @@ -110,7 +113,8 @@ abstract class AbstractCompositeVppReader readSubtree(final InstanceIdentifier id) { + private Optional readSubtree(final InstanceIdentifier id) + throws ReadFailedException { LOG.debug("{}: Reading subtree: {}", this, id); final Class next = VppRWUtils.getNextId(id, getManagedDataObjectType()).getType(); final ChildVppReader> vppReader = childReaders.get(next); @@ -139,7 +143,8 @@ abstract class AbstractCompositeVppReader id, B builder); + protected abstract void readCurrentAttributes(final InstanceIdentifier 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 parentId, - @Nonnull final Builder parentBuilder) { + @Nonnull final Builder parentBuilder) throws ReadFailedException { final Optional read = readCurrent(VppRWUtils.appendTypeToId(parentId, getManagedDataObjectType())); if(read.isPresent()) { @@ -87,7 +88,8 @@ public final class CompositeChildVppReader id, @Nonnull final B builder) { + protected void readCurrentAttributes(@Nonnull final InstanceIdentifier 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, K extends Identifier, B extends Builder> - extends AbstractCompositeVppReader implements ChildVppReader, ListVppReader -{ + extends AbstractCompositeVppReader implements ChildVppReader, ListVppReader { private static final Logger LOG = LoggerFactory.getLogger(CompositeListVppReader.class); @@ -59,10 +59,9 @@ public final class CompositeListVppReader * 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 managedDataObjectType, @Nonnull final List>> childReaders, @@ -87,12 +86,12 @@ public final class CompositeListVppReader public CompositeListVppReader(@Nonnull final Class managedDataObjectType, @Nonnull final ListVppReaderCustomizer customizer) { this(managedDataObjectType, VppRWUtils.emptyChildReaderList(), VppRWUtils.emptyAugReaderList(), - customizer); + customizer); } @Override public void read(@Nonnull final InstanceIdentifier id, - @Nonnull final Builder parentBuilder) { + @Nonnull final Builder parentBuilder) throws ReadFailedException { // Create ID pointing to current node final InstanceIdentifier currentId = VppRWUtils.appendTypeToId(id, getManagedDataObjectType()); // Read all, since current ID is definitely wildcarded @@ -102,7 +101,7 @@ public final class CompositeListVppReader @Override @Nonnull - public List readList(@Nonnull final InstanceIdentifier id) { + public List readList(@Nonnull final InstanceIdentifier id) throws ReadFailedException { LOG.trace("{}: Reading all list entries", this); final List allIds = customizer.getAllIds(id); LOG.debug("{}: Reading list entries for: {}", this, allIds); @@ -110,7 +109,7 @@ public final class CompositeListVppReader final ArrayList allEntries = new ArrayList<>(allIds.size()); for (K key : allIds) { final InstanceIdentifier.IdentifiableItem currentBdItem = - VppRWUtils.getCurrentIdItem(id, key); + VppRWUtils.getCurrentIdItem(id, key); final InstanceIdentifier keyedId = VppRWUtils.replaceLastInId(id, currentBdItem); final Optional read = readCurrent(keyedId); final DataObject singleItem = read.get(); @@ -121,7 +120,8 @@ public final class CompositeListVppReader } @Override - protected void readCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final B builder) { + protected void readCurrentAttributes(@Nonnull final InstanceIdentifier 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 id, @Nonnull final B builder) { + protected void readCurrentAttributes(@Nonnull final InstanceIdentifier 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> { - // 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 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 id, @Nonnull final B builder); + void readCurrentAttributes(@Nonnull final InstanceIdentifier 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, ? extends DataObject> readAll() { + public Multimap, ? 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 rootReader : rootReaders.values()) { LOG.debug("Reading from delegate: {}", rootReader); - if(rootReader instanceof ListVppReader) { + if (rootReader instanceof ListVppReader) { final List 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 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 read(@Nonnull final InstanceIdentifier id) { + public Optional read(@Nonnull final InstanceIdentifier id) + throws ReadFailedException { final InstanceIdentifier.PathArgument first = checkNotNull( - Iterables.getFirst(id.getPathArguments(), null), "Empty id"); + Iterables.getFirst(id.getPathArguments(), null), "Empty id"); final VppReader 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 Specific DataObject derived type, that is handled by this writer */ @@ -35,10 +36,11 @@ public interface VppWriter extends SubtreeManager { /** * 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 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 { * 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, DataObject> dataBefore, @Nonnull final Map, 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 { * 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> 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> 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> 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, Class> ID_TO_CLASS = - new Function, Class>() { - @Override - public Class apply(final InstanceIdentifier input) { - return input.getTargetType(); - } - }; + new Function, Class>() { + @Override + public Class apply(final InstanceIdentifier input) { + return input.getTargetType(); + } + }; private final Map, VppWriter> 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 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> processedNodes = Lists.newArrayList(); for (Map.Entry, VppWriter> rootWriterEntry : rootWriters - .entrySet()) { + .entrySet()) { final InstanceIdentifier 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, 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> processedNodes; private final Map, DataObject> nodesBefore; private final Map, DataObject> nodesAfter; private final WriteContext ctx; - public RevertImpl(final WriterRegistry delegatingWriterRegistry, - final List> processedNodes, - final Map, DataObject> nodesBefore, - final Map, DataObject> nodesAfter, final WriteContext ctx) { + ReverterImpl(final WriterRegistry delegatingWriterRegistry, + final List> processedNodes, + final Map, DataObject> nodesBefore, + final Map, 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> iterator = processedNodes.listIterator(processedNodes.size()); + public void revert() throws RevertFailedException { + final LinkedList> 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); } + } } } -- cgit 1.2.3-korg