2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.dom.spi.store;
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;
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;
35 * Abstract base class for {@link DOMStoreTreeChangePublisher} implementations.
37 public abstract class AbstractDOMStoreTreeChangePublisher
38 extends AbstractRegistrationTree<AbstractDOMDataTreeChangeListenerRegistration<?>>
39 implements DOMStoreTreeChangePublisher {
40 private static final Logger LOG = LoggerFactory.getLogger(AbstractDOMStoreTreeChangePublisher.class);
42 private static final Supplier<List<DataTreeCandidate>> LIST_SUPPLIER = () -> new ArrayList<>();
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
50 protected abstract void notifyListener(@Nonnull AbstractDOMDataTreeChangeListenerRegistration<?> registration,
51 @Nonnull Collection<DataTreeCandidate> changes);
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.
62 * @param registration Registration which is being closed
64 protected abstract void registrationRemoved(
65 @Nonnull AbstractDOMDataTreeChangeListenerRegistration<?> registration);
68 * Process a candidate tree with respect to registered listeners.
70 * @param candidate candidate three which needs to be processed
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);
79 try (RegistrationTreeSnapshot<AbstractDOMDataTreeChangeListenerRegistration<?>> snapshot
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);
86 for (Map.Entry<AbstractDOMDataTreeChangeListenerRegistration<?>, Collection<DataTreeCandidate>> entry:
87 listenerChanges.asMap().entrySet()) {
88 notifyListener(entry.getKey(), entry.getValue());
94 public <L extends DOMDataTreeChangeListener> AbstractDOMDataTreeChangeListenerRegistration<L>
95 registerTreeChangeListener(final YangInstanceIdentifier treeId, final L listener) {
96 // Take the write lock
99 final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> node =
100 findNodeFor(treeId.getPathArguments());
101 final AbstractDOMDataTreeChangeListenerRegistration<L> reg =
102 new AbstractDOMDataTreeChangeListenerRegistration<L>(listener) {
104 protected void removeRegistration() {
105 AbstractDOMStoreTreeChangePublisher.this.removeRegistration(node, this);
106 registrationRemoved(this);
110 addRegistration(node, reg);
113 // Always release the lock
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);
125 final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> exactChild
126 = node.getExactChild(arg);
127 if (exactChild != null) {
128 lookupAndNotify(args, offset + 1, exactChild, candidate, listenerChanges);
131 for (RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> c :
132 node.getInexactChildren(arg)) {
133 lookupAndNotify(args, offset + 1, c, candidate, listenerChanges);
136 notifyNode(candidate.getRootPath(), node, candidate.getRootNode(), listenerChanges);
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);
149 final Collection<AbstractDOMDataTreeChangeListenerRegistration<?>> regs = regNode.getRegistrations();
150 if (!regs.isEmpty()) {
151 addToListenerChanges(regs, path, candNode, listenerChanges);
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);
162 for (RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> rc :
163 regNode.getInexactChildren(candChild.getIdentifier())) {
164 notifyNode(path.node(candChild.getIdentifier()), rc, candChild, listenerChanges);
170 private static void addToListenerChanges(
171 final Collection<AbstractDOMDataTreeChangeListenerRegistration<?>> registrations,
172 final YangInstanceIdentifier path, final DataTreeCandidateNode node,
173 final Multimap<AbstractDOMDataTreeChangeListenerRegistration<?>, DataTreeCandidate> listenerChanges) {
174 final DataTreeCandidate dataTreeCandidate = DataTreeCandidates.newDataTreeCandidate(path, node);
176 for (AbstractDOMDataTreeChangeListenerRegistration<?> reg : registrations) {
177 listenerChanges.put(reg, dataTreeCandidate);