Merge "Stop scheduling the Install Snapshot check"
[controller.git] / opendaylight / md-sal / sal-inmemory-datastore / src / main / java / org / opendaylight / controller / md / sal / dom / store / impl / tree / ListenerTree.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.md.sal.dom.store.impl.tree;
9
10 import java.util.concurrent.locks.ReadWriteLock;
11 import java.util.concurrent.locks.ReentrantReadWriteLock;
12
13 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
14 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
15 import org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration;
16 import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
17 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
19 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 /**
24  * A set of listeners organized as a tree by node to which they listen. This class
25  * allows for efficient lookup of listeners when we walk the DataTreeCandidate.
26  *
27  * @author Robert Varga
28  */
29 public final class ListenerTree  {
30     private static final Logger LOG = LoggerFactory.getLogger(ListenerTree.class);
31     private final ReadWriteLock rwLock = new ReentrantReadWriteLock(true);
32     private final ListenerNode rootNode = new ListenerNode(null, null);
33
34     private ListenerTree() {
35         // Private to disallow direct instantiation
36     }
37
38     /**
39      * Create a new empty instance of the listener tree.
40      *
41      * @return An empty instance.
42      */
43     public static ListenerTree create() {
44         return new ListenerTree();
45     }
46
47     /**
48      * Registers listener on this node.
49      *
50      * @param path Full path on which listener is registered.
51      * @param listener Listener
52      * @param scope Scope of triggering event.
53      * @return Listener registration
54      */
55     public <L extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> DataChangeListenerRegistration<L> registerDataChangeListener(final YangInstanceIdentifier path,
56             final L listener, final DataChangeScope scope) {
57
58         // Take the write lock
59         rwLock.writeLock().lock();
60
61         try {
62             ListenerNode walkNode = rootNode;
63             for (final PathArgument arg : path.getPathArguments()) {
64                 walkNode = walkNode.ensureChild(arg);
65             }
66
67             final ListenerNode node = walkNode;
68             DataChangeListenerRegistration<L> reg = new DataChangeListenerRegistrationImpl<L>(listener) {
69                 @Override
70                 public DataChangeScope getScope() {
71                     return scope;
72                 }
73
74                 @Override
75                 public YangInstanceIdentifier getPath() {
76                     return path;
77                 }
78
79                 @Override
80                 protected void removeRegistration() {
81                     /*
82                      * TODO: Here's an interesting problem. The way the datastore works, it
83                      *       enqueues requests towards the listener, so the listener will be
84                      *       notified at some point in the future. Now if the registration is
85                      *       closed, we will prevent any new events from being delivered, but
86                      *       we have no way to purge that queue.
87                      *
88                      *       While this does not directly violate the ListenerRegistration
89                      *       contract, it is probably not going to be liked by the users.
90                      */
91
92                     // Take the write lock
93                     ListenerTree.this.rwLock.writeLock().lock();
94                     try {
95                         node.removeListener(this);
96                     } finally {
97                         // Always release the lock
98                         ListenerTree.this.rwLock.writeLock().unlock();
99                     }
100                 }
101             };
102
103             node.addListener(reg);
104             return reg;
105         } finally {
106             // Always release the lock
107             rwLock.writeLock().unlock();
108         }
109     }
110
111     /**
112      * Obtain a tree walking context. This context ensures a consistent view of
113      * the listener registrations. The context should be closed as soon as it
114      * is not required, because each unclosed instance blocks modification of
115      * the listener tree.
116      *
117      * @return A walker instance.
118      */
119     public ListenerWalker getWalker() {
120         /*
121          * TODO: The only current user of this method is local to the datastore.
122          *       Since this class represents a read-lock, losing a reference to
123          *       it is a _major_ problem, as the registration process will get
124          *       wedged, eventually grinding the system to a halt. Should an
125          *       external user exist, make the Walker a phantom reference, which
126          *       will cleanup the lock if not told to do so.
127          */
128         final ListenerWalker ret = new ListenerWalker(rwLock.readLock(), rootNode);
129         rwLock.readLock().lock();
130         return ret;
131     }
132
133     abstract static class DataChangeListenerRegistrationImpl<T extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> extends AbstractListenerRegistration<T> //
134     implements DataChangeListenerRegistration<T> {
135         public DataChangeListenerRegistrationImpl(final T listener) {
136             super(listener);
137         }
138     }
139 }