001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.mx.notification;
023:
024: import java.util.ArrayList;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.NoSuchElementException;
028:
029: import javax.management.JMException;
030: import javax.management.ListenerNotFoundException;
031: import javax.management.NotificationFilter;
032: import javax.management.NotificationListener;
033:
034: /**
035: * A notification listener registry.<p>
036: *
037: * For addition and removal, the registrations are deeply cloned to
038: * allow the registrations to be iterated externally without
039: * incurring the cost of synchronization.
040: *
041: * @see org.jboss.mx.notification.ListenerRegistration
042: *
043: * @author <a href="mailto:Adrian.Brock@HappeningTimes.com">Adrian Brock</a>.
044: * @author <a href="mailto:juha@jboss.org">Juha Lindfors</a>.
045: *
046: * @version $Revision: 57200 $
047: *
048: * <p><b>Revisions:</b>
049: *
050: * <p><b>20030806 Juha Lindfors:</b>
051: * <ul>
052: * <li>
053: * Added removeAll()
054: * </ul>
055: *
056: */
057: public class ListenerRegistry {
058: // Attributes ----------------------------------------------------
059:
060: /**
061: * A map of listeners to a list of NotificationRegistrations.
062: */
063: private HashMap listeners = new HashMap();
064:
065: /**
066: * The factory used to generate the listener registration.
067: */
068: private ListenerRegistrationFactory factory;
069:
070: // Constructor ---------------------------------------------------
071:
072: /**
073: * Create a notification listener registry using the default
074: * listener registration factory.
075: */
076: public ListenerRegistry() {
077: this (null);
078: }
079:
080: /**
081: * Create a notification listener registry using the passed
082: * listener registration factory.<p>
083: *
084: * @param factory the factory to create registrations, use
085: * null for the default factory
086: */
087: public ListenerRegistry(ListenerRegistrationFactory factory) {
088: if (factory == null)
089: this .factory = new DefaultListenerRegistrationFactory();
090: else
091: this .factory = factory;
092: }
093:
094: // Public --------------------------------------------------------
095:
096: /**
097: * Adds a listener to a broadcaster<p>
098: *
099: * @param listener the listener to register
100: * @param filter filters the notifications in the broadcaster, can be null
101: * @param handback the object to include in the notification, can be null
102: * @exception IllegalArgumentException for a null listener
103: * @exception JMException for an error adding to the registry
104: */
105: public void add(NotificationListener listener,
106: NotificationFilter filter, Object handback)
107: throws JMException {
108: if (listener == null)
109: throw new IllegalArgumentException("Null listener");
110:
111: synchronized (listeners) {
112: HashMap newListeners = (HashMap) listeners.clone();
113:
114: ArrayList registrations = (ArrayList) newListeners
115: .get(listener);
116: if (registrations == null) {
117: registrations = new ArrayList();
118: newListeners.put(listener, registrations);
119: } else {
120: registrations = (ArrayList) registrations.clone();
121: newListeners.put(listener, registrations);
122: }
123:
124: registrations.add(factory
125: .create(listener, filter, handback));
126:
127: listeners = newListeners;
128: }
129: }
130:
131: /**
132: * Removes all registrations for a listener.
133: *
134: * @param listener the listener to remove
135: * @exception ListenerNotFoundException when the listener is not registered
136: */
137: public void remove(NotificationListener listener)
138: throws ListenerNotFoundException {
139: ArrayList registrations = null;
140: synchronized (listeners) {
141: if (listeners.containsKey(listener) == false)
142: throw new ListenerNotFoundException(
143: "Listener not found " + listener);
144:
145: HashMap newListeners = (HashMap) listeners.clone();
146:
147: registrations = (ArrayList) newListeners.remove(listener);
148:
149: listeners = newListeners;
150: }
151:
152: for (Iterator iterator = registrations.iterator(); iterator
153: .hasNext();) {
154: ListenerRegistration registration = (ListenerRegistration) iterator
155: .next();
156: registration.removed();
157: }
158: }
159:
160: /**
161: * Removes only the registrations for a listener that match the filter and handback.
162: *
163: * @param listener the listener to remove
164: * @param filter the filter of the registration to remove
165: * @param handback the handback object of the registration to remove
166: * @exception ListenerNotFoundException when the listener is not registered
167: */
168: public void remove(NotificationListener listener,
169: NotificationFilter filter, Object handback)
170: throws ListenerNotFoundException {
171: ListenerRegistration registration = null;
172: synchronized (listeners) {
173: ArrayList registrations = (ArrayList) listeners
174: .get(listener);
175: if (registrations == null)
176: throw new ListenerNotFoundException(
177: "No registristrations for listener not listener="
178: + listener + " filter=" + filter
179: + " handback=" + handback);
180:
181: registration = new DefaultListenerRegistration(listener,
182: filter, handback);
183: int index = registrations.indexOf(registration);
184: if (index == -1)
185: throw new ListenerNotFoundException(
186: "Listener not found listener=" + listener
187: + " filter=" + filter + " handback="
188: + handback);
189:
190: HashMap newListeners = (HashMap) listeners.clone();
191:
192: registrations = (ArrayList) registrations.clone();
193: registration = (ListenerRegistration) registrations
194: .remove(index);
195: if (registrations.isEmpty())
196: newListeners.remove(listener);
197: else
198: newListeners.put(listener, registrations);
199:
200: listeners = newListeners;
201: }
202:
203: registration.removed();
204: }
205:
206: /**
207: * Removes all listeners from this listener registry.
208: */
209: public void removeAll() {
210: synchronized (listeners) {
211: listeners.clear();
212: }
213: }
214:
215: /**
216: * Retrieve an iterator over the registrations<p>
217: *
218: * The iterator behaves like a snapshot of the registrations
219: * is taken during this operation.
220: *
221: * @return the iterator
222: */
223: public ListenerRegistrationIterator iterator() {
224: return new ListenerRegistrationIterator();
225: }
226:
227: /**
228: * Test whether the registry is empty
229: *
230: * @return true when it is empty, false otherwise
231: */
232: public boolean isEmpty() {
233: return listeners.isEmpty();
234: }
235:
236: // Inner Classes -------------------------------------------------
237:
238: public class ListenerRegistrationIterator implements Iterator {
239: private Iterator listenerIterator;
240: private Iterator registrationIterator;
241:
242: /**
243: * Constructs a new ListenerRegistration iterator
244: */
245: public ListenerRegistrationIterator() {
246: listenerIterator = listeners.values().iterator();
247: }
248:
249: public boolean hasNext() {
250: if (registrationIterator == null
251: || registrationIterator.hasNext() == false) {
252: do {
253: if (listenerIterator.hasNext() == false)
254: return false;
255:
256: registrationIterator = ((ArrayList) listenerIterator
257: .next()).iterator();
258: } while (registrationIterator.hasNext() == false);
259: }
260: return true;
261: }
262:
263: public Object next() {
264: if (hasNext() == false)
265: throw new NoSuchElementException(
266: "Use hasNext before next");
267:
268: return registrationIterator.next();
269: }
270:
271: /**
272: * Convenience method to returned a typed object
273: */
274: public ListenerRegistration nextRegistration() {
275: return (ListenerRegistration) next();
276: }
277:
278: /**
279: * @exception UnsupportedOpertionException remove is not supported
280: */
281: public void remove() {
282: throw new UnsupportedOperationException(
283: "remove is not supported");
284: }
285: }
286: }
|