sort by descending number of suspected transaction leaks in trace output
[controller.git] / opendaylight / md-sal / sal-inmemory-datastore / src / main / java / org / opendaylight / controller / md / sal / dom / store / impl / ResolveDataChangeEventsTask.java
index 5fe9866b12ed0ec6e2a669de4cc7bd3b9957b641..2e45150bb34576f77e8f1a58d9fef913a7d2fb1b 100644 (file)
@@ -7,19 +7,19 @@
  */
 package org.opendaylight.controller.md.sal.dom.store.impl;
 
-import com.google.common.base.Optional;
+import com.google.common.annotations.Beta;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Multimap;
-
 import java.util.Collection;
 import java.util.Map.Entry;
-
+import java.util.Optional;
 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.ListenerTree;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree.Walker;
+import org.opendaylight.controller.sal.core.compat.DataChangeListenerRegistration;
+import org.opendaylight.controller.sal.core.compat.ListenerTree;
+import org.opendaylight.mdsal.dom.spi.RegistrationTreeSnapshot;
 import org.opendaylight.yangtools.util.concurrent.NotificationManager;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
@@ -33,10 +33,11 @@ import org.slf4j.LoggerFactory;
 /**
  * Resolve Data Change Events based on modifications and listeners
  *
- * Computes data change events for all affected registered listeners in data
- * tree.
+ * <p>
+ * Computes data change events for all affected registered listeners in data tree.
  */
