Updated registration of Binding Aware notification listeners.
[controller.git] / opendaylight / sal / yang-prototype / sal / sal-binding-broker-impl / src / main / java / org / opendaylight / controller / sal / binding / impl / NotificationModule.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. 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 package org.opendaylight.controller.sal.binding.impl;
9
10 import java.util.Collections;
11 import java.util.HashMap;
12 import java.util.HashSet;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Set;
16
17 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
18 import org.opendaylight.controller.sal.binding.api.BindingAwareService;
19 import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
20 import org.opendaylight.controller.sal.binding.api.NotificationService;
21 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerSession;
22 import org.opendaylight.controller.sal.binding.spi.SALBindingModule;
23 import org.opendaylight.controller.sal.binding.spi.Mapper;
24 import org.opendaylight.controller.sal.binding.spi.MappingProvider;
25 import org.opendaylight.controller.sal.binding.spi.MappingProvider.MappingExtensionFactory;
26
27 import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
28 import org.opendaylight.controller.yang.binding.DataObject;
29 import org.opendaylight.controller.yang.binding.Notification;
30 import org.opendaylight.controller.yang.binding.NotificationListener;
31 import org.opendaylight.controller.yang.common.QName;
32 import org.opendaylight.controller.yang.data.api.CompositeNode;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 public class NotificationModule implements SALBindingModule {
37
38     private ProviderSession biSession;
39     private org.opendaylight.controller.sal.core.api.notify.NotificationProviderService biNotifyService;
40     private BindingAwareBroker broker;
41     private MappingProvider mappingProvider;
42
43     private Map<Class<? extends Notification>, List<NotificationListener>> listeners = new HashMap<Class<? extends Notification>, List<NotificationListener>>();
44     private Set<QName> biNotifications = new HashSet<QName>();
45     private static final Logger log = LoggerFactory
46             .getLogger(NotificationModule.class);
47     private final BindingIndependentListener biListener = new BindingIndependentListener();
48
49     @Override
50     public Set<Class<? extends BindingAwareService>> getProvidedServices() {
51
52         Set<Class<? extends BindingAwareService>> ret = new HashSet<Class<? extends BindingAwareService>>();
53         ret.add(NotificationService.class);
54         ret.add(NotificationProviderService.class);
55         return ret;
56     }
57
58     @Override
59     public <T extends BindingAwareService> T getServiceForSession(
60             Class<T> service, ConsumerSession session) {
61         if (service == null)
62             throw new IllegalArgumentException("Service should not be null");
63         if (session == null)
64             throw new IllegalArgumentException("Session should not be null");
65
66         if (NotificationProviderSession.class.equals(service)) {
67             if (session instanceof org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderSession) {
68                 @SuppressWarnings("unchecked")
69                 T ret = (T) new NotificationProviderSession(session);
70                 return ret;
71             } else {
72                 throw new IllegalArgumentException(
73                         "NotificationProviderService is available only to ProviderSession");
74             }
75         }
76
77         if (NotificationService.class.equals(service)) {
78             @SuppressWarnings("unchecked")
79             T ret = (T) new NotificationSession(session);
80             return ret;
81         }
82         return null;
83     }
84
85     @Override
86     public Set<Class<? extends org.opendaylight.controller.sal.binding.api.BindingAwareProvider.ProviderFunctionality>> getSupportedProviderFunctionality() {
87         return Collections.emptySet();
88     }
89
90     @Override
91     public void setBroker(BindingAwareBroker broker) {
92         this.broker = broker;
93     }
94
95     @Override
96     public void setMappingProvider(MappingProvider provider) {
97         this.mappingProvider = provider;
98     }
99
100     @Override
101     public void onBISessionAvailable(ProviderSession session) {
102         biSession = session;
103         if (biSession != null) {
104             biNotifyService = session
105                     .getService(org.opendaylight.controller.sal.core.api.notify.NotificationProviderService.class);
106         }
107     }
108
109     private void notify(Notification notification) {
110         notifyBindingIndependent(notification);
111         notifyBindingAware(notification);
112     }
113
114     private void notifyBindingAware(Notification notification) {
115         Class<? extends Notification> type = notification.getClass();
116         List<NotificationListener> toNotify = listeners.get(type);
117
118         // Invocation of notification on registered listeners
119         if (toNotify != null) {
120
121             // We get factory for Notification Invoker
122             MappingExtensionFactory<NotificationInvoker> invokerFactory = mappingProvider
123                     .getExtensionFactory(NotificationInvoker.class);
124
125             // We get generated invoker for NoficiationListener interface
126             // associated to Notification Type
127             NotificationInvoker invoker = invokerFactory.forClass(type);
128             for (NotificationListener listener : toNotify) {
129                 try {
130                     // Invoker invokes the right method on subtype of
131                     // NotificationListener
132                     // associated to the type of notification
133                     invoker.notify(notification, listener);
134                 } catch (Exception e) {
135
136                 }
137             }
138         }
139     }
140
141     private void notifyBindingIndependent(Notification notification) {
142         Class<? extends Notification> type = notification.getClass();
143
144         if (biSession == null) {
145             return;
146         }
147         if (biSession.isClosed()) {
148             return;
149         }
150         if (biNotifyService == null) {
151             return;
152         }
153
154         // FIXME: Somehow we need to resolve this for class hierarchy.
155         // probably use type.getInterfaces()
156         Mapper<? extends Notification> mapper = mappingProvider.getMapper(type);
157         CompositeNode domNotification = mapper.domFromObject(notification);
158
159         biNotifyService.sendNotification(domNotification);
160     }
161
162     private void addBAListener(Class<? extends Notification> notificationType,
163             NotificationListener listener) {
164
165         BrokerUtils.addToMap(listeners, notificationType, listener);
166         Mapper<? extends Notification> mapper = mappingProvider
167                 .getMapper(notificationType);
168         QName biType = mapper.getQName();
169         if (false == biNotifications.contains(biType)) {
170             // The listener is not registered for binding independent
171             // notification
172             biNotifications.add(biType);
173
174             if (biNotifyService != null) {
175                 biNotifyService.addNotificationListener(biType, biListener);
176             }
177         }
178
179     }
180
181     private void removeBAListener(
182             Class<? extends Notification> notificationType,
183             NotificationListener listener) {
184         BrokerUtils.removeFromMap(listeners, notificationType, listener);
185     }
186
187     private class NotificationSession implements NotificationService {
188         private final ConsumerSession session;
189
190         public NotificationSession(ConsumerSession session) {
191             this.session = session;
192         }
193
194         private Map<Class<? extends Notification>, List<NotificationListener>> sessionListeners = new HashMap<Class<? extends Notification>, List<NotificationListener>>();
195
196         @Override
197         public void addNotificationListener(
198                 Class<? extends Notification> notificationType,
199                 NotificationListener listener) {
200
201             NotificationModule.this.addBAListener(notificationType, listener);
202             BrokerUtils.addToMap(sessionListeners, notificationType, listener);
203
204         }
205
206         @Override
207         public void removeNotificationListener(
208                 Class<? extends Notification> notificationType,
209                 NotificationListener listener) {
210             BrokerUtils.removeFromMap(sessionListeners, notificationType,
211                     listener);
212             NotificationModule.this
213                     .removeBAListener(notificationType, listener);
214         }
215
216     }
217
218     private class NotificationProviderSession extends NotificationSession
219             implements NotificationProviderService {
220
221         public NotificationProviderSession(ConsumerSession session) {
222             super(session);
223         }
224
225         @Override
226         public void notify(Notification notification) {
227             NotificationModule.this.notify(notification);
228         }
229
230     }
231
232     private class BindingIndependentListener
233             implements
234             org.opendaylight.controller.sal.core.api.notify.NotificationListener {
235
236         @Override
237         public Set<QName> getSupportedNotifications() {
238             return biNotifications;
239         }
240
241         @Override
242         public void onNotification(CompositeNode notification) {
243             NotificationModule.this
244                     .onBindingIndependentNotification(notification);
245         }
246
247     }
248
249     private void onBindingIndependentNotification(CompositeNode biNotification) {
250         QName biType = biNotification.getNodeType();
251
252         Mapper<DataObject> mapper = mappingProvider.getMapper(biType);
253         if (mapper == null) {
254             log.info("Received notification does not have a binding defined.");
255             return;
256         }
257         Class<DataObject> type = mapper.getDataObjectClass();
258
259         // We check if the received QName / type is really Notification
260         if (Notification.class.isAssignableFrom(type)) {
261             Notification notification = (Notification) mapper
262                     .objectFromDom(biNotification);
263             notifyBindingAware(notification);
264         } else {
265             // The generated type for this QName does not inherits from
266             // notification something went wrong - generated APIs and/or 
267             // provider sending notification
268             // which was incorectly described in the YANG schema.
269             log.error("Received notification " + biType
270                     + " is not binded as notification");
271         }
272
273     }
274 }