diff -r fd16c54261b3 -r 90ce3da70b43 jdk/src/share/classes/javax/management/NotificationBroadcasterSupport.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/javax/management/NotificationBroadcasterSupport.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,354 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executor; + +import com.sun.jmx.remote.util.ClassLogger; + +/** + *

Provides an implementation of {@link + * javax.management.NotificationEmitter NotificationEmitter} + * interface. This can be used as the super class of an MBean that + * sends notifications.

+ * + *

By default, the notification dispatch model is synchronous. + * That is, when a thread calls sendNotification, the + * NotificationListener.handleNotification method of each listener + * is called within that thread. You can override this default + * by overriding handleNotification in a subclass, or by passing an + * Executor to the constructor.

+ * + *

If the method call of a filter or listener throws an {@link Exception}, + * then that exception does not prevent other listeners from being invoked. However, + * if the method call of a filter or of {@code Executor.execute} or of + * {@code handleNotification} (when no {@code Excecutor} is specified) throws an + * {@link Error}, then that {@code Error} is propagated to the caller of + * {@link #sendNotification sendNotification}.

+ * + *

Remote listeners added using the JMX Remote API (see JMXConnector) are not + * usually called synchronously. That is, when sendNotification returns, it is + * not guaranteed that any remote listeners have yet received the notification.

+ * + * @since 1.5 + */ +public class NotificationBroadcasterSupport implements NotificationEmitter { + /** + * Constructs a NotificationBroadcasterSupport where each listener is invoked by the + * thread sending the notification. This constructor is equivalent to + * {@link NotificationBroadcasterSupport#NotificationBroadcasterSupport(Executor, + * MBeanNotificationInfo[] info) NotificationBroadcasterSupport(null, null)}. + */ + public NotificationBroadcasterSupport() { + this(null, (MBeanNotificationInfo[]) null); + } + + /** + * Constructs a NotificationBroadcasterSupport where each listener is invoked using + * the given {@link java.util.concurrent.Executor}. When {@link #sendNotification + * sendNotification} is called, a listener is selected if it was added with a null + * {@link NotificationFilter}, or if {@link NotificationFilter#isNotificationEnabled + * isNotificationEnabled} returns true for the notification being sent. The call to + * NotificationFilter.isNotificationEnabled takes place in the thread + * that called sendNotification. Then, for each selected listener, + * {@link Executor#execute executor.execute} is called with a command + * that calls the handleNotification method. + * This constructor is equivalent to + * {@link NotificationBroadcasterSupport#NotificationBroadcasterSupport(Executor, + * MBeanNotificationInfo[] info) NotificationBroadcasterSupport(executor, null)}. + * @param executor an executor used by the method sendNotification to + * send each notification. If it is null, the thread calling sendNotification + * will invoke the handleNotification method itself. + * @since 1.6 + */ + public NotificationBroadcasterSupport(Executor executor) { + this(executor, (MBeanNotificationInfo[]) null); + } + + /** + *

Constructs a NotificationBroadcasterSupport with information + * about the notifications that may be sent. Each listener is + * invoked by the thread sending the notification. This + * constructor is equivalent to {@link + * NotificationBroadcasterSupport#NotificationBroadcasterSupport(Executor, + * MBeanNotificationInfo[] info) + * NotificationBroadcasterSupport(null, info)}.

+ * + *

If the info array is not empty, then it is + * cloned by the constructor as if by {@code info.clone()}, and + * each call to {@link #getNotificationInfo()} returns a new + * clone.

+ * + * @param info an array indicating, for each notification this + * MBean may send, the name of the Java class of the notification + * and the notification type. Can be null, which is equivalent to + * an empty array. + * + * @since 1.6 + */ + public NotificationBroadcasterSupport(MBeanNotificationInfo... info) { + this(null, info); + } + + /** + *

Constructs a NotificationBroadcasterSupport with information about the notifications that may be sent, + * and where each listener is invoked using the given {@link java.util.concurrent.Executor}.

+ * + *

When {@link #sendNotification sendNotification} is called, a + * listener is selected if it was added with a null {@link + * NotificationFilter}, or if {@link + * NotificationFilter#isNotificationEnabled isNotificationEnabled} + * returns true for the notification being sent. The call to + * NotificationFilter.isNotificationEnabled takes + * place in the thread that called + * sendNotification. Then, for each selected + * listener, {@link Executor#execute executor.execute} is called + * with a command that calls the handleNotification + * method.

+ * + *

If the info array is not empty, then it is + * cloned by the constructor as if by {@code info.clone()}, and + * each call to {@link #getNotificationInfo()} returns a new + * clone.

+ * + * @param executor an executor used by the method + * sendNotification to send each notification. If it + * is null, the thread calling sendNotification will + * invoke the handleNotification method itself. + * + * @param info an array indicating, for each notification this + * MBean may send, the name of the Java class of the notification + * and the notification type. Can be null, which is equivalent to + * an empty array. + * + * @since 1.6 + */ + public NotificationBroadcasterSupport(Executor executor, + MBeanNotificationInfo... info) { + this.executor = (executor != null) ? executor : defaultExecutor; + + notifInfo = info == null ? NO_NOTIFICATION_INFO : info.clone(); + } + + /** + * Adds a listener. + * + * @param listener The listener to receive notifications. + * @param filter The filter object. If filter is null, no + * filtering will be performed before handling notifications. + * @param handback An opaque object to be sent back to the + * listener when a notification is emitted. This object cannot be + * used by the Notification broadcaster object. It should be + * resent unchanged with the notification to the listener. + * + * @exception IllegalArgumentException thrown if the listener is null. + * + * @see #removeNotificationListener + */ + public void addNotificationListener(NotificationListener listener, + NotificationFilter filter, + Object handback) { + + if (listener == null) { + throw new IllegalArgumentException ("Listener can't be null") ; + } + + listenerList.add(new ListenerInfo(listener, filter, handback)); + } + + public void removeNotificationListener(NotificationListener listener) + throws ListenerNotFoundException { + + ListenerInfo wildcard = new WildcardListenerInfo(listener); + boolean removed = + listenerList.removeAll(Collections.singleton(wildcard)); + if (!removed) + throw new ListenerNotFoundException("Listener not registered"); + } + + public void removeNotificationListener(NotificationListener listener, + NotificationFilter filter, + Object handback) + throws ListenerNotFoundException { + + ListenerInfo li = new ListenerInfo(listener, filter, handback); + boolean removed = listenerList.remove(li); + if (!removed) { + throw new ListenerNotFoundException("Listener not registered " + + "(with this filter and " + + "handback)"); + // or perhaps not registered at all + } + } + + public MBeanNotificationInfo[] getNotificationInfo() { + if (notifInfo.length == 0) + return notifInfo; + else + return notifInfo.clone(); + } + + + /** + * Sends a notification. + * + * If an {@code Executor} was specified in the constructor, it will be given one + * task per selected listener to deliver the notification to that listener. + * + * @param notification The notification to send. + */ + public void sendNotification(Notification notification) { + + if (notification == null) { + return; + } + + boolean enabled; + + for (ListenerInfo li : listenerList) { + try { + enabled = li.filter == null || + li.filter.isNotificationEnabled(notification); + } catch (Exception e) { + if (logger.debugOn()) { + logger.debug("sendNotification", e); + } + + continue; + } + + if (enabled) { + executor.execute(new SendNotifJob(notification, li)); + } + } + } + + /** + *

This method is called by {@link #sendNotification + * sendNotification} for each listener in order to send the + * notification to that listener. It can be overridden in + * subclasses to change the behavior of notification delivery, + * for instance to deliver the notification in a separate + * thread.

+ * + *

The default implementation of this method is equivalent to + *

+     * listener.handleNotification(notif, handback);
+     * 
+ * + * @param listener the listener to which the notification is being + * delivered. + * @param notif the notification being delivered to the listener. + * @param handback the handback object that was supplied when the + * listener was added. + * + */ + protected void handleNotification(NotificationListener listener, + Notification notif, Object handback) { + listener.handleNotification(notif, handback); + } + + // private stuff + private static class ListenerInfo { + NotificationListener listener; + NotificationFilter filter; + Object handback; + + ListenerInfo(NotificationListener listener, + NotificationFilter filter, + Object handback) { + this.listener = listener; + this.filter = filter; + this.handback = handback; + } + + public boolean equals(Object o) { + if (!(o instanceof ListenerInfo)) + return false; + ListenerInfo li = (ListenerInfo) o; + if (li instanceof WildcardListenerInfo) + return (li.listener == listener); + else + return (li.listener == listener && li.filter == filter + && li.handback == handback); + } + } + + private static class WildcardListenerInfo extends ListenerInfo { + WildcardListenerInfo(NotificationListener listener) { + super(listener, null, null); + } + + public boolean equals(Object o) { + assert (!(o instanceof WildcardListenerInfo)); + return o.equals(this); + } + } + + private List listenerList = + new CopyOnWriteArrayList(); + + // since 1.6 + private final Executor executor; + private final MBeanNotificationInfo[] notifInfo; + + private final static Executor defaultExecutor = new Executor() { + // DirectExecutor using caller thread + public void execute(Runnable r) { + r.run(); + } + }; + + private static final MBeanNotificationInfo[] NO_NOTIFICATION_INFO = + new MBeanNotificationInfo[0]; + + private class SendNotifJob implements Runnable { + public SendNotifJob(Notification notif, ListenerInfo listenerInfo) { + this.notif = notif; + this.listenerInfo = listenerInfo; + } + + public void run() { + try { + handleNotification(listenerInfo.listener, + notif, listenerInfo.handback); + } catch (Exception e) { + if (logger.debugOn()) { + logger.debug("SendNotifJob-run", e); + } + } + } + + private final Notification notif; + private final ListenerInfo listenerInfo; + } + + private static final ClassLogger logger = + new ClassLogger("javax.management", "NotificationBroadcasterSupport"); +}