/** * Copyright 2011, Big Switch Networks, Inc. * Originally created by David Erickson, Stanford University * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. **/ package net.floodlightcontroller.core.util; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.floodlightcontroller.core.IListener; import net.floodlightcontroller.core.annotations.LogMessageDoc; /** * Maintain lists of listeners ordered by dependency. * * @author readams * */ public class ListenerDispatcher<U, T extends IListener<U>> { protected static Logger logger = LoggerFactory.getLogger(ListenerDispatcher.class); volatile List<T> listeners = new ArrayList<T>(); private void visit(List<T> newlisteners, U type, HashSet<T> visited, List<T> ordering, T listener) { if (!visited.contains(listener)) { visited.add(listener); for (T i : newlisteners) { if (ispre(type, i, listener)) { visit(newlisteners, type, visited, ordering, i); } } ordering.add(listener); } } private boolean ispre(U type, T l1, T l2) { return (l2.isCallbackOrderingPrereq(type, l1.getName()) || l1.isCallbackOrderingPostreq(type, l2.getName())); } /** * Add a listener to the list of listeners * @param listener */ @LogMessageDoc(level="ERROR", message="No listener dependency solution: " + "No listeners without incoming dependencies", explanation="The set of listeners installed " + "have dependencies with no solution", recommendation="Install a different set of listeners " + "or install all dependencies. This is a defect in " + "the controller installation.") public void addListener(U type, T listener) { List<T> newlisteners = new ArrayList<T>(); if (listeners != null) newlisteners.addAll(listeners); newlisteners.add(listener); // Find nodes without outgoing edges List<T> terminals = new ArrayList<T>(); for (T i : newlisteners) { boolean isterm = true; for (T j : newlisteners) { if (ispre(type, i, j)) { isterm = false; break; } } if (isterm) { terminals.add(i); } } if (terminals.size() == 0) { logger.error("No listener dependency solution: " + "No listeners without incoming dependencies"); listeners = newlisteners; return; } // visit depth-first traversing in the opposite order from // the dependencies. Note we will not generally detect cycles HashSet<T> visited = new HashSet<T>(); List<T> ordering = new ArrayList<T>(); for (T term : terminals) { visit(newlisteners, type, visited, ordering, term); } listeners = ordering; } /** * Remove the given listener * @param listener the listener to remove */ public void removeListener(T listener) { if (listeners != null) { List<T> newlisteners = new ArrayList<T>(); newlisteners.addAll(listeners); newlisteners.remove(listener); listeners = newlisteners; } } /** * Clear all listeners */ public void clearListeners() { listeners = new ArrayList<T>(); } /** * Get the ordered list of listeners ordered by dependencies * @return */ public List<T> getOrderedListeners() { return listeners; } }