Bug 499: Improved data change listener tree management 48/5848/3
authorRobert Varga <rovarga@cisco.com>
Fri, 21 Mar 2014 12:30:45 +0000 (13:30 +0100)
committerRobert Varga <rovarga@cisco.com>
Fri, 4 Apr 2014 13:28:10 +0000 (15:28 +0200)
Change-Id: I62843c3c4df32dd5a5849511d41352fa6822550b
Signed-off-by: Tony Tkacik <ttkacik@cisco.com>
Signed-off-by: Robert Varga <rovarga@cisco.com>
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerImpl.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChangeListenerNotifyTask.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMImmutableDataChangeEvent.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DataChangeEventResolver.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/StoreUtils.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/ListenerRegistrationNode.java

index fcf8b40..fc87a91 100644 (file)
@@ -120,7 +120,7 @@ public class DOMDataBrokerImpl implements DOMDataBroker, AutoCloseable {
 
     private ListenableFuture<RpcResult<TransactionStatus>> submit(
             final WriteTransactionImpl<? extends DOMStoreWriteTransaction> transaction) {
 
     private ListenableFuture<RpcResult<TransactionStatus>> submit(
             final WriteTransactionImpl<? extends DOMStoreWriteTransaction> transaction) {
-        LOG.debug("Tx: {} is submitted for execution.",transaction.getIdentifier());
+        LOG.debug("Tx: {} is submitted for execution.", transaction.getIdentifier());
         return executor.submit(new CommitCoordination(transaction));
     }
 
         return executor.submit(new CommitCoordination(transaction));
     }
 
@@ -253,7 +253,8 @@ public class DOMDataBrokerImpl implements DOMDataBroker, AutoCloseable {
         }
 
         @Override
         }
 
         @Override
-        public void merge(final LogicalDatastoreType store, final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        public void merge(final LogicalDatastoreType store, final InstanceIdentifier path,
+                final NormalizedNode<?, ?> data) {
 
         }
     }
 
         }
     }
