Building a Glassfish Monitoring Client Using REST

The GlassFish 3.1 Application Server provides a REST web service interface that enables service consumers to access administrative and monitoring features.  Although GlassFish’s web-based admin console is generally regarded as one of the better app server consoles, in my experience with previous versions of GlassFish, there are some cases where the web console can be difficult to use.  For example, during clustered server failover scenarios, the web console will freeze until the network connection times out, which can take a minute or two.   To allow more control over the client-side response to server admin events, I’ve recently started a side project to develop a client-side Java API to the GlassFish REST admin interface.  The API is decoupled from a user interface and is intended to be utilized by a UI developer, but could also be used by any consumer application that needs GlassFish server status.

The scope of the GlassFish REST interface can be explored through the WADL file available from the GlassFish server.  There are two WADL files, one for the Management resources and another for the Monitoring resources.  The URIs for the WADL files follow below.

Management REST resources: http://serverURL:4848/management/application.wadl
Monitoring REST resourceshttp://serverURL:4848/monitoring/application.wadl

Using the URIs above, just point your browser to your GlassFish installation and the WADL files will be returned.  The Management WADL file is large, so it may take a while to load.  For a detailed description of the WADL schema, check out the W3C specification.

Despite what the names of the REST interfaces might lead you to believe, I’ve developed all of the features of the monitoring API library using the Management REST resources.  The Monitoring resources seem to be undeveloped, unless I am not interpreting the sparse WADL file correctly.  Either way, the Management resources provide all of the necessary capability.  Listing 1 below illustrates a small portion of the Management WADL file.

Listing 1: Partial WADL file detailing the available Management REST resources.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<application xmlns="http://research.sun.com/wadl/2006/10">
    <doc xmlns:jersey="http://jersey.dev.java.net/" jersey:generatedBy="Jersey: 1.5 01/14/2011 12:36 PM"/>
    <resources base="http://localhost:4848/management/">

<!-- snip snip -->

      <resource path="clusters/">

<!-- snip snip -->

          <resource path="list-clusters/">
              <method id="processGet" name="GET">
                  <response>
                      <representation mediaType="text/html;qs=2"/>
                      <representation mediaType="application/json"/>
                      <representation mediaType="application/xml"/>
                      <representation mediaType="application/x-www-form-urlencoded"/>
                  </response>
              </method>
              <method id="options" name="OPTIONS">
                  <response>
                      <representation mediaType="text/html;qs=2"/>
                      <representation mediaType="application/json"/>
                      <representation mediaType="application/xml"/>
                  </response>
              </method>
           </resource>
      </resource>

<!-- snip snip -->

  </resources>
</application>

As you can see from Listing 1, the list-clusters resource is accessed through the HTTP GET method at the URI http://localhost:4848/management/domain/clusters/list-clusters and supports four different response types.  I have used the Jersey REST client-side API for all of the REST calls in the GlassFish Monitoring API library, which conveniently abstracts away the details of forming valid HTTP calls.

Before I begin to describe the GlassFish Monitoring API software, let me be clear that this is a very early prototype, largely intended to flesh out the concepts.  I’ve started small with code to monitor clusters and their member instances, and the status data is simply written to the log — no GUI yet.  It is reasonable to conclude that there will be some restructuring of the software as I tie the API into a user interface and expand the scope to monitor more aspects of the GlassFish server.

Figure 1 below illustrates the top-level package structure for the GlassFish Monitoring API.  The client package contains a sample client that starts up the monitoring and status threads (more on that below).  As you might expect, the cluster and instance packages contain the classes for monitoring clusters and their instances.  The status package contains interfaces that are implemented by classes representing a a status monitor, a server status message and a DOM parser for XML status data.  The util package contains a few odds and ends, primarily for initializing pretty-printing XML responses from the server and to manage a configuration properties map that is built from an XML config file.

Figure 1. Top-level package structure for GlassFish Monitor Client API.

Figure 1 also depicts two interfaces — Subject and Observer.  As the names imply, these interfaces are implemented by classes that utilize the GoF Observer pattern, which is central to the organization of this API.

Figure 2 illustrates the class implementation (within the cluster package) of the ClusterListMonitor (subject) and ClusterListStatusObserver (observer).  Status monitoring and status reporting are separated into two different threads.  Monitoring classes, which implement both the StatusMonitor and Subject interfaces, run in their own thread and are responsible for polling the GlassFish server for the status of a specific item of interest (e.g. clusters).  Status observer classes implement the Observer interface, and they also run in their own thread.  Status observer classes are responsible for providing a response to the status reported by the monitor.  Responses could include updating a graphical user interface, writing to a log file or executing some predefined rule.

Figure 2. Class implementation to monitor and report cluster status data.

