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