@@ -269,15 +270,18 @@ public class DOMDataBrokerImpl implements DOMDataBroker, AutoCloseable {
         @Override
         public RpcResult<TransactionStatus> call() throws Exception {
 
         @Override
         public RpcResult<TransactionStatus> call() throws Exception {
 
-            Boolean canCommit = canCommit().get();
             try {
             try {
+                Boolean canCommit = canCommit().get();
+
                 if (canCommit) {
                     try {
                         preCommit().get();
                         try {
                             commit().get();
                 if (canCommit) {
                     try {
                         preCommit().get();
                         try {
                             commit().get();
-                            COORDINATOR_LOG.debug("Tx: {} Is commited.",transaction.getIdentifier());
-                            return Rpcs.getRpcResult(true, TransactionStatus.COMMITED, Collections.<RpcError>emptySet());
+                            COORDINATOR_LOG.debug("Tx: {} Is commited.", transaction.getIdentifier());
+                            return Rpcs.getRpcResult(true, TransactionStatus.COMMITED,
+                                    Collections.<RpcError> emptySet());
+
                         } catch (InterruptedException | ExecutionException e) {
                             COORDINATOR_LOG.error("Tx: {} Error during commit", transaction.getIdentifier(), e);
                         }
                         } catch (InterruptedException | ExecutionException e) {
                             COORDINATOR_LOG.error("Tx: {} Error during commit", transaction.getIdentifier(), e);
                         }
@@ -287,7 +291,7 @@ public class DOMDataBrokerImpl implements DOMDataBroker, AutoCloseable {
                                 transaction.getIdentifier(), e);
                     }
                 } else {
                                 transaction.getIdentifier(), e);
                     }
                 } else {
-                    COORDINATOR_LOG.info("Tx: {} Did not pass canCommit phase.");
+                    COORDINATOR_LOG.info("Tx: {} Did not pass canCommit phase.", transaction.getIdentifier());
                     abort().get();
                 }
             } catch (InterruptedException | ExecutionException e) {
                     abort().get();
                 }
             } catch (InterruptedException | ExecutionException e) {
@@ -299,7 +303,7 @@ public class DOMDataBrokerImpl implements DOMDataBroker, AutoCloseable {
             } catch (InterruptedException | ExecutionException e) {
                 COORDINATOR_LOG.error("Tx: {} Error during abort", transaction.getIdentifier(), e);
             }
             } catch (InterruptedException | ExecutionException e) {
                 COORDINATOR_LOG.error("Tx: {} Error during abort", transaction.getIdentifier(), e);
             }
-            return Rpcs.getRpcResult(false, TransactionStatus.FAILED, Collections.<RpcError>emptySet());
+            return Rpcs.getRpcResult(false, TransactionStatus.FAILED, Collections.<RpcError> emptySet());
         }
 
         public ListenableFuture<Void> preCommit() {
         }
 
         public ListenableFuture<Void> preCommit() {
index ff0fbf9..72ae24e 100644 (file)
@@ -1,7 +1,6 @@
 package org.opendaylight.controller.md.sal.dom.store.impl;
 
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
 package org.opendaylight.controller.md.sal.dom.store.impl;
 
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerRegistrationNode.DataChangeListenerRegistration;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.slf4j.Logger;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.slf4j.Logger;
index d0ebcf5..3c6a3d6 100644 (file)
@@ -63,6 +63,12 @@ public final class DOMImmutableDataChangeEvent implements
         return removedPaths;
     }
 
         return removedPaths;
     }
 
+    @Override
+    public String toString() {
+        return "DOMImmutableDataChangeEvent [created=" + createdData.keySet() + ", updated=" + updatedData.keySet()
+                + ", removed=" + removedPaths + "]";
+    }
+
     public static class Builder {
 
         private NormalizedNode<?, ?> after;
     public static class Builder {
 
         private NormalizedNode<?, ?> after;
@@ -73,7 +79,6 @@ public final class DOMImmutableDataChangeEvent implements
         private final ImmutableMap.Builder<InstanceIdentifier, NormalizedNode<?, ?>> updated = ImmutableMap.builder();
         private final ImmutableSet.Builder<InstanceIdentifier> removed = ImmutableSet.builder();
 
         private final ImmutableMap.Builder<InstanceIdentifier, NormalizedNode<?, ?>> updated = ImmutableMap.builder();
         private final ImmutableSet.Builder<InstanceIdentifier> removed = ImmutableSet.builder();
 
-
         private Builder() {
 
         }
         private Builder() {
 
         }
@@ -122,4 +127,3 @@ public final class DOMImmutableDataChangeEvent implements
     }
 
 }
     }
 
 }
-
index c2faf86..a032c79 100644 (file)
@@ -4,15 +4,19 @@ import static org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableData
 import static org.opendaylight.controller.md.sal.dom.store.impl.StoreUtils.append;
 import static org.opendaylight.controller.md.sal.dom.store.impl.tree.TreeNodeUtils.getChild;
 
 import static org.opendaylight.controller.md.sal.dom.store.impl.StoreUtils.append;
 import static org.opendaylight.controller.md.sal.dom.store.impl.tree.TreeNodeUtils.getChild;
 
+import java.util.ArrayList;
+import java.util.Collection;
+
 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.tree.ListenerRegistrationNode;
 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.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.tree.ListenerRegistrationNode;
 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.ListenerRegistrationNode.DataChangeListenerRegistration;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 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.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
@@ -20,6 +24,9 @@ import com.google.common.collect.ImmutableSet;
 
 public class DataChangeEventResolver {
 
 
 public class DataChangeEventResolver {
 
+
+    private  static final Logger LOG = LoggerFactory.getLogger(DataChangeEventResolver.class);
+
     private static final DOMImmutableDataChangeEvent NO_CHANGE = builder().build();
     private InstanceIdentifier rootPath;
     private ListenerRegistrationNode listenerRoot;
     private static final DOMImmutableDataChangeEvent NO_CHANGE = builder().build();
     private InstanceIdentifier rootPath;
     private ListenerRegistrationNode listenerRoot;
@@ -74,6 +81,7 @@ public class DataChangeEventResolver {
     }
 
     public Iterable<ChangeListenerNotifyTask> resolve() {
     }
 
     public Iterable<ChangeListenerNotifyTask> resolve() {
+        LOG.trace("Resolving events for {}" ,modificationRoot);
         resolveAnyChangeEvent(rootPath, Optional.of(listenerRoot), modificationRoot, beforeRoot, afterRoot);
         return tasks.build();
     }
         resolveAnyChangeEvent(rootPath, Optional.of(listenerRoot), modificationRoot, beforeRoot, afterRoot);
         return tasks.build();
     }
@@ -124,6 +132,7 @@ public class DataChangeEventResolver {
             InstanceIdentifier childPath = StoreUtils.append(path, childId);
             builder.merge(resolveCreateEvent(childPath, childListeners, child));
         }
             InstanceIdentifier childPath = StoreUtils.append(path, childId);
             builder.merge(resolveCreateEvent(childPath, childListeners, child));
         }
