2 * Copyright (c) 2016 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
9 package org.opendaylight.genius.datastoreutils;
11 import com.google.common.base.Preconditions;
12 import java.util.Collection;
13 import java.util.concurrent.LinkedBlockingQueue;
14 import java.util.concurrent.ThreadPoolExecutor;
15 import java.util.concurrent.TimeUnit;
16 import javax.annotation.PostConstruct;
17 import javax.annotation.PreDestroy;
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
20 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
21 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
22 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
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;
30 public abstract class AsyncDataTreeChangeListenerBase<T extends DataObject, K extends DataTreeChangeListener<T>>
31 implements DataTreeChangeListener<T>, ChainableDataTreeChangeListener<T>, AutoCloseable {
33 // Using non-static Logger so that logs easily identifies actual listener class
34 private final Logger log = LoggerFactory.getLogger(getClass());
36 private static final int DATATREE_CHANGE_HANDLER_THREAD_POOL_CORE_SIZE = 1;
37 private static final int DATATREE_CHANGE_HANDLER_THREAD_POOL_MAX_SIZE = 1;
38 private static final int DATATREE_CHANGE_HANDLER_THREAD_POOL_KEEP_ALIVE_TIME_SECS = 300;
39 private static final int STARTUP_LOOP_TICK = 500;
40 private static final int STARTUP_LOOP_MAX_RETRIES = 8;
42 private ListenerRegistration<K> listenerRegistration;
43 private final ChainableDataTreeChangeListenerImpl<T> chainingDelegate = new ChainableDataTreeChangeListenerImpl<>();
45 private static ThreadPoolExecutor dataTreeChangeHandlerExecutor = new ThreadPoolExecutor(
46 DATATREE_CHANGE_HANDLER_THREAD_POOL_CORE_SIZE,
47 DATATREE_CHANGE_HANDLER_THREAD_POOL_MAX_SIZE,
48 DATATREE_CHANGE_HANDLER_THREAD_POOL_KEEP_ALIVE_TIME_SECS,
50 new LinkedBlockingQueue<>());
52 protected final Class<T> clazz;
53 private final Class<K> eventClazz;
55 public AsyncDataTreeChangeListenerBase(Class<T> clazz, Class<K> eventClazz) {
56 this.clazz = Preconditions.checkNotNull(clazz, "Class can not be null!");
57 this.eventClazz = Preconditions.checkNotNull(eventClazz, "eventClazz can not be null!");
61 public void addBeforeListener(DataTreeChangeListener<T> listener) {
62 chainingDelegate.addBeforeListener(listener);
66 public void addAfterListener(DataTreeChangeListener<T> listener) {
67 chainingDelegate.addAfterListener(listener);
71 public void onDataTreeChanged(Collection<DataTreeModification<T>> changes) {
72 if (changes == null || changes.isEmpty()) {
76 DataTreeChangeHandler dataTreeChangeHandler = new DataTreeChangeHandler(changes);
77 dataTreeChangeHandlerExecutor.execute(dataTreeChangeHandler);
80 public void registerListener(LogicalDatastoreType dsType, final DataBroker db) {
81 final DataTreeIdentifier<T> treeId = new DataTreeIdentifier<>(dsType, getWildCardPath());
83 TaskRetryLooper looper = new TaskRetryLooper(STARTUP_LOOP_TICK, STARTUP_LOOP_MAX_RETRIES);
84 listenerRegistration = looper.loopUntilNoException(() -> db.registerDataTreeChangeListener(treeId, getDataTreeChangeListener()));
85 } catch (final Exception e) {
86 log.warn("{}: Data Tree Change listener registration failed.", eventClazz.getName());
87 log.debug("{}: Data Tree Change listener registration failed: {}", eventClazz.getName(), e);
88 throw new IllegalStateException( eventClazz.getName() + "{}startup failed. System needs restart.", e);
93 * Subclasses override this and place initialization logic here, notably
94 * calls to registerListener(). Note that the overriding method MUST repeat
95 * the PostConstruct annotation, because JSR 250 specifies that lifecycle
96 * methods "are called unless a subclass of the declaring class overrides
97 * the method without repeating the annotation". (The blueprint-maven-plugin
98 * would gen. XML which calls PostConstruct annotated methods even if they are
99 * in a subclass without repeating the annotation, but this is wrong and not
100 * JSR 250 compliant, and while working in BP, then causes issues e.g. when
101 * wiring with Guice for tests, so do always repeat it.)
104 protected void init() {
109 public void close() throws Exception {
110 if (listenerRegistration != null) {
112 listenerRegistration.close();
113 } catch (final Exception e) {
114 log.error("Error when cleaning up DataTreeChangeListener.", e);
116 listenerRegistration = null;
120 protected abstract InstanceIdentifier<T> getWildCardPath();
121 protected abstract void remove(InstanceIdentifier<T> key, T dataObjectModification);
122 protected abstract void update(InstanceIdentifier<T> key, T dataObjectModificationBefore, T dataObjectModificationAfter);
123 protected abstract void add(InstanceIdentifier<T> key, T dataObjectModification);
124 protected abstract K getDataTreeChangeListener();
126 public class DataTreeChangeHandler implements Runnable {
127 private final Collection<DataTreeModification<T>> changes;
129 public DataTreeChangeHandler(Collection<DataTreeModification<T>> changes) {
130 chainingDelegate.notifyBeforeOnDataTreeChanged(changes);
131 this.changes = changes;
132 for (DataTreeModification<T> change : changes) {
133 log.debug("sync run() {}", change);
139 for (DataTreeModification<T> change : changes) {
140 final InstanceIdentifier<T> key = change.getRootPath().getRootIdentifier();
141 final DataObjectModification<T> mod = change.getRootNode();
143 switch (mod.getModificationType()) {
145 remove(key, mod.getDataBefore());
147 case SUBTREE_MODIFIED:
148 update(key, mod.getDataBefore(), mod.getDataAfter());
151 if (mod.getDataBefore() == null) {
152 add(key, mod.getDataAfter());
154 update(key, mod.getDataBefore(), mod.getDataAfter());
158 // FIXME: May be not a good idea to throw.
159 throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType());
161 log.debug("async run() {}", change);
163 chainingDelegate.notifyAfterOnDataTreeChanged(changes);