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 javax.management.ObjectName; 30 import javax.management.MBeanServer; 31 import javax.management.MBeanServerConnection; 32 33 import java.io.IOException; 34 35 36 /** 37 * AMX behavior specific to Glassfish V3. 38 */ 39 public final class AMXGlassfish 40 { 41 public static final String DEFAULT_JMX_DOMAIN = "amx"; 42 43 /** Default domain support */ 44 public static final AMXGlassfish DEFAULT = new AMXGlassfish(DEFAULT_JMX_DOMAIN); 45 46 private final String mJMXDomain; 47 private final ObjectName mDomainRoot; 48 49 /** Anything other than {@link #DEFAULT} is not supported in Glassfish V3 */ 50 public AMXGlassfish(final String jmxDomain) 51 { 52 mJMXDomain = jmxDomain; 53 mDomainRoot = newObjectName("", "domain-root", null); 54 } 55 56 /** Return a version string, or null if not running in Glassfish */ 57 public static String getGlassfishVersion() 58 { 59 // must all exist as a check to verify that it's Glassfish V3 60 final String version = System.getProperty( "glassfish.version" ); 61 return version; 62 } 63 64 65 /** JMX domain used by AMX MBeans. 66 * <p> 67 * All MBeans in this domain must be AMX-compliant, see http://tinyurl.com/nryoqp = 68 https://glassfish.dev.java.net/nonav/v3/admin/planning/V3Changes/V3_AMX_SPI.html 69 */ 70 public String amxJMXDomain() 71 { 72 return mJMXDomain; 73 } 74 75 /** JMX domain used by AMX support MBeans. Private use only */ 76 public String amxSupportDomain() 77 { 78 return amxJMXDomain() + "-support"; 79 } 80 81 /** name of the Domain Admin Server (DAS) as found in an ObjectName */ 82 public String dasName() 83 { 84 return "server"; 85 } 86 87 /** name of the Domain Admin Server (DAS) <config> */ 88 public String dasConfig() 89 { 90 return dasName() + "-config"; 91 } 92 93 /** return the ObjectName of the AMX DomainRoot MBean */ 94 public ObjectName domainRoot() 95 { 96 return mDomainRoot; 97 } 98 99 /** ObjectName for top-level monitoring MBean (parent of those for each server) */ 100 public ObjectName monitoringRoot() 101 { 102 return newObjectName("/", "mon", null); 103 } 104 105 /** ObjectName for top-level monitoring MBean for specified server */ 106 public ObjectName serverMon(final String serverName) 107 { 108 return newObjectName("/mon", "server-mon", serverName); 109 } 110 111 /** ObjectName for top-level monitoring MBean for the DAS. */ 112 public ObjectName serverMonForDAS() { 113 return serverMon( "server" ) ; 114 } 115 116 /** Make a new AMX ObjectName with unchecked exception. 117 * name must be null to create a singleton ObjectName. 118 * Note that the arguments must not contain the characters 119 * @param pp The parent part 120 * @param type The ObjectName type 121 * @param name The ObjectName name 122 * @return The objectname with pp, type, and (optionally) name. 123 */ 124 public ObjectName newObjectName( 125 final String pp, 126 final String type, 127 final String name) 128 { 129 String props = prop(AMX.PARENT_PATH_KEY, pp) + "," + prop(AMX.TYPE_KEY, type); 130 if (name != null) { 131 props = props + "," + prop(AMX.NAME_KEY, name); 132 } 133 134 return newObjectName( props); 135 } 136 137 /** Make a new ObjectName for AMX domain with unchecked exception */ 138 public ObjectName newObjectName(final String s) 139 { 140 String name = s; 141 if ( ! name.startsWith( amxJMXDomain() ) ) { 142 name = amxJMXDomain() + ":" + name; 143 } 144 145 return AMXUtil.newObjectName( name ); 146 } 147 148 private static String prop(final String key, final String value) 149 { 150 return key + "=" + value; 151 } 152 153 /** 154 ObjectName for {@link BootAMXMBean} 155 */ 156 public ObjectName getBootAMXMBeanObjectName() 157 { 158 return AMXUtil.newObjectName( amxSupportDomain() + ":type=boot-amx" ); 159 } 160 161 /** 162 Invoke the bootAMX() method on {@link BootAMXMBean}. Upon return, 163 AMX continues to load. 164 A cilent should call {@link invokeWaitAMXReady} prior to use. 165 */ 166 public void invokeBootAMX(final MBeanServerConnection conn) 167 { 168 // start AMX and wait for it to be ready 169 try 170 { 171 conn.invoke( getBootAMXMBeanObjectName(), BootAMXMBean.BOOT_AMX_OPERATION_NAME, null, null); 172 } 173 catch (final Exception e) 174 { 175 e.printStackTrace(); 176 throw new RuntimeException(e); 177 } 178 } 179 180 /** 181 Invoke the waitAMXReady() method on the DomainRoot MBean, which must already be loaded. 182 */ 183 private static void invokeWaitAMXReady(final MBeanServerConnection conn, final ObjectName objectName) 184 { 185 try 186 { 187 conn.invoke( objectName, "waitAMXReady", null, null ); 188 } 189 catch( final Exception e ) 190 { 191 throw new RuntimeException(e); 192 } 193 } 194 195 /** 196 Listen for the registration of AMX DomainRoot 197 Listening starts automatically. 198 */ 199 public <T extends MBeanListener.Callback> MBeanListener<T> listenForDomainRoot( 200 final MBeanServerConnection server, 201 final T callback) 202 { 203 final MBeanListener<T> listener = new MBeanListener<T>( server, domainRoot(), callback); 204 listener.startListening(); 205 return listener; 206 } 207 208 private static final class WaitForDomainRootListenerCallback extends MBeanListener.CallbackImpl { 209 private final MBeanServerConnection mConn; 210 211 public WaitForDomainRootListenerCallback( final MBeanServerConnection conn ) { 212 mConn = conn; 213 } 214 215 @Override 216 public void mbeanRegistered(final ObjectName objectName, final MBeanListener listener) { 217 super.mbeanRegistered(objectName,listener); 218 invokeWaitAMXReady(mConn, objectName); 219 mLatch.countDown(); 220 } 221 } 222 223 /** 224 Wait until AMX has loaded and is ready for use. 225 <p> 226 This will <em>not</em> cause AMX to load; it will block forever until AMX is ready. In other words, 227 don't call this method unless it's a convenient thread that can wait forever. 228 */ 229 public ObjectName waitAMXReady( final MBeanServerConnection server) 230 { 231 final WaitForDomainRootListenerCallback callback = new WaitForDomainRootListenerCallback(server); 232 listenForDomainRoot( server, callback ); 233 callback.await(); 234 return callback.getRegistered(); 235 } 236 237 /** 238 Listen for the registration of the {@link BootAMXMBean}. 239 Listening starts automatically. See {@link AMXBooter#BootAMXCallback}. 240 */ 241 public <T extends MBeanListener.Callback> MBeanListener<T> listenForBootAMX( 242 final MBeanServerConnection server, 243 final T callback) 244 { 245 final MBeanListener<T> listener = new MBeanListener<T>( server, getBootAMXMBeanObjectName(), callback); 246 listener.startListening(); 247 return listener; 248 } 249 250 /** 251 Callback for {@link MBeanListener} that waits for the BootAMXMBean to appear; 252 it always will load early in server startup. Once it has loaded, AMX can be booted 253 via {@link #bootAMX}. A client should normally just call {@link #bootAMX}, but 254 this callback may be suclassed if desired, and used as a trigger to 255 boot AMX and then take other dependent actions. 256 */ 257 public static class BootAMXCallback extends MBeanListener.CallbackImpl 258 { 259 private final MBeanServerConnection mConn; 260 public BootAMXCallback(final MBeanServerConnection conn) 261 { 262 mConn = conn; 263 } 264 265 @Override 266 public void mbeanRegistered(final ObjectName objectName, final MBeanListener listener) 267 { 268 super.mbeanRegistered(objectName, listener); 269 mLatch.countDown(); 270 } 271 } 272 273 /** 274 Ensure that AMX is loaded and ready to use. This method returns only when all 275 AMX subsystems have been loaded. 276 It can be called more than once without ill effect, subsequent calls are ignored. 277 @param conn connection to the MBeanServer 278 @return the ObjectName of the domain-root MBean 279 */ 280 public ObjectName bootAMX(final MBeanServerConnection conn) 281 throws IOException 282 { 283 final ObjectName domainRoot = domainRoot(); 284 285 if ( !conn.isRegistered( domainRoot ) ) 286 { 287 // wait for the BootAMXMBean to be available (loads at startup) 288 final BootAMXCallback callback = new BootAMXCallback(conn); 289 listenForBootAMX(conn, callback); 290 callback.await(); // block until the MBean appears 291 292 invokeBootAMX(conn); 293 294 final WaitForDomainRootListenerCallback drCallback = new WaitForDomainRootListenerCallback(conn); 295 listenForDomainRoot(conn, drCallback); 296 drCallback.await(); 297 298 invokeWaitAMXReady(conn, domainRoot); 299 } 300 else 301 { 302 invokeWaitAMXReady(conn, domainRoot ); 303 } 304 return domainRoot; 305 } 306 307 public ObjectName bootAMX(final MBeanServer server) 308 { 309 try 310 { 311 return bootAMX( (MBeanServerConnection)server); 312 } 313 catch( final IOException e ) 314 { 315 throw new RuntimeException(e); 316 } 317 } 318 }