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