Making a module monitorable in GlassFish V3

Table of Contents
1.0 Introduction
2.0 Monitoring Pluggability
3.0 Probe Provider
3.1 Defining the Probes
3.1.1 Provider as a class with annotations
3.1.2 Provider as an XML
3.2 Emitting Probes by Providers
3.3 Registering the Provider
3.4 Packaging the Probe Provider
3.5 Dependencies for Provider
4.0 StatsProvider objects
4.1 Writing StatsProvider Object
4.2 Registering StatsProvider Object
4.3 Unregistering StatsProvider Object
4.4 Packaging & Dependency of StatsProvider Objects
5.0 Configuration
6.0 Monitoring Clients
6.1 DTrace
6.2 Scripting Client
6.3 JMX/AMX
6.4 REST
6.5 asadmin get/list
6.6 asadmin monitor
7.0 References

Author: Prashanth Abbagani
Email: abbagani@dev.glassfish.java.net
Last Modified: 6/30/09

1.0 Introduction

Salient features of module based monitoring infrastructure are
Monitoring Architecture Diagram

2.0 Monitoring Pluggability

As a subsystem owner, you would bring in your own monitoring implementation into the GlassFish. The monitoring infrastructure will provide you API to do the following
  1. Define the Probe points from your subsystem as part of your Provider implementation. Probe points are the monitoring events, emitted when glassfish goes through that code path. The Providers and its probes are discovered and registered automatically and thus exposed to the DTrace, Client-Scripting and Probe Listener clients. This is explained in detail in the next sub section.
  2. You implement the StatsProvider (Listener) objects, which listen to a specific set of probes, collects the data into the Statistic (StatsProvider member variables) objects. You then instantiate and publish the StatsProvider object using the Montiroing Framework API. As part of publishing these objects, the statistics are automatically exposed through different clients (asadmin, GUI, JMX/AMX, REST). Explained in subsequent sections in detail
  3. The Monitoring configuration (turning the monitoring flag to ON/OFF) for your module and the actions to turn ON/OFF the listeners (and the collection of data in the statistcis inside your StatsProvider objects) accordingly, is handled automatically by the Monitoring infrastructure. Explained in Section 5.0 in detail.

3.0 Probe Provider

You will use the provider API to expose the probes for your subsystem. A provider is either a Class or  an XML file, defined by the contianer/module. It defines the set of events that are emitted by the provider. Providers are discovered and registered automatically, as and when the modules are loaded into the glassfish.

3.1 Defining the Probes

You will need to define the probe points from where the probes (monitoring events) are fired. There are essentially two ways to do this.
3.1.1 Provider as class with annotation
Using these annotations before your (new) class & method headers and specifying the list of parameters to expose, you would define the Provider class. You will need to instantiate this class and call the (empty) method from where you want to emit this probe (see the second example).
*Note that there is a restriction on the parameter types of your Probe, which needs to be only primitive types (for now). This restriction is placed because of DTrace. We plan to relax this restriction going forward, by accomodating the composite types in some fashion and exposing to the DTrace clients.

Defining the Provider Class

@ProbeProvider(moduleProviderName="glassfish", moduleName="web", probeProviderName="jsp")
Class JspProbeProvider {
...
    @Probe(name="jspLoadedEvent")
    public void jspLoadedEvent(
        @ProbeParam("jsp")
        String jsp,
        @ProbeParam("hostName")
        String hostName) { }
....
}
      
Emitting the probe

Class JspProbeEmitterImpl.java {
...
public JspProbeEmitterImpl() {
           JspProbeProvider jspProvider = new JspProbeProvider();
                ......
          }
          public jspLoaded (String jsp, String vsId) {
               jspProvider.jspLoadedEvent(jsp, vsId);
          }
...
}
3.1.2 Provider as an XML
This will allow you to define probes in a non-intrusive way. Just feed the XML file to the Monitoring infrastructure and make the probes active. Your module doesn't have to depend on any Monitoring Framework API if you do this way.

