c9a842070f2b26cfb41a555fb367bc343157cfd9
[controller.git] / opendaylight / md-sal / sal-inmemory-datastore / src / main / java / org / opendaylight / controller / md / sal / dom / store / impl / ResolveDataChangeState.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;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.collect.Iterables;
12 import com.google.common.collect.Multimap;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.HashMap;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Map.Entry;
20 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
21 import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.Builder;
22 import org.opendaylight.controller.sal.core.compat.DataChangeListenerRegistration;
23 import org.opendaylight.mdsal.dom.spi.RegistrationTreeNode;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
29 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 /**
34  * Recursion state used in {@link ResolveDataChangeEventsTask}. Instances of this
35  * method track which listeners are affected by a particular change node. It takes
36  * care of properly inheriting SUB/ONE listeners and also provides a means to
37  * understand when actual processing need not occur.
38  */
39 final class ResolveDataChangeState {
40     private static final Logger LOG = LoggerFactory.getLogger(ResolveDataChangeState.class);
41
42     /**
43      * Inherited from all parents.
44      */
45     private final Iterable<Builder> inheritedSub;
46
47     /**
48      * Inherited from immediate parent.
49      */
50     private final Collection<Builder> inheritedOne;
51     private final YangInstanceIdentifier nodeId;
52     private final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> nodes;
53
54     private final Map<DataChangeListenerRegistration<?>, Builder> subBuilders;
55     private final Map<DataChangeListenerRegistration<?>, Builder> oneBuilders;
56     private final Map<DataChangeListenerRegistration<?>, Builder> baseBuilders;
57
58     private ResolveDataChangeState(final YangInstanceIdentifier nodeId,
59             final Iterable<Builder> inheritedSub, final Collection<Builder> inheritedOne,
60             final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> nodes) {
61         this.nodeId = Preconditions.checkNotNull(nodeId);
62         this.nodes = Preconditions.checkNotNull(nodes);
63         this.inheritedSub = Preconditions.checkNotNull(inheritedSub);
64         this.inheritedOne = Preconditions.checkNotNull(inheritedOne);
65
66         /*
67          * Collect the nodes which need to be propagated from us to the child.
68          */
69         final Map<DataChangeListenerRegistration<?>, Builder> sub = new HashMap<>();
70         final Map<DataChangeListenerRegistration<?>, Builder> one = new HashMap<>();
71         final Map<DataChangeListenerRegistration<?>, Builder> base = new HashMap<>();
72         for (RegistrationTreeNode<DataChangeListenerRegistration<?>> n : nodes) {
73             for (DataChangeListenerRegistration<?> l : n.getRegistrations()) {
74                 final Builder b = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE);
75                 switch (l.getScope()) {
76                     case BASE:
77                         base.put(l, b);
78                         break;
79                     case ONE:
80                         one.put(l, b);
81                         break;
82                     case SUBTREE:
83                         sub.put(l, b);
84                         break;
85                     default:
86                         break;
87                 }
88             }
89         }
90
91         baseBuilders = maybeEmpty(base);
92         oneBuilders = maybeEmpty(one);
93         subBuilders = maybeEmpty(sub);
94     }
95
96     private static <K, V> Map<K, V> maybeEmpty(final Map<K, V> map) {
97         if (map.isEmpty()) {
98             return Collections.emptyMap();
99         }
100         return map;
101     }
102
103     /**
104      * Create an initial state handle at a particular root node.
105      *
106      * @param rootId root instance identifier
107      * @param registrationTreeNode root node
108      */
109     public static ResolveDataChangeState initial(final YangInstanceIdentifier rootId,
110             final RegistrationTreeNode<DataChangeListenerRegistration<?>> registrationTreeNode) {
111         return new ResolveDataChangeState(rootId, Collections.<Builder>emptyList(),
112             Collections.<Builder>emptyList(), Collections.singletonList(registrationTreeNode));
113     }
114
115     /**
116      * Create a state handle for iterating over a particular child.
117      *
118      * @param childId ID of the child
119      * @return State handle
120      */
121     public ResolveDataChangeState child(final PathArgument childId) {
122         /*
123          * We instantiate a concatenation only when needed:
124          *
125          * 1) If our collection is empty, we reuse the parent's. This is typically the case
126          *    for intermediate node, which should be the vast majority.
127          * 2) If the parent's iterable is a Collection and it is empty, reuse our collection.
128          *    This is the case for the first node which defines a subtree listener in a
129          *    particular subtree.
130          * 3) Concatenate the two collections. This happens when we already have some
131          *    subtree listeners and we encounter a node which adds a few more.
132          *
133          * This allows us to lower number of objects allocated and also
134          * speeds up Iterables.isEmpty() in needsProcessing().
135          *
136          * Note that the check for Collection in 2) relies on precisely this logic, which
137          * ensures that we simply cannot see an empty concatenation, but rather start off with
138          * an empty collection, then switch to a non-empty collection and finally switch to
139          * a concatenation. This saves us from instantiating iterators, which a trivial
140          * Iterables.isEmpty() would do as soon as we cross case 3).
141          */
142         final Iterable<Builder> sb;
143         if (!subBuilders.isEmpty()) {
144             if (inheritedSub instanceof Collection && ((Collection<?>) inheritedSub).isEmpty()) {
145                 sb = subBuilders.values();
146             } else {
147                 sb = Iterables.concat(inheritedSub, subBuilders.values());
148             }
149         } else {
150             sb = inheritedSub;
151         }
152
153         return new ResolveDataChangeState(nodeId.node(childId), sb,
154             oneBuilders.values(), getListenerChildrenWildcarded(nodes, childId));
155     }
156
157     /**
158      * Get the current path.
159      *
160      * @return Current path.
161      */
162     public YangInstanceIdentifier getPath() {
163         return nodeId;
164     }
165
166     /**
167      * Check if this child needs processing.
168      *
169      * @return True if processing needs to occur, false otherwise.
170      */
171     public boolean needsProcessing() {
172         // May have underlying listeners, so we need to process
173         if (!nodes.isEmpty()) {
174             return true;
175         }
176         // Have ONE listeners
177         if (!inheritedOne.isEmpty()) {
178             return true;
179         }
180
181         /*
182          * Have SUBTREE listeners
183          *
184          * This is slightly magical replacement for !Iterables.isEmpty(inheritedSub).
185          * It relies on the logic in child(), which gives us the guarantee that when
186          * inheritedSub is not a Collection, it is guaranteed to be non-empty (which
187          * means we need to process). If it is a collection, we still need to check
188          * it for emptiness.
189          *
190          * Unlike Iterables.isEmpty() this code does not instantiate any temporary
191          * objects and is thus more efficient.
192          */
193         if (inheritedSub instanceof Collection) {
194             return !((Collection<?>) inheritedSub).isEmpty();
195         }
196
197         // Non-Collection => non-empty => have to process
198         return true;
199     }
200
201     /**
202      * Add an event to all current listeners.
203      */
204     public void addEvent(final DOMImmutableDataChangeEvent event) {
205         // Subtree builders get always notified
206         for (Builder b : subBuilders.values()) {
207             b.merge(event);
208         }
209         for (Builder b : inheritedSub) {
210             b.merge(event);
211         }
212
213         if (event.getScope() == DataChangeScope.ONE || event.getScope() == DataChangeScope.BASE) {
214             for (Builder b : oneBuilders.values()) {
215                 b.merge(event);
216             }
217         }
218
219         if (event.getScope() == DataChangeScope.BASE) {
220             for (Builder b : inheritedOne) {
221                 b.merge(event);
222             }
223             for (Builder b : baseBuilders.values()) {
224                 b.merge(event);
225             }
226         }
227     }
228
229     /**
230      * Gather all non-empty events into the provided map.
231      *
232      * @param before before-image
233      * @param after after-image
234      * @param map target map
235      */
236     public void collectEvents(final NormalizedNode<?, ?> before, final NormalizedNode<?, ?> after,
237             final Multimap<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> map) {
238         for (Entry<DataChangeListenerRegistration<?>, Builder> e : baseBuilders.entrySet()) {
239             final Builder b = e.getValue();
240             if (!b.isEmpty()) {
241                 map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
242             }
243         }
244         for (Entry<DataChangeListenerRegistration<?>, Builder> e : oneBuilders.entrySet()) {
245             final Builder b = e.getValue();
246             if (!b.isEmpty()) {
247                 map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
248             }
249         }
250         for (Entry<DataChangeListenerRegistration<?>, Builder> e : subBuilders.entrySet()) {
251             final Builder b = e.getValue();
252             if (!b.isEmpty()) {
253                 map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
254             }
255         }
256
257         LOG.trace("Collected events {}", map);
258     }
259
260     private static Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> getListenerChildrenWildcarded(
261             final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> parentNodes,
262             final PathArgument child) {
263         if (parentNodes.isEmpty()) {
264             return Collections.emptyList();
265         }
266
267         final List<RegistrationTreeNode<DataChangeListenerRegistration<?>>> result = new ArrayList<>();
268         if (child instanceof NodeWithValue || child instanceof NodeIdentifierWithPredicates) {
269             NodeIdentifier wildcardedIdentifier = new NodeIdentifier(child.getNodeType());
270             addChildNodes(result, parentNodes, wildcardedIdentifier);
271         }
272         addChildNodes(result, parentNodes, child);
273         return result;
274     }
275
276     private static void addChildNodes(final List<RegistrationTreeNode<DataChangeListenerRegistration<?>>> result,
277             final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> parentNodes,
278             final PathArgument childIdentifier) {
279         for (RegistrationTreeNode<DataChangeListenerRegistration<?>> node : parentNodes) {
280             RegistrationTreeNode<DataChangeListenerRegistration<?>> child = node.getExactChild(childIdentifier);
281             if (child != null) {
282                 result.add(child);
283             }
284         }
285     }
286 }