2 * Copyright © 2017 Ericsson India Global Services Pvt Ltd. 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.netvirt.elan.l2gw.listeners;
10 import java.util.Collection;
11 import java.util.HashMap;
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;
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)
40 public abstract class ChildListener<P extends DataObject, C extends DataObject, G>
41 implements DataTreeChangeListener<P>, AutoCloseable {
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;
47 protected final DataBroker dataBroker;
48 private ListenerRegistration<?> registration;
49 private final boolean processParentDeletes;
51 public ChildListener(DataBroker dataBroker, boolean processParentDeletes) {
52 this.dataBroker = dataBroker;
53 this.processParentDeletes = processParentDeletes;
56 public void init() throws Exception {
57 registration = registerListener(LogicalDatastoreType.OPERATIONAL, getParentWildCardPath());
60 protected ListenerRegistration<?> registerListener(final LogicalDatastoreType dsType,
61 final InstanceIdentifier wildCard) throws Exception {
62 DataTreeIdentifier<P> treeId = DataTreeIdentifier.create(dsType, wildCard);
63 TaskRetryLooper looper = new TaskRetryLooper(STARTUP_LOOP_TICK, STARTUP_LOOP_MAX_RETRIES);
64 return looper.loopUntilNoException(() -> dataBroker.registerDataTreeChangeListener(treeId, this));
68 * Upon parent added will be called.
69 * @param parent - Added parent node
71 protected abstract void onParentAdded(DataTreeModification<P> parent);
74 * Upon parent delete will be called.
75 * @param parent - Deleted parent
77 protected abstract void onParentRemoved(InstanceIdentifier<P> parent);
80 * Based on the return type of this function , processing on the dataTreeChanges takes place.
81 * @param parent - Parent node which is add/update/delete
82 * @return boolean value .
84 protected abstract boolean proceed(InstanceIdentifier<P> parent);
87 * Group of data for bulk child update cases.
88 * @param child - Subtree Node data
89 * @return - Group value
91 protected abstract G getGroup(C child);
94 * Process the added/updated/Deleted subtree data.
95 * @param updatedMacsGrouped - Updated Subtree Data
96 * @param deletedMacsGrouped - Deleted Subtree Data
98 protected abstract void onUpdate(
99 Map<G, Map<InstanceIdentifier, C>> updatedMacsGrouped,
100 Map<G, Map<InstanceIdentifier, C>> deletedMacsGrouped);
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
108 protected abstract Map<InstanceIdentifier<C>, DataObjectModification<C>> getChildMod(
109 InstanceIdentifier<P> parentIid,
110 DataObjectModification<P> mod);
112 protected abstract InstanceIdentifier<Node> getParentWildCardPath();
116 public void close() {
117 if (registration != null) {
118 registration.close();
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);
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
136 void extractUpdatedAndDeletedMacs(Collection<DataTreeModification<P>> changes,
137 Map<G, Map<InstanceIdentifier, C>> updatedMacsGrouped,
138 Map<G, Map<InstanceIdentifier, C>> deletedMacsGrouped) {
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)) {
146 if (modification.getDataBefore() == null) {
147 onParentAdded(change);
149 LOG.info("Unexpected write to parent before {}", modification.getDataBefore());
150 LOG.info("Unexpected write to parent after {}", modification.getDataAfter());
152 extractDataChanged(iid, modification, updatedMacsGrouped, deletedMacsGrouped);
154 case SUBTREE_MODIFIED:
155 extractDataChanged(iid, modification, updatedMacsGrouped, deletedMacsGrouped);
158 if (processParentDeletes) {
159 extractDataChanged(iid, modification, updatedMacsGrouped, deletedMacsGrouped);
161 onParentRemoved(iid);
162 //Do not process the disconnected nodes
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
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) {
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);
188 C dataBefore = childMod.getDataBefore();
189 C dataAfter = childMod.getDataAfter();
190 switch (modificationType) {
192 case SUBTREE_MODIFIED:
193 group = getGroup(dataAfter);
194 updatedMacsGrouped.computeIfAbsent(group, (grp) -> new ConcurrentHashMap<>());
195 updatedMacsGrouped.get(group).put(childIid, dataAfter);
198 group = getGroup(dataBefore);
199 deletedMacsGrouped.computeIfAbsent(group, (grp) -> new ConcurrentHashMap<>());
200 deletedMacsGrouped.get(group).put(childIid, dataBefore);
208 protected DataObjectModification.@Nullable ModificationType getModificationType(
209 final DataObjectModification<? extends DataObject> mod) {
211 return mod.getModificationType();
212 } catch (IllegalStateException e) {
213 //LOG.warn("Failed to get the modification type for mod {}", mod);