I had some concern about the overhead of using two threads for each unique monitored item, but ultimately thought the benefits justified the overhead.  One of those benefits is the ability to establish a wait time for the status observer thread, which decouples it from the blocking status request of the monitoring thread.  If the status monitoring thread is blocking for an unresponsive server, the status observer thread can abort the wait for a status update and take some alternative action, such as update a user interface component for a monitored item to denote the “unresponsive” status.  The ability to have this type of control over blocking status requests was one of my primary motives for developing this API.  With previous versions of GlassFish, one could only watch impatiently as the admin web console froze until the network connection to a failed server timed out.  I expect the monitoring and observer threads to manage fairly coarse-grained server items, such as the set of all clusters monitored by the classes in Figure 2, so I don’t envision more than a dozen or so unique monitored items, which would keep the number of threads acceptably low.  The instance monitor and observer classes, in the instance package, are structured similarly to the cluster classes in Figure 2.

Figure 3 depicts the interaction between the Monitor and Observer threads described above.

Figure 3. The monitor (subject) and observer run in separate threads.

The thread loop (run method) and the REST operation (queryStatusOfClusters method) for the ClusterListMonitor class are depicted in Listing 2 below.  The run method is simple — query the GlassFish server for status and notify the observers, then sleep for the user-specified time.  The REST operation is performed using the Jersey API to construct the GET operation and specify the response type, then the operation is executed on the GlassFish server and the XML response is passed back to be parsed.  See the full listing for all of the gory details.

Listing 2: Some salient portions of the ClusterListMonitor class.

//snip snip

public class ClusterListMonitor implements StatusMonitor, Subject, Runnable {

//snip snip

public void run() {
   while(true) {
      //query the GF rest interface for the cluster status
      clusterStatusList = this.queryGFStatus();
      //notify the observers of the new status
      notifyObservers(clusterStatusList);
      //sleeping for the user-specified polling rate
      try {
         Thread.sleep(pollingRate);
      } catch(InterruptedException ie) {
          logger.log(Level.INFO, "Cluster Monitor Thread Problem: {0}", new Object[]{ie});
      }
   }
}

//snip snip

/**
 * Poll the server for status and parse the XML response
 *
 * @return The status reported by the GF server
 */
public ArrayList<Status> queryGFStatus() {
    ArrayList<Status> statusList = null;

    if(restClient != null) {
        String response = queryStatusOfClusters(restClient);
        if(response != null)
          statusList = parseClusterStatusResponse(response);
    }

    return statusList;
}

/**
 * Performs the GET operation on http://baseURL/management/domain/clusters/list-clusters
 *
 * @param client The Jersey rest client
 *
 * @return The XML text
 */
private String queryStatusOfClusters(Client client) {

   //Construct the resource and perform the GET operation
   WebResource webResource = client.resource(restURL);
   ClientResponse response = webResource.accept("application/xml").get(ClientResponse.class);

   int status = response.getStatus();
   logger.log(Level.FINEST, "list-clusters Status =  {0}", new Object[]{status});

   String textEntity = response.getEntity(String.class);
   if(textEntity != null) {
      logger.log(Level.FINEST, "list-clusters Response =  {0}", new Object[]{textEntity});
      StatusUtilities.writeResponseToFile(textEntity, "cluster-status.xml");
   }
   else
      logger.log(Level.FINEST, "list-clusters Response =  No Clusters");

   return textEntity;
}

//snip snip

}

Listing 3 below illustrates a sample config.xml file, which allows the user to configure the base URL for the GlassFish REST interface, the status polling rate and the observer wait time. The config.xml file resides at the root of the JAR file.

Listing 3: XML config file allows users to specify configuration properities.

<?xml version="1.0" encoding="UTF-8"?>

<config>
  <baseURL>http://localhost:4848</baseURL>
  <pollingrate>5000</pollingrate>
  <responsewaittime>10000</responsewaittime>
</config>

Once I’ve expanded the scope of the API library, I expect to provide individual configuration properties for each monitored item.  So, for example, the polling rate could be set differently for each monitor thread.

Conclusion

As I mentioned above, this is an early prototype of the API software, and there are still several design concepts that I’m thinking through, but I wanted to get it out there so I could return to my primary project — so caveat emptor.  I’ll continue to provide updates to this library over time, but for now it will be moving to the back burner.  Feel free to contact me if you have any questions.  Have fun!

Resources

  • The source code for the GlassFish Monitoring API library is available at GitHub.  The source is released under the Apache License, Version 2.0.
  • The GlassFish Monitoring API library JavaDocs (still a bit rough).
  • The GlassFish REST Admin API documentation.
  • Here and here are a couple of blog entries that provide some introductory information about the GlassFish REST Admin API.
About these ads

One thought on “Building a Glassfish Monitoring Client Using REST

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s