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