Freeze upstream versions
[netvirt.git] / elanmanager / impl / src / main / java / org / opendaylight / netvirt / elan / l2gw / listeners / ChildListener.java
1 /*
2  * Copyright © 2017 Ericsson India Global Services Pvt Ltd. 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.netvirt.elan.l2gw.listeners;
9
10 import java.util.Collection;
11 import java.util.HashMap;
12 import java.util.Map;
13 import java.util.concurrent.ConcurrentHashMap;
14 import javax.annotation.PreDestroy;
15 import org.eclipse.jdt.annotation.Nullable;
16 import org.opendaylight.genius.datastoreutils.TaskRetryLooper;
17 import org.opendaylight.mdsal.binding.api.DataBroker;
18 import org.opendaylight.mdsal.binding.api.DataObjectModification;
19 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
20 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
21 import org.opendaylight.mdsal.binding.api.DataTreeModification;
22 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
23 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
24 import org.opendaylight.yangtools.concepts.ListenerRegistration;
25 import org.opendaylight.yangtools.yang.binding.DataObject;
26 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * ChildListener class listens to provided P node and its child node C.
32  * It can act on some special condition based on the parameter set.
33  * When the children are updated and same update is processed by parent listener , it
34  * takes a toll on the performance , specially in cases where child updates are too often .
35  * In case there are any Sub-Tree modification , it can be handled and not handled based on the parameters.
36  * @param <P> - Parent DataObject
37  * @param <C> - Child DataObject (Subtree)
38  * @param <G> - Group type (Mostly key of Subtree DataObject)
39  */
40 public abstract class ChildListener<P extends DataObject, C extends DataObject, G>
41         implements DataTreeChangeListener<P>, AutoCloseable {
42
43     private static final Logger LOG = LoggerFactory.getLogger(ChildListener.class);
44     private static final long STARTUP_LOOP_TICK = 500;
45     private static final int STARTUP_LOOP_MAX_RETRIES = 8;
46
47     protected final DataBroker dataBroker;
48     private ListenerRegistration<?> registration;
49     private final boolean processParentDeletes;
50
51     public ChildListener(DataBroker dataBroker, boolean processParentDeletes) {
52         this.dataBroker = dataBroker;
53         this.processParentDeletes = processParentDeletes;
54     }
55
56     public void init() throws Exception {
57         //registration = registerListener(LogicalDatastoreType.OPERATIONAL, getParentWildCardPath());
58     }
59
60     protected ListenerRegistration<?> registerListener(final LogicalDatastoreType dsType,
61                                                  final InstanceIdentifier wildCard) throws Exception {
62         if (registration != null) {
63             LOG.error("LocalUcast listener already registered");
64             return registration;
65         }
66         DataTreeIdentifier<P> treeId = DataTreeIdentifier.create(dsType, wildCard);
67         TaskRetryLooper looper = new TaskRetryLooper(STARTUP_LOOP_TICK, STARTUP_LOOP_MAX_RETRIES);
68         registration = looper.loopUntilNoException(() -> dataBroker.registerDataTreeChangeListener(treeId, this));
69         return registration;
70     }
71
72     /**
73      * Upon parent added will be called.
74      * @param parent - Added parent node
75      */
76     protected abstract void onParentAdded(DataTreeModification<P> parent);
77
78     /**
79      * Upon parent delete will be called.
80      * @param parent - Deleted parent
81      */
82     protected abstract void onParentRemoved(InstanceIdentifier<P> parent);
83
84     /**
85      * Based on the return type of this function , processing on the dataTreeChanges takes place.
86      * @param parent - Parent node which is add/update/delete
87      * @return boolean value .
88      */
89     protected abstract boolean proceed(InstanceIdentifier<P> parent);
90
91     /**
92      * Group of data for bulk child update cases.
93      * @param child - Subtree Node data
94      * @return - Group value
95      */
96     protected abstract G getGroup(C child);
97
98     /**
99      * Process the added/updated/Deleted subtree data.
100      * @param updatedMacsGrouped - Updated Subtree Data
101      * @param deletedMacsGrouped - Deleted Subtree Data
102      */
103     protected abstract void onUpdate(
104             Map<G, Map<InstanceIdentifier, C>> updatedMacsGrouped,
105             Map<G, Map<InstanceIdentifier, C>> deletedMacsGrouped);
106
107     /**
108      * Returns the all subtree data modified.
109      * @param parentIid - Parent Node iid
110      * @param mod - Modified Data
111      * @return subtree modified data map with IID
112      */
113     protected abstract Map<InstanceIdentifier<C>, DataObjectModification<C>> getChildMod(
114             InstanceIdentifier<P> parentIid,
115             DataObjectModification<P> mod);
116
117     protected abstract InstanceIdentifier<Node> getParentWildCardPath();
118
119     @Override
120     @PreDestroy
121     public void close() {
122         if (registration != null) {
123             registration.close();
124             registration = null;
125         }
126     }
127
128     @Override
129     public void onDataTreeChanged(final Collection<DataTreeModification<P>> changes) {
130         Map<G, Map<InstanceIdentifier, C>> updatedData = new HashMap<>();
131         Map<G, Map<InstanceIdentifier, C>> deletedData = new HashMap<>();
132         extractUpdatedAndDeletedMacs(changes, updatedData, deletedData);
133         onUpdate(updatedData, deletedData);
134     }
135
136     /**
137      * Taked the data changes and process them based on Add/Delete/Update or Subtree Modification.
138      * @param changes - Data tree changes
139      * @param updatedMacsGrouped - Updated Subtree Data
140      * @param deletedMacsGrouped - Deleted Subtree Data
141      */
142     void extractUpdatedAndDeletedMacs(Collection<DataTreeModification<P>> changes,
143                                       Map<G, Map<InstanceIdentifier, C>> updatedMacsGrouped,
144                                       Map<G, Map<InstanceIdentifier, C>> deletedMacsGrouped) {
145         changes.stream()
146                 .filter(change -> proceed(change.getRootPath().getRootIdentifier()))
147                 .forEach((change) -> {
148                     InstanceIdentifier<P> iid = change.getRootPath().getRootIdentifier();
149                     DataObjectModification<P> modification = change.getRootNode();
150                     switch (getModificationType(modification)) {
151                         case WRITE:
152                             if (modification.getDataBefore() == null) {
153                                 onParentAdded(change);
154                             }
155                             extractDataChanged(iid, modification, updatedMacsGrouped, deletedMacsGrouped);
156                             break;
157                         case SUBTREE_MODIFIED:
158                             extractDataChanged(iid, modification, updatedMacsGrouped, deletedMacsGrouped);
159                             break;
160                         case DELETE:
161                             if (processParentDeletes) {
162                                 extractDataChanged(iid, modification, updatedMacsGrouped, deletedMacsGrouped);
163                             }
164                             onParentRemoved(iid);
165                             //Do not process the disconnected nodes
166                             break;
167                         default:
168                             break;
169                     }
170                 });
171     }
172
173     /**
174      * Only when Subtree modification happen this is called .
175      * @param key - IID of child data
176      * @param parentModification - Modified data
177      * @param updatedMacsGrouped - Subtree updated data
178      * @param deletedMacsGrouped - Subtree deleted datas
179      */
180     private void extractDataChanged(final InstanceIdentifier<P> key,
181                                     final DataObjectModification<P> parentModification,
182                                     final Map<G, Map<InstanceIdentifier, C>> updatedMacsGrouped,
183                                     final Map<G, Map<InstanceIdentifier, C>> deletedMacsGrouped) {
184
185         Map<InstanceIdentifier<C>, DataObjectModification<C>> children = getChildMod(key, parentModification);
186         for (Map.Entry<InstanceIdentifier<C>, DataObjectModification<C>> entry : children.entrySet()) {
187             DataObjectModification<C> childMod = entry.getValue();
188             InstanceIdentifier<C> childIid = entry.getKey();
189             DataObjectModification.ModificationType modificationType = getModificationType(childMod);
190             G group;
191             C dataBefore = childMod.getDataBefore();
192             C dataAfter = childMod.getDataAfter();
193             switch (modificationType) {
194                 case WRITE:
195                 case SUBTREE_MODIFIED:
196                     group = getGroup(dataAfter);
197                     updatedMacsGrouped.computeIfAbsent(group, (grp) -> new ConcurrentHashMap<>());
198                     updatedMacsGrouped.get(group).put(childIid, dataAfter);
199                     break;
200                 case DELETE:
201                     group = getGroup(dataBefore);
202                     deletedMacsGrouped.computeIfAbsent(group, (grp) -> new ConcurrentHashMap<>());
203                     deletedMacsGrouped.get(group).put(childIid, dataBefore);
204                     break;
205                 default:
206                     break;
207             }
208         }
209     }
210
211     protected DataObjectModification.@Nullable ModificationType getModificationType(
212             final DataObjectModification<? extends DataObject> mod) {
213         try {
214             return mod.getModificationType();
215         } catch (IllegalStateException e) {
216             //LOG.warn("Failed to get the modification type for mod {}", mod);
217         }
218         return null;
219     }
220 }