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.StoreUtils.append;
import java.util.Collection;
import java.util.Collections;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.Builder;
import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.SimpleEventFactory;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeCandidate;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeCandidateNode;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree.Node;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree.Walker;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
import org.slf4j.LoggerFactory;
import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
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>
- *
*/
-public class ResolveDataChangeEventsTask implements Callable<Iterable<ChangeListenerNotifyTask>> {
+final class ResolveDataChangeEventsTask implements Callable<Iterable<ChangeListenerNotifyTask>> {
private static final Logger LOG = LoggerFactory.getLogger(ResolveDataChangeEventsTask.class);
private static final DOMImmutableDataChangeEvent NO_CHANGE = builder(DataChangeScope.BASE).build();
- private InstanceIdentifier rootPath;
- private ListenerTree listenerRoot;
- private NodeModification modificationRoot;
- private Optional<StoreMetadataNode> beforeRoot;
- private Optional<StoreMetadataNode> afterRoot;
private final Multimap<ListenerTree.Node, DOMImmutableDataChangeEvent> events = HashMultimap.create();
+ private final DataTreeCandidate candidate;
+ private final ListenerTree listenerRoot;
- protected InstanceIdentifier getRootPath() {
- return rootPath;
- }
-
- protected ResolveDataChangeEventsTask setRootPath(final InstanceIdentifier rootPath) {
- this.rootPath = rootPath;
- return this;
- }
-
- protected ListenerTree getListenerRoot() {
- return listenerRoot;
- }
-
- protected ResolveDataChangeEventsTask setListenerRoot(final ListenerTree listenerRoot) {
- this.listenerRoot = listenerRoot;
- return this;
- }
-
- protected NodeModification getModificationRoot() {
- return modificationRoot;
- }
-
- protected ResolveDataChangeEventsTask setModificationRoot(final NodeModification modificationRoot) {
- this.modificationRoot = modificationRoot;
- return this;
- }
-
- protected Optional<StoreMetadataNode> getBeforeRoot() {
- return beforeRoot;
- }
-
- protected ResolveDataChangeEventsTask setBeforeRoot(final Optional<StoreMetadataNode> beforeRoot) {
- this.beforeRoot = beforeRoot;
- return this;
- }
-
- protected Optional<StoreMetadataNode> getAfterRoot() {
- return afterRoot;
- }
-
- protected ResolveDataChangeEventsTask setAfterRoot(final Optional<StoreMetadataNode> afterRoot) {
- this.afterRoot = afterRoot;
- return this;
+ 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
public Iterable<ChangeListenerNotifyTask> call() {
- LOG.trace("Resolving events for {}", modificationRoot);
-
try (final Walker w = listenerRoot.getWalker()) {
- resolveAnyChangeEvent(rootPath, Collections.singleton(w.getRootNode()), modificationRoot, beforeRoot,
- afterRoot);
+ resolveAnyChangeEvent(candidate.getRootPath(), Collections.singleton(w.getRootNode()), candidate.getRootNode());
return createNotificationTasks();
}
}
* @return Data Change Event of this node and all it's children
*/
private DOMImmutableDataChangeEvent resolveAnyChangeEvent(final InstanceIdentifier path,
- final Collection<ListenerTree.Node> listeners, final NodeModification modification,
- final Optional<StoreMetadataNode> before, final Optional<StoreMetadataNode> after) {
- // No listeners are present in listener registration subtree
- // no before and after state is present
- if (!before.isPresent() && !after.isPresent()) {
+ final Collection<ListenerTree.Node> listeners, final DataTreeCandidateNode node) {
+
+ if (node.getModificationType() != ModificationType.UNMODIFIED &&
+ !node.getDataAfter().isPresent() && !node.getDataBefore().isPresent()) {
+ LOG.debug("Modification at {} has type {}, but no before- and after-data. Assuming unchanged.",
+ path, node.getModificationType());
return NO_CHANGE;
}
- switch (modification.getModificationType()) {
+
+ // no before and after state is present
+
+ switch (node.getModificationType()) {
case SUBTREE_MODIFIED:
- return resolveSubtreeChangeEvent(path, listeners, modification, before.get(), after.get());
+ return resolveSubtreeChangeEvent(path, listeners, node);
case MERGE:
case WRITE:
- if (before.isPresent()) {
- return resolveReplacedEvent(path, listeners, before.get().getData(), after.get().getData());
+ Preconditions.checkArgument(node.getDataAfter().isPresent(),
+ "Modification at {} has type {} but no after-data", path, node.getModificationType());
+ if (node.getDataBefore().isPresent()) {
+ return resolveReplacedEvent(path, listeners, node.getDataBefore().get(), node.getDataAfter().get());
} else {
- return resolveCreateEvent(path, listeners, after.get());
+ return resolveCreateEvent(path, listeners, node.getDataAfter().get());
}
case DELETE:
- return resolveDeleteEvent(path, listeners, before.get());
- default:
+ Preconditions.checkArgument(node.getDataBefore().isPresent(),
+ "Modification at {} has type {} but no before-data", path, node.getModificationType());
+ return resolveDeleteEvent(path, listeners, node.getDataBefore().get());
+ case UNMODIFIED:
return NO_CHANGE;
}
+ throw new IllegalStateException(String.format("Unhandled node state %s at %s", node.getModificationType(), path));
}
private DOMImmutableDataChangeEvent resolveReplacedEvent(final InstanceIdentifier path,
NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> afterCont = (NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>>) afterData;
return resolveNodeContainerReplaced(path, listeners, beforeCont, afterCont);
} else if (!beforeData.equals(afterData)) {
- // Node is either of Leaf type (does not contain child nodes)
- // or we do not have listeners, so normal equals method is
- // sufficient for determining change.
+ // Node is Leaf type (does not contain child nodes)
+ // so normal equals method is sufficient for determining change.
LOG.trace("Resolving leaf replace event for {} , before {}, after {}",path,beforeData,afterData);
DOMImmutableDataChangeEvent event = builder(DataChangeScope.BASE).setBefore(beforeData).setAfter(afterData)
.addUpdated(path, beforeData, afterData).build();
private DOMImmutableDataChangeEvent resolveNodeContainerReplaced(final InstanceIdentifier path,
final Collection<Node> listeners,
final NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> beforeCont,
- final NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> afterCont) {
+ final NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> afterCont) {
final Set<PathArgument> alreadyProcessed = new HashSet<>();
final List<DOMImmutableDataChangeEvent> childChanges = new LinkedList<>();
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()));
}
* @return
*/
private DOMImmutableDataChangeEvent resolveCreateEvent(final InstanceIdentifier path,
- final Collection<ListenerTree.Node> listeners, final StoreMetadataNode afterState) {
+ final Collection<ListenerTree.Node> listeners, final NormalizedNode<?, ?> afterState) {
@SuppressWarnings({ "unchecked", "rawtypes" })
- final NormalizedNode<PathArgument, ?> node = (NormalizedNode) afterState.getData();
+ final NormalizedNode<PathArgument, ?> node = (NormalizedNode) afterState;
return resolveSameEventRecursivelly(path, listeners, node, DOMImmutableDataChangeEvent.getCreateEventFactory());
}
private DOMImmutableDataChangeEvent resolveDeleteEvent(final InstanceIdentifier path,
- final Collection<ListenerTree.Node> listeners, final StoreMetadataNode beforeState) {
+ final Collection<ListenerTree.Node> listeners, final NormalizedNode<?, ?> beforeState) {
@SuppressWarnings({ "unchecked", "rawtypes" })
- final NormalizedNode<PathArgument, ?> node = (NormalizedNode) beforeState.getData();
+ final NormalizedNode<PathArgument, ?> node = (NormalizedNode) beforeState;
return resolveSameEventRecursivelly(path, listeners, node, DOMImmutableDataChangeEvent.getRemoveEventFactory());
}
final DOMImmutableDataChangeEvent event = eventFactory.create(path, node);
DOMImmutableDataChangeEvent propagateEvent = event;
// We have listeners for this node or it's children, so we will try
- // to do additional processing
+ // to do additional processing
if (node instanceof NormalizedNodeContainer<?, ?, ?>) {
LOG.trace("Resolving subtree recursive event for {}, type {}", path, eventFactory);
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 {
- // We do not dispatch leaf events since Binding Aware components do not support them.
- propagateEvent = builder(DataChangeScope.BASE).build();
}
if (!listeners.isEmpty()) {
addPartialTask(listeners, propagateEvent);
}
private DOMImmutableDataChangeEvent resolveSubtreeChangeEvent(final InstanceIdentifier path,
- final Collection<ListenerTree.Node> listeners, final NodeModification modification,
- final StoreMetadataNode before, final StoreMetadataNode after) {
+ final Collection<ListenerTree.Node> listeners, final DataTreeCandidateNode modification) {
- Builder one = builder(DataChangeScope.ONE).setBefore(before.getData()).setAfter(after.getData());
+ Preconditions.checkArgument(modification.getDataBefore().isPresent(), "Subtree change with before-data not present at path %s", path);
+ Preconditions.checkArgument(modification.getDataAfter().isPresent(), "Subtree change with after-data not present at path %s", path);
- Builder subtree = builder(DataChangeScope.SUBTREE).setBefore(before.getData()).setAfter(after.getData());
+ Builder one = builder(DataChangeScope.ONE).
+ setBefore(modification.getDataBefore().get()).
+ setAfter(modification.getDataAfter().get());
+ Builder subtree = builder(DataChangeScope.SUBTREE).
+ setBefore(modification.getDataBefore().get()).
+ setAfter(modification.getDataAfter().get());
- for (NodeModification childMod : modification.getModifications()) {
+ 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);
- Optional<StoreMetadataNode> childBefore = before.getChild(childId);
- Optional<StoreMetadataNode> childAfter = after.getChild(childId);
-
switch (childMod.getModificationType()) {
case WRITE:
case MERGE:
case DELETE:
- one.merge(resolveAnyChangeEvent(childPath, childListeners, childMod, childBefore, childAfter));
+ one.merge(resolveAnyChangeEvent(childPath, childListeners, childMod));
break;
case SUBTREE_MODIFIED:
- subtree.merge(resolveSubtreeChangeEvent(childPath, childListeners, childMod, childBefore.get(),
- childAfter.get()));
+ subtree.merge(resolveSubtreeChangeEvent(childPath, childListeners, childMod));
break;
case UNMODIFIED:
// no-op
}
}
- public static ResolveDataChangeEventsTask create() {
- return new ResolveDataChangeEventsTask();
+ public static ResolveDataChangeEventsTask create(final DataTreeCandidate candidate, final ListenerTree listenerTree) {
+ return new ResolveDataChangeEventsTask(candidate, listenerTree);
}
}