"Booting AMX" in GlassFish 3 with Groovy

In my previous blog post, I looked at using JMX as one of multiple methods supported by GlassFish 3 for its administration, monitoring, and management. In this blog post, I look in more detail at monitoring and managing GlassFish 3 via JMX and Groovy. I focus on local connection to GlassFish using the Attach API in this post, but I have covered remote JMX access of GlassFish in a previous post (see also Remote Glassfish V3.1 and the mystical JMX settings). The MBeans used for administration are generally the same in either case.

Because I'm using the Attach API in this post's examples to connect to local Java processes that are started by me, I don't need to specify the JMX remote connection properties (java.rmi.server.hostname,com.sun.management.jmxremote.port, and com.sun.management.jmxremote.ssl=false, and com.sun.management.jmxremote.authenticate) for remote access. The easiest way to find Java processes meeting the standard of being on the local machine and being processes that I started is via use of jps (pre-JDK 7 and JDK 7) or jcmd (JDK 7 only), which I show in the next two screen snapshots.

As the above images indicate, GlassFish is running locally with PID (AKA Process ID or pid) 1584.

To begin, I'm going to use Groovy to access the relevant MBeanServerConnection via the Attach API. This is done as shown in the following Groovy code (next two code listings) in which the method retrieveServerConnection(String) accepts the PID and returns the MBeanServerConnection for that Java process. That method uses another method in the code listing, retrieveUrlForPid(String, String), which uses the Attach API to provide a JMXServiceURL for the Java process.

retrieveServerConnection(String)

/**
 * Provide an MBeanServerConnection based on the provided process ID (pid).
 *
 * @param pid Process ID of Java process for which MBeanServerConnection is
 *    desired.
 * @return MBeanServerConnection connecting to Java process identified by pid.
 */
def MBeanServerConnection retrieveServerConnection(String pid)
{
   def connectorAddressStr = "com.sun.management.jmxremote.localConnectorAddress"
   def jmxUrl = retrieveUrlForPid(pid, connectorAddressStr)
   def jmxConnector = JMXConnectorFactory.connect(jmxUrl)
   return jmxConnector.getMBeanServerConnection()
}

retrieveUrlForPid(String, String)

/**
 * Provide JMX URL for attaching to the provided process ID (pid).
 *
 * @param @pid Process ID for which JMX URL is needed to connect.
 * @param @connectorAddressStr String for connecting.
 * @return JMX URL to communicating with Java process identified by pid.
 */
def JMXServiceURL retrieveUrlForPid(String pid, String connectorAddressStr)
{
   // Attach to the target application's virtual machine
   def vm = VirtualMachine.attach(pid)

   // Obtain Connector Address
   def connectorAddress =
      vm.getAgentProperties().getProperty(connectorAddressStr)

   // Load Agent if no connector address is available
   if (connectorAddress == null)
   {
      def agent = vm.getSystemProperties().getProperty("java.home") +
          File.separator + "lib" + File.separator + "management-agent.jar"
      vm.loadAgent(agent)

      // agent is started, get the connector address
      connectorAddress =
         vm.getAgentProperties().getProperty(connectorAddressStr)
   }

   return new JMXServiceURL(connectorAddress);
}

With access to the MBeanServerConnection, I can start to do all types of useful things with Groovy and JMX to manage and monitor the a Java process. For example, the next code listing demonstrates how easy it is to now list the MBeans exposed by the Java process identified by a provided PID.

displayHostedMBeans(MBeanServerConnection)

/**
 * Display MBeans hosted on the provided MBeanServerConnection.
 *
 * @param mbeanServer MBeanServerConnection for which hosted MBeans are to be
 *    provided.
 */
def displayHostedMBeans(MBeanServerConnection mbeanServer)
{
   mbeanServer.queryNames(null, null).each
   {
      println it
   }
}

Running the above Groovy method against my running GlassFish instance with PID 1584 is demonstrated in the next screen snapshot.

The next code listing and associated screen snapshot demonstrate using Groovy and JMX to find not only the exposed MBeans, but to also provide the attributes and operations available on each of those exposed MBeans.

displayHostedMBeansAttributesAndOperations(MBeanServerConnection)

/**
 * Display MBeans hosted on provided MBean Server along with the attributes and
 * operations available on each MBean.
 */
def displayHostedMBeansAttributesAndOperations(MBeanServerConnection mbeanServer)
{
   mbeanServer.queryNames(null, null).each
   { mbeanObjectName ->
      def mbeanInfo = mbeanServer.getMBeanInfo(mbeanObjectName)
      println mbeanObjectName
      println "\tAttributes:"
      mbeanInfo.attributes.each
      { attribute ->
         println "\t\t${attribute.type} ${attribute.name}"
      }
      println "\tOperations:"
      mbeanInfo.operations.each
      { operation ->
         def operationStr = new StringBuilder();
         operationStr << "\t\t" << operation.name << "("
         operation.signature.each
         { parameter ->
            operationStr << parameter.type << " " << parameter.name << ", "
         }
         def operationString = operationStr.contains(",") ? operationStr.substring(0, operationStr.length()-2) : operationStr
         println "${operationString})"
      }
      println ""
   }
}

The previously displayed screen snapshot shows the bootAMX operation available on the amx-support:type=boot-amx MBean. This needs to be run to make other GlassFish-provided AMX MBean available. The next snippet of Groovy code, a method I've named bootAmx(MBeanServerConnection), invokes this bootAMX operation to instruct GlassFish to expose significantly more details via its JMX interface.

bootAmx(MBeanServerConnection)

/**
 * "Boot AMX" on GlassFish.
 *
 * @param mbeanServer MBeanServerConnection to be used to invoke bootAMX
 *    operation.
 */
def bootAmx(MBeanServerConnection mbeanServer)
{
   def amxObjectName = new ObjectName("amx-support:type=boot-amx")
   mbeanServer.invoke(amxObjectName, "bootAMX", null, null)
}

When the simple Groovy just shown is run, GlassFish exposes significant more functionality via JMX. Now that I've "booted AMX," I can rerun displayHostedMBeans(MBeanServerConnection) to see the new MBeans that are available. A portion of this is shown next in the screen snapshot and the small-font text below it contains the entire output.

Related:
1 2 Page 1
Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.