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.ExecutorService;
14 import javax.annotation.PostConstruct;
15 import javax.annotation.PreDestroy;
16 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
17 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
18 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
19 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
20 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
21 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
22 import org.opendaylight.genius.utils.SuperTypeUtil;
23 import org.opendaylight.infrautils.utils.concurrent.Executors;
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 public abstract class AsyncDataTreeChangeListenerBase<T extends DataObject, K extends DataTreeChangeListener<T>>
32 implements DataTreeChangeListener<T>, ChainableDataTreeChangeListener<T>, AutoCloseable {
34 private static final Logger LOG = LoggerFactory.getLogger(AsyncDataTreeChangeListenerBase.class);
36 private ListenerRegistration<K> listenerRegistration;
37 private final ChainableDataTreeChangeListenerImpl<T> chainingDelegate = new ChainableDataTreeChangeListenerImpl<>();
39 private final ExecutorService dataTreeChangeHandlerExecutor =
40 Executors.newSingleThreadExecutor("AsyncDataTreeChangeListenerBase-DataTreeChangeHandler", LOG);
41 protected final Class<T> clazz;
43 protected AsyncDataTreeChangeListenerBase() {
44 this.clazz = SuperTypeUtil.getTypeParameter(getClass(), 0);
48 public AsyncDataTreeChangeListenerBase(Class<T> clazz, Class<K> eventClazz) {
49 this.clazz = Preconditions.checkNotNull(clazz, "Class can not be null!");
53 public void addBeforeListener(DataTreeChangeListener<T> listener) {
54 chainingDelegate.addBeforeListener(listener);
58 public void addAfterListener(DataTreeChangeListener<T> listener) {
59 chainingDelegate.addAfterListener(listener);
63 public void onDataTreeChanged(Collection<DataTreeModification<T>> changes) {
64 if (changes == null || changes.isEmpty()) {
68 DataTreeChangeHandler dataTreeChangeHandler = new DataTreeChangeHandler(changes);
69 dataTreeChangeHandlerExecutor.execute(dataTreeChangeHandler);
72 public void registerListener(LogicalDatastoreType dsType, final DataBroker db) {
73 final DataTreeIdentifier<T> treeId = new DataTreeIdentifier<>(dsType, getWildCardPath());
74 listenerRegistration = db.registerDataTreeChangeListener(treeId, getDataTreeChangeListener());
78 * Subclasses override this and place initialization logic here, notably
79 * calls to registerListener(). Note that the overriding method MUST repeat
80 * the PostConstruct annotation, because JSR 250 specifies that lifecycle
81 * methods "are called unless a subclass of the declaring class overrides
82 * the method without repeating the annotation". (The blueprint-maven-plugin
83 * would gen. XML which calls PostConstruct annotated methods even if they are
84 * in a subclass without repeating the annotation, but this is wrong and not
85 * JSR 250 compliant, and while working in BP, then causes issues e.g. when
86 * wiring with Guice for tests, so do always repeat it.)
89 protected void init() {
95 dataTreeChangeHandlerExecutor.shutdownNow();
97 if (listenerRegistration != null) {
99 listenerRegistration.close();
101 listenerRegistration = null;
106 protected abstract InstanceIdentifier<T> getWildCardPath();
108 protected abstract void remove(InstanceIdentifier<T> key, T dataObjectModification);
110 protected abstract void update(InstanceIdentifier<T> key,
111 T dataObjectModificationBefore, T dataObjectModificationAfter);
113 protected abstract void add(InstanceIdentifier<T> key, T dataObjectModification);
115 protected abstract K getDataTreeChangeListener();
117 public class DataTreeChangeHandler implements Runnable {
118 private final Collection<DataTreeModification<T>> changes;
120 public DataTreeChangeHandler(Collection<DataTreeModification<T>> changes) {
121 chainingDelegate.notifyBeforeOnDataTreeChanged(changes);
122 this.changes = changes;
127 for (DataTreeModification<T> change : changes) {
128 final InstanceIdentifier<T> key = change.getRootPath().getRootIdentifier();
129 final DataObjectModification<T> mod = change.getRootNode();
131 switch (mod.getModificationType()) {
133 remove(key, mod.getDataBefore());
135 case SUBTREE_MODIFIED:
136 update(key, mod.getDataBefore(), mod.getDataAfter());
139 if (mod.getDataBefore() == null) {
140 add(key, mod.getDataAfter());
142 update(key, mod.getDataBefore(), mod.getDataAfter());
146 // FIXME: May be not a good idea to throw.
147 throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType());
150 chainingDelegate.notifyAfterOnDataTreeChanged(changes);