Defining the Provider XML

<probe-provider moduleProviderName="glassfish" moduleName="web" probeProviderName="jsp" >
    <probe name="jspLoadedEvent"                                                         
       <class>com.sun.enterprise.web.jsp.JspProbeEmitterImpl </class>
       <method>jspLoaded</method>
       <signature>void (String,String)</signature>
       <probe-param type="String" name="jsp"/>
       <probe-param type="String" name="hostName"/>
       <return-param type="void" />
       <location>RETURN/ENTRY/EXCEPTION</location>
    </probe>
</probe-provider>

The Container class's method being called

        Class JspProbeEmitterImpl.java{
        ...
      public jspLoaded (String jsp, String vsId) {
            ....
      }
       }

3.2 Emitting Probes by Providers

This section explains how to emit probes (events) from the Providers (Section 3.1.1). This scenario is applicable when you are using the @ProbeProvider annotation approach of writing the provider. For XML approach, you dont have to do anything, the probes are automatically emitted when it visits the code path described in your XML.

Section 3.1.1 explains with example on how to do this.

3.3 Registering the Provider

The Monitoring infrastructure will discover and register the providers automatically when you follow the instructions on packaging the providers, which are explained in section 3.4.

3.4 Packaging the Probe Provider

This section explains on how to package your Probe Provider. As explained there are two approaches to write a provider, either by annotating a class or by writing an XML. When the provider is written as a class (Section 3.1.1) or as an XML (Section 3.1.2), you will need to do the following step so the providers can be discovered by the Monitoring Infrastructure automatically.

You will have to list all the provider class names and xml file names in your jar's META-INF/MANIFEST.MF file. Following is the format.

probe-provider-class-names : <probe-provider-class-name[,probe-provider-class-name]>
probe-provider-xml-file-names : <path of xml file name w.r.t root [, xml file name]>

You will need to put one of the above entries, not both (mutually exclusive). You could automate this by putting the following in your modules pom.xml

        <plugins>
            <plugin>
                <groupId>com.sun.enterprise</groupId>
                <artifactId>hk2-maven-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <probe-provider-class-names>        
                                org.glassfish.web.admin.monitor.RequestProbeProvider,org.glassfish.web.admin.monitor.JspProbeProvider
                            </probe-provider-class-names>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>

