This removes unused methods and adds interface contract documentation.
Change-Id: I7bc57d034da1d0503d76afa6f4c09ce6a6bfe669
Signed-off-by: Robert Varga <rovarga@cisco.com>
ready = true;
LOG.debug("Store transaction: {} : Ready", getIdentifier());
- mutableTree.seal();
+ mutableTree.ready();
return store.submit(this);
}
package org.opendaylight.controller.md.sal.dom.store.impl;
import static org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.builder;
-import static org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreUtils.append;
import java.util.Collection;
import java.util.Collections;
import com.google.common.collect.Multimap;
/**
- *
* Resolve Data Change Events based on modifications and listeners
*
* Computes data change events for all affected registered listeners in data
* tree.
- *
- * Prerequisites for computation is to set all parameters properly:
- * <ul>
- * <li>{@link #setRootPath(InstanceIdentifier)} - Root path of datastore
- * <li>{@link #setListenerRoot(ListenerTree)} - Root of listener registration
- * tree, which contains listeners to be notified
- * <li>{@link #setModificationRoot(NodeModification)} - Modification root, for
- * which events should be computed
- * <li>{@link #setBeforeRoot(Optional)} - State of before modification occurred
- * <li>{@link #setAfterRoot(Optional)} - State of after modification occurred
- * </ul>
- *
*/
final class ResolveDataChangeEventsTask implements Callable<Iterable<ChangeListenerNotifyTask>> {
private static final Logger LOG = LoggerFactory.getLogger(ResolveDataChangeEventsTask.class);
private final DataTreeCandidate candidate;
private final ListenerTree listenerRoot;
- public ResolveDataChangeEventsTask(DataTreeCandidate candidate, ListenerTree listenerTree) {
+ public ResolveDataChangeEventsTask(final DataTreeCandidate candidate, final ListenerTree listenerTree) {
this.candidate = Preconditions.checkNotNull(candidate);
this.listenerRoot = Preconditions.checkNotNull(listenerTree);
}
* Implementation of done as Map-Reduce with two steps: 1. resolving events
* and their mapping to listeners 2. merging events affecting same listener
*
- * @return Iterable of Notification Tasks which needs to be executed in
+ * @return An {@link Iterable} of Notification Tasks which needs to be executed in
* order to delivery data change events.
*/
@Override
for (NormalizedNode<PathArgument, ?> beforeChild : beforeCont.getValue()) {
PathArgument childId = beforeChild.getIdentifier();
alreadyProcessed.add(childId);
- InstanceIdentifier childPath = append(path, childId);
+ InstanceIdentifier childPath = path.node(childId);
Collection<ListenerTree.Node> childListeners = getListenerChildrenWildcarded(listeners, childId);
Optional<NormalizedNode<PathArgument, ?>> afterChild = afterCont.getChild(childId);
DOMImmutableDataChangeEvent childChange = resolveNodeContainerChildUpdated(childPath, childListeners,
// and it was not present in previous loop, that means it is
// created.
Collection<ListenerTree.Node> childListeners = getListenerChildrenWildcarded(listeners, childId);
- InstanceIdentifier childPath = append(path,childId);
+ InstanceIdentifier childPath = path.node(childId);
childChanges.add(resolveSameEventRecursivelly(childPath , childListeners, afterChild,
DOMImmutableDataChangeEvent.getCreateEventFactory()));
}
PathArgument childId = child.getIdentifier();
LOG.trace("Resolving event for child {}", childId);
Collection<Node> childListeners = getListenerChildrenWildcarded(listeners, childId);
- eventBuilder.merge(resolveSameEventRecursivelly(append(path, childId), childListeners, child, eventFactory));
+ eventBuilder.merge(resolveSameEventRecursivelly(path.node(childId), childListeners, child, eventFactory));
}
propagateEvent = eventBuilder.build();
} else {
for (DataTreeCandidateNode childMod : modification.getChildNodes()) {
PathArgument childId = childMod.getIdentifier();
- InstanceIdentifier childPath = append(path, childId);
+ InstanceIdentifier childPath = path.node(childId);
Collection<ListenerTree.Node> childListeners = getListenerChildrenWildcarded(listeners, childId);
switch (childMod.getModificationType()) {
}
}
- public static ResolveDataChangeEventsTask create(DataTreeCandidate candidate, ListenerTree listenerTree) {
+ public static ResolveDataChangeEventsTask create(final DataTreeCandidate candidate, final ListenerTree listenerTree) {
return new ResolveDataChangeEventsTask(candidate, listenerTree);
}
}
import com.google.common.base.Preconditions;
+/**
+ * Exception thrown when a proposed change fails validation before being
+ * applied into the datastore. This can have multiple reasons, for example
+ * the datastore has been concurrently modified such that a conflicting
+ * node is present, or the modification is structurally incorrect.
+ */
public class DataPreconditionFailedException extends Exception {
private static final long serialVersionUID = 1L;
private final InstanceIdentifier path;
+ /**
+ * Create a new instance.
+ *
+ * @param path Object path which caused this exception
+ * @param message Specific message describing the failure
+ */
public DataPreconditionFailedException(final InstanceIdentifier path, final String message) {
- super(message);
- this.path = Preconditions.checkNotNull(path);
+ this(path, message, null);
}
-
+ /**
+ * Create a new instance, initializing
+ *
+ * @param path Object path which caused this exception
+ * @param message Specific message describing the failure
+ * @param cause Exception which triggered this failure, may be null
+ */
public DataPreconditionFailedException(final InstanceIdentifier path, final String message, final Throwable cause) {
super(message, cause);
this.path = Preconditions.checkNotNull(path);
}
+ /**
+ * Returns the offending object path.
+ *
+ * @return Path of the offending object
+ */
public InstanceIdentifier getPath() {
return path;
}
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+/**
+ * An encapsulation of a validated data tree modification. This candidate
+ * is ready for atomic commit to the datastore. It allows access to before-
+ * and after-state as it will be seen in to subsequent commit. This capture
+ * can be accessed for reference, but cannot be modified and the content
+ * is limited to nodes which were affected by the modification from which
+ * this instance originated.
+ */
public interface DataTreeCandidate {
+ /**
+ * Get the candidate tree root node.
+ *
+ * @return Candidate tree root node
+ */
DataTreeCandidateNode getRootNode();
+
+ /**
+ * Get the candidate tree root path. This is the path of the root node
+ * relative to the root of InstanceIdentifier namespace.
+ *
+ * @return Relative path of the root node
+ */
InstanceIdentifier getRootPath();
}
import com.google.common.base.Optional;
+/**
+ * A single node within a {@link DataTreeCandidate}. The nodes are organized
+ * in tree hierarchy, reflecting the modification from which this candidate
+ * was created. The node itself exposes the before- and after-image of the
+ * tree restricted to the modified nodes.
+ */
public interface DataTreeCandidateNode {
+ /**
+ * Get the node identifier.
+ *
+ * @return The node identifier.
+ */
PathArgument getIdentifier();
+
+ /**
+ * Get an unmodifiable iterable of modified child nodes.
+ *
+ * @return Unmodifiable iterable of modified child nodes.
+ */
Iterable<DataTreeCandidateNode> getChildNodes();
+ /**
+ * Return the type of modification this node is undergoing.
+ *
+ * @return Node modification type.
+ */
ModificationType getModificationType();
+
+ /**
+ * Return the before-image of data corresponding to the node.
+ *
+ * @return Node data as they were present in the tree before
+ * the modification was applied.
+ */
Optional<NormalizedNode<?, ?>> getDataAfter();
+
+ /**
+ * Return the after-image of data corresponding to the node.
+ *
+ * @return Node data as they will be present in the tree after
+ * the modification is applied.
+ */
Optional<NormalizedNode<?, ?>> getDataBefore();
}
* has the ability to rebase itself to a new snapshot.
*/
public interface DataTreeModification extends DataTreeSnapshot {
- void delete(InstanceIdentifier path);
- void merge(InstanceIdentifier path, NormalizedNode<?, ?> data);
- void write(InstanceIdentifier path, NormalizedNode<?, ?> data);
- void seal();
+ /**
+ * Delete the node at specified path.
+ *
+ * @param path Node path
+ */
+ void delete(InstanceIdentifier path);
+
+ /**
+ * Merge the specified data with the currently-present data
+ * at specified path.
+ *
+ * @param path Node path
+ * @param data Data to be merged
+ */
+ void merge(InstanceIdentifier path, NormalizedNode<?, ?> data);
+
+ /**
+ * Replace the data at specified path with supplied data.
+ *
+ * @param path Node path
+ * @param data New node data
+ */
+ void write(InstanceIdentifier path, NormalizedNode<?, ?> data);
+
+ /**
+ * Finish creation of a modification, making it ready for application
+ * to the data tree. Any calls to this object's methods will result
+ * in undefined behavior, possibly with an
+ * {@link IllegalStateException} being thrown.
+ */
+ void ready();
}
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
+/**
+ * A set of listeners organized as a tree by node to which they listen. This class
+ * allows for efficient lookup of listeners when we walk the DataTreeCandidate.
+ */
public final class ListenerTree {
private static final Logger LOG = LoggerFactory.getLogger(ListenerTree.class);
private final ReadWriteLock rwLock = new ReentrantReadWriteLock(true);
private final Node rootNode = new Node(null, null);
private ListenerTree() {
-
+ // Private to disallow direct instantiation
}
+ /**
+ * Create a new empty instance of the listener tree.
+ *
+ * @return An empty instance.
+ */
public static ListenerTree create() {
return new ListenerTree();
}
}
}
+ /**
+ * Obtain a tree walking context. This context ensures a consistent view of
+ * the listener registrations. The context should be closed as soon as it
+ * is not required, because each unclosed instance blocks modification of
+ * the listener tree.
+ *
+ * @return A walker instance.
+ */
public Walker getWalker() {
/*
* TODO: The only current user of this method is local to the datastore.
return ret;
}
+ /**
+ * A walking context, pretty much equivalent to an iterator, but it
+ * exposes the undelying tree structure.
+ */
public static final class Walker implements AutoCloseable {
private final Lock lock;
private final Node node;
*/
package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+/**
+ * Enumeration of all possible node modification states. These are used in
+ * data tree modification context to quickly assess what sort of modification
+ * the node is undergoing.
+ */
public enum ModificationType {
-
/**
- *
- * Node is unmodified
- *
- *
+ * Node is currently unmodified.
*/
UNMODIFIED,
+
/**
- *
- * Child of tree node was modified
- *
+ * A child node, either direct or indirect, has been modified. This means
+ * that the data representation of this node has potentially changed.
*/
SUBTREE_MODIFIED,
+
/**
- * Tree node was replaced with new value / subtree
- *
+ * This node has been placed into the tree, potentially completely replacing
+ * pre-existing contents.
*/
WRITE,
+
/**
- *
- * Tree node is to be deleted.
- *
+ * This node has been deleted along with any of its child nodes.
*/
DELETE,
/**
- *
- * Tree node is to be merged with existing one.
- *
+ * Node has been written into the tree, but instead of replacing pre-existing
+ * contents, it has been merged. This means that any incoming nodes which
+ * were present in the tree have been replaced, but their child nodes have
+ * been retained.
*/
- MERGE
+ MERGE,
}
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import com.google.common.base.Optional;
+
/**
- *
- * Tree node which contains references to it's leafs
+ * A tree node which has references to its child leaves. This are typically
+ * internal non-data leaves, such as containers, lists, etc.
*
* @param <C> Final node type
*/
public interface StoreTreeNode<C extends StoreTreeNode<C>> {
/**
- *
- * Returns direct child of the node
+ * Returns a direct child of the node
*
* @param child Identifier of child
* @return Optional with node if the child is existing, {@link Optional#absent()} otherwise.
*/
package org.opendaylight.controller.md.sal.dom.store.impl.tree;
-import java.util.Set;
-
-import org.opendaylight.yangtools.concepts.Identifiable;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
-import com.google.common.base.Function;
import com.google.common.base.Strings;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.primitives.UnsignedLong;
+/**
+ * Data store tree manipulation utilities.
+ */
public final class StoreUtils {
private static final int STRINGTREE_INDENT = 4;
- private final static Function<Identifiable<Object>, Object> EXTRACT_IDENTIFIER = new Function<Identifiable<Object>, Object>() {
- @Override
- public Object apply(final Identifiable<Object> input) {
- return input.getIdentifier();
- }
- };
-
private StoreUtils() {
throw new UnsupportedOperationException("Utility class should not be instantiated");
}
- /*
- * Suppressing warnings here allows us to fool the compiler enough
- * such that we can reuse a single function for all applicable types
- * and present it in a type-safe manner to our users.
+ /**
+ * Convert a data subtree under a node into a human-readable string format.
+ *
+ * @param node Data subtree root
+ * @return String containing a human-readable form of the subtree.
*/
- @SuppressWarnings({ "unchecked", "rawtypes" })
- public static <V> Function<Identifiable<V>, V> identifierExtractor() {
- return (Function) EXTRACT_IDENTIFIER;
- }
-
- public static final UnsignedLong increase(final UnsignedLong original) {
- return original.plus(UnsignedLong.ONE);
- }
-
- public static final InstanceIdentifier append(final InstanceIdentifier parent, final PathArgument arg) {
- return new InstanceIdentifier(ImmutableList.<PathArgument> builder().addAll(parent.getPath()).add(arg).build());
- }
-
- public static <V> Set<V> toIdentifierSet(final Iterable<? extends Identifiable<V>> children) {
- return FluentIterable.from(children).transform(StoreUtils.<V> identifierExtractor()).toSet();
- }
-
public static String toStringTree(final NormalizedNode<?, ?> node) {
- StringBuilder builder = new StringBuilder();
+ final StringBuilder builder = new StringBuilder();
toStringTree(builder, node, 0);
return builder.toString();
}
import java.util.List;
import java.util.Map;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.TreeNode;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
+/**
+ * A set of utility methods for interacting with {@link TreeNode} objects.
+ */
public final class TreeNodeUtils {
private TreeNodeUtils() {
throw new UnsupportedOperationException("Utility class should not be instantiated");
* @param tree Data Tree
* @param path Path to the node
* @return Optional with node if the node is present in tree, {@link Optional#absent()} otherwise.
- *
*/
public static <T extends StoreTreeNode<T>> Optional<T> findNode(final T tree, final InstanceIdentifier path) {
Optional<T> current = Optional.<T> of(tree);
}
@Override
- public synchronized void seal() {
+ public synchronized void ready() {
Preconditions.checkState(!sealed, "Attempted to seal an already-sealed Data Tree.");
sealed = true;
rootNode.seal();
import com.google.common.base.Optional;
+/**
+ * Internal interface representing a modification action of a particular node.
+ * It is used by the validation code to allow for a read-only view of the
+ * modification tree as we should never modify that during validation.
+ */
interface NodeModification extends Identifiable<PathArgument> {
+ /**
+ * Get the type of modification.
+ *
+ * @return Modification type.
+ */
ModificationType getType();
+
+ /**
+ * Get the original tree node to which the modification is to be applied.
+ *
+ * @return The original node, or {@link Optional#absent()} if the node is
+ * a new node.
+ */
Optional<TreeNode> getOriginal();
+
+ /**
+ * Get a read-only view of children nodes.
+ *
+ * @return Iterable of all children nodes.
+ */
Iterable<? extends NodeModification> getChildren();
}
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
+/**
+ * Internal utility class for an empty candidate. We instantiate this class
+ * for empty modifications, saving memory and processing speed. Instances
+ * of this class are explicitly recognized and processing of them is skipped.
+ */
final class NoopDataTreeCandidate extends AbstractDataTreeCandidate {
private static final DataTreeCandidateNode ROOT = new DataTreeCandidateNode() {
@Override
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreUtils;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.DataNodeContainerModificationStrategy.ListEntryModificationStrategy;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.ValueNodeModificationStrategy.LeafSetEntryModificationStrategy;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.MutableTreeNode;
final PathArgument childId = childMod.getIdentifier();
final Optional<TreeNode> childMeta = currentMeta.getChild(childId);
- InstanceIdentifier childPath = StoreUtils.append(path, childId);
+ InstanceIdentifier childPath = path.node(childId);
resolveChildOperation(childId).checkApplicable(childPath, childMod, childMeta);
}
}
import com.google.common.base.Preconditions;
-/*
+/**
* A very basic data tree node.
*/
abstract class AbstractTreeNode implements TreeNode {
* any interactions with it will result in undefined behavior.
*/
public interface MutableTreeNode extends StoreTreeNode<TreeNode> {
+ /**
+ * Set the data component of the node.
+ *
+ * @param data New data component, may not be null.
+ */
void setData(NormalizedNode<?, ?> data);
+
+ /**
+ * Set the new subtree version. This is typically invoked when the user
+ * has modified some of this node's children.
+ *
+ * @param subtreeVersion New subtree version.
+ */
void setSubtreeVersion(Version subtreeVersion);
+
+ /**
+ * Add a new child node. This acts as add-or-replace operation, e.g. it
+ * succeeds even if a conflicting child is already present.
+ *
+ * @param child New child node.
+ */
void addChild(TreeNode child);
+
+ /**
+ * Remove a child node. This acts as delete-or-nothing operation, e.g. it
+ * succeeds even if the corresponding child is not present.
+ *
+ * @param id Child identificator.
+ */
void removeChild(PathArgument id);
+
+ /**
+ * Finish node modification and return a read-only view of this node. After
+ * this method is invoked, any further calls to this object's method result
+ * in undefined behavior.
+ *
+ * @return Read-only view of this node.
+ */
TreeNode seal();
}
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-/*
+/**
* A very basic data tree node. It has a version (when it was last modified),
* a subtree version (when any of its children were modified) and some read-only
* data.
import com.google.common.base.Optional;
+/**
+ * Concretization of AbstractTreeNode for leaf nodes which only contain data.
+ * Instances of this class report all children as absent, subtree version
+ * equal to this node's version and do not support mutable view.
+ */
final class ValueNode extends AbstractTreeNode {
private static final Logger LOG = LoggerFactory.getLogger(ValueNode.class);