/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.mx.notification; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.NoSuchElementException; import javax.management.JMException; import javax.management.ListenerNotFoundException; import javax.management.NotificationFilter; import javax.management.NotificationListener; /** * A notification listener registry.<p> * * For addition and removal, the registrations are deeply cloned to * allow the registrations to be iterated externally without * incurring the cost of synchronization. * * @see org.jboss.mx.notification.ListenerRegistration * * @author <a href="mailto:Adrian.Brock@HappeningTimes.com">Adrian Brock</a>. * @author <a href="mailto:juha@jboss.org">Juha Lindfors</a>. * * @version $Revision: 81019 $ * * <p><b>Revisions:</b> * * <p><b>20030806 Juha Lindfors:</b> * <ul> * <li> * Added removeAll() * </ul> * */ public class ListenerRegistry { // Attributes ---------------------------------------------------- /** * A map of listeners to a list of NotificationRegistrations. */ private HashMap listeners = new HashMap(); /** * The factory used to generate the listener registration. */ private ListenerRegistrationFactory factory; // Constructor --------------------------------------------------- /** * Create a notification listener registry using the default * listener registration factory. */ public ListenerRegistry() { this(null); } /** * Create a notification listener registry using the passed * listener registration factory.<p> * * @param factory the factory to create registrations, use * null for the default factory */ public ListenerRegistry(ListenerRegistrationFactory factory) { if (factory == null) this.factory = new DefaultListenerRegistrationFactory(); else this.factory = factory; } // Public -------------------------------------------------------- /** * Adds a listener to a broadcaster<p> * * @param listener the listener to register * @param filter filters the notifications in the broadcaster, can be null * @param handback the object to include in the notification, can be null * @exception IllegalArgumentException for a null listener * @exception JMException for an error adding to the registry */ public void add(NotificationListener listener, NotificationFilter filter, Object handback) throws JMException { if (listener == null) throw new IllegalArgumentException("Null listener"); synchronized(listeners) { HashMap newListeners = (HashMap) listeners.clone(); ArrayList registrations = (ArrayList) newListeners.get(listener); if (registrations == null) { registrations = new ArrayList(); newListeners.put(listener, registrations); } else { registrations = (ArrayList) registrations.clone(); newListeners.put(listener, registrations); } registrations.add(factory.create(listener, filter, handback)); listeners = newListeners; } } /** * Removes all registrations for a listener. * * @param listener the listener to remove * @exception ListenerNotFoundException when the listener is not registered */ public void remove(NotificationListener listener) throws ListenerNotFoundException { ArrayList registrations = null; synchronized(listeners) { if (listeners.containsKey(listener) == false) throw new ListenerNotFoundException("Listener not found " + listener); HashMap newListeners = (HashMap) listeners.clone(); registrations = (ArrayList) newListeners.remove(listener); listeners = newListeners; } for (Iterator iterator = registrations.iterator(); iterator.hasNext();) { ListenerRegistration registration = (ListenerRegistration) iterator.next(); registration.removed(); } } /** * Removes only the registrations for a listener that match the filter and handback. * * @param listener the listener to remove * @param filter the filter of the registration to remove * @param handback the handback object of the registration to remove * @exception ListenerNotFoundException when the listener is not registered */ public void remove(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException { ListenerRegistration registration = null; synchronized(listeners) { ArrayList registrations = (ArrayList) listeners.get(listener); if (registrations == null) throw new ListenerNotFoundException("No registristrations for listener not listener=" + listener + " filter=" + filter + " handback=" + handback); registration = new DefaultListenerRegistration(listener, filter, handback); int index = registrations.indexOf(registration); if (index == -1) throw new ListenerNotFoundException("Listener not found listener=" + listener + " filter=" + filter + " handback=" + handback); HashMap newListeners = (HashMap) listeners.clone(); registrations = (ArrayList) registrations.clone(); registration = (ListenerRegistration) registrations.remove(index); if (registrations.isEmpty()) newListeners.remove(listener); else newListeners.put(listener, registrations); listeners = newListeners; } registration.removed(); } /** * Removes all listeners from this listener registry. */ public void removeAll() { synchronized (listeners) { listeners.clear(); } } /** * Retrieve an iterator over the registrations<p> * * The iterator behaves like a snapshot of the registrations * is taken during this operation. * * @return the iterator */ public ListenerRegistrationIterator iterator() { return new ListenerRegistrationIterator(); } /** * Test whether the registry is empty * * @return true when it is empty, false otherwise */ public boolean isEmpty() { return listeners.isEmpty(); } // Inner Classes ------------------------------------------------- public class ListenerRegistrationIterator implements Iterator { private Iterator listenerIterator; private Iterator registrationIterator; /** * Constructs a new ListenerRegistration iterator */ public ListenerRegistrationIterator() { listenerIterator = listeners.values().iterator(); } public boolean hasNext() { if (registrationIterator == null || registrationIterator.hasNext() == false) { do { if (listenerIterator.hasNext() == false) return false; registrationIterator = ((ArrayList) listenerIterator.next()).iterator(); } while (registrationIterator.hasNext() == false); } return true; } public Object next() { if (hasNext() == false) throw new NoSuchElementException("Use hasNext before next"); return registrationIterator.next(); } /** * Convenience method to returned a typed object */ public ListenerRegistration nextRegistration() { return (ListenerRegistration) next(); } /** * @exception UnsupportedOpertionException remove is not supported */ public void remove() { throw new UnsupportedOperationException("remove is not supported"); } } }