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