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