import com.google.common.annotations.Beta;
import com.google.common.base.Optional;
import javax.annotation.Nonnull;
+import javax.annotation.concurrent.NotThreadSafe;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
* the tree.
*/
@Beta
+@NotThreadSafe
public interface DataTreeSnapshotCursor extends AutoCloseable {
/**
* Move the cursor to the specified child of the current position.
*
* @param child Child identifier
- * @throws BackendFailedException when implementation-specific errors occurs
+ * @throws BackendFailedException when an implementation-specific error occurs
* while servicing the request.
* @throws IllegalArgumentException when specified identifier does not identify
* a valid child, or if that child is not an
/**
* Move the cursor to the specified child of the current position. This is
* the equivalent of multiple invocations of {@link #enter(PathArgument)},
- * except the operation is performed atomically.
+ * except the operation is performed all at once.
*
* @param path Nested child identifier
- * @throws BackendFailedException when implementation-specific errors occurs
+ * @throws BackendFailedException when an implementation-specific error occurs
* while servicing the request.
* @throws IllegalArgumentException when specified path does not identify
* a valid child, or if that child is not an
* argument.
*
* @param path Nested child identifier
- * @throws BackendFailedException when implementation-specific errors occurs
+ * @throws BackendFailedException when an implementation-specific error occurs
* while servicing the request.
* @throws IllegalArgumentException when specified path does not identify
* a valid child, or if that child is not an
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+
+import com.google.common.base.Preconditions;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshotCursor;
+
+abstract class AbstractCursor<T extends AbstractCursorAware> implements DataTreeSnapshotCursor {
+ @SuppressWarnings("rawtypes")
+ private static final AtomicIntegerFieldUpdater<AbstractCursor> CLOSED_UPDATER =
+ AtomicIntegerFieldUpdater.newUpdater(AbstractCursor.class, "closed");
+ private final YangInstanceIdentifier rootPath;
+ private final T parent;
+ private volatile int closed;
+
+ AbstractCursor(final T parent, final YangInstanceIdentifier rootPath) {
+ this.rootPath = Preconditions.checkNotNull(rootPath);
+ this.parent = Preconditions.checkNotNull(parent);
+ }
+
+ final T getParent() {
+ return parent;
+ }
+
+ final YangInstanceIdentifier getRootPath() {
+ return rootPath;
+ }
+
+
+ final void ensureNotClosed() {
+ Preconditions.checkState(closed == 0, "Modification cursor has been closed");
+ }
+
+ @Override
+ public final void enter(final PathArgument... path) {
+ enter(Arrays.asList(path));
+ }
+
+ @Override
+ public final void exit() {
+ exit(1);
+ }
+
+ @Override
+ public final void close() {
+ if (CLOSED_UPDATER.compareAndSet(this, 0, 1)) {
+ parent.closeCursor(this);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+
+import com.google.common.base.Preconditions;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class AbstractCursorAware {
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractCursorAware.class);
+ @SuppressWarnings("rawtypes")
+ private static final AtomicReferenceFieldUpdater<AbstractCursorAware, AbstractCursor> CURSOR_UPDATER =
+ AtomicReferenceFieldUpdater.newUpdater(AbstractCursorAware.class, AbstractCursor.class, "cursor");
+ private volatile AbstractCursor<?> cursor = null;
+
+ protected <T extends AbstractCursor<?>> T openCursor(final T cursor) {
+ final boolean success = CURSOR_UPDATER.compareAndSet(this, null, cursor);
+ Preconditions.checkState(success, "Modification %s has cursor attached at path %s", this, this.cursor.getRootPath());
+ return cursor;
+ }
+
+ final void closeCursor(final AbstractCursor<?> cursor) {
+ final boolean success = CURSOR_UPDATER.compareAndSet(this, cursor, null);
+ if (!success) {
+ LOG.warn("Attempted to close cursor %s while %s is open", cursor, this.cursor);
+ }
+ }
+}
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.CursorAwareDataTreeModification;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModificationCursor;
import org.opendaylight.yangtools.yang.data.api.schema.tree.StoreTreeNodes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-final class InMemoryDataTreeModification implements DataTreeModification {
- private static final AtomicIntegerFieldUpdater<InMemoryDataTreeModification> UPDATER =
+final class InMemoryDataTreeModification extends AbstractCursorAware implements CursorAwareDataTreeModification {
+ private static final AtomicIntegerFieldUpdater<InMemoryDataTreeModification> SEALED_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(InMemoryDataTreeModification.class, "sealed");
private static final Logger LOG = LoggerFactory.getLogger(InMemoryDataTreeModification.class);
}
}
- private void upgradeIfPossible() {
+ void upgradeIfPossible() {
if (rootNode.getOperation() == LogicalOperation.NONE) {
strategyTree.upgradeIfPossible();
}
}
}
+ static void checkIdentifierReferencesData(final PathArgument arg, final NormalizedNode<?, ?> data) {
+ Preconditions.checkArgument(arg.equals(data.getIdentifier()),
+ "Instance identifier references %s but data identifier is %s", arg, data.getIdentifier());
+ }
+
private static void checkIdentifierReferencesData(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
if (!path.isEmpty()) {
final PathArgument lastArg = path.getLastPathArgument();
Preconditions.checkArgument(lastArg != null, "Instance identifier %s has invalid null path argument", path);
- Preconditions.checkArgument(lastArg.equals(data.getIdentifier()),
- "Instance identifier references %s but data identifier is %s", lastArg, data.getIdentifier());
+ checkIdentifierReferencesData(lastArg, data);
} else {
final QName type = data.getNodeType();
Preconditions.checkArgument(SchemaContext.NAME.equals(type), "Incorrect name %s of root node", type);
}
@Override
+ public DataTreeModificationCursor createCursor(final YangInstanceIdentifier path) {
+ final OperationWithModification op = resolveModificationFor(path);
+ return openCursor(new InMemoryDataTreeModificationCursor(this, path, op));
+ }
+
public void ready() {
- final boolean wasRunning = UPDATER.compareAndSet(this, 0, 1);
+ final boolean wasRunning = SEALED_UPDATER.compareAndSet(this, 0, 1);
Preconditions.checkState(wasRunning, "Attempted to seal an already-sealed Data Tree.");
AbstractReadyIterator current = AbstractReadyIterator.create(rootNode, strategyTree);
current = current.process();
} while (current != null);
}
-
}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Iterator;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModificationCursor;
+
+final class InMemoryDataTreeModificationCursor extends AbstractCursor<InMemoryDataTreeModification> implements DataTreeModificationCursor {
+ private final Deque<OperationWithModification> stack = new ArrayDeque<>();
+
+ InMemoryDataTreeModificationCursor(final InMemoryDataTreeModification parent, final YangInstanceIdentifier rootPath, final OperationWithModification rootOp) {
+ super(parent, rootPath);
+ stack.push(rootOp);
+ }
+
+ private OperationWithModification resolveChildModification(final PathArgument child) {
+ getParent().upgradeIfPossible();
+
+ final OperationWithModification op = stack.peek();
+ final Optional<ModificationApplyOperation> potential = op.getApplyOperation().getChild(child);
+ if (potential.isPresent()) {
+ final ModificationApplyOperation operation = potential.get();
+ final ModifiedNode modification = op.getModification().modifyChild(child, operation.getChildPolicy());
+
+ return OperationWithModification.from(operation, modification);
+ }
+
+ // Node not found, construct its path
+ final Collection<PathArgument> path = new ArrayList<>();
+ path.addAll(getRootPath().getPathArguments());
+
+ final Iterator<OperationWithModification> it = stack.descendingIterator();
+ // Skip the first entry, as it's already accounted for in rootPath
+ it.next();
+
+ while (it.hasNext()) {
+ path.add(it.next().getModification().getIdentifier());
+ }
+
+ throw new SchemaValidationFailedException(String.format("Child %s is not present in schema tree.", path));
+ }
+
+ @Override
+ public void enter(final PathArgument child) {
+ stack.push(resolveChildModification(child));
+ }
+
+ @Override
+ public void enter(final Iterable<PathArgument> path) {
+ int depth = 0;
+ for (PathArgument child : path) {
+ try {
+ stack.push(resolveChildModification(child));
+ } catch (Exception e) {
+ // Undo what we have done
+ for (int i = 0; i < depth; ++i) {
+ stack.pop();
+ }
+ throw new IllegalArgumentException(e);
+ }
+ depth++;
+ }
+ }
+
+ @Override
+ public void exit(final int depth) {
+ Preconditions.checkArgument(depth >= 0);
+ Preconditions.checkState(depth < stack.size());
+
+ for (int i = 0; i < depth; i++) {
+ stack.pop();
+ }
+ }
+
+ @Override
+ public Optional<NormalizedNode<?, ?>> readNode(final PathArgument child) {
+ return stack.peek().read(child, getParent().getVersion());
+ }
+
+ @Override
+ public void delete(final PathArgument child) {
+ ensureNotClosed();
+ resolveChildModification(child).delete();
+ }
+
+ @Override
+ public void merge(final PathArgument child, final NormalizedNode<?, ?> data) {
+ ensureNotClosed();
+ InMemoryDataTreeModification.checkIdentifierReferencesData(child, data);
+ resolveChildModification(child).merge(data);
+ }
+
+ @Override
+ public void write(final PathArgument child, final NormalizedNode<?, ?> data) {
+ ensureNotClosed();
+ InMemoryDataTreeModification.checkIdentifierReferencesData(child, data);
+ resolveChildModification(child).write(data);
+ }
+}
import com.google.common.base.Preconditions;
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.NormalizedNodeContainer;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.CursorAwareDataTreeSnapshot;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshotCursor;
import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-final class InMemoryDataTreeSnapshot implements DataTreeSnapshot {
+final class InMemoryDataTreeSnapshot extends AbstractCursorAware implements CursorAwareDataTreeSnapshot {
private final RootModificationApplyOperation applyOper;
private final SchemaContext schemaContext;
private final TreeNode rootNode;
return new InMemoryDataTreeModification(this, applyOper);
}
+ @Override
+ public DataTreeSnapshotCursor createCursor(final YangInstanceIdentifier path) {
+ final Optional<NormalizedNode<?, ?>> maybeRoot = NormalizedNodes.findNode(rootNode.getData(), path);
+ if (!maybeRoot.isPresent()) {
+ return null;
+ }
+
+ final NormalizedNode<?, ?> root = maybeRoot.get();
+ Preconditions.checkArgument(root instanceof NormalizedNodeContainer, "Child %s is not a container", path);
+ return openCursor(new InMemoryDataTreeSnapshotCursor(this, path, (NormalizedNodeContainer<?, ?, ?>)root));
+ }
+
@Override
public String toString() {
return rootNode.getSubtreeVersion().toString();
}
-
}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+
+final class InMemoryDataTreeSnapshotCursor extends AbstractCursor<InMemoryDataTreeSnapshot> {
+ private final Deque<NormalizedNodeContainer<?, ?, ?>> stack = new ArrayDeque<>();
+
+ InMemoryDataTreeSnapshotCursor(final InMemoryDataTreeSnapshot parent, final YangInstanceIdentifier rootPath, final NormalizedNodeContainer<?, ?, ?> normalizedNode) {
+ super(parent, rootPath);
+ stack.push(normalizedNode);
+ }
+
+ @Override
+ public void enter(final PathArgument child) {
+ final Optional<NormalizedNode<?, ?>> maybeChildNode = NormalizedNodes.getDirectChild(stack.peek(), child);
+ Preconditions.checkArgument(maybeChildNode.isPresent(), "Child %s not found", child);
+
+ final NormalizedNode<?, ?> childNode = maybeChildNode.get();
+ Preconditions.checkArgument(childNode instanceof NormalizedNodeContainer, "Child %s is not a container", child);
+ stack.push((NormalizedNodeContainer<?, ?, ?>) childNode);
+ }
+
+ @Override
+ public void enter(final Iterable<PathArgument> path) {
+ final Optional<NormalizedNode<?, ?>> maybeChildNode = NormalizedNodes.findNode(stack.peek(), path);
+ Preconditions.checkArgument(maybeChildNode.isPresent(), "Child %s not found", path);
+
+ final NormalizedNode<?, ?> childNode = maybeChildNode.get();
+ Preconditions.checkArgument(childNode instanceof NormalizedNodeContainer, "Child %s is not a container", path);
+
+ int depth = 0;
+ for (PathArgument arg : path) {
+ try {
+ enter(arg);
+ } catch (Exception e) {
+ for (int i = 0; i < depth; ++i) {
+ stack.pop();
+ }
+ throw new IllegalArgumentException(e);
+ }
+
+ ++depth;
+ }
+ }
+
+ @Override
+ public void exit(final int depth) {
+ Preconditions.checkArgument(depth >= 0);
+ Preconditions.checkState(depth < stack.size());
+
+ for (int i = 0; i < depth; ++i) {
+ stack.pop();
+ }
+ }
+
+ @Override
+ public Optional<NormalizedNode<?, ?>> readNode(final PathArgument child) {
+ return NormalizedNodes.findNode(stack.peek(), child);
+ }
+}
*/
package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
final class OperationWithModification {
+ private static final Function<TreeNode, NormalizedNode<?, ?>> READ_DATA = new Function<TreeNode, NormalizedNode<?, ?>>() {
+ @Override
+ public NormalizedNode<?, ?> apply(final TreeNode input) {
+ return input.getData();
+ }
+ };
private final ModifiedNode modification;
-
private final ModificationApplyOperation applyOperation;
private OperationWithModification(final ModificationApplyOperation op, final ModifiedNode mod) {
- this.modification = mod;
- this.applyOperation = op;
+ this.modification = Preconditions.checkNotNull(mod);
+ this.applyOperation = Preconditions.checkNotNull(op);
}
void write(final NormalizedNode<?, ?> value) {
}
private void recursiveMerge(final NormalizedNode<?,?> data) {
- if (data instanceof NormalizedNodeContainer<?,?,?>) {
+ if (data instanceof NormalizedNodeContainer) {
@SuppressWarnings({ "rawtypes", "unchecked" })
final
NormalizedNodeContainer<?,?,NormalizedNode<PathArgument, ?>> dataContainer = (NormalizedNodeContainer) data;
* retain children created by past write operation.
* These writes will then be pushed down in the tree while there are merge modifications on these children
*/
- if (modification.getOperation().equals(LogicalOperation.WRITE)) {
+ if (modification.getOperation() == LogicalOperation.WRITE) {
@SuppressWarnings({ "rawtypes", "unchecked" })
final
NormalizedNodeContainer<?,?,NormalizedNode<PathArgument, ?>> odlDataContainer =
modification.delete();
}
+ /**
+ * Read a particular child. If the child has been modified and does not have a stable
+ * view, one will we instantiated with specified version.
+ *
+ * @param child
+ * @param version
+ * @return
+ */
+ Optional<NormalizedNode<?, ?>> read(final PathArgument child, final Version version) {
+ final Optional<ModifiedNode> maybeChild = modification.getChild(child);
+ if (maybeChild.isPresent()) {
+ final ModifiedNode childNode = maybeChild.get();
+
+ Optional<TreeNode> snapshot = childNode.getSnapshot();
+ if (snapshot == null) {
+ // Snapshot is not present, force instantiation
+ snapshot = applyOperation.getChild(child).get().apply(childNode, childNode.getOriginal(), version);
+ }
+
+ return snapshot.transform(READ_DATA);
+ }
+
+ Optional<TreeNode> snapshot = modification.getSnapshot();
+ if (snapshot == null) {
+ snapshot = apply(modification.getOriginal(), version);
+ }
+
+ if (snapshot.isPresent()) {
+ return snapshot.get().getChild(child).transform(READ_DATA);
+ }
+
+ return Optional.absent();
+ }
+
public ModifiedNode getModification() {
return modification;
}