bffb506c7f97dd1d50d2962edcc308aa031ec879
[mdsal.git] / dom / mdsal-dom-spi / src / main / java / org / opendaylight / mdsal / dom / spi / store / AbstractDOMStoreTreeChangePublisher.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.mdsal.dom.spi.store;
9
10 import com.google.common.collect.ImmutableList;
11 import java.util.Collection;
12 import java.util.List;
13 import javax.annotation.Nonnull;
14 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
15 import org.opendaylight.mdsal.dom.spi.AbstractDOMDataTreeChangeListenerRegistration;
16 import org.opendaylight.mdsal.dom.spi.AbstractRegistrationTree;
17 import org.opendaylight.mdsal.dom.spi.RegistrationTreeNode;
18 import org.opendaylight.mdsal.dom.spi.RegistrationTreeSnapshot;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
21 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
22 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 /**
28  * Abstract base class for {@link DOMStoreTreeChangePublisher} implementations.
29  */
30 public abstract class AbstractDOMStoreTreeChangePublisher
31     extends AbstractRegistrationTree<AbstractDOMDataTreeChangeListenerRegistration<?>>
32         implements DOMStoreTreeChangePublisher {
33     private static final Logger LOG = LoggerFactory.getLogger(AbstractDOMStoreTreeChangePublisher.class);
34
35     /**
36      * Callback for subclass to notify specified registrations
37      * of a candidate at a specified path. This method is guaranteed
38      * to be only called from within {@link #processCandidateTree(DataTreeCandidate)}.
39      * @param registrations Registrations which are affected by the candidate node
40      * @param path Path of changed candidate node. Guaranteed to match the path specified by the registration
41      * @param node Candidate node
42      */
43     protected abstract void notifyListeners(
44             @Nonnull Collection<AbstractDOMDataTreeChangeListenerRegistration<?>> registrations,
45             @Nonnull YangInstanceIdentifier path, @Nonnull DataTreeCandidateNode node);
46
47     /**
48      * Callback notifying the subclass that the specified registration is being
49      * closed and it's user no longer
50      * wishes to receive notifications. This notification is invoked while
51      * the {@link org.opendaylight.yangtools.concepts.ListenerRegistration#close()}
52      * method is executing. Subclasses can use this callback to properly
53      * remove any delayed notifications pending
54      * towards the registration.
55      *
56      * @param registration Registration which is being closed
57      */
58     protected abstract void registrationRemoved(
59             @Nonnull AbstractDOMDataTreeChangeListenerRegistration<?> registration);
60
61     /**
62      * Process a candidate tree with respect to registered listeners.
63      *
64      * @param candidate candidate three which needs to be processed
65      */
66     protected final void processCandidateTree(@Nonnull final DataTreeCandidate candidate) {
67         final DataTreeCandidateNode node = candidate.getRootNode();
68         if (node.getModificationType() == ModificationType.UNMODIFIED) {
69             LOG.debug("Skipping unmodified candidate {}", candidate);
70             return;
71         }
72
73         try (final RegistrationTreeSnapshot<AbstractDOMDataTreeChangeListenerRegistration<?>> snapshot
74                 = takeSnapshot()) {
75             final List<PathArgument> toLookup
76                 = ImmutableList.copyOf(candidate.getRootPath().getPathArguments());
77             lookupAndNotify(toLookup, 0, snapshot.getRootNode(), candidate);
78         }
79     }
80
81     @Override
82     public <L extends DOMDataTreeChangeListener> AbstractDOMDataTreeChangeListenerRegistration<L>
83         registerTreeChangeListener(final YangInstanceIdentifier treeId, final L listener) {
84         // Take the write lock
85         takeLock();
86         try {
87             final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> node =
88                     findNodeFor(treeId.getPathArguments());
89             final AbstractDOMDataTreeChangeListenerRegistration<L> reg =
90                     new AbstractDOMDataTreeChangeListenerRegistration<L>(listener) {
91                 @Override
92                 protected void removeRegistration() {
93                     AbstractDOMStoreTreeChangePublisher.this.removeRegistration(node, this);
94                     registrationRemoved(this);
95                 }
96             };
97
98             addRegistration(node, reg);
99             return reg;
100         } finally {
101             // Always release the lock
102             releaseLock();
103         }
104     }
105
106     private void lookupAndNotify(final List<PathArgument> args,
107             final int offset, final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> node,
108                     final DataTreeCandidate candidate) {
109         if (args.size() != offset) {
110             final PathArgument arg = args.get(offset);
111
112             final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> exactChild
113                 = node.getExactChild(arg);
114             if (exactChild != null) {
115                 lookupAndNotify(args, offset + 1, exactChild, candidate);
116             }
117
118             for (RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> c :
119                     node.getInexactChildren(arg)) {
120                 lookupAndNotify(args, offset + 1, c, candidate);
121             }
122         } else {
123             notifyNode(candidate.getRootPath(), node, candidate.getRootNode());
124         }
125     }
126
127     private void notifyNode(final YangInstanceIdentifier path,
128             final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> regNode,
129             final DataTreeCandidateNode candNode) {
130         if (candNode.getModificationType() == ModificationType.UNMODIFIED) {
131             LOG.debug("Skipping unmodified candidate {}", path);
132             return;
133         }
134
135         final Collection<AbstractDOMDataTreeChangeListenerRegistration<?>> regs = regNode.getRegistrations();
136         if (!regs.isEmpty()) {
137             notifyListeners(regs, path, candNode);
138         }
139
140         for (DataTreeCandidateNode candChild : candNode.getChildNodes()) {
141             if (candChild.getModificationType() != ModificationType.UNMODIFIED) {
142                 final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> regChild =
143                         regNode.getExactChild(candChild.getIdentifier());
144                 if (regChild != null) {
145                     notifyNode(path.node(candChild.getIdentifier()), regChild, candChild);
146                 }
147
148                 for (RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> rc :
149                     regNode.getInexactChildren(candChild.getIdentifier())) {
150                     notifyNode(path.node(candChild.getIdentifier()), rc, candChild);
151                 }
152             }
153         }
154     }
155 }