Merge "Bug 8873 - Bundle based reconciliation to enable bundling of messages"
[openflowplugin.git] / openflowplugin / src / main / java / org / opendaylight / openflowplugin / openflow / md / core / session / SessionManagerOFImpl.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
9 package org.opendaylight.openflowplugin.openflow.md.core.session;
10
11 import com.google.common.util.concurrent.ListeningExecutorService;
12 import java.util.Arrays;
13 import java.util.Collection;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Map.Entry;
17 import java.util.concurrent.ConcurrentHashMap;
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
20 import org.opendaylight.openflowplugin.api.openflow.md.core.ConnectionConductor;
21 import org.opendaylight.openflowplugin.api.openflow.md.core.IMDMessageTranslator;
22 import org.opendaylight.openflowplugin.api.openflow.md.core.SwitchConnectionDistinguisher;
23 import org.opendaylight.openflowplugin.api.openflow.md.core.TranslatorKey;
24 import org.opendaylight.openflowplugin.api.openflow.md.core.session.SessionContext;
25 import org.opendaylight.openflowplugin.api.openflow.md.core.session.SessionListener;
26 import org.opendaylight.openflowplugin.api.openflow.md.core.session.SwitchSessionKeyOF;
27 import org.opendaylight.openflowplugin.api.openflow.md.queue.PopListener;
28 import org.opendaylight.openflowplugin.api.openflow.statistics.MessageSpy;
29 import org.opendaylight.openflowplugin.extension.api.core.extension.ExtensionConverterProvider;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
31 import org.opendaylight.yangtools.concepts.ListenerRegistration;
32 import org.opendaylight.yangtools.util.ListenerRegistry;
33 import org.opendaylight.yangtools.yang.binding.DataContainer;
34 import org.opendaylight.yangtools.yang.binding.DataObject;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 /**
39  * @author mirehak
40  */
41 public class SessionManagerOFImpl implements ConjunctSessionManager {
42
43     protected static final Logger LOG = LoggerFactory.getLogger(SessionManagerOFImpl.class);
44     private static SessionManagerOFImpl instance;
45     private ConcurrentHashMap<SwitchSessionKeyOF, SessionContext> sessionLot;
46     private Map<TranslatorKey, Collection<IMDMessageTranslator<OfHeader, List<DataObject>>>> translatorMapping;
47     private Map<Class<? extends DataObject>, Collection<PopListener<DataObject>>> popListenerMapping;
48
49     protected ListenerRegistry<SessionListener> sessionListeners;
50     private NotificationProviderService notificationProviderService;
51
52     private DataBroker dataBroker;
53     private ListeningExecutorService rpcPool;
54
55
56     /**
57      * @return singleton instance
58      */
59     public static ConjunctSessionManager getInstance() {
60         if (instance == null) {
61             synchronized (SessionContextOFImpl.class) {
62                 if (instance == null) {
63                     instance = new SessionManagerOFImpl();
64                 }
65             }
66         }
67         return instance;
68     }
69
70     /**
71      * close and release singleton instance
72      */
73     public static void releaseInstance() {
74         if (instance != null) {
75             synchronized (SessionManagerOFImpl.class) {
76                 if (instance != null) {
77                     instance.close();
78                     instance = null;
79                 }
80             }
81         }
82     }
83
84     private SessionManagerOFImpl() {
85         LOG.debug("singleton creating");
86         sessionLot = new ConcurrentHashMap<>();
87         sessionListeners = new ListenerRegistry<>();
88     }
89
90     @Override
91     public SessionContext getSessionContext(SwitchSessionKeyOF sessionKey) {
92         return sessionLot.get(sessionKey);
93     }
94
95     @Override
96     public void invalidateSessionContext(SwitchSessionKeyOF sessionKey) {
97         SessionContext context = getSessionContext(sessionKey);
98         if (context == null) {
99             LOG.info("context for invalidation not found");
100         } else {
101             synchronized (context) {
102                 if (context.isValid()) {
103                     for (Entry<SwitchConnectionDistinguisher, ConnectionConductor> auxEntry : context.getAuxiliaryConductors()) {
104                         invalidateAuxiliary(sessionKey, auxEntry.getKey());
105                     }
106                     context.getPrimaryConductor().disconnect();
107                     context.setValid(false);
108                     removeSessionContext(context);
109                     // TODO:: notify listeners
110                 } else {
111                     LOG.warn("Ignore invalid session context: {}",
112                              Arrays.toString(sessionKey.getId()));
113                 }
114             }
115         }
116     }
117
118     private void invalidateDeadSessionContext(SessionContext sessionContext) {
119         if (sessionContext == null) {
120             LOG.info("context for invalidation not found");
121         } else {
122             synchronized (sessionContext) {
123                 if (sessionContext.isValid()) {
124                     for (Entry<SwitchConnectionDistinguisher, ConnectionConductor> auxEntry : sessionContext
125                              .getAuxiliaryConductors()) {
126                         invalidateAuxiliary(sessionContext, auxEntry.getKey(), true);
127                     }
128                     sessionContext.setValid(false);
129                     removeSessionContext(sessionContext);
130                     // TODO:: notify listeners
131                 } else {
132                     LOG.warn("Ignore invalid dead session context: {}",
133                              Arrays.toString(
134                                  sessionContext.getSessionKey().getId()));
135                 }
136             }
137         }
138     }
139
140     private void removeSessionContext(SessionContext sessionContext) {
141         if (LOG.isDebugEnabled()) {
142             LOG.debug("removing session: {}", Arrays.toString(sessionContext.getSessionKey().getId()));
143         }
144         if (sessionLot.remove(sessionContext.getSessionKey(), sessionContext)) {
145             sessionNotifier.onSessionRemoved(sessionContext);
146         } else {
147             // This should never happen.
148             LOG.warn("Ignore session context that was already removed: {}",
149                      Arrays.toString(sessionContext.getSessionKey().getId()));
150         }
151     }
152
153     @Override
154     public void addSessionContext(SwitchSessionKeyOF sessionKey, SessionContext context) {
155         synchronized (context) {
156             sessionLot.put(sessionKey, context);
157             sessionNotifier.onSessionAdded(sessionKey, context);
158             context.setValid(true);
159         }
160     }
161
162     @Override
163     public void setRole(SessionContext context) {
164         sessionNotifier.setRole(context);
165     }
166     @Override
167     public void invalidateAuxiliary(SwitchSessionKeyOF sessionKey,
168                                     SwitchConnectionDistinguisher connectionCookie) {
169         SessionContext context = getSessionContext(sessionKey);
170         invalidateAuxiliary(context, connectionCookie, true);
171     }
172
173     /**
174      * @param context
175      * @param connectionCookie
176      * @param disconnect       true if auxiliary connection is to be disconnected
177      */
178     private static void invalidateAuxiliary(SessionContext context, SwitchConnectionDistinguisher connectionCookie,
179                                             boolean disconnect) {
180         if (context == null) {
181             LOG.info("context for invalidation not found");
182         } else {
183             ConnectionConductor auxiliaryConductor = context.removeAuxiliaryConductor(connectionCookie);
184             if (auxiliaryConductor == null) {
185                 LOG.warn("auxiliary conductor not found");
186             } else {
187                 if (disconnect) {
188                     auxiliaryConductor.disconnect();
189                 }
190             }
191         }
192     }
193
194     @Override
195     public void invalidateOnDisconnect(ConnectionConductor conductor) {
196         if (conductor.getAuxiliaryKey() == null) {
197             invalidateDeadSessionContext(conductor.getSessionContext());
198             // TODO:: notify listeners
199         } else {
200             invalidateAuxiliary(conductor.getSessionContext(), conductor.getAuxiliaryKey(), false);
201         }
202     }
203
204     @Override
205     public void setTranslatorMapping(Map<TranslatorKey, Collection<IMDMessageTranslator<OfHeader, List<DataObject>>>> translatorMapping) {
206         this.translatorMapping = translatorMapping;
207     }
208
209     @Override
210     public ListenerRegistration<SessionListener> registerSessionListener(SessionListener listener) {
211         LOG.debug("registerSessionListener");
212         return sessionListeners.register(listener);
213     }
214
215     private final SessionListener sessionNotifier = new SessionListener() {
216
217         @Override
218         public void onSessionAdded(SwitchSessionKeyOF sessionKey, SessionContext context) {
219             for (ListenerRegistration<SessionListener> listener : sessionListeners) {
220                 try {
221                     listener.getInstance().onSessionAdded(sessionKey, context);
222                 } catch (Exception e) {
223                     LOG.error("Unhandled exeption occured while invoking onSessionAdded on listener", e);
224                 }
225             }
226         }
227
228         @Override
229         public void setRole(SessionContext context) {
230             for (ListenerRegistration<SessionListener> listener : sessionListeners) {
231                 try {
232                     listener.getInstance().setRole(context);
233                 } catch (Exception e) {
234                     LOG.error("Unhandled exeption occured while invoking setRole on listener", e);
235                 }
236             }
237         }
238
239         @Override
240         public void onSessionRemoved(SessionContext context) {
241             for (ListenerRegistration<SessionListener> listener : sessionListeners) {
242                 try {
243                     listener.getInstance().onSessionRemoved(context);
244                 } catch (Exception e) {
245                     LOG.error("Unhandled exeption occured while invoking onSessionRemoved on listener", e);
246                 }
247             }
248         }
249     };
250     private MessageSpy<DataContainer> messageSpy;
251     private ExtensionConverterProvider extensionConverterProvider;
252
253
254     @Override
255     public Map<TranslatorKey, Collection<IMDMessageTranslator<OfHeader, List<DataObject>>>> getTranslatorMapping() {
256         return this.translatorMapping;
257     }
258
259     @Override
260     public void setNotificationProviderService(
261             NotificationProviderService notificationProviderService) {
262         this.notificationProviderService = notificationProviderService;
263
264     }
265
266     @Override
267     public DataBroker getDataBroker() {
268         return dataBroker;
269     }
270
271     @Override
272     public void setDataBroker(DataBroker dataBroker) {
273         this.dataBroker = dataBroker;
274
275     }
276
277     @Override
278     public NotificationProviderService getNotificationProviderService() {
279         return notificationProviderService;
280     }
281
282     @Override
283     public Map<Class<? extends DataObject>, Collection<PopListener<DataObject>>> getPopListenerMapping() {
284         return popListenerMapping;
285     }
286
287     @Override
288     public void setPopListenerMapping(
289             Map<Class<? extends DataObject>, Collection<PopListener<DataObject>>> popListenerMapping) {
290         this.popListenerMapping = popListenerMapping;
291     }
292
293     @Override
294     public void close() {
295         LOG.debug("close");
296         synchronized (sessionLot) {
297             for (SessionContext sessionContext : sessionLot.values()) {
298                 sessionContext.getPrimaryConductor().disconnect();
299             }
300             // TODO: handle timeouted shutdown
301             rpcPool.shutdown();
302         }
303
304         for (ListenerRegistration<SessionListener> listenerRegistration : sessionListeners) {
305             SessionListener listener = listenerRegistration.getInstance();
306             if (listener instanceof AutoCloseable) {
307                 try {
308                     ((AutoCloseable) listener).close();
309                 } catch (Exception e) {
310                     LOG.warn("closing of sessionListenerRegistration failed", e);
311                 }
312             }
313         }
314     }
315
316     @Override
317     public void setRpcPool(ListeningExecutorService rpcPool) {
318         this.rpcPool = rpcPool;
319     }
320
321     @Override
322     public ListeningExecutorService getRpcPool() {
323         return rpcPool;
324     }
325
326     @Override
327     public void setMessageSpy(MessageSpy<DataContainer> messageSpy) {
328         this.messageSpy = messageSpy;
329     }
330
331     @Override
332     public MessageSpy<DataContainer> getMessageSpy() {
333         return messageSpy;
334     }
335
336     @Override
337     public void setExtensionConverterProvider(
338             ExtensionConverterProvider extensionConverterProvider) {
339         this.extensionConverterProvider = extensionConverterProvider;
340     }
341
342     /**
343      * @return the extensionConverterProvider
344      */
345     @Override
346     public ExtensionConverterProvider getExtensionConverterProvider() {
347         return extensionConverterProvider;
348     }
349
350     @Override
351     public Collection<SessionContext> getAllSessions() {
352         return sessionLot.values();
353     }
354 }