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 */
AMXGlassfish(final String jmxDomain)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 */
getGlassfishVersion()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     */
amxJMXDomain()70     public String amxJMXDomain()
71     {
72         return mJMXDomain;
73     }
74 
75     /** JMX domain used by AMX support MBeans.  Private use only */
amxSupportDomain()76     public String amxSupportDomain()
77     {
78         return amxJMXDomain() + "-support";
79     }
80 
81     /** name of the Domain Admin Server (DAS) as found in an ObjectName */
dasName()82     public String dasName()
83     {
84         return "server";
85     }
86 
87     /** name of the Domain Admin Server (DAS) &lt;config> */
dasConfig()88     public String dasConfig()
89     {
90         return dasName() + "-config";
91     }
92 
93     /** return the ObjectName of the AMX DomainRoot MBean */
domainRoot()94     public ObjectName domainRoot()
95     {
96         return mDomainRoot;
97     }
98 
99     /** ObjectName for top-level monitoring MBean (parent of those for each server) */
monitoringRoot()100     public ObjectName monitoringRoot()
101     {
102         return newObjectName("/", "mon", null);
103     }
104 
105     /** ObjectName for top-level monitoring MBean for specified server */
serverMon(final String serverName)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. */
serverMonForDAS()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      */
newObjectName( final String pp, final String type, final String name)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 */
newObjectName(final String s)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 
prop(final String key, final String value)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      */
getBootAMXMBeanObjectName()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     */
invokeBootAMX(final MBeanServerConnection conn)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      */
invokeWaitAMXReady(final MBeanServerConnection conn, final ObjectName objectName)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      */
listenForDomainRoot( final MBeanServerConnection server, final T callback)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 
WaitForDomainRootListenerCallback( final MBeanServerConnection conn )211         public WaitForDomainRootListenerCallback( final MBeanServerConnection conn ) {
212             mConn = conn;
213         }
214 
215         @Override
mbeanRegistered(final ObjectName objectName, final MBeanListener listener)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      */
waitAMXReady( final MBeanServerConnection server)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      */
listenForBootAMX( final MBeanServerConnection server, final T callback)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;
BootAMXCallback(final MBeanServerConnection conn)260         public BootAMXCallback(final MBeanServerConnection conn)
261         {
262             mConn = conn;
263         }
264 
265         @Override
mbeanRegistered(final ObjectName objectName, final MBeanListener listener)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      */
bootAMX(final MBeanServerConnection conn)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 
bootAMX(final MBeanServer server)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 }
319