Showing the changes to java/pkg/javax/management/NotificationBroadcasterSupport.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,354 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package javax.management; | ||
| 27 | |||
| 28 | import java.util.Collections; | ||
| 29 | import java.util.List; | ||
| 30 | import java.util.concurrent.CopyOnWriteArrayList; | ||
| 31 | import java.util.concurrent.Executor; | ||
| 32 | |||
| 33 | import com.sun.jmx.remote.util.ClassLogger; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * <p>Provides an implementation of {@link | ||
| 37 | * javax.management.NotificationEmitter NotificationEmitter} | ||
| 38 | * interface. This can be used as the super class of an MBean that | ||
| 39 | * sends notifications.</p> | ||
| 40 | * | ||
| 41 | * <p>By default, the notification dispatch model is synchronous. | ||
| 42 | * That is, when a thread calls sendNotification, the | ||
| 43 | * <code>NotificationListener.handleNotification</code> method of each listener | ||
| 44 | * is called within that thread. You can override this default | ||
| 45 | * by overriding <code>handleNotification</code> in a subclass, or by passing an | ||
| 46 | * Executor to the constructor.</p> | ||
| 47 | * | ||
| 48 | * <p>If the method call of a filter or listener throws an {@link Exception}, | ||
| 49 | * then that exception does not prevent other listeners from being invoked. However, | ||
| 50 | * if the method call of a filter or of {@code Executor.execute} or of | ||
| 51 | * {@code handleNotification} (when no {@code Excecutor} is specified) throws an | ||
| 52 | * {@link Error}, then that {@code Error} is propagated to the caller of | ||
| 53 | * {@link #sendNotification sendNotification}.</p> | ||
| 54 | * | ||
| 55 | * <p>Remote listeners added using the JMX Remote API (see JMXConnector) are not | ||
| 56 | * usually called synchronously. That is, when sendNotification returns, it is | ||
| 57 | * not guaranteed that any remote listeners have yet received the notification.</p> | ||
| 58 | * | ||
| 59 | * @since 1.5 | ||
| 60 | */ | ||
| 61 | public class NotificationBroadcasterSupport implements NotificationEmitter { | ||
| 62 | /** | ||
| 63 | * Constructs a NotificationBroadcasterSupport where each listener is invoked by the | ||
| 64 | * thread sending the notification. This constructor is equivalent to | ||
| 65 | * {@link NotificationBroadcasterSupport#NotificationBroadcasterSupport(Executor, | ||
| 66 | * MBeanNotificationInfo[] info) NotificationBroadcasterSupport(null, null)}. | ||
| 67 | */ | ||
| 68 | public NotificationBroadcasterSupport() { | ||
| 69 | this(null, (MBeanNotificationInfo[]) null); | ||
| 70 | } | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Constructs a NotificationBroadcasterSupport where each listener is invoked using | ||
| 74 | * the given {@link java.util.concurrent.Executor}. When {@link #sendNotification | ||
| 75 | * sendNotification} is called, a listener is selected if it was added with a null | ||
| 76 | * {@link NotificationFilter}, or if {@link NotificationFilter#isNotificationEnabled | ||
| 77 | * isNotificationEnabled} returns true for the notification being sent. The call to | ||
| 78 | * <code>NotificationFilter.isNotificationEnabled</code> takes place in the thread | ||
| 79 | * that called <code>sendNotification</code>. Then, for each selected listener, | ||
| 80 | * {@link Executor#execute executor.execute} is called with a command | ||
| 81 | * that calls the <code>handleNotification</code> method. | ||
| 82 | * This constructor is equivalent to | ||
| 83 | * {@link NotificationBroadcasterSupport#NotificationBroadcasterSupport(Executor, | ||
| 84 | * MBeanNotificationInfo[] info) NotificationBroadcasterSupport(executor, null)}. | ||
| 85 | * @param executor an executor used by the method <code>sendNotification</code> to | ||
| 86 | * send each notification. If it is null, the thread calling <code>sendNotification</code> | ||
| 87 | * will invoke the <code>handleNotification</code> method itself. | ||
| 88 | * @since 1.6 | ||
| 89 | */ | ||
| 90 | public NotificationBroadcasterSupport(Executor executor) { | ||
| 91 | this(executor, (MBeanNotificationInfo[]) null); | ||
| 92 | } | ||
| 93 | |||
| 94 | /** | ||
| 95 | * <p>Constructs a NotificationBroadcasterSupport with information | ||
| 96 | * about the notifications that may be sent. Each listener is | ||
| 97 | * invoked by the thread sending the notification. This | ||
| 98 | * constructor is equivalent to {@link | ||
| 99 | * NotificationBroadcasterSupport#NotificationBroadcasterSupport(Executor, | ||
| 100 | * MBeanNotificationInfo[] info) | ||
| 101 | * NotificationBroadcasterSupport(null, info)}.</p> | ||
| 102 | * | ||
| 103 | * <p>If the <code>info</code> array is not empty, then it is | ||
| 104 | * cloned by the constructor as if by {@code info.clone()}, and | ||
| 105 | * each call to {@link #getNotificationInfo()} returns a new | ||
| 106 | * clone.</p> | ||
| 107 | * | ||
| 108 | * @param info an array indicating, for each notification this | ||
| 109 | * MBean may send, the name of the Java class of the notification | ||
| 110 | * and the notification type. Can be null, which is equivalent to | ||
| 111 | * an empty array. | ||
| 112 | * | ||
| 113 | * @since 1.6 | ||
| 114 | */ | ||
| 115 | public NotificationBroadcasterSupport(MBeanNotificationInfo... info) { | ||
| 116 | this(null, info); | ||
| 117 | } | ||
| 118 | |||
| 119 | /** | ||
| 120 | * <p>Constructs a NotificationBroadcasterSupport with information about the notifications that may be sent, | ||
| 121 | * and where each listener is invoked using the given {@link java.util.concurrent.Executor}.</p> | ||
| 122 | * | ||
| 123 | * <p>When {@link #sendNotification sendNotification} is called, a | ||
| 124 | * listener is selected if it was added with a null {@link | ||
| 125 | * NotificationFilter}, or if {@link | ||
| 126 | * NotificationFilter#isNotificationEnabled isNotificationEnabled} | ||
| 127 | * returns true for the notification being sent. The call to | ||
| 128 | * <code>NotificationFilter.isNotificationEnabled</code> takes | ||
| 129 | * place in the thread that called | ||
| 130 | * <code>sendNotification</code>. Then, for each selected | ||
| 131 | * listener, {@link Executor#execute executor.execute} is called | ||
| 132 | * with a command that calls the <code>handleNotification</code> | ||
| 133 | * method.</p> | ||
| 134 | * | ||
| 135 | * <p>If the <code>info</code> array is not empty, then it is | ||
| 136 | * cloned by the constructor as if by {@code info.clone()}, and | ||
| 137 | * each call to {@link #getNotificationInfo()} returns a new | ||
| 138 | * clone.</p> | ||
| 139 | * | ||
| 140 | * @param executor an executor used by the method | ||
| 141 | * <code>sendNotification</code> to send each notification. If it | ||
| 142 | * is null, the thread calling <code>sendNotification</code> will | ||
| 143 | * invoke the <code>handleNotification</code> method itself. | ||
| 144 | * | ||
| 145 | * @param info an array indicating, for each notification this | ||
| 146 | * MBean may send, the name of the Java class of the notification | ||
| 147 | * and the notification type. Can be null, which is equivalent to | ||
| 148 | * an empty array. | ||
| 149 | * | ||
| 150 | * @since 1.6 | ||
| 151 | */ | ||
| 152 | public NotificationBroadcasterSupport(Executor executor, | ||
| 153 | MBeanNotificationInfo... info) { | ||
| 154 | this.executor = (executor != null) ? executor : defaultExecutor; | ||
| 155 | |||
| 156 | notifInfo = info == null ? NO_NOTIFICATION_INFO : info.clone(); | ||
| 157 | } | ||
| 158 | |||
| 159 | /** | ||
| 160 | * Adds a listener. | ||
| 161 | * | ||
| 162 | * @param listener The listener to receive notifications. | ||
| 163 | * @param filter The filter object. If filter is null, no | ||
| 164 | * filtering will be performed before handling notifications. | ||
| 165 | * @param handback An opaque object to be sent back to the | ||
| 166 | * listener when a notification is emitted. This object cannot be | ||
| 167 | * used by the Notification broadcaster object. It should be | ||
| 168 | * resent unchanged with the notification to the listener. | ||
| 169 | * | ||
| 170 | * @exception IllegalArgumentException thrown if the listener is null. | ||
| 171 | * | ||
| 172 | * @see #removeNotificationListener | ||
| 173 | */ | ||
| 174 | public void addNotificationListener(NotificationListener listener, | ||
| 175 | NotificationFilter filter, | ||
| 176 | Object handback) { | ||
| 177 | |||
| 178 | if (listener == null) { | ||
| 179 | throw new IllegalArgumentException ("Listener can't be null") ; | ||
| 180 | } | ||
| 181 | |||
| 182 | listenerList.add(new ListenerInfo(listener, filter, handback)); | ||
| 183 | } | ||
| 184 | |||
| 185 | public void removeNotificationListener(NotificationListener listener) | ||
| 186 | throws ListenerNotFoundException { | ||
| 187 | |||
| 188 | ListenerInfo wildcard = new WildcardListenerInfo(listener); | ||
| 189 | boolean removed = | ||
| 190 | listenerList.removeAll(Collections.singleton(wildcard)); | ||
| 191 | if (!removed) | ||
| 192 | throw new ListenerNotFoundException("Listener not registered"); | ||
| 193 | } | ||
| 194 | |||
| 195 | public void removeNotificationListener(NotificationListener listener, | ||
| 196 | NotificationFilter filter, | ||
| 197 | Object handback) | ||
| 198 | throws ListenerNotFoundException { | ||
| 199 | |||
| 200 | ListenerInfo li = new ListenerInfo(listener, filter, handback); | ||
| 201 | boolean removed = listenerList.remove(li); | ||
| 202 | if (!removed) { | ||
| 203 | throw new ListenerNotFoundException("Listener not registered " + | ||
| 204 | "(with this filter and " + | ||
| 205 | "handback)"); | ||
| 206 | // or perhaps not registered at all | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | public MBeanNotificationInfo[] getNotificationInfo() { | ||
| 211 | if (notifInfo.length == 0) | ||
| 212 | return notifInfo; | ||
| 213 | else | ||
| 214 | return notifInfo.clone(); | ||
| 215 | } | ||
| 216 | |||
| 217 | |||
| 218 | /** | ||
| 219 | * Sends a notification. | ||
| 220 | * | ||
| 221 | * If an {@code Executor} was specified in the constructor, it will be given one | ||
| 222 | * task per selected listener to deliver the notification to that listener. | ||
| 223 | * | ||
| 224 | * @param notification The notification to send. | ||
| 225 | */ | ||
| 226 | public void sendNotification(Notification notification) { | ||
| 227 | |||
| 228 | if (notification == null) { | ||
| 229 | return; | ||
| 230 | } | ||
| 231 | |||
| 232 | boolean enabled; | ||
| 233 | |||
| 234 | for (ListenerInfo li : listenerList) { | ||
| 235 | try { | ||
| 236 | enabled = li.filter == null || | ||
| 237 | li.filter.isNotificationEnabled(notification); | ||
| 238 | } catch (Exception e) { | ||
| 239 | if (logger.debugOn()) { | ||
| 240 | logger.debug("sendNotification", e); | ||
| 241 | } | ||
| 242 | |||
| 243 | continue; | ||
| 244 | } | ||
| 245 | |||
| 246 | if (enabled) { | ||
| 247 | executor.execute(new SendNotifJob(notification, li)); | ||
| 248 | } | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | /** | ||
| 253 | * <p>This method is called by {@link #sendNotification | ||
| 254 | * sendNotification} for each listener in order to send the | ||
| 255 | * notification to that listener. It can be overridden in | ||
| 256 | * subclasses to change the behavior of notification delivery, | ||
| 257 | * for instance to deliver the notification in a separate | ||
| 258 | * thread.</p> | ||
| 259 | * | ||
| 260 | * <p>The default implementation of this method is equivalent to | ||
| 261 | * <pre> | ||
| 262 | * listener.handleNotification(notif, handback); | ||
| 263 | * </pre> | ||
| 264 | * | ||
| 265 | * @param listener the listener to which the notification is being | ||
| 266 | * delivered. | ||
| 267 | * @param notif the notification being delivered to the listener. | ||
| 268 | * @param handback the handback object that was supplied when the | ||
| 269 | * listener was added. | ||
| 270 | * | ||
| 271 | */ | ||
| 272 | protected void handleNotification(NotificationListener listener, | ||
| 273 | Notification notif, Object handback) { | ||
| 274 | listener.handleNotification(notif, handback); | ||
| 275 | } | ||
| 276 | |||
| 277 | // private stuff | ||
| 278 | private static class ListenerInfo { | ||
| 279 | NotificationListener listener; | ||
| 280 | NotificationFilter filter; | ||
| 281 | Object handback; | ||
| 282 | |||
| 283 | ListenerInfo(NotificationListener listener, | ||
| 284 | NotificationFilter filter, | ||
| 285 | Object handback) { | ||
| 286 | this.listener = listener; | ||
| 287 | this.filter = filter; | ||
| 288 | this.handback = handback; | ||
| 289 | } | ||
| 290 | |||
| 291 | public boolean equals(Object o) { | ||
| 292 | if (!(o instanceof ListenerInfo)) | ||
| 293 | return false; | ||
| 294 | ListenerInfo li = (ListenerInfo) o; | ||
| 295 | if (li instanceof WildcardListenerInfo) | ||
| 296 | return (li.listener == listener); | ||
| 297 | else | ||
| 298 | return (li.listener == listener && li.filter == filter | ||
| 299 | && li.handback == handback); | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | private static class WildcardListenerInfo extends ListenerInfo { | ||
| 304 | WildcardListenerInfo(NotificationListener listener) { | ||
| 305 | super(listener, null, null); | ||
| 306 | } | ||
| 307 | |||
| 308 | public boolean equals(Object o) { | ||
| 309 | assert (!(o instanceof WildcardListenerInfo)); | ||
| 310 | return o.equals(this); | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | private List<ListenerInfo> listenerList = | ||
| 315 | new CopyOnWriteArrayList<ListenerInfo>(); | ||
| 316 | |||
| 317 | // since 1.6 | ||
| 318 | private final Executor executor; | ||
| 319 | private final MBeanNotificationInfo[] notifInfo; | ||
| 320 | |||
| 321 | private final static Executor defaultExecutor = new Executor() { | ||
| 322 | // DirectExecutor using caller thread | ||
| 323 | public void execute(Runnable r) { | ||
| 324 | r.run(); | ||
| 325 | } | ||
| 326 | }; | ||
| 327 | |||
| 328 | private static final MBeanNotificationInfo[] NO_NOTIFICATION_INFO = | ||
| 329 | new MBeanNotificationInfo[0]; | ||
| 330 | |||
| 331 | private class SendNotifJob implements Runnable { | ||
| 332 | public SendNotifJob(Notification notif, ListenerInfo listenerInfo) { | ||
| 333 | this.notif = notif; | ||
| 334 | this.listenerInfo = listenerInfo; | ||
| 335 | } | ||
| 336 | |||
| 337 | public void run() { | ||
| 338 | try { | ||
| 339 | handleNotification(listenerInfo.listener, | ||
| 340 | notif, listenerInfo.handback); | ||
| 341 | } catch (Exception e) { | ||
| 342 | if (logger.debugOn()) { | ||
| 343 | logger.debug("SendNotifJob-run", e); | ||
| 344 | } | ||
| 345 | } | ||
| 346 | } | ||
| 347 | |||
| 348 | private final Notification notif; | ||
| 349 | private final ListenerInfo listenerInfo; | ||
| 350 | } | ||
| 351 | |||
| 352 | private static final ClassLogger logger = | ||
| 353 | new ClassLogger("javax.management", "NotificationBroadcasterSupport"); | ||
| 354 | } | ||