1 /* 2 * Copyright (c) 2009, 2010, 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 27 package com.sun.org.glassfish.external.amx; 28 29 import java.util.Set; 30 import javax.management.MBeanServer; 31 import javax.management.MBeanServerConnection; 32 import javax.management.MBeanServerNotification; 33 import javax.management.Notification; 34 import javax.management.NotificationListener; 35 import javax.management.ObjectName; 36 import java.util.concurrent.CountDownLatch; 37 38 import static com.sun.org.glassfish.external.amx.AMX.*; 39 40 /** 41 * Listens for registration of MBeans of various types. 42 * Intended usage is for subsystems to lazy-load only when the Parent 43 * MBean is registered. 44 */ 45 @com.sun.org.glassfish.external.arc.Taxonomy(stability = com.sun.org.glassfish.external.arc.Stability.UNCOMMITTED) 46 public class MBeanListener<T extends MBeanListener.Callback> implements NotificationListener 47 { debug(final Object o)48 private static void debug(final Object o) { System.out.println( "" + o ); } 49 50 /** listen for MBeans in a given domain of a given type[name] 51 OR an ObjectName (below) */ 52 private final String mJMXDomain; 53 private final String mType; 54 private final String mName; 55 56 /** mType and mName should be null if mObjectName is non-null, and vice versa */ 57 private final ObjectName mObjectName; 58 59 private final MBeanServerConnection mMBeanServer; 60 61 private final T mCallback; 62 toString()63 public String toString() 64 { 65 return "MBeanListener: ObjectName=" + mObjectName + ", type=" + mType + ", name=" + mName; 66 } 67 getType()68 public String getType() 69 { 70 return mType; 71 } 72 getName()73 public String getName() 74 { 75 return mName; 76 } 77 getMBeanServer()78 public MBeanServerConnection getMBeanServer() 79 { 80 return mMBeanServer; 81 } 82 83 /** Callback interface. */ 84 public interface Callback 85 { mbeanRegistered(final ObjectName objectName, final MBeanListener listener)86 public void mbeanRegistered(final ObjectName objectName, final MBeanListener listener); mbeanUnregistered(final ObjectName objectName, final MBeanListener listener)87 public void mbeanUnregistered(final ObjectName objectName, final MBeanListener listener); 88 } 89 90 /** 91 Default callback implementation, can be subclassed if needed 92 Remembers only the last MBean that was seen. 93 */ 94 public static class CallbackImpl implements MBeanListener.Callback 95 { 96 private volatile ObjectName mRegistered = null; 97 private volatile ObjectName mUnregistered = null; 98 private final boolean mStopAtFirst; 99 CallbackImpl()100 public CallbackImpl() { 101 this(true); 102 } 103 CallbackImpl(final boolean stopAtFirst)104 public CallbackImpl(final boolean stopAtFirst) 105 { 106 mStopAtFirst = stopAtFirst; 107 } 108 getRegistered()109 public ObjectName getRegistered() { return mRegistered; } getUnregistered()110 public ObjectName getUnregistered() { return mUnregistered; } 111 112 protected final CountDownLatch mLatch = new CountDownLatch(1); 113 114 /** Optional: wait for the CountDownLatch to fire 115 If used, the subclass should countDown() the latch when the 116 appropriate event happens 117 */ await()118 public void await() 119 { 120 try 121 { 122 mLatch.await(); // wait until BootAMXMBean is ready 123 } 124 catch (InterruptedException e) 125 { 126 throw new RuntimeException(e); 127 } 128 } 129 mbeanRegistered(final ObjectName objectName, final MBeanListener listener)130 public void mbeanRegistered(final ObjectName objectName, final MBeanListener listener) 131 { 132 mRegistered = objectName; 133 if ( mStopAtFirst ) 134 { 135 listener.stopListening(); 136 } 137 } mbeanUnregistered(final ObjectName objectName, final MBeanListener listener)138 public void mbeanUnregistered(final ObjectName objectName, final MBeanListener listener) 139 { 140 mUnregistered = objectName; 141 if ( mStopAtFirst ) 142 { 143 listener.stopListening(); 144 } 145 } 146 } 147 getCallback()148 public T getCallback() 149 { 150 return mCallback; 151 } 152 153 /** 154 * Listener for a specific MBean. 155 * Caller must call {@link #start} to start listening. 156 * @param server 157 * @param objectName 158 * @param callback 159 */ MBeanListener( final MBeanServerConnection server, final ObjectName objectName, final T callback)160 public MBeanListener( 161 final MBeanServerConnection server, 162 final ObjectName objectName, 163 final T callback) 164 { 165 mMBeanServer = server; 166 mObjectName = objectName; 167 mJMXDomain = null; 168 mType = null; 169 mName = null; 170 mCallback = callback; 171 } 172 173 /** 174 * Listener for all MBeans of specified type, with or without a name. 175 * Caller must call {@link #start} to start listening. 176 * @param server 177 * @param type type of the MBean (as found in the ObjectName) 178 * @param callback 179 */ MBeanListener( final MBeanServerConnection server, final String domain, final String type, final T callback)180 public MBeanListener( 181 final MBeanServerConnection server, 182 final String domain, 183 final String type, 184 final T callback) 185 { 186 this(server, domain, type, null, callback); 187 } 188 189 /** 190 * Listener for MBeans of specified type, with specified name (or any name 191 * if null is passed for the name). 192 * Caller must call {@link #start} to start listening. 193 * @param server 194 * @param type type of the MBean (as found in the ObjectName) 195 * @param name name of the MBean, or null if none 196 * @param callback 197 */ MBeanListener( final MBeanServerConnection server, final String domain, final String type, final String name, final T callback)198 public MBeanListener( 199 final MBeanServerConnection server, 200 final String domain, 201 final String type, 202 final String name, 203 final T callback) 204 { 205 mMBeanServer = server; 206 mJMXDomain = domain; 207 mType = type; 208 mName = name; 209 mObjectName = null; 210 mCallback = callback; 211 } 212 213 isRegistered( final MBeanServerConnection conn, final ObjectName objectName )214 private boolean isRegistered( final MBeanServerConnection conn, final ObjectName objectName ) 215 { 216 try 217 { 218 return conn.isRegistered(objectName); 219 } 220 catch (final Exception e) 221 { 222 throw new RuntimeException(e); 223 } 224 } 225 226 /** 227 Start listening. If the required MBean(s) are already present, the callback 228 will be synchronously made before returning. It is also possible that the 229 callback could happen twice for the same MBean. 230 */ startListening()231 public void startListening() 232 { 233 // race condition: must listen *before* looking for existing MBeans 234 try 235 { 236 mMBeanServer.addNotificationListener( AMXUtil.getMBeanServerDelegateObjectName(), this, null, this); 237 } 238 catch (final Exception e) 239 { 240 throw new RuntimeException("Can't add NotificationListener", e); 241 } 242 243 if ( mObjectName != null ) 244 { 245 if ( isRegistered(mMBeanServer, mObjectName) ) 246 { 247 mCallback.mbeanRegistered(mObjectName, this); 248 } 249 } 250 else 251 { 252 // query for AMX MBeans of the requisite type 253 String props = TYPE_KEY + "=" + mType; 254 if (mName != null) 255 { 256 props = props + "," + NAME_KEY + mName; 257 } 258 259 final ObjectName pattern = AMXUtil.newObjectName(mJMXDomain + ":" +props); 260 try 261 { 262 final Set<ObjectName> matched = mMBeanServer.queryNames(pattern, null); 263 for (final ObjectName objectName : matched) 264 { 265 mCallback.mbeanRegistered(objectName, this); 266 } 267 } 268 catch( final Exception e ) 269 { 270 throw new RuntimeException(e); 271 } 272 } 273 } 274 275 276 /** unregister the listener */ stopListening()277 public void stopListening() 278 { 279 try 280 { 281 mMBeanServer.removeNotificationListener( AMXUtil.getMBeanServerDelegateObjectName(), this); 282 } 283 catch (final Exception e) 284 { 285 throw new RuntimeException("Can't remove NotificationListener " + this, e); 286 } 287 } 288 handleNotification( final Notification notifIn, final Object handback)289 public void handleNotification( 290 final Notification notifIn, 291 final Object handback) 292 { 293 if (notifIn instanceof MBeanServerNotification) 294 { 295 final MBeanServerNotification notif = (MBeanServerNotification) notifIn; 296 final ObjectName objectName = notif.getMBeanName(); 297 298 boolean match = false; 299 if ( mObjectName != null && mObjectName.equals(objectName) ) 300 { 301 match = true; 302 } 303 else if ( objectName.getDomain().equals( mJMXDomain ) ) 304 { 305 if ( mType != null && mType.equals(objectName.getKeyProperty(TYPE_KEY)) ) 306 { 307 final String mbeanName = objectName.getKeyProperty(NAME_KEY); 308 if (mName != null && mName.equals(mbeanName)) 309 { 310 match = true; 311 } 312 } 313 } 314 315 if ( match ) 316 { 317 final String notifType = notif.getType(); 318 if (MBeanServerNotification.REGISTRATION_NOTIFICATION.equals(notifType)) 319 { 320 mCallback.mbeanRegistered(objectName, this); 321 } 322 else if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION.equals(notifType)) 323 { 324 mCallback.mbeanUnregistered(objectName, this); 325 } 326 } 327 } 328 } 329 330 } 331