BUG-956 deadlock by rpc invocation - phase2
[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 java.util.Arrays;
12 import java.util.Collection;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Map.Entry;
16 import java.util.concurrent.ConcurrentHashMap;
17
18 import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
19 import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
20 import org.opendaylight.openflowplugin.openflow.md.core.ConnectionConductor;
21 import org.opendaylight.openflowplugin.openflow.md.core.IMDMessageTranslator;
22 import org.opendaylight.openflowplugin.openflow.md.core.SwitchConnectionDistinguisher;
23 import org.opendaylight.openflowplugin.openflow.md.core.TranslatorKey;
24 import org.opendaylight.openflowplugin.openflow.md.queue.PopListener;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
26 import org.opendaylight.yangtools.concepts.ListenerRegistration;
27 import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
28 import org.opendaylight.yangtools.yang.binding.DataObject;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 import com.google.common.util.concurrent.ListeningExecutorService;
33
34 /**
35  * @author mirehak
36  */
37 public class SessionManagerOFImpl implements SessionManager {
38
39     protected static final Logger LOG = LoggerFactory.getLogger(SessionManagerOFImpl.class);
40     private static SessionManagerOFImpl instance;
41     private ConcurrentHashMap<SwitchSessionKeyOF, SessionContext> sessionLot;
42     private Map<TranslatorKey, Collection<IMDMessageTranslator<OfHeader, List<DataObject>>>> translatorMapping;
43     private Map<Class<? extends DataObject>, Collection<PopListener<DataObject>>> popListenerMapping;
44
45     protected ListenerRegistry<SessionListener> sessionListeners;
46     private NotificationProviderService notificationProviderService;
47
48     private DataProviderService dataProviderService;
49     private ListeningExecutorService rpcPool;
50     
51
52     /**
53      * @return singleton instance
54      */
55     public static synchronized SessionManager getInstance() {
56         if (instance == null) {
57             instance = new SessionManagerOFImpl();
58         }
59         return instance;
60     }
61     
62     /**
63      * close and release singleton instace
64      */
65     public static synchronized void releaseInstance() {
66         instance.close();
67         instance = null;
68     }
69
70     private SessionManagerOFImpl() {
71         LOG.debug("singleton creating");
72         sessionLot = new ConcurrentHashMap<>();
73         sessionListeners = new ListenerRegistry<>();
74     }
75
76     @Override
77     public SessionContext getSessionContext(SwitchSessionKeyOF sessionKey) {
78         return sessionLot.get(sessionKey);
79     }
80
81     @Override
82     public void invalidateSessionContext(SwitchSessionKeyOF sessionKey) {
83         SessionContext context = getSessionContext(sessionKey);
84         if (context == null) {
85             LOG.warn("context for invalidation not found");
86         } else {
87             for (Entry<SwitchConnectionDistinguisher, ConnectionConductor> auxEntry : context.getAuxiliaryConductors()) {
88                 invalidateAuxiliary(sessionKey, auxEntry.getKey());
89             }
90             context.getPrimaryConductor().disconnect();
91             context.setValid(false);
92             removeSessionContext(context);
93             // TODO:: notify listeners
94         }
95     }
96
97     private void invalidateDeadSessionContext(SessionContext sessionContext) {
98         if (sessionContext == null) {
99             LOG.warn("context for invalidation not found");
100         } else {
101             for (Entry<SwitchConnectionDistinguisher, ConnectionConductor> auxEntry : sessionContext
102                     .getAuxiliaryConductors()) {
103                 invalidateAuxiliary(sessionContext, auxEntry.getKey(), true);
104             }
105             sessionContext.setValid(false);
106             removeSessionContext(sessionContext);
107             // TODO:: notify listeners
108         }
109     }
110
111     private void removeSessionContext(SessionContext sessionContext) {
112         if (LOG.isDebugEnabled()) {
113             LOG.debug("removing session: {}", Arrays.toString(sessionContext.getSessionKey().getId()));
114         }
115         sessionLot.remove(sessionContext.getSessionKey(), sessionContext);
116         sessionNotifier.onSessionRemoved(sessionContext);
117     }
118
119     @Override
120     public void addSessionContext(SwitchSessionKeyOF sessionKey, SessionContext context) {
121         sessionLot.put(sessionKey, context);
122
123         sessionNotifier.onSessionAdded(sessionKey, context);
124
125     }
126
127     @Override
128     public void invalidateAuxiliary(SwitchSessionKeyOF sessionKey,
129             SwitchConnectionDistinguisher connectionCookie) {
130         SessionContext context = getSessionContext(sessionKey);
131         invalidateAuxiliary(context, connectionCookie, true);
132     }
133
134     /**
135      * @param context
136      * @param connectionCookie
137      * @param disconnect
138      *            true if auxiliary connection is to be disconnected
139      */
140     private static void invalidateAuxiliary(SessionContext context, SwitchConnectionDistinguisher connectionCookie,
141             boolean disconnect) {
142         if (context == null) {
143             LOG.warn("context for invalidation not found");
144         } else {
145             ConnectionConductor auxiliaryConductor = context.removeAuxiliaryConductor(connectionCookie);
146             if (auxiliaryConductor == null) {
147                 LOG.warn("auxiliary conductor not found");
148             } else {
149                 if (disconnect) {
150                     auxiliaryConductor.disconnect();
151                 }
152             }
153         }
154     }
155
156     @Override
157     public void invalidateOnDisconnect(ConnectionConductor conductor) {
158         if (conductor.getAuxiliaryKey() == null) {
159             invalidateDeadSessionContext(conductor.getSessionContext());
160             // TODO:: notify listeners
161         } else {
162             invalidateAuxiliary(conductor.getSessionContext(), conductor.getAuxiliaryKey(), false);
163         }
164     }
165
166     @Override
167     public void setTranslatorMapping(Map<TranslatorKey, Collection<IMDMessageTranslator<OfHeader, List<DataObject>>>> translatorMapping) {
168         this.translatorMapping = translatorMapping;
169     }
170
171     @Override
172     public ListenerRegistration<SessionListener> registerSessionListener(SessionListener listener) {
173         LOG.debug("registerSessionListener");
174         return sessionListeners.register(listener);
175     }
176
177     private final SessionListener sessionNotifier = new SessionListener() {
178
179         @Override
180         public void onSessionAdded(SwitchSessionKeyOF sessionKey, SessionContext context) {
181             for (ListenerRegistration<SessionListener> listener : sessionListeners) {
182                 try {
183                     listener.getInstance().onSessionAdded(sessionKey, context);
184                 } catch (Exception e) {
185                     LOG.error("Unhandled exeption occured while invoking onSessionAdded on listener", e);
186                 }
187             }
188         }
189
190         @Override
191         public void onSessionRemoved(SessionContext context) {
192             for (ListenerRegistration<SessionListener> listener : sessionListeners) {
193                 try {
194                     listener.getInstance().onSessionRemoved(context);
195                 } catch (Exception e) {
196                     LOG.error("Unhandled exeption occured while invoking onSessionRemoved on listener", e);
197                 }
198             }
199         }
200     };
201     
202
203     @Override
204     public Map<TranslatorKey, Collection<IMDMessageTranslator<OfHeader, List<DataObject>>>> getTranslatorMapping() {
205         return this.translatorMapping;
206     }
207
208     @Override
209     public void setNotificationProviderService(
210             NotificationProviderService notificationProviderService) {
211         this.notificationProviderService = notificationProviderService;
212
213     }
214
215     @Override
216     public DataProviderService getDataProviderService() {
217         return dataProviderService;
218     }
219
220     @Override
221     public void setDataProviderService(DataProviderService dataServiceProvider) {
222         this.dataProviderService = dataServiceProvider;
223
224     }
225
226     @Override
227     public NotificationProviderService getNotificationProviderService() {
228         return notificationProviderService;
229     }
230
231     @Override
232     public Map<Class<? extends DataObject>, Collection<PopListener<DataObject>>> getPopListenerMapping() {
233         return popListenerMapping;
234     }
235
236     @Override
237     public void setPopListenerMapping(
238             Map<Class<? extends DataObject>, Collection<PopListener<DataObject>>> popListenerMapping) {
239         this.popListenerMapping = popListenerMapping;
240     }
241     
242     @Override
243     public void close() {
244         LOG.debug("close");
245         sessionListeners = null;
246         synchronized (sessionLot) {
247             for (SessionContext sessionContext : sessionLot.values()) {
248                 sessionContext.getPrimaryConductor().disconnect();
249             }
250             // TODO: handle timeouted shutdown
251             rpcPool.shutdown();
252         }
253     }
254
255     @Override
256     public void setRpcPool(ListeningExecutorService rpcPool) {
257         this.rpcPool = rpcPool;
258     }
259     
260     @Override
261     public ListeningExecutorService getRpcPool() {
262         return rpcPool;
263     }
264 }