Merge "Fcaps: changing alarm text parameter to be same while raising and clearing"
[genius.git] / mdsalutil / mdsalutil-api / src / main / java / org / opendaylight / genius / datastoreutils / AsyncDataTreeChangeListenerBase.java
1 /*
2  * Copyright (c) 2016 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
9 package org.opendaylight.genius.datastoreutils;
10
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;
29
30 public abstract class AsyncDataTreeChangeListenerBase<T extends DataObject, K extends DataTreeChangeListener<T>>
31         implements DataTreeChangeListener<T>, ChainableDataTreeChangeListener<T>, AutoCloseable {
32
33     private static final Logger LOG = LoggerFactory.getLogger(AsyncDataTreeChangeListenerBase.class);
34
35     private static final int DATATREE_CHANGE_HANDLER_THREAD_POOL_CORE_SIZE = 1;
36     private static final int DATATREE_CHANGE_HANDLER_THREAD_POOL_MAX_SIZE = 1;
37     private static final int DATATREE_CHANGE_HANDLER_THREAD_POOL_KEEP_ALIVE_TIME_SECS = 300;
38     private static final int STARTUP_LOOP_TICK = 500;
39     private static final int STARTUP_LOOP_MAX_RETRIES = 8;
40
41     private ListenerRegistration<K> listenerRegistration;
42     private final ChainableDataTreeChangeListenerImpl<T> chainingDelegate = new ChainableDataTreeChangeListenerImpl<>();
43
44     private static ThreadPoolExecutor dataTreeChangeHandlerExecutor = new ThreadPoolExecutor(
45             DATATREE_CHANGE_HANDLER_THREAD_POOL_CORE_SIZE,
46             DATATREE_CHANGE_HANDLER_THREAD_POOL_MAX_SIZE,
47             DATATREE_CHANGE_HANDLER_THREAD_POOL_KEEP_ALIVE_TIME_SECS,
48             TimeUnit.SECONDS,
49             new LinkedBlockingQueue<>());
50
51     protected final Class<T> clazz;
52     private final Class<K> eventClazz;
53
54     public AsyncDataTreeChangeListenerBase(Class<T> clazz, Class<K> eventClazz) {
55         this.clazz = Preconditions.checkNotNull(clazz, "Class can not be null!");
56         this.eventClazz = Preconditions.checkNotNull(eventClazz, "eventClazz can not be null!");
57     }
58
59     @Override
60     public void addAfterListener(DataTreeChangeListener<T> listener) {
61         chainingDelegate.addAfterListener(listener);
62     }
63
64     @Override
65     public void onDataTreeChanged(Collection<DataTreeModification<T>> changes) {
66         if (changes == null || changes.isEmpty()) {
67             return;
68         }
69
70         DataTreeChangeHandler dataTreeChangeHandler = new DataTreeChangeHandler(changes);
71         dataTreeChangeHandlerExecutor.execute(dataTreeChangeHandler);
72     }
73
74     public void registerListener(LogicalDatastoreType dsType, final DataBroker db) {
75         final DataTreeIdentifier<T> treeId = new DataTreeIdentifier<>(dsType, getWildCardPath());
76         try {
77             TaskRetryLooper looper = new TaskRetryLooper(STARTUP_LOOP_TICK, STARTUP_LOOP_MAX_RETRIES);
78             listenerRegistration = looper.loopUntilNoException(() -> db.registerDataTreeChangeListener(treeId, getDataTreeChangeListener()));
79         } catch (final Exception e) {
80             LOG.warn("{}: Data Tree Change listener registration failed.", eventClazz.getName());
81             LOG.debug("{}: Data Tree Change listener registration failed: {}", eventClazz.getName(), e);
82             throw new IllegalStateException( eventClazz.getName() + "{}startup failed. System needs restart.", e);
83         }
84     }
85
86     /**
87      * Subclasses override this and place initialization logic here, notably
88      * calls to registerListener(). Note that the overriding method MUST repeat
89      * the PostConstruct annotation, because JSR 250 specifies that lifecycle
90      * methods "are called unless a subclass of the declaring class overrides
91      * the method without repeating the annotation".  (The blueprint-maven-plugin
92      * would gen. XML which calls PostConstruct annotated methods even if they are
93      * in a subclass without repeating the annotation, but this is wrong and not
94      * JSR 250 compliant, and while working in BP, then causes issues e.g. when
95      * wiring with Guice for tests, so do always repeat it.)
96      */
97     @PostConstruct
98     protected void init() {
99     }
100
101     @Override
102     @PreDestroy
103     public void close() throws Exception {
104         if (listenerRegistration != null) {
105             try {
106                 listenerRegistration.close();
107             } catch (final Exception e) {
108                 LOG.error("Error when cleaning up DataTreeChangeListener.", e);
109             }
110             listenerRegistration = null;
111         }
112     }
113
114     protected abstract InstanceIdentifier<T> getWildCardPath();
115     protected abstract void remove(InstanceIdentifier<T> key, T dataObjectModification);
116     protected abstract void update(InstanceIdentifier<T> key, T dataObjectModificationBefore, T dataObjectModificationAfter);
117     protected abstract void add(InstanceIdentifier<T> key, T dataObjectModification);
118     protected abstract K getDataTreeChangeListener();
119
120     public class DataTreeChangeHandler implements Runnable {
121         private final Collection<DataTreeModification<T>> changes;
122
123         public DataTreeChangeHandler(Collection<DataTreeModification<T>> changes) {
124             this.changes = changes;
125         }
126
127         @Override
128         public void run() {
129             for (DataTreeModification<T> change : changes) {
130                 final InstanceIdentifier<T> key = change.getRootPath().getRootIdentifier();
131                 final DataObjectModification<T> mod = change.getRootNode();
132
133                 switch (mod.getModificationType()) {
134                     case DELETE:
135                         remove(key, mod.getDataBefore());
136                         break;
137                     case SUBTREE_MODIFIED:
138                         update(key, mod.getDataBefore(), mod.getDataAfter());
139                         break;
140                     case WRITE:
141                         if (mod.getDataBefore() == null) {
142                             add(key, mod.getDataAfter());
143                         } else {
144                             update(key, mod.getDataBefore(), mod.getDataAfter());
145                         }
146                         break;
147                     default:
148                         // FIXME: May be not a good idea to throw.
149                         throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType());
150                 }
151             }
152             chainingDelegate.notifyAfterOnDataTreeChanged(changes);
153         }
154     }
155 }