/** * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.openflowplugin.openflow.md.core.session; import com.google.common.util.concurrent.ListeningExecutorService; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.sal.binding.api.NotificationProviderService; import org.opendaylight.openflowplugin.api.openflow.md.core.ConnectionConductor; import org.opendaylight.openflowplugin.api.openflow.md.core.IMDMessageTranslator; import org.opendaylight.openflowplugin.api.openflow.md.core.SwitchConnectionDistinguisher; import org.opendaylight.openflowplugin.api.openflow.md.core.TranslatorKey; import org.opendaylight.openflowplugin.api.openflow.md.core.session.SessionContext; import org.opendaylight.openflowplugin.api.openflow.md.core.session.SessionListener; import org.opendaylight.openflowplugin.api.openflow.md.core.session.SwitchSessionKeyOF; import org.opendaylight.openflowplugin.api.openflow.md.queue.PopListener; import org.opendaylight.openflowplugin.api.openflow.statistics.MessageSpy; import org.opendaylight.openflowplugin.extension.api.core.extension.ExtensionConverterProvider; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.util.ListenerRegistry; import org.opendaylight.yangtools.yang.binding.DataContainer; import org.opendaylight.yangtools.yang.binding.DataObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author mirehak */ public class SessionManagerOFImpl implements ConjunctSessionManager { protected static final Logger LOG = LoggerFactory.getLogger(SessionManagerOFImpl.class); private static SessionManagerOFImpl instance; private ConcurrentHashMap<SwitchSessionKeyOF, SessionContext> sessionLot; private Map<TranslatorKey, Collection<IMDMessageTranslator<OfHeader, List<DataObject>>>> translatorMapping; private Map<Class<? extends DataObject>, Collection<PopListener<DataObject>>> popListenerMapping; protected ListenerRegistry<SessionListener> sessionListeners; private NotificationProviderService notificationProviderService; private DataBroker dataBroker; private ListeningExecutorService rpcPool; /** * @return singleton instance */ public static ConjunctSessionManager getInstance() { if (instance == null) { synchronized (SessionContextOFImpl.class) { if (instance == null) { instance = new SessionManagerOFImpl(); } } } return instance; } /** * close and release singleton instance */ public static void releaseInstance() { if (instance != null) { synchronized (SessionManagerOFImpl.class) { if (instance != null) { instance.close(); instance = null; } } } } private SessionManagerOFImpl() { LOG.debug("singleton creating"); sessionLot = new ConcurrentHashMap<>(); sessionListeners = new ListenerRegistry<>(); } @Override public SessionContext getSessionContext(SwitchSessionKeyOF sessionKey) { return sessionLot.get(sessionKey); } @Override public void invalidateSessionContext(SwitchSessionKeyOF sessionKey) { SessionContext context = getSessionContext(sessionKey); if (context == null) { LOG.info("context for invalidation not found"); } else { synchronized (context) { if (context.isValid()) { for (Entry<SwitchConnectionDistinguisher, ConnectionConductor> auxEntry : context.getAuxiliaryConductors()) { invalidateAuxiliary(sessionKey, auxEntry.getKey()); } context.getPrimaryConductor().disconnect(); context.setValid(false); removeSessionContext(context); // TODO:: notify listeners } else { LOG.warn("Ignore invalid session context: {}", Arrays.toString(sessionKey.getId())); } } } } private void invalidateDeadSessionContext(SessionContext sessionContext) { if (sessionContext == null) { LOG.info("context for invalidation not found"); } else { synchronized (sessionContext) { if (sessionContext.isValid()) { for (Entry<SwitchConnectionDistinguisher, ConnectionConductor> auxEntry : sessionContext .getAuxiliaryConductors()) { invalidateAuxiliary(sessionContext, auxEntry.getKey(), true); } sessionContext.setValid(false); removeSessionContext(sessionContext); // TODO:: notify listeners } else { LOG.warn("Ignore invalid dead session context: {}", Arrays.toString( sessionContext.getSessionKey().getId())); } } } } private void removeSessionContext(SessionContext sessionContext) { if (LOG.isDebugEnabled()) { LOG.debug("removing session: {}", Arrays.toString(sessionContext.getSessionKey().getId())); } if (sessionLot.remove(sessionContext.getSessionKey(), sessionContext)) { sessionNotifier.onSessionRemoved(sessionContext); } else { // This should never happen. LOG.warn("Ignore session context that was already removed: {}", Arrays.toString(sessionContext.getSessionKey().getId())); } } @Override public void addSessionContext(SwitchSessionKeyOF sessionKey, SessionContext context) { synchronized (context) { sessionLot.put(sessionKey, context); sessionNotifier.onSessionAdded(sessionKey, context); context.setValid(true); } } @Override public void setRole(SessionContext context) { sessionNotifier.setRole(context); } @Override public void invalidateAuxiliary(SwitchSessionKeyOF sessionKey, SwitchConnectionDistinguisher connectionCookie) { SessionContext context = getSessionContext(sessionKey); invalidateAuxiliary(context, connectionCookie, true); } /** * @param context * @param connectionCookie * @param disconnect true if auxiliary connection is to be disconnected */ private static void invalidateAuxiliary(SessionContext context, SwitchConnectionDistinguisher connectionCookie, boolean disconnect) { if (context == null) { LOG.info("context for invalidation not found"); } else { ConnectionConductor auxiliaryConductor = context.removeAuxiliaryConductor(connectionCookie); if (auxiliaryConductor == null) { LOG.warn("auxiliary conductor not found"); } else { if (disconnect) { auxiliaryConductor.disconnect(); } } } } @Override public void invalidateOnDisconnect(ConnectionConductor conductor) { if (conductor.getAuxiliaryKey() == null) { invalidateDeadSessionContext(conductor.getSessionContext()); // TODO:: notify listeners } else { invalidateAuxiliary(conductor.getSessionContext(), conductor.getAuxiliaryKey(), false); } } @Override public void setTranslatorMapping(Map<TranslatorKey, Collection<IMDMessageTranslator<OfHeader, List<DataObject>>>> translatorMapping) { this.translatorMapping = translatorMapping; } @Override public ListenerRegistration<SessionListener> registerSessionListener(SessionListener listener) { LOG.debug("registerSessionListener"); return sessionListeners.register(listener); } private final SessionListener sessionNotifier = new SessionListener() { @Override public void onSessionAdded(SwitchSessionKeyOF sessionKey, SessionContext context) { for (ListenerRegistration<SessionListener> listener : sessionListeners) { try { listener.getInstance().onSessionAdded(sessionKey, context); } catch (Exception e) { LOG.error("Unhandled exeption occured while invoking onSessionAdded on listener", e); } } } @Override public void setRole(SessionContext context) { for (ListenerRegistration<SessionListener> listener : sessionListeners) { try { listener.getInstance().setRole(context); } catch (Exception e) { LOG.error("Unhandled exeption occured while invoking setRole on listener", e); } } } @Override public void onSessionRemoved(SessionContext context) { for (ListenerRegistration<SessionListener> listener : sessionListeners) { try { listener.getInstance().onSessionRemoved(context); } catch (Exception e) { LOG.error("Unhandled exeption occured while invoking onSessionRemoved on listener", e); } } } }; private MessageSpy<DataContainer> messageSpy; private ExtensionConverterProvider extensionConverterProvider; @Override public Map<TranslatorKey, Collection<IMDMessageTranslator<OfHeader, List<DataObject>>>> getTranslatorMapping() { return this.translatorMapping; } @Override public void setNotificationProviderService( NotificationProviderService notificationProviderService) { this.notificationProviderService = notificationProviderService; } @Override public DataBroker getDataBroker() { return dataBroker; } @Override public void setDataBroker(DataBroker dataBroker) { this.dataBroker = dataBroker; } @Override public NotificationProviderService getNotificationProviderService() { return notificationProviderService; } @Override public Map<Class<? extends DataObject>, Collection<PopListener<DataObject>>> getPopListenerMapping() { return popListenerMapping; } @Override public void setPopListenerMapping( Map<Class<? extends DataObject>, Collection<PopListener<DataObject>>> popListenerMapping) { this.popListenerMapping = popListenerMapping; } @Override public void close() { LOG.debug("close"); synchronized (sessionLot) { for (SessionContext sessionContext : sessionLot.values()) { sessionContext.getPrimaryConductor().disconnect(); } // TODO: handle timeouted shutdown rpcPool.shutdown(); } for (ListenerRegistration<SessionListener> listenerRegistration : sessionListeners) { SessionListener listener = listenerRegistration.getInstance(); if (listener instanceof AutoCloseable) { try { ((AutoCloseable) listener).close(); } catch (Exception e) { LOG.warn("closing of sessionListenerRegistration failed", e); } } } } @Override public void setRpcPool(ListeningExecutorService rpcPool) { this.rpcPool = rpcPool; } @Override public ListeningExecutorService getRpcPool() { return rpcPool; } @Override public void setMessageSpy(MessageSpy<DataContainer> messageSpy) { this.messageSpy = messageSpy; } @Override public MessageSpy<DataContainer> getMessageSpy() { return messageSpy; } @Override public void setExtensionConverterProvider( ExtensionConverterProvider extensionConverterProvider) { this.extensionConverterProvider = extensionConverterProvider; } /** * @return the extensionConverterProvider */ @Override public ExtensionConverterProvider getExtensionConverterProvider() { return extensionConverterProvider; } @Override public Collection<SessionContext> getAllSessions() { return sessionLot.values(); } }