c1865c87b1a76556b03addd3b9e0848839e513ba
[controller.git] / opendaylight / md-sal / sal-dom-spi / src / main / java / org / opendaylight / controller / md / sal / dom / spi / AbstractRegistrationTree.java
1 /**
2  * Copyright (c) 2015 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.spi;
9
10 import java.util.concurrent.locks.ReadWriteLock;
11 import java.util.concurrent.locks.ReentrantReadWriteLock;
12 import javax.annotation.Nonnull;
13 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
14
15 /**
16  * An abstract tree of registrations. Allows a read-only snapshot to be taken.
17  *
18  * @param <T> Type of registered object
19  */
20 public abstract class AbstractRegistrationTree<T> {
21     private final ReadWriteLock rwLock = new ReentrantReadWriteLock(true);
22     private final RegistrationTreeNode<T> rootNode = new RegistrationTreeNode<>(null, null);
23
24     protected AbstractRegistrationTree() {
25
26     }
27
28     /**
29      * Acquire the read-write lock. This should be done before invoking {@link #findNodeFor(Iterable)}.
30      */
31     protected final void takeLock() {
32         rwLock.writeLock().lock();
33     }
34
35     /**
36      * Release the read-write lock. This should be done after invocation of {@link #findNodeFor(Iterable)}
37      * and modification of the returned node. Note that callers should do so in a finally block.
38      */
39     protected final void releaseLock() {
40         rwLock.writeLock().unlock();
41     }
42
43     /**
44      * Find an existing, or allocate a fresh, node for a particular path. Must be called with the
45      * read-write lock held.
46      *
47      * @param path Path to find a node for
48      * @return A registration node for the specified path
49      */
50     @Nonnull protected final RegistrationTreeNode<T> findNodeFor(@Nonnull final Iterable<PathArgument> path) {
51         RegistrationTreeNode<T> walkNode = rootNode;
52         for (final PathArgument arg : path) {
53             walkNode = walkNode.ensureChild(arg);
54         }
55
56         return walkNode;
57     }
58
59     /**
60      * Add a registration to a particular node. The node must have been returned via {@link #findNodeFor(Iterable)}
61      * and the lock must still be held.
62      *
63      * @param node Tree node
64      * @param registration Registration instance
65      */
66     protected final void addRegistration(@Nonnull final RegistrationTreeNode<T> node, @Nonnull final T registration) {
67         node.addRegistration(registration);
68     }
69
70     /**
71      * Remove a registration from a particular node. This method must not be called while the read-write lock
72      * is held.
73      *
74      * @param node Tree node
75      * @param registration Registration instance
76      */
77     protected final void removeRegistration(@Nonnull final RegistrationTreeNode<T> node, @Nonnull final T registration) {
78         // Take the write lock
79         rwLock.writeLock().lock();
80         try {
81             node.removeRegistration(registration);
82         } finally {
83             // Always release the lock
84             rwLock.writeLock().unlock();
85         }
86     }
87
88     /**
89      * Obtain a tree snapshot. This snapshot ensures a consistent view of
90      * registrations. The snapshot should be closed as soon as it is not required,
91      * because each unclosed instance blocks modification of this tree.
92      *
93      * @return A snapshot instance.
94      */
95     @Nonnull public final RegistrationTreeSnapshot<T> takeSnapshot() {
96         final RegistrationTreeSnapshot<T> ret = new RegistrationTreeSnapshot<>(rwLock.readLock(), rootNode);
97         rwLock.readLock().lock();
98         return ret;
99     }
100 }