-final class ResolveDataChangeEventsTask {
+@Beta
+public final class ResolveDataChangeEventsTask {
     private static final Logger LOG = LoggerFactory.getLogger(ResolveDataChangeEventsTask.class);
 
     private final DataTreeCandidate candidate;
@@ -44,7 +45,7 @@ final class ResolveDataChangeEventsTask {
 
     private Multimap<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> collectedEvents;
 
-    public ResolveDataChangeEventsTask(final DataTreeCandidate candidate, final ListenerTree listenerTree) {
+    private ResolveDataChangeEventsTask(final DataTreeCandidate candidate, final ListenerTree listenerTree) {
         this.candidate = Preconditions.checkNotNull(candidate);
         this.listenerRoot = Preconditions.checkNotNull(listenerTree);
     }
@@ -52,8 +53,9 @@ final class ResolveDataChangeEventsTask {
     /**
      * Resolves and submits notification tasks to the specified manager.
      */
-    public synchronized void resolve(final NotificationManager<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> manager) {
-        try (final Walker w = listenerRoot.getWalker()) {
+    public synchronized void resolve(final NotificationManager<DataChangeListenerRegistration<?>,
+            DOMImmutableDataChangeEvent> manager) {
+        try (RegistrationTreeSnapshot<DataChangeListenerRegistration<?>> w = listenerRoot.takeSnapshot()) {
             // Defensive: reset internal state
             collectedEvents = ArrayListMultimap.create();
 
@@ -65,7 +67,8 @@ final class ResolveDataChangeEventsTask {
              * Convert to tasks, but be mindful of multiple values -- those indicate multiple
              * wildcard matches, which need to be merged.
              */
-            for (Entry<DataChangeListenerRegistration<?>, Collection<DOMImmutableDataChangeEvent>> e : collectedEvents.asMap().entrySet()) {
+            for (Entry<DataChangeListenerRegistration<?>, Collection<DOMImmutableDataChangeEvent>> e :
+                    collectedEvents.asMap().entrySet()) {
                 final Collection<DOMImmutableDataChangeEvent> col = e.getValue();
                 final DOMImmutableDataChangeEvent event;
 
@@ -87,7 +90,7 @@ final class ResolveDataChangeEventsTask {
     }
 
     /**
-     * Resolves data change event for supplied node
+     * Resolves data change event for supplied node.
      *
      * @param path
      *            Path to current node in tree
@@ -103,38 +106,49 @@ final class ResolveDataChangeEventsTask {
      * @return True if the subtree changed, false otherwise
      */
     private boolean resolveAnyChangeEvent(final ResolveDataChangeState state, final DataTreeCandidateNode node) {
-        if (node.getModificationType() != ModificationType.UNMODIFIED &&
-                !node.getDataAfter().isPresent() && !node.getDataBefore().isPresent()) {
+        final Optional<NormalizedNode<?, ?>> maybeBefore = node.getDataBefore();
+        final Optional<NormalizedNode<?, ?>> maybeAfter = node.getDataAfter();
+        final ModificationType type = node.getModificationType();
+
+        if (type != ModificationType.UNMODIFIED && !maybeAfter.isPresent() && !maybeBefore.isPresent()) {
             LOG.debug("Modification at {} has type {}, but no before- and after-data. Assuming unchanged.",
-                    state.getPath(), node.getModificationType());
+                    state.getPath(), type);
             return false;
         }
 
         // no before and after state is present
 
-        switch (node.getModificationType()) {
-        case SUBTREE_MODIFIED:
-            return resolveSubtreeChangeEvent(state, node);
-        case MERGE:
-        case WRITE:
-            Preconditions.checkArgument(node.getDataAfter().isPresent(),
-                    "Modification at {} has type {} but no after-data", state.getPath(), node.getModificationType());
-            if (!node.getDataBefore().isPresent()) {
-                resolveCreateEvent(state, node.getDataAfter().get());
-                return true;
-            }
+        switch (type) {
+            case SUBTREE_MODIFIED:
+                return resolveSubtreeChangeEvent(state, node);
+            case APPEARED:
+            case WRITE:
+                Preconditions.checkArgument(maybeAfter.isPresent(),
+                        "Modification at {} has type {} but no after-data", state.getPath(), type);
+                if (!maybeBefore.isPresent()) {
+                    @SuppressWarnings({ "unchecked", "rawtypes" })
+                    final NormalizedNode<PathArgument, ?> afterNode = (NormalizedNode)maybeAfter.get();
+                    resolveSameEventRecursivelly(state, afterNode, DOMImmutableDataChangeEvent.getCreateEventFactory());
+                    return true;
+                }
 
-            return resolveReplacedEvent(state, node.getDataBefore().get(), node.getDataAfter().get());
-        case DELETE:
-            Preconditions.checkArgument(node.getDataBefore().isPresent(),
-                    "Modification at {} has type {} but no before-data", state.getPath(), node.getModificationType());
-            resolveDeleteEvent(state, node.getDataBefore().get());
-            return true;
-        case UNMODIFIED:
-            return false;
+                return resolveReplacedEvent(state, maybeBefore.get(), maybeAfter.get());
+            case DISAPPEARED:
+            case DELETE:
+                Preconditions.checkArgument(maybeBefore.isPresent(),
+                        "Modification at {} has type {} but no before-data", state.getPath(), type);
+
+                @SuppressWarnings({ "unchecked", "rawtypes" })
+                final NormalizedNode<PathArgument, ?> beforeNode = (NormalizedNode)maybeBefore.get();
+                resolveSameEventRecursivelly(state, beforeNode, DOMImmutableDataChangeEvent.getRemoveEventFactory());
+                return true;
+            case UNMODIFIED:
+                return false;
+            default:
+                break;
         }
 
-        throw new IllegalStateException(String.format("Unhandled node state %s at %s", node.getModificationType(), state.getPath()));
+        throw new IllegalStateException(String.format("Unhandled node state %s at %s", type, state.getPath()));
     }
 
     private boolean resolveReplacedEvent(final ResolveDataChangeState state,
@@ -147,11 +161,14 @@ final class ResolveDataChangeEventsTask {
              * resolution of changes on children level and can not
              * shortcut resolution.
              */
-            LOG.trace("Resolving subtree replace event for {} before {}, after {}", state.getPath(), beforeData, afterData);
+            LOG.trace("Resolving subtree replace event for {} before {}, after {}", state.getPath(), beforeData,
+                    afterData);
             @SuppressWarnings("unchecked")
-            NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> beforeCont = (NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>>) beforeData;
+            NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> beforeCont =
+                (NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>>) beforeData;
             @SuppressWarnings("unchecked")
-            NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> afterCont = (NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>>) afterData;
+            NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> afterCont =
+                (NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>>) afterData;
             return resolveNodeContainerReplaced(state, beforeCont, afterCont);
         }
 
@@ -163,7 +180,8 @@ final class ResolveDataChangeEventsTask {
         }
 
         LOG.trace("Resolving leaf replace event for {} , before {}, after {}", state.getPath(), beforeData, afterData);
-        DOMImmutableDataChangeEvent event = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE).addUpdated(state.getPath(), beforeData, afterData).build();
+        DOMImmutableDataChangeEvent event = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE)
+                .addUpdated(state.getPath(), beforeData, afterData).build();
         state.addEvent(event);
         state.collectEvents(beforeData, afterData, collectedEvents);
         return true;
@@ -196,7 +214,8 @@ final class ResolveDataChangeEventsTask {
              * created.
              */
             if (!beforeCont.getChild(childId).isPresent()) {
-                resolveSameEventRecursivelly(state.child(childId), afterChild, DOMImmutableDataChangeEvent.getCreateEventFactory());
+                resolveSameEventRecursivelly(state.child(childId), afterChild,
+                        DOMImmutableDataChangeEvent.getCreateEventFactory());
                 childChanged = true;
             }
         }
@@ -223,26 +242,6 @@ final class ResolveDataChangeEventsTask {
         return true;
     }
 
-    /**
-     * Resolves create events deep down the interest listener tree.
-     *
-     * @param path
-     * @param listeners
-     * @param afterState
-     * @return
-     */
-    private void resolveCreateEvent(final ResolveDataChangeState state, final NormalizedNode<?, ?> afterState) {
-        @SuppressWarnings({ "unchecked", "rawtypes" })
-        final NormalizedNode<PathArgument, ?> node = (NormalizedNode) afterState;
-        resolveSameEventRecursivelly(state, node, DOMImmutableDataChangeEvent.getCreateEventFactory());
-    }
-
-    private void resolveDeleteEvent(final ResolveDataChangeState state, final NormalizedNode<?, ?> beforeState) {
-        @SuppressWarnings({ "unchecked", "rawtypes" })
-        final NormalizedNode<PathArgument, ?> node = (NormalizedNode) beforeState;
-        resolveSameEventRecursivelly(state, node, DOMImmutableDataChangeEvent.getRemoveEventFactory());
-    }
-
     private void resolveSameEventRecursivelly(final ResolveDataChangeState state,
             final NormalizedNode<PathArgument, ?> node, final SimpleEventFactory eventFactory) {
         if (!state.needsProcessing()) {
@@ -258,7 +257,8 @@ final class ResolveDataChangeEventsTask {
             // Node has children, so we will try to resolve it's children
             // changes.
             @SuppressWarnings("unchecked")
-            NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> container = (NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>>) node;
+            NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> container =
+                (NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>>) node;
             for (NormalizedNode<PathArgument, ?> child : container.getValue()) {
                 final PathArgument childId = child.getIdentifier();
 
@@ -273,38 +273,53 @@ final class ResolveDataChangeEventsTask {
         state.collectEvents(event.getOriginalSubtree(), event.getUpdatedSubtree(), collectedEvents);
     }
 
-    private boolean resolveSubtreeChangeEvent(final ResolveDataChangeState state, final DataTreeCandidateNode modification) {
-        Preconditions.checkArgument(modification.getDataBefore().isPresent(), "Subtree change with before-data not present at path %s", state.getPath());
-        Preconditions.checkArgument(modification.getDataAfter().isPresent(), "Subtree change with after-data not present at path %s", state.getPath());
+    private boolean resolveSubtreeChangeEvent(final ResolveDataChangeState state,
+            final DataTreeCandidateNode modification) {
+        final Optional<NormalizedNode<?, ?>> maybeBefore = modification.getDataBefore();
+        final Optional<NormalizedNode<?, ?>> maybeAfter = modification.getDataAfter();
+
+        Preconditions.checkArgument(maybeBefore.isPresent(), "Subtree change with before-data not present at path %s",
+                state.getPath());
+        Preconditions.checkArgument(maybeAfter.isPresent(), "Subtree change with after-data not present at path %s",
+                state.getPath());
+
+        if (!state.needsProcessing()) {
+            LOG.trace("Not processing modified subtree {}", state.getPath());
+            return true;
+        }
 
         DataChangeScope scope = null;
         for (DataTreeCandidateNode childMod : modification.getChildNodes()) {
             final ResolveDataChangeState childState = state.child(childMod.getIdentifier());
 
             switch (childMod.getModificationType()) {
-            case WRITE:
-            case MERGE:
-            case DELETE:
-                if (resolveAnyChangeEvent(childState, childMod)) {
-                    scope = DataChangeScope.ONE;
-                }
-                break;
-            case SUBTREE_MODIFIED:
-                if (resolveSubtreeChangeEvent(childState, childMod) && scope == null) {
-                    scope = DataChangeScope.SUBTREE;
-                }
-                break;
-            case UNMODIFIED:
-                // no-op
-                break;
+                case APPEARED:
+                case DELETE:
+                case DISAPPEARED:
+                case WRITE:
+                    if (resolveAnyChangeEvent(childState, childMod)) {
+                        scope = DataChangeScope.ONE;
+                    }
+                    break;
+                case SUBTREE_MODIFIED:
+                    if (resolveSubtreeChangeEvent(childState, childMod) && scope == null) {
+                        scope = DataChangeScope.SUBTREE;
+                    }
+                    break;
+                case UNMODIFIED:
+                    // no-op
+                    break;
+                default:
+                    break;
             }
         }
 
-        final NormalizedNode<?, ?> before = modification.getDataBefore().get();
-        final NormalizedNode<?, ?> after = modification.getDataAfter().get();
+        final NormalizedNode<?, ?> before = maybeBefore.get();
+        final NormalizedNode<?, ?> after = maybeAfter.get();
 
         if (scope != null) {
-            DOMImmutableDataChangeEvent one = DOMImmutableDataChangeEvent.builder(scope).addUpdated(state.getPath(), before, after).build();
+            DOMImmutableDataChangeEvent one = DOMImmutableDataChangeEvent.builder(scope)
+                    .addUpdated(state.getPath(), before, after).build();
             state.addEvent(one);
         }
 
@@ -312,7 +327,8 @@ final class ResolveDataChangeEventsTask {
         return scope != null;
     }
 
-    public static ResolveDataChangeEventsTask create(final DataTreeCandidate candidate, final ListenerTree listenerTree) {
+    public static ResolveDataChangeEventsTask create(final DataTreeCandidate candidate,
+            final ListenerTree listenerTree) {
         return new ResolveDataChangeEventsTask(candidate, listenerTree);
     }
 }