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) &lt;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 }