+
         DOMImmutableDataChangeEvent event = builder.build();
         if (listeners.isPresent()) {
             addNotifyTask(listeners.get().getListeners(), event);
         DOMImmutableDataChangeEvent event = builder.build();
         if (listeners.isPresent()) {
             addNotifyTask(listeners.get().getListeners(), event);
@@ -169,7 +178,7 @@ public class DataChangeEventResolver {
             switch (childMod.getModificationType()) {
             case WRITE:
             case DELETE:
             switch (childMod.getModificationType()) {
             case WRITE:
             case DELETE:
-                one.merge(resolveAnyChangeEvent(childPath, childListen, childMod, childBefore, childBefore));
+                one.merge(resolveAnyChangeEvent(childPath, childListen, childMod, childBefore, childAfter));
                 break;
             case SUBTREE_MODIFIED:
                 subtree.merge(resolveSubtreeChangeEvent(childPath, childListen, childMod, childBefore.get(),
                 break;
             case SUBTREE_MODIFIED:
                 subtree.merge(resolveSubtreeChangeEvent(childPath, childListen, childMod, childBefore.get(),
@@ -194,16 +203,27 @@ public class DataChangeEventResolver {
         return builder().build();
     }
 
         return builder().build();
     }
 
-    private void addNotifyTask(final ListenerRegistrationNode listenerRegistrationNode, final DataChangeScope one,
+    private void addNotifyTask(final ListenerRegistrationNode listenerRegistrationNode, final DataChangeScope scope,
             final DOMImmutableDataChangeEvent event) {
             final DOMImmutableDataChangeEvent event) {
-
-
+        Collection<DataChangeListenerRegistration<?>> potential = listenerRegistrationNode.getListeners();
+        if(potential.isEmpty()) {
+            return;
+        }
+        ArrayList<DataChangeListenerRegistration<?>> toNotify = new ArrayList<>(potential.size());
+        for(DataChangeListenerRegistration<?> listener : potential) {
+            if(scope.equals(listener.getScope())) {
+                toNotify.add(listener);
+            }
+        }
+        addNotifyTask(toNotify, event);
 
     }
 
 
     }
 
-    private void addNotifyTask(final Iterable<DataChangeListenerRegistration<?>> listeners,
+    private void addNotifyTask(final Collection<DataChangeListenerRegistration<?>> listeners,
             final DOMImmutableDataChangeEvent event) {
             final DOMImmutableDataChangeEvent event) {
-        tasks .add(new ChangeListenerNotifyTask(ImmutableSet.copyOf(listeners),event));
+        if(!listeners.isEmpty()) {
+            tasks.add(new ChangeListenerNotifyTask(ImmutableSet.copyOf(listeners),event));
+        }
     }
 
     public static DataChangeEventResolver create() {
     }
 
     public static DataChangeEventResolver create() {
index 0944c2e..0cd00ca 100644 (file)
@@ -20,7 +20,6 @@ import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerRegistrati
 import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
 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.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.TreeNodeUtils;
 import org.opendaylight.controller.sal.core.spi.data.DOMStore;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
 import org.opendaylight.controller.sal.core.spi.data.DOMStore;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
@@ -98,13 +97,15 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable<String>, Sch
     @Override
     public <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(
             final InstanceIdentifier path, final L listener, final DataChangeScope scope) {
     @Override
     public <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(
             final InstanceIdentifier path, final L listener, final DataChangeScope scope) {
-
-        Optional<ListenerRegistrationNode> listenerNode = TreeNodeUtils.findNode(listenerTree, path);
-        checkState(listenerNode.isPresent());
+        LOG.debug("{}: Registering data change listener {} for {}",name,listener,path);
+        ListenerRegistrationNode listenerNode = listenerTree;
+        for(PathArgument arg :path.getPath()) {
+            listenerNode = listenerNode.ensureChild(arg);
+        }
         synchronized (listener) {
             notifyInitialState(path, listener);
         }
         synchronized (listener) {
             notifyInitialState(path, listener);
         }
-        return listenerNode.get().registerDataChangeListener(listener, scope);
+        return listenerNode.registerDataChangeListener(path,listener, scope);
     }
 
     private void notifyInitialState(final InstanceIdentifier path,
     }
 
     private void notifyInitialState(final InstanceIdentifier path,
@@ -138,6 +139,7 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable<String>, Sch
     private synchronized void commit(final DataAndMetadataSnapshot currentSnapshot,
             final StoreMetadataNode newDataTree, final Iterable<ChangeListenerNotifyTask> listenerTasks) {
         LOG.debug("Updating Store snaphot version: {} with version:{}",currentSnapshot.getMetadataTree().getSubtreeVersion(),newDataTree.getSubtreeVersion());
     private synchronized void commit(final DataAndMetadataSnapshot currentSnapshot,
             final StoreMetadataNode newDataTree, final Iterable<ChangeListenerNotifyTask> listenerTasks) {
         LOG.debug("Updating Store snaphot version: {} with version:{}",currentSnapshot.getMetadataTree().getSubtreeVersion(),newDataTree.getSubtreeVersion());
+
         if(LOG.isTraceEnabled()) {
             LOG.trace("Data Tree is {}",StoreUtils.toStringTree(newDataTree));
         }
         if(LOG.isTraceEnabled()) {
             LOG.trace("Data Tree is {}",StoreUtils.toStringTree(newDataTree));
         }
@@ -323,7 +325,6 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable<String>, Sch
                     proposedSubtree = operationTree.apply(modification, Optional.of(metadataTree),
                             increase(metadataTree.getSubtreeVersion()));
 
                     proposedSubtree = operationTree.apply(modification, Optional.of(metadataTree),
                             increase(metadataTree.getSubtreeVersion()));
 
-
                     listenerTasks = DataChangeEventResolver.create() //
                             .setRootPath(PUBLIC_ROOT_PATH) //
                             .setBeforeRoot(Optional.of(metadataTree)) //
                     listenerTasks = DataChangeEventResolver.create() //
                             .setRootPath(PUBLIC_ROOT_PATH) //
                             .setBeforeRoot(Optional.of(metadataTree)) //
index df58d62..02c2a4f 100644 (file)
@@ -45,6 +45,11 @@ public final class StoreUtils {
         return new InitialDataChangeEvent(path, data.getData());
     }
 
         return new InitialDataChangeEvent(path, data.getData());
     }
 
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static <V> Function<Identifiable<V>, V> identifierExtractor() {
+        return (Function) EXTRACT_IDENTIFIER;
+    }
+
     private static final class InitialDataChangeEvent implements
             AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> {
 
     private static final class InitialDataChangeEvent implements
             AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> {
 
@@ -88,30 +93,25 @@ public final class StoreUtils {
 
     }
 
 
     }
 
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public static <V> Function<Identifiable<V>,V> identifierExtractor() {
-        return (Function) EXTRACT_IDENTIFIER;
-    }
-
     public static <V> Set<V> toIdentifierSet(final Iterable<? extends Identifiable<V>> children) {
     public static <V> Set<V> toIdentifierSet(final Iterable<? extends Identifiable<V>> children) {
-        return FluentIterable.from(children).transform(StoreUtils.<V>identifierExtractor()).toSet();
+        return FluentIterable.from(children).transform(StoreUtils.<V> identifierExtractor()).toSet();
     }
 
     public static String toStringTree(final StoreMetadataNode metaNode) {
         StringBuilder builder = new StringBuilder();
     }
 
     public static String toStringTree(final StoreMetadataNode metaNode) {
         StringBuilder builder = new StringBuilder();
-        toStringTree(builder, metaNode,0);
+        toStringTree(builder, metaNode, 0);
         return builder.toString();
 
     }
 
         return builder.toString();
 
     }
 
-    private static void toStringTree(final StringBuilder builder, final StoreMetadataNode metaNode,final int offset) {
+    private static void toStringTree(final StringBuilder builder, final StoreMetadataNode metaNode, final int offset) {
         String prefix = Strings.repeat(" ", offset);
         builder.append(prefix).append(toStringTree(metaNode.getIdentifier()));
         NormalizedNode<?, ?> dataNode = metaNode.getData();
         String prefix = Strings.repeat(" ", offset);
         builder.append(prefix).append(toStringTree(metaNode.getIdentifier()));
         NormalizedNode<?, ?> dataNode = metaNode.getData();
-        if(dataNode instanceof NormalizedNodeContainer<?,?,?>)  {
+        if (dataNode instanceof NormalizedNodeContainer<?, ?, ?>) {
             builder.append(" {").append("\n");
             builder.append(" {").append("\n");
-            for(StoreMetadataNode child : metaNode.getChildren()) {
-                toStringTree(builder, child, offset+4);
+            for (StoreMetadataNode child : metaNode.getChildren()) {
+                toStringTree(builder, child, offset + 4);
             }
             builder.append(prefix).append("}");
         } else {
             }
             builder.append(prefix).append("}");
         } else {
@@ -121,7 +121,7 @@ public final class StoreUtils {
     }
 
     private static String toStringTree(final PathArgument identifier) {
     }
 
     private static String toStringTree(final PathArgument identifier) {
-        ifidentifier instanceof NodeIdentifierWithPredicates) {
+        if (identifier instanceof NodeIdentifierWithPredicates) {
             StringBuilder builder = new StringBuilder();
             builder.append(identifier.getNodeType().getLocalName());
             builder.append(((NodeIdentifierWithPredicates) identifier).getKeyValues().values());
             StringBuilder builder = new StringBuilder();
             builder.append(identifier.getNodeType().getLocalName());
             builder.append(((NodeIdentifierWithPredicates) identifier).getKeyValues().values());
index ee49eff..e48438d 100644 (file)
@@ -1,5 +1,6 @@
 package org.opendaylight.controller.md.sal.dom.store.impl.tree;
 
 package org.opendaylight.controller.md.sal.dom.store.impl.tree;
 
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -12,10 +13,14 @@ import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 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.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Optional;
 
 
 import com.google.common.base.Optional;
 
-public class ListenerRegistrationNode implements StoreTreeNode<ListenerRegistrationNode>,Identifiable<PathArgument> {
+public class ListenerRegistrationNode implements StoreTreeNode<ListenerRegistrationNode>, Identifiable<PathArgument> {
+
+    private final Logger LOG = LoggerFactory.getLogger(ListenerRegistrationNode.class);
 
     private final ListenerRegistrationNode parent;
     private final Map<PathArgument, ListenerRegistrationNode> children;
 
     private final ListenerRegistrationNode parent;
     private final Map<PathArgument, ListenerRegistrationNode> children;
@@ -23,10 +28,10 @@ public class ListenerRegistrationNode implements StoreTreeNode<ListenerRegistrat
     private final HashSet<DataChangeListenerRegistration<?>> listeners;
 
     private ListenerRegistrationNode(final PathArgument identifier) {
     private final HashSet<DataChangeListenerRegistration<?>> listeners;
 
     private ListenerRegistrationNode(final PathArgument identifier) {
-        this(null,identifier);
+        this(null, identifier);
     }
 
     }
 
-    private ListenerRegistrationNode(final ListenerRegistrationNode parent,final PathArgument identifier) {
+    private ListenerRegistrationNode(final ListenerRegistrationNode parent, final PathArgument identifier) {
         this.parent = parent;
         this.identifier = identifier;
         children = new HashMap<>();
         this.parent = parent;
         this.identifier = identifier;
         children = new HashMap<>();
@@ -42,23 +47,38 @@ public class ListenerRegistrationNode implements StoreTreeNode<ListenerRegistrat
         return identifier;
     }
 
         return identifier;
     }
 
-    public Iterable<DataChangeListenerRegistration<?>> getListeners() {
-        return listeners;
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public Collection<org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration<?>> getListeners() {
+        return (Collection) listeners;
     }
 
     @Override
     public synchronized Optional<ListenerRegistrationNode> getChild(final PathArgument child) {
     }
 
     @Override
     public synchronized Optional<ListenerRegistrationNode> getChild(final PathArgument child) {
+        return Optional.fromNullable(children.get(child));
+    }
+
+    public synchronized ListenerRegistrationNode ensureChild(final PathArgument child) {
         ListenerRegistrationNode potential = (children.get(child));
         ListenerRegistrationNode potential = (children.get(child));
-        if(potential == null) {
+        if (potential == null) {
             potential = new ListenerRegistrationNode(this, child);
             children.put(child, potential);
         }
             potential = new ListenerRegistrationNode(this, child);
             children.put(child, potential);
         }
-        return Optional.of(potential);
+        return potential;
     }
 
     }
 
-    public <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerDataChangeListener(
+    /**
+     *
+     * Registers listener on this node.
+     *
+     * @param path Full path on which listener is registered.
+     * @param listener Listener
+     * @param scope Scope of triggering event.
+     * @return
+     */
+    public <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerDataChangeListener(final InstanceIdentifier path,
             final L listener, final DataChangeScope scope) {
             final L listener, final DataChangeScope scope) {
-        DataChangeListenerRegistration<L> listenerReg = new DataChangeListenerRegistration<L>(listener, scope,this);
+
+        DataChangeListenerRegistration<L> listenerReg = new DataChangeListenerRegistration<L>(path,listener, scope, this);
         listeners.add(listenerReg);
         return listenerReg;
     }
         listeners.add(listenerReg);
         return listenerReg;
     }
@@ -68,9 +88,8 @@ public class ListenerRegistrationNode implements StoreTreeNode<ListenerRegistrat
         removeThisIfUnused();
     }
 
         removeThisIfUnused();
     }
 
-
     private void removeThisIfUnused() {
     private void removeThisIfUnused() {
-        if(parent != null && listeners.isEmpty() && children.isEmpty()) {
+        if (parent != null && listeners.isEmpty() && children.isEmpty()) {
             parent.removeChildIfUnused(this);
         }
     }
             parent.removeChildIfUnused(this);
         }
     }
@@ -80,8 +99,8 @@ public class ListenerRegistrationNode implements StoreTreeNode<ListenerRegistrat
     }
 
     private boolean areChildrenUnused() {
     }
 
     private boolean areChildrenUnused() {
-        for(ListenerRegistrationNode child :  children.values()) {
-            if(!child.isUnused()) {
+        for (ListenerRegistrationNode child : children.values()) {
+            if (!child.isUnused()) {
                 return false;
             }
         }
                 return false;
             }
         }
@@ -92,23 +111,24 @@ public class ListenerRegistrationNode implements StoreTreeNode<ListenerRegistrat
         // FIXME Remove unnecessary
     }
 
         // FIXME Remove unnecessary
     }
 
-
-
-
-    public static class DataChangeListenerRegistration<T extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> extends AbstractObjectRegistration<T>
-            implements ListenerRegistration<T> {
+    public static class DataChangeListenerRegistration<T extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>>
+            extends AbstractObjectRegistration<T> implements
+            org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration<T> {
 
         private final DataChangeScope scope;
         private ListenerRegistrationNode node;
 
         private final DataChangeScope scope;
         private ListenerRegistrationNode node;
+        private final InstanceIdentifier path;
 
 
-        public DataChangeListenerRegistration(final T listener, final DataChangeScope scope, final ListenerRegistrationNode node) {
+        public DataChangeListenerRegistration(final InstanceIdentifier path,final T listener, final DataChangeScope scope,
+                final ListenerRegistrationNode node) {
             super(listener);
             super(listener);
-
+            this.path = path;
             this.scope = scope;
             this.node = node;
         }
 
             this.scope = scope;
             this.node = node;
         }
 
-        protected DataChangeScope getScope() {
+        @Override
+        public DataChangeScope getScope() {
             return scope;
         }
 
             return scope;
         }
 
@@ -117,5 +137,10 @@ public class ListenerRegistrationNode implements StoreTreeNode<ListenerRegistrat
             node.removeListener(this);
             node = null;
         }
             node.removeListener(this);
             node = null;
         }
+
+        @Override
+        public InstanceIdentifier getPath() {
+            return path;
+        }
     }
 }
     }
 }