summaryrefslogtreecommitdiffstats
path: root/v3po/data-impl/src/main/java/io/fd/honeycomb
diff options
context:
space:
mode:
authorMaros Marsalek <mmarsale@cisco.com>2016-05-17 09:10:39 +0200
committerMaros Marsalek <mmarsale@cisco.com>2016-05-23 09:24:12 +0000
commit799ee62f15dff07534beaf98a3ac551deffade38 (patch)
treea47c4696cf6ecff4fbb83c161c5d8e7e6b8af3c8 /v3po/data-impl/src/main/java/io/fd/honeycomb
parentf19306a9263cb8dd8c10a4867633fbbe14db2e4c (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/src/main/java/io/fd/honeycomb')
-rw-r--r--v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTree.java173
-rw-r--r--v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java134
-rw-r--r--v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtils.java1
-rw-r--r--v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java174
-rw-r--r--v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java122
-rw-r--r--v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java4
-rw-r--r--v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java68
-rw-r--r--v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegator.java (renamed from v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/OperationalDataTree.java)73
-rw-r--r--v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java103
9 files changed, 578 insertions, 274 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));
+ }
}