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.
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.
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