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