diff options
author | Maros Marsalek <mmarsale@cisco.com> | 2016-05-17 09:10:39 +0200 |
---|---|---|
committer | Maros Marsalek <mmarsale@cisco.com> | 2016-05-23 09:24:12 +0000 |
commit | d54ea758da8dcf71d52727c4f01f87090c50bf2e (patch) | |
tree | bf86e49ad4899b50c97654ae144d4b2e8902d1b8 /v3po/data-impl | |
parent | 9e59a344c5a5b81fb7b7292184e849ad0fc9507c (diff) |
HONEYCOMB-61: Add BA broker for context data tree
With broker, context data can be accessed in a transactional
manner, same as config data
+ Renamed data-api concepts to not include DataTree
+ Renamed context related concepts to better distinguish between them
+ Now passing full ReadContext to read customizers
+ Naming context is backed by context data broker
Change-Id: I0b2876dd74a31a9ced7d9b5145672868e12f8b82
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
Diffstat (limited to 'v3po/data-impl')
17 files changed, 716 insertions, 355 deletions
diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTree.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTree.java deleted file mode 100644 index 1e118ff2a..000000000 --- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTree.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static com.google.common.base.Preconditions.checkNotNull; -import static io.fd.honeycomb.v3po.data.impl.DataTreeUtils.childrenFromNormalized; - -import com.google.common.base.Optional; -import com.google.common.util.concurrent.CheckedFuture; -import com.google.common.util.concurrent.Futures; -import io.fd.honeycomb.v3po.data.DataTreeSnapshot; -import io.fd.honeycomb.v3po.data.ModifiableDataTree; -import io.fd.honeycomb.v3po.data.ReadableDataTree; -import io.fd.honeycomb.v3po.translate.TranslationException; -import io.fd.honeycomb.v3po.translate.util.write.TransactionWriteContext; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import io.fd.honeycomb.v3po.translate.write.WriterRegistry; -import java.util.Collections; -import java.util.Map; -import javax.annotation.Nonnull; -import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; -import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; -import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * DataTree implementation for configuration data. - */ -public final class ConfigDataTree implements ModifiableDataTree { - - private static final Logger LOG = LoggerFactory.getLogger(ConfigDataTree.class); - - private final BindingNormalizedNodeSerializer serializer; - private final DataTree dataTree; - private final WriterRegistry writerRegistry; - public static final ReadableDataTree EMPTY_OPERATIONAL = new ReadableDataTree() { - @Override - public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read( - @Nonnull final YangInstanceIdentifier path) { - return Futures.immediateCheckedFuture(Optional.<NormalizedNode<?, ?>>absent()); - } - }; - - /** - * Creates configuration data tree instance. - * - * @param serializer service for serialization between Java Binding Data representation and NormalizedNode - * representation. - * @param dataTree data tree for configuration data representation - * @param writerRegistry service for translation between Java Binding Data and data provider, capable of performing - * bulk updates. - */ - public ConfigDataTree(@Nonnull final BindingNormalizedNodeSerializer serializer, - @Nonnull final DataTree dataTree, @Nonnull final WriterRegistry writerRegistry) { - this.serializer = checkNotNull(serializer, "serializer should not be null"); - this.dataTree = checkNotNull(dataTree, "dataTree should not be null"); - this.writerRegistry = checkNotNull(writerRegistry, "writerRegistry should not be null"); - } - - @Override - public DataTreeSnapshot takeSnapshot() { - return new ConfigSnapshot(dataTree.takeSnapshot()); - } - - @Override - public void modify(final DataTreeModification modification) - throws DataValidationFailedException, TranslationException { - LOG.debug("ConfigDataTree.modify"); - - dataTree.validate(modification); - - final DataTreeCandidate candidate = dataTree.prepare(modification); - - final DataTreeCandidateNode rootNode = candidate.getRootNode(); - final YangInstanceIdentifier rootPath = candidate.getRootPath(); - final Optional<NormalizedNode<?, ?>> normalizedDataBefore = rootNode.getDataBefore(); - final Optional<NormalizedNode<?, ?>> normalizedDataAfter = rootNode.getDataAfter(); - LOG.debug("ConfigDataTree.modify() rootPath={}, rootNode={}, dataBefore={}, dataAfter={}", - rootPath, rootNode, normalizedDataBefore, normalizedDataAfter); - - final Map<InstanceIdentifier<?>, DataObject> nodesBefore = extractNetconfData(normalizedDataBefore); - LOG.debug("ConfigDataTree.modify() extracted nodesBefore={}", nodesBefore.keySet()); - - final Map<InstanceIdentifier<?>, DataObject> nodesAfter = extractNetconfData(normalizedDataAfter); - LOG.debug("ConfigDataTree.modify() extracted nodesAfter={}", nodesAfter.keySet()); - - final DOMDataReadOnlyTransaction beforeTx = new ReadOnlyTransaction(EMPTY_OPERATIONAL, takeSnapshot()); - final ConfigSnapshot modificationSnapshot = new ConfigSnapshot(modification); - final DOMDataReadOnlyTransaction afterTx = new ReadOnlyTransaction(EMPTY_OPERATIONAL, modificationSnapshot); - try (final WriteContext ctx = new TransactionWriteContext(serializer, beforeTx, afterTx)) { - writerRegistry.update(nodesBefore, nodesAfter, ctx); - } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException e) { - LOG.warn("Failed to apply all changes", e); - LOG.info("Trying to revert successful changes for current transaction"); - - try { - e.revertChanges(); - LOG.info("Changes successfully reverted"); - } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException revertFailedException) { - // fail with failed revert - LOG.error("Failed to revert successful changes", revertFailedException); - throw revertFailedException; - } - - throw e; // fail with success revert - } catch (TranslationException e) { - LOG.error("Error while processing data change (before={}, after={})", nodesBefore, nodesAfter, e); - throw e; - } - - dataTree.commit(candidate); - } - - private Map<InstanceIdentifier<?>, DataObject> extractNetconfData( - final Optional<NormalizedNode<?, ?>> parentOptional) { - if (parentOptional.isPresent()) { - final DataContainerNode parent = (DataContainerNode) parentOptional.get(); - return childrenFromNormalized(parent, serializer); - } - return Collections.emptyMap(); - } - - private final static class ConfigSnapshot implements DataTreeSnapshot { - private final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot snapshot; - - ConfigSnapshot(@Nonnull final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot snapshot) { - this.snapshot = snapshot; - } - - @Override - public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read( - @Nonnull final YangInstanceIdentifier path) { - final Optional<NormalizedNode<?, ?>> node = snapshot.readNode(path); - if (LOG.isTraceEnabled() && node.isPresent()) { - LOG.trace("ConfigSnapshot.read: {}", node.get()); - } - return Futures.immediateCheckedFuture(node); - } - - @Override - public DataTreeModification newModification() { - return snapshot.newModification(); - } - } -} - - - diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java index a0b585143..c418ed332 100644 --- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java +++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java @@ -16,10 +16,13 @@ package io.fd.honeycomb.v3po.data.impl; -import com.google.common.base.Preconditions; -import io.fd.honeycomb.v3po.data.DataTreeSnapshot; -import io.fd.honeycomb.v3po.data.ModifiableDataTree; -import io.fd.honeycomb.v3po.data.ReadableDataTree; +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.honeycomb.v3po.data.DataModification; +import io.fd.honeycomb.v3po.data.ModifiableDataManager; +import io.fd.honeycomb.v3po.data.ReadableDataManager; +import java.io.Closeable; +import java.io.IOException; import java.util.Collections; import java.util.Map; import javax.annotation.Nonnull; @@ -42,45 +45,36 @@ import org.slf4j.LoggerFactory; * Data Broker which provides data transaction functionality for YANG capable data provider using {@link NormalizedNode} * data format. */ -public class DataBroker implements DOMDataBroker { +public class DataBroker implements DOMDataBroker, Closeable { private static final Logger LOG = LoggerFactory.getLogger(DataBroker.class); - private final ReadableDataTree operationalDataTree; - private final ModifiableDataTree configDataTree; + private final TransactionFactory transactionFactory; /** * Creates DataBroker instance. * - * @param operationalDataTree operational data - * @param configDataTree configuration data + * @param transactionFactory transaction producing factory */ - public DataBroker(@Nonnull final ReadableDataTree operationalDataTree, - @Nonnull final ModifiableDataTree configDataTree) { - this.operationalDataTree = - Preconditions.checkNotNull(operationalDataTree, "operationalDataTree should not be null"); - this.configDataTree = Preconditions.checkNotNull(configDataTree, "configDataTree should not be null"); - LOG.trace("DataBroker({}).init() operationalDataTree={}, configDataTree={}", this, operationalDataTree, configDataTree); + public DataBroker(final TransactionFactory transactionFactory) { + this.transactionFactory = transactionFactory; } @Override public DOMDataReadOnlyTransaction newReadOnlyTransaction() { LOG.trace("DataBroker({}).newReadOnlyTransaction()", this); - return new ReadOnlyTransaction(operationalDataTree, configDataTree.takeSnapshot()); + return transactionFactory.newReadOnlyTransaction(); } @Override public DOMDataReadWriteTransaction newReadWriteTransaction() { LOG.trace("DataBroker({}).newReadWriteTransaction()", this); - final DataTreeSnapshot configSnapshot = configDataTree.takeSnapshot(); - final DOMDataReadOnlyTransaction readOnlyTx = new ReadOnlyTransaction(operationalDataTree, configSnapshot); - final DOMDataWriteTransaction writeOnlyTx = new WriteTransaction(configDataTree, configSnapshot); - return new ReadWriteTransaction(readOnlyTx, writeOnlyTx); + return transactionFactory.newReadWriteTransaction(); } @Override public DOMDataWriteTransaction newWriteOnlyTransaction() { LOG.trace("DataBroker({}).newWriteOnlyTransaction()", this); - return new WriteTransaction(configDataTree); + return transactionFactory.newWriteOnlyTransaction(); } @Override @@ -101,6 +95,104 @@ public class DataBroker implements DOMDataBroker { public Map<Class<? extends DOMDataBrokerExtension>, DOMDataBrokerExtension> getSupportedExtensions() { return Collections.emptyMap(); } + + /** + * Create DataBroker for a modifiable config DT, but only readable Operational + */ + @Nonnull + public static DataBroker create(@Nonnull final ModifiableDataManager configDataTree, + @Nonnull final ReadableDataManager operationalDataTree) { + checkNotNull(operationalDataTree, "operationalDataTree should not be null"); + checkNotNull(configDataTree, "configDataTree should not be null"); + return new DataBroker(new MainPipelineTxFactory(configDataTree, operationalDataTree)); + } + + /** + * Create DataBroker for modifiable operational DT, but no support for config + */ + @Nonnull + public static DataBroker create(@Nonnull final ModifiableDataManager operationalDataTree) { + checkNotNull(operationalDataTree, "operationalDataTree should not be null"); + return new DataBroker(new ContextPipelineTxFactory(operationalDataTree)); + } + + @Override + public void close() throws IOException { + // NOOP + } + + /** + * Transaction provider factory to be used by {@link DataBroker} + */ + public interface TransactionFactory { + + DOMDataReadOnlyTransaction newReadOnlyTransaction(); + + DOMDataReadWriteTransaction newReadWriteTransaction(); + + DOMDataWriteTransaction newWriteOnlyTransaction(); + } + + /** + * Transaction factory specific for Honeycomb's main pipeline (config: read+write, operational: read-only) + */ + private static class MainPipelineTxFactory implements TransactionFactory { + private final ReadableDataManager operationalDataTree; + private final ModifiableDataManager configDataTree; + + MainPipelineTxFactory(@Nonnull final ModifiableDataManager configDataTree, + @Nonnull final ReadableDataManager operationalDataTree) { + this.operationalDataTree = operationalDataTree; + this.configDataTree = configDataTree; + } + + @Override + public DOMDataReadOnlyTransaction newReadOnlyTransaction() { + return ReadOnlyTransaction.create(configDataTree.newModification(), operationalDataTree); + } + + @Override + public DOMDataReadWriteTransaction newReadWriteTransaction() { + final DataModification configModification = configDataTree.newModification(); + return new ReadWriteTransaction( + ReadOnlyTransaction.create(configModification, operationalDataTree), + WriteTransaction.createConfigOnly(configModification)); + } + + @Override + public DOMDataWriteTransaction newWriteOnlyTransaction() { + return WriteTransaction.createConfigOnly(configDataTree.newModification()); + } + } + + /** + * Transaction factory specific for Honeycomb's context pipeline (config: none, operational: read+write) + */ + private static class ContextPipelineTxFactory implements TransactionFactory { + private final ModifiableDataManager operationalDataTree; + + ContextPipelineTxFactory(@Nonnull final ModifiableDataManager operationalDataTree) { + this.operationalDataTree = operationalDataTree; + } + + @Override + public DOMDataReadOnlyTransaction newReadOnlyTransaction() { + return ReadOnlyTransaction.createOperationalOnly(operationalDataTree); + } + + @Override + public DOMDataReadWriteTransaction newReadWriteTransaction() { + final DataModification dataModification = operationalDataTree.newModification(); + return new ReadWriteTransaction( + ReadOnlyTransaction.createOperationalOnly(dataModification), + WriteTransaction.createOperationalOnly(dataModification)); + } + + @Override + public DOMDataWriteTransaction newWriteOnlyTransaction() { + return WriteTransaction.createOperationalOnly(operationalDataTree.newModification()); + } + } } diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtils.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtils.java index de83a198b..5833459ea 100644 --- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtils.java +++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtils.java @@ -34,6 +34,7 @@ import org.slf4j.LoggerFactory; * Utility class for various operations on DataTree. */ final class DataTreeUtils { + private static final Logger LOG = LoggerFactory.getLogger(DataTreeUtils.class); private DataTreeUtils() { diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java new file mode 100644 index 000000000..2b9010747 --- /dev/null +++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.data.impl; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.util.concurrent.Futures.immediateCheckedFuture; +import static io.fd.honeycomb.v3po.data.impl.DataTreeUtils.childrenFromNormalized; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.v3po.data.DataModification; +import io.fd.honeycomb.v3po.data.ReadableDataManager; +import io.fd.honeycomb.v3po.translate.TranslationException; +import io.fd.honeycomb.v3po.translate.util.write.TransactionMappingContext; +import io.fd.honeycomb.v3po.translate.util.write.TransactionWriteContext; +import io.fd.honeycomb.v3po.translate.write.WriteContext; +import io.fd.honeycomb.v3po.translate.write.WriterRegistry; +import java.util.Collections; +import java.util.Map; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Extension of {@link ModifiableDataTreeManager} that propagates data changes to underlying writer layer before they + * are fully committed in the backing data tree. Data changes are propagated in BA format. + */ +public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager { + + private static final Logger LOG = LoggerFactory.getLogger(ModifiableDataTreeDelegator.class); + private static final ReadableDataManager EMPTY_OPERATIONAL = p -> immediateCheckedFuture(Optional.absent()); + + // TODO what to use instead of deprecated BindingNormalizedNodeSerializer ? + private final BindingNormalizedNodeSerializer serializer; + private final WriterRegistry writerRegistry; + private final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker; + + /** + * Creates configuration data tree instance. + * @param serializer service for serialization between Java Binding Data representation and NormalizedNode + * representation. + * @param dataTree data tree for configuration data representation + * @param writerRegistry service for translation between Java Binding Data and data provider, capable of performing + * @param contextBroker BA broker providing full access to mapping context data + */ + public ModifiableDataTreeDelegator(@Nonnull final BindingNormalizedNodeSerializer serializer, + @Nonnull final DataTree dataTree, + @Nonnull final WriterRegistry writerRegistry, + @Nonnull final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker) { + super(dataTree); + this.contextBroker = checkNotNull(contextBroker, "contextBroker should not be null"); + this.serializer = checkNotNull(serializer, "serializer should not be null"); + this.writerRegistry = checkNotNull(writerRegistry, "writerRegistry should not be null"); + } + + @Override + public DataModification newModification() { + return new ConfigSnapshot(super.newModification()); + } + + private final class ConfigSnapshot extends ModifiableDataTreeManager.ConfigSnapshot { + + private final DataModification untouchedModification; + + /** + * @param untouchedModification DataModification captured while this modification/snapshot was created. + * To be used later while invoking writers to provide them with before state + * (state without current modifications). + * It must be captured as close as possible to when current modification started. + */ + ConfigSnapshot(final DataModification untouchedModification) { + this.untouchedModification = untouchedModification; + } + + /** + * Pass the changes to underlying writer layer. + * Transform from BI to BA. + * Revert(Write data before to subtrees that have been successfully modified before failure) in case of failure. + */ + @Override + protected void processCandidate(final DataTreeCandidate candidate) + throws TranslationException { + final DataTreeCandidateNode rootNode = candidate.getRootNode(); + final YangInstanceIdentifier rootPath = candidate.getRootPath(); + final Optional<NormalizedNode<?, ?>> normalizedDataBefore = rootNode.getDataBefore(); + final Optional<NormalizedNode<?, ?>> normalizedDataAfter = rootNode.getDataAfter(); + LOG.debug("ConfigDataTree.modify() rootPath={}, rootNode={}, dataBefore={}, dataAfter={}", + rootPath, rootNode, normalizedDataBefore, normalizedDataAfter); + + final Map<InstanceIdentifier<?>, DataObject> nodesBefore = toBindingAware(normalizedDataBefore); + LOG.debug("ConfigDataTree.modify() extracted nodesBefore={}", nodesBefore.keySet()); + final Map<InstanceIdentifier<?>, DataObject> nodesAfter = toBindingAware(normalizedDataAfter); + LOG.debug("ConfigDataTree.modify() extracted nodesAfter={}", nodesAfter.keySet()); + + try (final WriteContext ctx = getTransactionWriteContext()) { + writerRegistry.update(nodesBefore, nodesAfter, ctx); + + final CheckedFuture<Void, TransactionCommitFailedException> contextUpdateResult = + ((TransactionMappingContext) ctx.getMappingContext()).submit(); + // Blocking on context data update + contextUpdateResult.checkedGet(); + + } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException e) { + LOG.warn("Failed to apply all changes", e); + LOG.info("Trying to revert successful changes for current transaction"); + + try { + e.revertChanges(); + LOG.info("Changes successfully reverted"); + } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException revertFailedException) { + // fail with failed revert + LOG.error("Failed to revert successful changes", revertFailedException); + throw revertFailedException; + } + + throw e; // fail with success revert + } catch (TransactionCommitFailedException e) { + // FIXME revert should probably occur when context is not written successfully, but can that even happen ? + final String msg = "Error while updating mapping context data"; + LOG.error(msg, e); + throw new TranslationException(msg, e); + } catch (TranslationException e) { + LOG.error("Error while processing data change (before={}, after={})", nodesBefore, nodesAfter, e); + throw e; + } + } + + private TransactionWriteContext getTransactionWriteContext() { + // Before Tx must use modification + final DOMDataReadOnlyTransaction beforeTx = ReadOnlyTransaction.create(untouchedModification, EMPTY_OPERATIONAL); + // After Tx must use current modification + final DOMDataReadOnlyTransaction afterTx = ReadOnlyTransaction.create(this, EMPTY_OPERATIONAL); + final TransactionMappingContext mappingContext = new TransactionMappingContext( + contextBroker.newReadWriteTransaction()); + return new TransactionWriteContext(serializer, beforeTx, afterTx, mappingContext); + } + + private Map<InstanceIdentifier<?>, DataObject> toBindingAware(final Optional<NormalizedNode<?, ?>> parentOptional) { + if (parentOptional.isPresent()) { + final DataContainerNode parent = (DataContainerNode) parentOptional.get(); + return childrenFromNormalized(parent, serializer); + } + return Collections.emptyMap(); + } + } +} + + + diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java new file mode 100644 index 000000000..1082c479b --- /dev/null +++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.data.impl; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.util.concurrent.Futures.immediateCheckedFuture; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.v3po.data.DataModification; +import io.fd.honeycomb.v3po.data.ModifiableDataManager; +import io.fd.honeycomb.v3po.translate.TranslationException; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * DataTree backed implementation for modifiable data manager. + */ +public class ModifiableDataTreeManager implements ModifiableDataManager { + + private static final Logger LOG = LoggerFactory.getLogger(ModifiableDataTreeManager.class); + + private final DataTree dataTree; + + public ModifiableDataTreeManager(@Nonnull final DataTree dataTree) { + this.dataTree = checkNotNull(dataTree, "dataTree should not be null"); + } + + @Override + public DataModification newModification() { + return new ConfigSnapshot(); + } + + @Override + public final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(@Nonnull final YangInstanceIdentifier path) { + return newModification().read(path); + } + + protected class ConfigSnapshot implements DataModification { + private final DataTreeModification modification; + private boolean validated = false; + + ConfigSnapshot() { + this(dataTree.takeSnapshot().newModification()); + } + + protected ConfigSnapshot(final DataTreeModification modification) { + this.modification = modification; + } + + @Override + public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read( + @Nonnull final YangInstanceIdentifier path) { + final Optional<NormalizedNode<?, ?>> node = modification.readNode(path); + if (LOG.isTraceEnabled() && node.isPresent()) { + LOG.trace("ConfigSnapshot.read: {}", node.get()); + } + return immediateCheckedFuture(node); + } + + @Override + public final void delete(final YangInstanceIdentifier path) { + modification.delete(path); + } + + @Override + public final void merge(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) { + modification.merge(path, data); + } + + @Override + public final void write(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) { + modification.write(path, data); + } + + @Override + public final void commit() throws DataValidationFailedException, TranslationException { + if(!validated) { + validate(); + } + final DataTreeCandidate candidate = dataTree.prepare(modification); + processCandidate(candidate); + dataTree.commit(candidate); + } + + protected void processCandidate(final DataTreeCandidate candidate) throws TranslationException { + // NOOP + } + + @Override + public final void validate() throws DataValidationFailedException { + modification.ready(); + dataTree.validate(modification); + validated = true; + } + } +} + + + diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java index 25d1d8dda..a54b7f148 100644 --- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java +++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java @@ -127,6 +127,10 @@ public class PersistingDataTreeAdapter implements org.opendaylight.yangtools.yan if(currentRoot.isPresent()) { try { LOG.trace("Persisting current data: {} into: {}", currentRoot.get(), path); + // Make sure the file gets overwritten + if(Files.exists(path)) { + Files.delete(path); + } // TODO once we are in static environment, do the writer, streamWriter and NNWriter initialization only once final JsonWriter jsonWriter = createJsonWriter(Files.newOutputStream(path, StandardOpenOption.CREATE), true); diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java index 83c9e30bd..c38fa27ca 100644 --- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java +++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java @@ -16,14 +16,16 @@ package io.fd.honeycomb.v3po.data.impl; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; + import com.google.common.base.Function; import com.google.common.base.Optional; -import com.google.common.base.Preconditions; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import io.fd.honeycomb.v3po.data.ReadableDataTree; -import io.fd.honeycomb.v3po.data.DataTreeSnapshot; +import io.fd.honeycomb.v3po.data.ReadableDataManager; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; @@ -37,17 +39,27 @@ import org.slf4j.LoggerFactory; final class ReadOnlyTransaction implements DOMDataReadOnlyTransaction { private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyTransaction.class); - private volatile ReadableDataTree operationalData; - private volatile DataTreeSnapshot configSnapshot; - ReadOnlyTransaction(@Nonnull final ReadableDataTree operationalData, - @Nonnull final DataTreeSnapshot configSnapshot) { - this.operationalData = Preconditions.checkNotNull(operationalData, "operationalData should not be null"); - this.configSnapshot = Preconditions.checkNotNull(configSnapshot, "config should not be null"); + @Nullable + private volatile ReadableDataManager operationalData; + @Nullable + private volatile ReadableDataManager configSnapshot; + + private volatile boolean closed = false; + + /** + * @param configData config data tree manager. Null if config reads are not to be supported + * @param operationalData operational data tree manager. Null if operational reads are not to be supported + */ + private ReadOnlyTransaction(@Nullable final ReadableDataManager configData, + @Nullable final ReadableDataManager operationalData) { + this.configSnapshot = configData; + this.operationalData = operationalData; } @Override public void close() { + closed = true; configSnapshot = null; operationalData = null; } @@ -57,12 +69,13 @@ final class ReadOnlyTransaction implements DOMDataReadOnlyTransaction { final LogicalDatastoreType store, final YangInstanceIdentifier path) { LOG.debug("ReadOnlyTransaction.read(), store={}, path={}", store, path); - - Preconditions.checkState(configSnapshot != null, "Transaction was closed"); + checkState(!closed, "Transaction has been closed"); if (store == LogicalDatastoreType.OPERATIONAL) { + checkArgument(operationalData != null, "{} reads not supported", store); return operationalData.read(path); } else { + checkArgument(configSnapshot != null, "{} reads not supported", store); return configSnapshot.read(path); } } @@ -73,7 +86,6 @@ final class ReadOnlyTransaction implements DOMDataReadOnlyTransaction { LOG.debug("ReadOnlyTransaction.exists() store={}, path={}", store, path); ListenableFuture<Boolean> listenableFuture = Futures.transform(read(store, path), IS_NODE_PRESENT); - return Futures.makeChecked(listenableFuture, ANY_EX_TO_READ_FAILED_EXCEPTION_MAPPER); } @@ -83,23 +95,25 @@ final class ReadOnlyTransaction implements DOMDataReadOnlyTransaction { return this; } + @Nonnull + static ReadOnlyTransaction createOperationalOnly(@Nonnull final ReadableDataManager operationalData) { + return new ReadOnlyTransaction(null, requireNonNull(operationalData)); + } + + @Nonnull + static ReadOnlyTransaction createConfigOnly(@Nonnull final ReadableDataManager configData) { + return new ReadOnlyTransaction(requireNonNull(configData), null); + } + + @Nonnull + static ReadOnlyTransaction create(@Nonnull final ReadableDataManager configData, + @Nonnull final ReadableDataManager operationalData) { + return new ReadOnlyTransaction(requireNonNull(configData), requireNonNull(operationalData)); + } private static final Function<? super Optional<NormalizedNode<?, ?>>, ? extends Boolean> IS_NODE_PRESENT = - new Function<Optional<NormalizedNode<?, ?>>, Boolean>() { - @Nullable - @Override - public Boolean apply(@Nullable final Optional<NormalizedNode<?, ?>> input) { - return input == null - ? Boolean.FALSE - : input.isPresent(); - } - }; + (Function<Optional<NormalizedNode<?, ?>>, Boolean>) input -> input == null ? Boolean.FALSE : input.isPresent(); private static final Function<? super Exception, ReadFailedException> ANY_EX_TO_READ_FAILED_EXCEPTION_MAPPER = - new Function<Exception, ReadFailedException>() { - @Override - public ReadFailedException apply(@Nullable final Exception e) { - return new ReadFailedException("Exists failed", e); - } - }; + (Function<Exception, ReadFailedException>) e -> new ReadFailedException("Exists failed", e); }
\ No newline at end of file diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/OperationalDataTree.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegator.java index 175f22d34..e660ee429 100644 --- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/OperationalDataTree.java +++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegator.java @@ -26,15 +26,18 @@ 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.data.ReadableDataTree; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.data.ReadableDataManager; +import io.fd.honeycomb.v3po.translate.MappingContext; +import io.fd.honeycomb.v3po.translate.ModificationCache; import io.fd.honeycomb.v3po.translate.read.ReadContext; import io.fd.honeycomb.v3po.translate.read.ReadFailedException; import io.fd.honeycomb.v3po.translate.read.ReaderRegistry; +import io.fd.honeycomb.v3po.translate.util.write.TransactionMappingContext; import java.util.Collection; import java.util.Map; import javax.annotation.Nonnull; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState; @@ -61,13 +64,14 @@ import org.slf4j.LoggerFactory; /** * ReadableDataTree implementation for operational data. */ -public final class OperationalDataTree implements ReadableDataTree { - private static final Logger LOG = LoggerFactory.getLogger(OperationalDataTree.class); +public final class ReadableDataTreeDelegator implements ReadableDataManager { + private static final Logger LOG = LoggerFactory.getLogger(ReadableDataTreeDelegator.class); private final BindingNormalizedNodeSerializer serializer; private final ReaderRegistry readerRegistry; private final SchemaContext globalContext; private final DOMDataBroker netconfMonitoringDomDataBrokerDependency; + private final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker; /** * Creates operational data tree instance. @@ -75,12 +79,16 @@ public final class OperationalDataTree implements ReadableDataTree { * representation. * @param globalContext service for obtaining top level context data from all yang modules. * @param readerRegistry service responsible for translation between DataObjects and data provider. - * @param netconfMonitoringDomDataBrokerDependency + * @param netconfMonitoringDomDataBrokerDependency TODO remove + * @param contextBroker BA broker for context data */ - public OperationalDataTree(@Nonnull BindingNormalizedNodeSerializer serializer, - @Nonnull final SchemaContext globalContext, @Nonnull ReaderRegistry readerRegistry, - final DOMDataBroker netconfMonitoringDomDataBrokerDependency) { + public ReadableDataTreeDelegator(@Nonnull BindingNormalizedNodeSerializer serializer, + @Nonnull final SchemaContext globalContext, + @Nonnull final ReaderRegistry readerRegistry, + @Nonnull final DOMDataBroker netconfMonitoringDomDataBrokerDependency, + @Nonnull final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker) { this.netconfMonitoringDomDataBrokerDependency = netconfMonitoringDomDataBrokerDependency; + this.contextBroker = checkNotNull(contextBroker, "contextBroker should not be null"); this.globalContext = checkNotNull(globalContext, "globalContext should not be null"); this.serializer = checkNotNull(serializer, "serializer should not be null"); this.readerRegistry = checkNotNull(readerRegistry, "reader should not be null"); @@ -91,16 +99,35 @@ public final class OperationalDataTree implements ReadableDataTree { org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> read( @Nonnull final YangInstanceIdentifier yangInstanceIdentifier) { - try(ReadContext ctx = new ReadContextImpl()) { + try(TransactionMappingContext mappingContext = new TransactionMappingContext(contextBroker.newReadWriteTransaction()); + ReadContext ctx = new ReadContextImpl(mappingContext)) { + + final Optional<NormalizedNode<?, ?>> value; if (checkNotNull(yangInstanceIdentifier).equals(YangInstanceIdentifier.EMPTY)) { - return Futures.immediateCheckedFuture(readRoot(ctx)); + value = readRoot(ctx); } else { - return Futures.immediateCheckedFuture(readNode(yangInstanceIdentifier, ctx)); + value = readNode(yangInstanceIdentifier, ctx); } + + // Submit context mapping updates + final CheckedFuture<Void, TransactionCommitFailedException> contextUpdateResult = + ((TransactionMappingContext) ctx.getMappingContext()).submit(); + // Blocking on context data update + contextUpdateResult.checkedGet(); + + return Futures.immediateCheckedFuture(value); + } catch (ReadFailedException e) { return Futures.immediateFailedCheckedFuture( - new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException( - "Failed to read VPP data", e)); + new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException( + "Failed to read VPP data", e)); + } catch (TransactionCommitFailedException e) { + // FIXME revert should probably occur when context is not written successfully, but can that even happen ? + final String msg = "Error while updating mapping context data"; + LOG.error(msg, e); + return Futures.immediateFailedCheckedFuture( + new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException(msg, e) + ); } } @@ -108,6 +135,8 @@ public final class OperationalDataTree implements ReadableDataTree { final ReadContext ctx) throws ReadFailedException { // FIXME workaround for: https://git.opendaylight.org/gerrit/#/c/37499/ + // Just delete, dedicated reader from NetconfMonitoringReaderModule takes care of netconf state data + // TODO test connecting with netconf and issuing a get (netconf-state) data should be provided if(yangInstanceIdentifier.getPathArguments().size() > 0 && yangInstanceIdentifier.getPathArguments().get(0).getNodeType().equals(NetconfState.QNAME)) { return readFromNetconfDs(yangInstanceIdentifier); @@ -222,17 +251,29 @@ public final class OperationalDataTree implements ReadableDataTree { } private static final class ReadContextImpl implements ReadContext { - public final Context ctx = new Context(); + + public final ModificationCache ctx = new ModificationCache(); + private final MappingContext mappingContext; + + private ReadContextImpl(final MappingContext mappingContext) { + this.mappingContext = mappingContext; + } @Nonnull @Override - public Context getContext() { + public ModificationCache getModificationCache() { return ctx; } + @Nonnull + @Override + public MappingContext getMappingContext() { + return mappingContext; + } + @Override public void close() { - // Make sure to clear the storage in case some customizer stored it to prevent memory leaks + // Make sure to clear the storage in case some customizer stored a reference to it to prevent memory leaks ctx.close(); } } diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java index fbeba7e07..3644a9fe7 100644 --- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java +++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java @@ -16,6 +16,8 @@ package io.fd.honeycomb.v3po.data.impl; +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.CANCELED; import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.COMMITED; import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.FAILED; @@ -26,10 +28,10 @@ import com.google.common.base.Preconditions; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import io.fd.honeycomb.v3po.data.ModifiableDataTree; -import io.fd.honeycomb.v3po.data.DataTreeSnapshot; +import io.fd.honeycomb.v3po.data.DataModification; import io.fd.honeycomb.v3po.translate.TranslationException; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.annotation.concurrent.NotThreadSafe; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; @@ -38,7 +40,6 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,30 +48,21 @@ import org.slf4j.LoggerFactory; final class WriteTransaction implements DOMDataWriteTransaction { private static final Logger LOG = LoggerFactory.getLogger(WriteTransaction.class); - private final ModifiableDataTree configDataTree; - private DataTreeModification modification; - private TransactionStatus status; - - WriteTransaction(@Nonnull final ModifiableDataTree configDataTree, - @Nonnull final DataTreeSnapshot configSnapshot) { - this.configDataTree = Preconditions.checkNotNull(configDataTree, "configDataTree should not be null"); - Preconditions.checkNotNull(configSnapshot, "configSnapshot should not be null"); - // initialize transaction state: - modification = configSnapshot.newModification(); - status = NEW; - } - WriteTransaction(@Nonnull final ModifiableDataTree configDataTree) { - this(configDataTree, configDataTree.takeSnapshot()); - } + @Nullable + private DataModification operationalModification; + @Nullable + private DataModification configModification; + private TransactionStatus status = NEW; - private static void checkConfigurationWrite(final LogicalDatastoreType store) { - Preconditions.checkArgument(LogicalDatastoreType.CONFIGURATION == store, "Write is not supported for operational data store"); + private WriteTransaction(@Nullable final DataModification configModification, + @Nullable final DataModification operationalModification) { + this.operationalModification = operationalModification; + this.configModification = configModification; } private void checkIsNew() { Preconditions.checkState(status == NEW, "Transaction was submitted or canceled"); - Preconditions.checkState(modification != null, "DataTree modification should not be null"); } @Override @@ -78,8 +70,21 @@ final class WriteTransaction implements DOMDataWriteTransaction { final NormalizedNode<?, ?> data) { LOG.debug("WriteTransaction.put() store={}, path={}, data={}", store, path, data); checkIsNew(); - checkConfigurationWrite(store); - modification.write(path, data); + handleOperation(store, (modification) -> modification.write(path, data)); + } + + private void handleOperation(final LogicalDatastoreType store, + final java.util.function.Consumer<DataModification> r) { + switch (store) { + case CONFIGURATION: + checkArgument(configModification != null, "Modification of {} is not supported", store); + r.accept(configModification); + break; + case OPERATIONAL: + checkArgument(operationalModification != null, "Modification of {} is not supported", store); + r.accept(operationalModification); + break; + } } @Override @@ -87,8 +92,7 @@ final class WriteTransaction implements DOMDataWriteTransaction { final NormalizedNode<?, ?> data) { LOG.debug("WriteTransaction.merge() store={}, path={}, data={}", store, path, data); checkIsNew(); - checkConfigurationWrite(store); - modification.merge(path, data); + handleOperation(store, (modification) -> modification.merge(path, data)); } @Override @@ -98,7 +102,6 @@ final class WriteTransaction implements DOMDataWriteTransaction { return false; } else { status = CANCELED; - modification = null; return true; } } @@ -107,29 +110,38 @@ final class WriteTransaction implements DOMDataWriteTransaction { public void delete(LogicalDatastoreType store, final YangInstanceIdentifier path) { LOG.debug("WriteTransaction.delete() store={}, path={}", store, path); checkIsNew(); - checkConfigurationWrite(store); - modification.delete(path); + handleOperation(store, (modification) -> modification.delete(path)); } @Override public CheckedFuture<Void, TransactionCommitFailedException> submit() { - LOG.debug("WriteTransaction.submit()"); + LOG.trace("WriteTransaction.submit()"); checkIsNew(); - // seal transaction: - modification.ready(); - status = SUBMITED; - try { - configDataTree.modify(modification); + status = SUBMITED; + + // Validate first to catch any issues before attempting commit + if (configModification != null) { + configModification.validate(); + } + if (operationalModification != null) { + operationalModification.validate(); + } + + if(configModification != null) { + configModification.commit(); + } + if(operationalModification != null) { + operationalModification.commit(); + } + status = COMMITED; } catch (DataValidationFailedException | TranslationException e) { status = FAILED; LOG.error("Failed modify data tree", e); return Futures.immediateFailedCheckedFuture( - new TransactionCommitFailedException("Failed to validate DataTreeModification", e)); - } finally { - modification = null; + new TransactionCommitFailedException("Failed to validate DataTreeModification", e)); } return Futures.immediateCheckedFuture(null); } @@ -144,4 +156,21 @@ final class WriteTransaction implements DOMDataWriteTransaction { public Object getIdentifier() { return this; } + + + @Nonnull + static WriteTransaction createOperationalOnly(@Nonnull final DataModification operationalData) { + return new WriteTransaction(null, requireNonNull(operationalData)); + } + + @Nonnull + static WriteTransaction createConfigOnly(@Nonnull final DataModification configData) { + return new WriteTransaction(requireNonNull(configData), null); + } + + @Nonnull + static WriteTransaction create(@Nonnull final DataModification configData, + @Nonnull final DataModification operationalData) { + return new WriteTransaction(requireNonNull(configData), requireNonNull(operationalData)); + } } diff --git a/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java b/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java index 86a3f9db4..3e871e09a 100644 --- a/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java +++ b/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java @@ -1,11 +1,14 @@ package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411; -import io.fd.honeycomb.v3po.data.DataTreeSnapshot; -import io.fd.honeycomb.v3po.data.ModifiableDataTree; -import io.fd.honeycomb.v3po.data.impl.ConfigDataTree; -import io.fd.honeycomb.v3po.translate.TranslationException; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.v3po.data.DataModification; +import io.fd.honeycomb.v3po.data.ModifiableDataManager; +import io.fd.honeycomb.v3po.data.impl.ModifiableDataTreeDelegator; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,14 +38,15 @@ public class ConfigDataTreeModule extends public java.lang.AutoCloseable createInstance() { LOG.debug("ConfigDataTreeModule.createInstance()"); return new CloseableConfigDataTree( - new ConfigDataTree(getSerializerDependency(), getDataTreeDependency(), getWriterRegistryDependency())); + new ModifiableDataTreeDelegator(getSerializerDependency(), getDataTreeDependency(), getWriterRegistryDependency(), + getContextBindingBrokerDependency())); } - private static final class CloseableConfigDataTree implements ModifiableDataTree, AutoCloseable { + private static final class CloseableConfigDataTree implements ModifiableDataManager, AutoCloseable { - private final ConfigDataTree delegate; + private final ModifiableDataTreeDelegator delegate; - CloseableConfigDataTree(final ConfigDataTree delegate) { + CloseableConfigDataTree(final ModifiableDataTreeDelegator delegate) { this.delegate = delegate; } @@ -53,16 +57,15 @@ public class ConfigDataTreeModule extends } @Override - public void modify(final DataTreeModification modification) - throws DataValidationFailedException, TranslationException { - LOG.trace("CloseableConfigDataTree.modify modification={}", modification); - delegate.modify(modification); + public DataModification newModification() { + LOG.trace("CloseableConfigDataTree.newModification"); + return delegate.newModification(); } @Override - public DataTreeSnapshot takeSnapshot() { - LOG.trace("CloseableConfigDataTree.takeSnapshot"); - return delegate.takeSnapshot(); + public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read( + @Nonnull final YangInstanceIdentifier path) { + return delegate.read(path); } } } diff --git a/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java b/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java index 2fbba75fa..286eaf664 100644 --- a/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java +++ b/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java @@ -2,8 +2,8 @@ package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl. import com.google.common.base.Optional; import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.data.ReadableDataTree; -import io.fd.honeycomb.v3po.data.impl.OperationalDataTree; +import io.fd.honeycomb.v3po.data.ReadableDataManager; +import io.fd.honeycomb.v3po.data.impl.ReadableDataTreeDelegator; import javax.annotation.Nonnull; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -37,15 +37,15 @@ public class OperationalDataTreeModule extends public java.lang.AutoCloseable createInstance() { LOG.debug("OperationalDataTreeModule.createInstance()"); return new CloseableOperationalDataTree( - new OperationalDataTree(getSerializerDependency(), getSchemaServiceDependency().getGlobalContext(), - getReaderRegistryDependency(), getNetconfMonitoringDomDataBrokerDependency())); + new ReadableDataTreeDelegator(getSerializerDependency(), getSchemaServiceDependency().getGlobalContext(), + getReaderRegistryDependency(), getNetconfMonitoringDomDataBrokerDependency(), getContextBindingBrokerDependency())); } - private static final class CloseableOperationalDataTree implements ReadableDataTree, AutoCloseable { + private static final class CloseableOperationalDataTree implements ReadableDataManager, AutoCloseable { - private final OperationalDataTree delegate; + private final ReadableDataTreeDelegator delegate; - CloseableOperationalDataTree(final OperationalDataTree delegate) { + CloseableOperationalDataTree(final ReadableDataTreeDelegator delegate) { this.delegate = delegate; } diff --git a/v3po/data-impl/src/main/yang/data-impl.yang b/v3po/data-impl/src/main/yang/data-impl.yang index 454e99a66..3af9d8d3f 100644 --- a/v3po/data-impl/src/main/yang/data-impl.yang +++ b/v3po/data-impl/src/main/yang/data-impl.yang @@ -121,6 +121,14 @@ module data-impl { } } + container context-binding-broker { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity md-sal-binding:binding-async-data-broker; + } + } + } } } @@ -171,6 +179,15 @@ module data-impl { } } + container context-binding-broker { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity md-sal-binding:binding-async-data-broker; + } + } + } + } } }
\ No newline at end of file diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java index a2908d591..55b92b50b 100644 --- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java +++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java @@ -22,9 +22,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; -import io.fd.honeycomb.v3po.data.ReadableDataTree; -import io.fd.honeycomb.v3po.data.ModifiableDataTree; -import io.fd.honeycomb.v3po.data.DataTreeSnapshot; +import io.fd.honeycomb.v3po.data.ReadableDataManager; +import io.fd.honeycomb.v3po.data.ModifiableDataManager; +import io.fd.honeycomb.v3po.data.DataModification; import java.util.Map; import org.junit.Before; import org.junit.Test; @@ -42,18 +42,18 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; public class DataBrokerTest { @Mock - private ReadableDataTree operationalData; + private ReadableDataManager operationalData; @Mock - private ModifiableDataTree confiDataTree; + private ModifiableDataManager confiDataTree; @Mock - private DataTreeSnapshot configSnapshot; + private DataModification configSnapshot; private DataBroker broker; @Before public void setUp() { initMocks(this); - when(confiDataTree.takeSnapshot()).thenReturn(configSnapshot); - broker = new DataBroker(operationalData, confiDataTree); + when(confiDataTree.newModification()).thenReturn(configSnapshot); + broker = DataBroker.create(confiDataTree, operationalData); } @Test @@ -64,7 +64,7 @@ public class DataBrokerTest { // verify that read and write transactions use the same config snapshot verify(configSnapshot).read(path); - verify(configSnapshot).newModification(); + verify(confiDataTree).newModification(); } @Test @@ -72,7 +72,7 @@ public class DataBrokerTest { final DOMDataWriteTransaction writeTx = broker.newWriteOnlyTransaction(); // verify that write transactions use config snapshot - verify(configSnapshot).newModification(); + verify(confiDataTree).newModification(); } @Test diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTreeTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java index b7f8b9d2c..fed32da8a 100644 --- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTreeTest.java +++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java @@ -25,13 +25,15 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import com.google.common.base.Optional; import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.data.DataTreeSnapshot; +import com.google.common.util.concurrent.Futures; +import io.fd.honeycomb.v3po.data.DataModification; import io.fd.honeycomb.v3po.translate.TranslationException; import io.fd.honeycomb.v3po.translate.write.WriteContext; import io.fd.honeycomb.v3po.translate.write.WriterRegistry; @@ -42,6 +44,7 @@ import java.util.Map; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Ethernet; @@ -55,9 +58,8 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; -public class ConfigDataTreeTest { +public class ModifiableDataTreeDelegatorTest { @Mock private WriterRegistry writer; @@ -66,14 +68,16 @@ public class ConfigDataTreeTest { @Mock private DataTree dataTree; @Mock - private DataTreeModification modification; + private org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification modification; + @Mock + private DataBroker contextBroker; - private ConfigDataTree configDataTree; + private ModifiableDataTreeManager configDataTree; @Before public void setUp() { initMocks(this); - configDataTree = new ConfigDataTree(serializer, dataTree, writer); + configDataTree = new ModifiableDataTreeDelegator(serializer, dataTree, writer, contextBroker); } @Test @@ -81,17 +85,18 @@ public class ConfigDataTreeTest { final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class); when(dataTree.takeSnapshot()).thenReturn(snapshot); + when(snapshot.newModification()).thenReturn(modification); final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class); final Optional node = mock(Optional.class); - doReturn(node).when(snapshot).readNode(path); + doReturn(node).when(modification).readNode(path); - final DataTreeSnapshot dataTreeSnapshot = configDataTree.takeSnapshot(); + final DataModification dataTreeSnapshot = configDataTree.newModification(); final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future = dataTreeSnapshot.read(path); - verify(dataTree).takeSnapshot(); - verify(snapshot).readNode(path); + verify(dataTree, times(2)).takeSnapshot(); + verify(modification).readNode(path); assertTrue(future.isDone()); assertEquals(node, future.get()); @@ -102,19 +107,29 @@ public class ConfigDataTreeTest { final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class); when(dataTree.takeSnapshot()).thenReturn(snapshot); - when(snapshot.newModification()).thenReturn(modification); - final DataTreeSnapshot dataTreeSnapshot = configDataTree.takeSnapshot(); - final DataTreeModification newModification = dataTreeSnapshot.newModification(); - verify(dataTree).takeSnapshot(); - verify(snapshot).newModification(); - - assertEquals(modification, newModification); + final DataModification dataTreeSnapshot = configDataTree.newModification(); + // Snapshot captured twice, so that original data could be provided to translation layer without any possible + // modification + verify(dataTree, times(2)).takeSnapshot(); + verify(snapshot, times(2)).newModification(); } @Test public void testCommitSuccessful() throws Exception { + final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot + snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class); + when(dataTree.takeSnapshot()).thenReturn(snapshot); + when(snapshot.newModification()).thenReturn(modification); + final DataTreeCandidate prepare = mock(DataTreeCandidate.class); + doReturn(prepare).when(dataTree).prepare(modification); + + final org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction ctxTransaction = mock( + org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction.class); + doReturn(ctxTransaction).when(contextBroker).newReadWriteTransaction(); + doReturn(Futures.immediateCheckedFuture(null)).when(ctxTransaction).submit(); + final DataObject dataBefore = mockDataObject("before", Ethernet.class); final DataObject dataAfter = mockDataObject("after", Ethernet.class); @@ -128,7 +143,9 @@ public class ConfigDataTreeTest { when(rootNode.getDataAfter()).thenReturn(Optional.<NormalizedNode<?, ?>>fromNullable(nodeAfter)); // Run the test - configDataTree.modify(modification); + doReturn(rootNode).when(prepare).getRootNode(); + final DataModification dataModification = configDataTree.newModification(); + dataModification.commit(); // Verify all changes were processed: verify(writer).update( @@ -138,6 +155,8 @@ public class ConfigDataTreeTest { // Verify modification was validated verify(dataTree).validate(modification); + // Verify context transaction was finished + verify(ctxTransaction).submit(); } private Map<InstanceIdentifier<?>, DataObject> mapOf(final DataObject dataBefore, final Class<Ethernet> type) { @@ -154,6 +173,13 @@ public class ConfigDataTreeTest { @Test public void testCommitUndoSuccessful() throws Exception { + final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot + snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class); + when(dataTree.takeSnapshot()).thenReturn(snapshot); + when(snapshot.newModification()).thenReturn(modification); + final DataTreeCandidate prepare = mock(DataTreeCandidate.class); + doReturn(prepare).when(dataTree).prepare(modification); + // Prepare data changes: final DataObject dataBefore = mockDataObject("before", Ethernet.class); final DataObject dataAfter = mockDataObject("after", Ethernet.class); @@ -177,7 +203,9 @@ public class ConfigDataTreeTest { // Run the test try { - configDataTree.modify(modification); + doReturn(rootNode).when(prepare).getRootNode(); + final DataModification dataModification = configDataTree.newModification(); + dataModification.commit(); } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException e) { verify(writer).update(anyMap(), anyMap(), any(WriteContext.class)); verify(reverter).revert(); @@ -190,6 +218,13 @@ public class ConfigDataTreeTest { @Test public void testCommitUndoFailed() throws Exception { + final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot + snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class); + when(dataTree.takeSnapshot()).thenReturn(snapshot); + when(snapshot.newModification()).thenReturn(modification); + final DataTreeCandidate prepare = mock(DataTreeCandidate.class); + doReturn(prepare).when(dataTree).prepare(modification); + // Prepare data changes: final DataObject dataBefore = mockDataObject("before", Ethernet.class); final DataObject dataAfter = mockDataObject("after", Ethernet.class); @@ -219,7 +254,9 @@ public class ConfigDataTreeTest { // Run the test try { - configDataTree.modify(modification); + doReturn(rootNode).when(prepare).getRootNode(); + final DataModification dataModification = configDataTree.newModification(); + dataModification.commit(); } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException e) { verify(writer).update(anyMap(), anyMap(), any(WriteContext.class)); verify(reverter).revert(); diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java index 30fdd1392..a13621725 100644 --- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java +++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java @@ -24,8 +24,8 @@ import static org.mockito.MockitoAnnotations.initMocks; import com.google.common.base.Optional; import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.data.ReadableDataTree; -import io.fd.honeycomb.v3po.data.DataTreeSnapshot; +import io.fd.honeycomb.v3po.data.ReadableDataManager; +import io.fd.honeycomb.v3po.data.DataModification; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -37,16 +37,16 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; public class ReadOnlyTransactionTest { @Mock - private ReadableDataTree operationalData; + private ReadableDataManager operationalData; @Mock - private DataTreeSnapshot configSnapshot; + private DataModification configSnapshot; private ReadOnlyTransaction readOnlyTx; @Before public void setUp() { initMocks(this); - readOnlyTx = new ReadOnlyTransaction(operationalData, configSnapshot); + readOnlyTx = ReadOnlyTransaction.create(configSnapshot, operationalData); } @Test diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/OperationalDataTreeTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java index 7e6abcdcd..4492c70a2 100644 --- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/OperationalDataTreeTest.java +++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java @@ -42,6 +42,7 @@ import java.util.Map; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; @@ -58,14 +59,14 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -public class OperationalDataTreeTest { +public class ReadableDataTreeDelegatorTest { @Mock private BindingNormalizedNodeSerializer serializer; @Mock private ReaderRegistry reader; - private OperationalDataTree operationalData; + private ReadableDataTreeDelegator operationalData; @Mock private InstanceIdentifier<DataObject> id; @@ -81,16 +82,23 @@ public class OperationalDataTreeTest { private DOMDataBroker netconfMonitoringBroker; @Mock private DOMDataReadOnlyTransaction domDataReadOnlyTransaction; + @Mock + private DataBroker contextBroker; @Before public void setUp() { initMocks(this); - operationalData = new OperationalDataTree(serializer, globalContext, reader, netconfMonitoringBroker); + operationalData = new ReadableDataTreeDelegator(serializer, globalContext, reader, netconfMonitoringBroker, contextBroker); doReturn(schemaNode).when(globalContext).getDataChildByName(any(QName.class)); doReturn(domDataReadOnlyTransaction).when(netconfMonitoringBroker).newReadOnlyTransaction(); doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(domDataReadOnlyTransaction) .read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class)); + + final org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction ctxTransaction = mock( + org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction.class); + doReturn(ctxTransaction).when(contextBroker).newReadWriteTransaction(); + doReturn(Futures.immediateCheckedFuture(null)).when(ctxTransaction).submit(); } @Test diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java index d0360dbd8..9cde27d2b 100644 --- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java +++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java @@ -23,12 +23,10 @@ import static org.junit.Assert.fail; 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.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.data.ModifiableDataTree; -import io.fd.honeycomb.v3po.data.DataTreeSnapshot; +import io.fd.honeycomb.v3po.data.DataModification; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -36,54 +34,48 @@ import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; public class WriteTransactionTest { @Mock - private ModifiableDataTree configDataTree; - @Mock - private DataTreeSnapshot configSnapshot; + private DataModification configSnapshot; @Mock private YangInstanceIdentifier path; @Mock private NormalizedNode<?,?> data; - @Mock - private DataTreeModification dataTreeModification; private WriteTransaction writeTx; @Before public void setUp() { initMocks(this); - when(configSnapshot.newModification()).thenReturn(dataTreeModification); - writeTx = new WriteTransaction(configDataTree, configSnapshot); + writeTx = WriteTransaction.createConfigOnly(configSnapshot); } @Test public void testPut() { writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data); - verify(dataTreeModification).write(path, data); + verify(configSnapshot).write(path, data); } @Test(expected = IllegalArgumentException.class) public void testPutOperational() { writeTx.put(LogicalDatastoreType.OPERATIONAL, path, data); - verify(dataTreeModification).write(path, data); + verify(configSnapshot).write(path, data); } @Test(expected = IllegalStateException.class) public void testOnFinishedTx() { writeTx.submit(); writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data); - verify(dataTreeModification).write(path, data); + verify(configSnapshot).write(path, data); } @Test public void testMerge() { writeTx.merge(LogicalDatastoreType.CONFIGURATION, path, data); - verify(dataTreeModification).merge(path, data); + verify(configSnapshot).merge(path, data); } @Test @@ -100,19 +92,19 @@ public class WriteTransactionTest { @Test public void testDelete() { writeTx.delete(LogicalDatastoreType.CONFIGURATION, path); - verify(dataTreeModification).delete(path); + verify(configSnapshot).delete(path); } @Test public void testSubmit() throws Exception { writeTx.submit(); - verify(dataTreeModification).ready(); - verify(configDataTree).modify(dataTreeModification); + verify(configSnapshot).validate(); + verify(configSnapshot).commit(); } @Test public void testSubmitFailed() throws Exception { - doThrow(mock(DataValidationFailedException.class)).when(configDataTree).modify(dataTreeModification); + doThrow(mock(DataValidationFailedException.class)).when(configSnapshot).commit(); final CheckedFuture<Void, TransactionCommitFailedException> future = writeTx.submit(); try { future.get(); |