--- /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;
+
+/**
+ * <p>Provides an implementation of {@link
+ * javax.management.NotificationEmitter NotificationEmitter}
+ * interface. This can be used as the super class of an MBean that
+ * sends notifications.</p>
+ *
+ * <p>By default, the notification dispatch model is synchronous.
+ * That is, when a thread calls sendNotification, the
+ * <code>NotificationListener.handleNotification</code> method of each listener
+ * is called within that thread. You can override this default
+ * by overriding <code>handleNotification</code> in a subclass, or by passing an
+ * Executor to the constructor.</p>
+ *
+ * <p>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}.</p>
+ *
+ * <p>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.</p>
+ *
+ * @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
+ * <code>NotificationFilter.isNotificationEnabled</code> takes place in the thread
+ * that called <code>sendNotification</code>. Then, for each selected listener,
+ * {@link Executor#execute executor.execute} is called with a command
+ * that calls the <code>handleNotification</code> method.
+ * This constructor is equivalent to
+ * {@link NotificationBroadcasterSupport#NotificationBroadcasterSupport(Executor,
+ * MBeanNotificationInfo[] info) NotificationBroadcasterSupport(executor, null)}.
+ * @param executor an executor used by the method <code>sendNotification</code> to
+ * send each notification. If it is null, the thread calling <code>sendNotification</code>
+ * will invoke the <code>handleNotification</code> method itself.
+ * @since 1.6
+ */
+ public NotificationBroadcasterSupport(Executor executor) {
+ this(executor, (MBeanNotificationInfo[]) null);
+ }
+
+ /**
+ * <p>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)}.</p>
+ *
+ * <p>If the <code>info</code> 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.</p>
+ *
+ * @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);
+ }
+
+ /**
+ * <p>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}.</p>
+ *
+ * <p>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
+ * <code>NotificationFilter.isNotificationEnabled</code> takes
+ * place in the thread that called
+ * <code>sendNotification</code>. Then, for each selected
+ * listener, {@link Executor#execute executor.execute} is called
+ * with a command that calls the <code>handleNotification</code>
+ * method.</p>
+ *
+ * <p>If the <code>info</code> 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.</p>
+ *
+ * @param executor an executor used by the method
+ * <code>sendNotification</code> to send each notification. If it
+ * is null, the thread calling <code>sendNotification</code> will
+ * invoke the <code>handleNotification</code> 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));
+ }
+ }
+ }
+
+ /**
+ * <p>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.</p>
+ *
+ * <p>The default implementation of this method is equivalent to
+ * <pre>
+ * listener.handleNotification(notif, handback);
+ * </pre>
+ *
+ * @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<ListenerInfo> listenerList =
+ new CopyOnWriteArrayList<ListenerInfo>();
+
+ // 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");
+}