3.5 Dependencies for Provider
Your module when doing the 'Class' approach (Section 3.1.1), you need to have the runtime and build-time dependencies on the following Jars (which will be external'ized). Apart from this, your module doesn't have any other dependency for your probe-provider to work in the GF environment.

management-api.jar (This Jar consists of the annotation definitions, ex. @ProbeProvider, @Probe and @ProbeParam).

<dependency>

           <groupId>org.glassfish.external</groupId>
           <artifactId>management-api</artifactId>
           <version>3.0.0-b003</version> {// Do not specify version if your module is under glassfish v3 project.  The correct version will be picked up from v3/pom.xml}
</dependency>

4.0 StatsProvider objects

A Probe listener (part of the functionality of StatsProvider Object) is basically an object that receives callbacks from the providers. A method in the listener can be "marked" as callback method for a particular event from a specific provider.

StatsProvider are the objects which gather and expose the monitoring data of various components. They do the following functionality

4.1 Writing StatsProvider Object

This section describes on how to write a StatsProvider object.
*Note: We need to make sure the ObjectNames are compatible with V2 style for existing components.

import org.glassfish.external.statistics.CountStatistic;
import org.glassfish.external.statistics.impl.CountStatisticImpl;
import org.glassfish.external.probe.provider.annotations.ProbeListener;
import org.glassfish.external.probe.provider.annotations.ProbeParam;
import org.glassfish.gmbal.ManagedAttribute;
import org.glassfish.gmbal.ManagedObject;

@ManagedObject
class JspStatsProvider() {

    private CountStatisticImpl activeJspsLoadedCount = new CountStatisticImpl("ActiveJspsLoaded", "count", "Number of currently loaded JSPs");
    private CountStatisticImpl totalJspsLoadedCount = new CountStatisticImpl("TotalJspsLoaded", "count", "Cumulative number of JSP pages that have been loaded into the web module");
                                                                                                                                     ^--- Statistic name - capitalize first letter of each word, no spaces
....

    @ManagedAttribute(id="activejspsloadedcount")   <----
id should be all lowercase
    public CountStatistic  getActiveJspsLoadedCount(){
         return activeJspsLoadedCount;
    }

    @ManagedAttribute(id="totaljspsloadedcount")
    public CountStatistic  getTotalJspsLoadedCount(){
         return totalJspsLoadedCount;
    }

   @ProbeListener("glassfish:web:jsp:jspLoadedEvent")
    public void jspLoadedEvent(
        @ProbeParam("jsp")
        String jsp,

        @ProbeParam("hostName")
        String hostName) {

        activeJspsLoadedCount.increment();
        totalJspsLoadedCount.increment();
    }
....
}

The annotations are explained below

@ManagedObject - This is a Gmbal annotation telling that this is a managed object and is exposed via. JMX/AMX.
@ManagedAttribute(<attribute-name>) - The name of the attribute when exposed through JMX/AMX, also will be used to publish it as a leaf node in the Monitoring Registry
@ProbeListener(<probe-provider-identifier>) - This annotation specifies the callback (listener) method when a probe (mentioned as parameter) is fired.
@ProbeParam(<param-name>) - This annotation allows you to mention only the param-names that you are interested in, others wont be passed along.

Naming Convention

For consistency between v2 and v3, between MBean attributes, dotted names, REST, and Admin Console, as well as across all statistics, please use the following naming conventions for attribute ids and statistic names.
Attribute ids and Statistic names are displayed in the CLI dotted name from the asadmin get command, REST, Admin Console, and monitoring MBeans.

CLI dotted-name (asadmin get --monitor=true server.web.jsp.*)
server.web.jsp.activejspsloadedcount-count = 1
server.web.jsp.activejspsloadedcount-description = Number of currently loaded JSPs
server.web.jsp.activejspsloadedcount-lastsampletime = 1249556014500
server.web.jsp.activejspsloadedcount-name = ActiveJspsLoaded
server.web.jsp.activejspsloadedcount-starttime = 1249556014500
server.web.jsp.activejspsloadedcount-unit = count

REST (http://localhost:4848/monitoring/domain/server/web/jsp)
activejspsloadedcount :
        Count : 1

Admin Console (http://localhost:4848)


Jconsole

4.2 Registering StatsProvider object

To enable monitoring for your Module, you first write a Probe Provider (Section 3.0), then you would write the StatsProvider Objects (Section 4.1). You would then control the registering and unregistering of these objects using Monitoring Framework, either at statup/shutdown or may be based on some config-change events (like when a new jdbc-connection-pool is created). When registring the StatsProvider object, the Monitoring Framework will automatically register them for JMX/AMX, REST and Monitoring Registry (which will be referred to by asadmin/Admin GUI clients), while also registering the listeners for associated Probes. Following is the ex.

StatsProviderManager.register("jsp-container", PluginPoint.SERVER, "applications/app1/jsp1", new JspStatsProvider());

The StatsProvider object will be registered under the “server” node in the Monitoring registry. Third parameter gives the intermediary sub-tree (followed after the 'server' node) in the Monitoring registry under which the Statistics objects (defined in the StatsProvider object) would be created as leaf nodes. The fourth parameter is the StatsProvider object itself.

>asadmin get server.applications.app1.jsp1.*   //will return these stats objects

4.3 Unregistering the StatsProvider Object

When unregistering the StatProvider object, the Monitoring Framework will automatically unregister them from JMX/AMX, REST clients and MonitoringRegistry (which will be referred to by asadmin/Admin GUI clients), while also unregistering the listeners for associated Probes. Following is the ex.

StatsProviderManager.unregister(jspStatsProvider);

4.4 Packaging & Dependency of StatsProvider

The StatsProvider of a Module can be packaged as a separate OSGI jar or It can be part of the Provider/Container. There is no restriction on how you would package the StatsProvider objects.

StatsProvider Module will have dependency on the following jars

          management-api.jar (definitions for @ProbeListener, the StatsProviderManager API, and the Statistic API)
         
gmbal.jar (definitions for @ManagedObject, @ManagedAttribute, and @AMXMetadata)

         
Put the following dependencies in your pom.xml

<dependency>
           <groupId>org.glassfish.external</groupId>
           <artifactId>management-api</artifactId>
           <version>3.0.0-b003</version> {// Do not specify version if your module is under glassfish v3 project.  The correct version will be picked up from v3/pom.xml}
</dependency>

<dependency>
           <groupId>org.glassfish.gmbal</groupId>
           <artifactId>gmbal</artifactId>
           <version>3.0.0-b009</version> {// Do not specify version if your module is under glassfish v3 project.  The correct version will be picked up from v3/pom.xml}
</dependency>

5.0 Configuration

The configuration of monitoring for your module is automatically handled by the Monitoring Infrastructure. The first parameter that you gave as part of the MonitoringFramework.register API, will be used to construct the config sub-element and saved in the domain.xml (an attribute under the <monitoring-service>/<monitoring-level> element). The management of this configuration is supported by asadmin (get/set/list)/GUI/JMX/REST (all standard clients of config). The enable/disable of the monitoring flag for the component is handled automatically, by turning on/off the associated listner(s) to the probes.

asadmin set command

glassfish/bin>asadmin get "*"|grep monitoring-level
configs.config.server-config.monitoring-service.module-monitoring-levels.web-container=HIGH
configs.config.server-config.monitoring-service.module-monitoring-levels.jvm=OFF
configs.config.server-config.monitoring-service.module-monitoring-levels.http-service=HIGH

glassfish/bin>asadmin set configs.config.server-config.monitoring-service.module-monitoring-levels.jvm=HIGH

configs.config.server-config.monitoring-service.module-monitoring-levels.jvm=HIGH

6.0 Monitoring Clients

There are several ways the monitoring is exposed to the end user. These are done in an uniform and seamless manner, so the Monitoring providers would not have to do anything from their part. Following are the different ways.

6.1 DTrace

When the Probe Provider is registered with the monitoring infrastructure (Note that StatsProvider objects have nothing to do with this), the probes from glassfish components are automatically exposed (for Solaris) so the DTrace clients can write their scripts to listen to these probes. We use the BTrace as the underlying infrastructure to do this. Note that as a provider, you wont have to do anything for this to work.

6.2 Scripting Client

We expose all our probes to a scripting client that would allow GF user to enable/disable the probes and get the data they want. This is similar to DTrace philosophy but will work on any OS for GlassFish runtime. We will have the infrastructure with a Scripting Container, also the asadmin and GUI clients for deploying and running the scripts written in JavaScript and JRuby (next release) and opening up a Comet channel to display the data as and when the probe is fired (a Push model, does it asynchronously). Note that the script is something similar to a StatsProvider object in this case, the difference being, the user could write adhoc script (no compilation is necessary) and just run it on the fly to see the data instantly.

Javascript example

var nServlets=0;
function getCallbackMethod() {
    return "requestStart";
 }
 function requestStart(hostName) {
    nServlets=nServlets+1;
    ScriptContainer.println("js:requestStart: called servlet count = " + nServlets);
}
println('Javascript> Invoking testMethod method from JavaScript...');
params = java.lang.reflect.Array.newInstance(java.lang.String, 1);
params[0]="hostName";
ScriptContainer.registerListener('glassfish.web.requestStart', params, getCallbackMethod());

asadmin deploy --type clientscript <javascript file-location>....
asadmin run-script --script <script-name>

Apart from the adhoc scripting support, we will also provide pre-canned script capability, which user would be able to invoke directly. The output from the scripting container is name=value pairs with the value coming from the Object.toString(). Following is the example

asadmin run-script --probe "glassfish.web.requestStart" --paramnames "moduleName,hostName"

The module owner doesn't have to do anything to get this functionality. Its available by default, when the container (or module) comes up with its probe provider.

6.3 JMX/AMX

We use Gmbal infrastructure to expose the Statistics objects as JMX/AMX. As part of the MonitoringFramework.register API, we are exposing these Statistics objects as JMX/AMX automatically.

Example: view jvm AMX mbeans with jconsole:

1. asadmin start-domain
2. asadmin set server.monitoring-service.module-monitoring-levels.jvm=HIGH
3. Start jconsole by typing 'jconsole' on command line
4. In jconsole connection window, select remote process and type 'localhost:8686' (leave username and password blank), and Connect
5. Goto Mbeans tab.
6. Expand 'amx' folder
7. Look for class-loading-system-mon, compilation-system-mon, garbage-collector-mon, memory-mon, operating-system-mon, runtime-mon folder.



To view web container mbeans

1. asadmin set server.monitoring-service.module-monitoring-levels.web-container=HIGH
2. deploy web app
3. Look for jsp-mon, servlet-mon, session-mon, web-request-mon folder.

AMX Monitoriong MBean Object Names

The object name is made up of 4 parts:

[1]  : [2]                                      , [3]                                          , [4]
amx:pp=/mon/server-mon[server],type=class-loading-system-mon,name=jvm/class-loading-system

[1] amx is the domain name for all the amx mbeans
[2] pp is the parent path, 'server' monitoring
[3] type is the 'interface' type which comes from annotation @AMXMetadata(type="class-loading-system-mon").  If @AMXMetadata is not specified, the type defaults to the classname, such as JVMClassLoadingStatsProvider.
[4] name is used to uniquely identify mbeans of the same type value.  name is derived from the sub-tree (third param) used in StatsProviderManager.register("jvm", PluginPoint.SERVER, "jvm/class-loading-system", new JVMClassLoadingStatsProvider());

6.4 REST

We use the REST infrastructure to expose the Statistics objects as REST API. As part of the MonitoringFramework.register API we notify the REST infrastructure to expose these Statistics objects as REST automatically. Please see REST One-pager on how this happens and the URI to access these objects.

6.5 asadmin get/list

As explained earlier, the asadmin get and list commands (with --monitor=true option to differentiate from the config get/list commands) will automatically expose the monitoring data, once you publish your statistic objects into the monitoring registry. It would look something like below

CLI List Command

6.6 asadmin monitor

To be able to plug in your own monitoring type and view its related output, you should be able to use the pluggability feature of the CLI monitor command. Please follow the link for an explanation on how this works.

This command displays commonly monitored statistics for Enterprise Server components and services. Monitoring output is displayed continuously in a tabular format; the --interval option can be used to display output at a particular interval (default 30 seconds).

C:\glassfishV3\v3-prelude\v3_prelude_release\glassfish\bin>asadmin monitor --type=webmodule server
asc  ast   rst   st    ajlc  mjlc tjlc   aslc    mslc      tslc
0     0     0     1     0     0     0     8        8          8

C:\glassfishV3\v3-prelude\v3_prelude_release\glassfish\bin>asadmin monitor --type=jvm server
                               JVM Monitoring
UpTime(ms)                          Heap and NonHeap Memory(bytes)
current                   min             max             low       high      count
11623062             8585216    167313408  0          0          38531072

7.0 References

Monitoring One-Pager
REST One-Pager