Take advantage of MultipartTransactionAware
[controller.git] / opendaylight / md-sal / sal-dom-broker / src / main / java / org / opendaylight / controller / sal / dom / broker / 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.dom.broker;
9
10 import java.util.Collection;
11 import java.util.Collections;
12 import java.util.Map;
13 import java.util.Map.Entry;
14 import java.util.Set;
15
16 import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
17 import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
18 import org.opendaylight.controller.sal.core.api.BrokerService;
19 import org.opendaylight.controller.sal.core.api.Consumer.ConsumerFunctionality;
20 import org.opendaylight.controller.sal.core.api.Provider.ProviderFunctionality;
21 import org.opendaylight.controller.sal.core.api.notify.NotificationListener;
22 import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService;
23 import org.opendaylight.controller.sal.core.api.notify.NotificationService;
24 import org.opendaylight.controller.sal.core.spi.BrokerModule;
25 import org.opendaylight.yangtools.concepts.Registration;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 import com.google.common.collect.HashMultimap;
32 import com.google.common.collect.ImmutableSet;
33 import com.google.common.collect.Multimap;
34
35 public class NotificationModule implements BrokerModule {
36     private static Logger log = LoggerFactory
37             .getLogger(NotificationModule.class);
38
39     private final Multimap<QName, NotificationListener> listeners = HashMultimap
40             .create();
41
42     private static final Set<Class<? extends BrokerService>> PROVIDED_SERVICE_TYPE = ImmutableSet
43             .<Class<? extends BrokerService>>of(NotificationService.class,
44                     NotificationPublishService.class);
45
46     private static final Set<Class<? extends ConsumerFunctionality>> SUPPORTED_CONSUMER_FUNCTIONALITY = ImmutableSet
47             .of((Class<? extends ConsumerFunctionality>) NotificationListener.class,
48                     NotificationListener.class); // Workaround: if we use the
49                                                  // version of method with only
50                                                  // one argument, the generics
51                                                  // inference will not work
52
53     @Override
54     public Set<Class<? extends BrokerService>> getProvidedServices() {
55         return PROVIDED_SERVICE_TYPE;
56     }
57
58     @Override
59     public Set<Class<? extends ConsumerFunctionality>> getSupportedConsumerFunctionality() {
60         return SUPPORTED_CONSUMER_FUNCTIONALITY;
61     }
62
63     @Override
64     public <T extends BrokerService> T getServiceForSession(Class<T> service,
65             ConsumerSession session) {
66         if (NotificationPublishService.class.equals(service)
67                 && session instanceof ProviderSession) {
68             @SuppressWarnings("unchecked")
69             T ret = (T) newNotificationPublishService(session);
70             return ret;
71         } else if (NotificationService.class.equals(service)) {
72
73             @SuppressWarnings("unchecked")
74             T ret = (T) newNotificationConsumerService(session);
75             return ret;
76         }
77
78         throw new IllegalArgumentException(
79                 "The requested session-specific service is not provided by this module.");
80     }
81
82     private void sendNotification(CompositeNode notification) {
83         QName type = notification.getNodeType();
84         Collection<NotificationListener> toNotify = listeners.get(type);
85         log.trace("Publishing notification " + type);
86
87         if (toNotify == null) {
88             // No listeners were registered - returns.
89             return;
90         }
91
92         for (NotificationListener listener : toNotify) {
93             try {
94                 // FIXME: ensure that notification is immutable
95                 listener.onNotification(notification);
96             } catch (Exception e) {
97                 log.error("Uncaught exception in NotificationListener", e);
98             }
99         }
100
101     }
102
103     private NotificationService newNotificationConsumerService(
104             ConsumerSession session) {
105         return new NotificationConsumerSessionImpl();
106     }
107
108     private NotificationPublishService newNotificationPublishService(
109             ConsumerSession session) {
110         return new NotificationProviderSessionImpl();
111     }
112
113     private class NotificationConsumerSessionImpl implements
114             NotificationService {
115
116         private final Multimap<QName, NotificationListener> consumerListeners = HashMultimap
117                 .create();
118         private boolean closed = false;
119
120
121         @Override
122         public Registration<NotificationListener> addNotificationListener(QName notification,
123                 NotificationListener listener) {
124             checkSessionState();
125             if (notification == null) {
126                 throw new IllegalArgumentException(
127                         "Notification type must not be null.");
128             }
129             if (listener == null) {
130                 throw new IllegalArgumentException("Listener must not be null.");
131             }
132
133             consumerListeners.put(notification, listener);
134             listeners.put(notification, listener);
135             log.trace("Registered listener for notification: " + notification);
136             return null; // Return registration Object.
137         }
138
139         public void removeNotificationListener(QName notification,
140                 NotificationListener listener) {
141             checkSessionState();
142             if (notification == null) {
143                 throw new IllegalArgumentException(
144                         "Notification type must not be null.");
145             }
146             if (listener == null) {
147                 throw new IllegalArgumentException("Listener must not be null.");
148             }
149             consumerListeners.remove(notification, listener);
150             listeners.remove(notification, listener);
151         }
152
153         public void closeSession() {
154             closed = true;
155             Map<QName, Collection<NotificationListener>> toRemove = consumerListeners
156                     .asMap();
157             for (Entry<QName, Collection<NotificationListener>> entry : toRemove
158                     .entrySet()) {
159                 listeners.remove(entry.getKey(), entry.getValue());
160             }
161         }
162
163         protected void checkSessionState() {
164             if (closed)
165                 throw new IllegalStateException("Session is closed");
166         }
167     }
168
169     private class NotificationProviderSessionImpl extends
170             NotificationConsumerSessionImpl implements
171             NotificationPublishService {
172
173         @Override
174         public void sendNotification(CompositeNode notification) {
175             checkSessionState();
176             if (notification == null)
177                 throw new IllegalArgumentException(
178                         "Notification must not be null.");
179             NotificationModule.this.sendNotification(notification);
180         }
181
182         @Override
183         public void publish(CompositeNode notification) {
184             sendNotification(notification);
185         }
186     }
187
188     @Override
189     public Set<Class<? extends ProviderFunctionality>> getSupportedProviderFunctionality() {
190         return Collections.emptySet();
191     }
192 }