Java Dynamic Management Kit 5.0 Tutorial

Part II Agent Applications

The agent is the central component of the Java Management Extensions (JMX) management architecture. An agent contains MBeans and hides their implementation behind a standardized management interface, lets management applications connect and interact with all MBeans, and provides filtering for handling large numbers of MBeans. JMX agents are dynamic because resources can be added and removed, connections can be closed and reopened with a different protocol, and services can be added and removed as management needs evolve.

In Part I, we saw how to represent resources as MBeans. However, MBeans can represent any object whose functionality you need to manage. In particular, management services and remote connectivity are handled by objects that are also MBeans. This creates a homogeneous model where an agent is a framework containing different kinds of MBeans and enabling them to interact.

The main component of an agent is the MBean server. It registers all MBeans in the agent and exposes them for management. The role of the MBean server is to be the liaison between any object available to be managed and any object with a management request. Usually resource MBeans are managed either by remote applications through connectivity MBeans or by local management service MBeans. This model allows a management service itself to be managed. Connectors and services can also be created, modified, or removed dynamically.

This part focuses on the functionality of the MBean server and the Java objects which are needed to create a simple agent. Details about programming managers and about using connectors and services will be covered later.

This part contains the following chapters:

Chapter 5 MBean Server in a Minimal Agent

An agent application is a program written in the Java language that contains an MBean server and a means of accessing its functionality. This would be a minimal agent, anything less could not be managed. In our example of a minimal agent, we will access the MBean server through the HTML protocol adaptor from a web browser.

In a real management solution, the agent could be instantiated and loaded with MBeans for all services and resources that it might need when started. However, a minimal agent might also be used when resources and services are unknown at the time when it is launched. They would be instantiated dynamically at a later date by some management application connected to the agent. This flexibility shows how the Java Dynamic Management Kit (DMK) lets you develop many different management solutions, depending on your intended deployment strategy.

For now we will focus on the functionality of the MBean server and how to interact with it through the HTML protocol adaptor. The code samples in this chapter are from the files in the MinimalAgent example directory located in the main examplesDir (see “Directories and Classpath” in the Preface).

This chapter covers the following topics:

MBean Server Classes

Before writing an agent application, it is important to understand the functionality of the MBean server. It is actually an interface and a factory object defined by the agent specification level of the JMX specification. The Java DMK provides an implementation of this interface and factory. The factory object finds or creates the MBean server instance, making it possible to substitute different implementations of the MBean server.

MBeanServer Interface

The specification of the interface defines all operations that can be applied to resources and other agent objects through the MBean server. Its methods can be divided into three main groups:

MBean Server Implementation and Factory

The MBeanServerImpl class in the Java DMK implements the MBeanServer interface. It is the class that will be instantiated in an agent. However, it is never instantiated directly by the agent application. Instead, you rely on the MBean server factory to return a new instance of the implementing class.

The MBeanServerFactory is a static class defined by the JMX specification that makes the agent application independent of the MBean server implementation. It resides in the Java virtual machine and centralizes all MBean server instantiation. The MBeanServerFactory class provides two static methods:

You must use this class to create an MBean server so that other objects can obtain its reference by calling the findMBeanServer method. This method enables dynamically loaded objects to find the MBean server in an agent that has already been started.

Minimal Agent

The minimal agent contains only the essential components of a complete agent application. These are:

Example 5–1 shows the complete application from the MinimalAgent.java file:


Example 5–1 Minimal Agent

import javax.management.ObjectInstance;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;

public class MinimalAgent {

    public static void main(String[] args) {
        
        // Instantiate the MBean server
        System.out.println("\nCreate the MBean server");
        MBeanServer server = MBeanServerFactory.createMBeanServer();
        
        // Create and start some way of communicating:
        //    - an HTML protocol adaptor
        //    - an HTTP connector server
        //    - an RMI connector server
        // Any single one of these would be sufficient
        try {

            System.out.println(
                "\nCreate and start an HTML protocol adaptor");
				ObjectInstance html = 
		server.createMBean("com.sun.jdmk.comm.HtmlAdaptorServer", 
					  null);
            server.invoke(html.getObjectName(), "start",
                          new Object[0], new String[0]);

            System.out.println(
                "\nCreate and start an HTTP connector server");
            ObjectInstance http = server.createMBean(
				"com.sun.jdmk.comm.HttpConnectorServer", 
					  null);
            server.invoke(http.getObjectName(), "start",
                          new Object[0], new String[0]);

            System.out.println(
                "\nCreate and start an RMI connector server");
            ObjectInstance rmi = server.createMBean(
                "com.sun.jdmk.comm.RmiConnectorServer", 
					  null);
            server.invoke(rmi.getObjectName(), "start",
                          new Object[0], new String[0]);

        } catch(Exception e) {
            e.printStackTrace();
            return;
        }

        System.out.println(
"\nNow, you can point your browser to http://localhost:8082/");
        System.out.println(
"or start your management application to connect to this agent.\n");

    }
}

One of the most important lines of code in this example is the instantiation of the MBean server:

MBeanServer server = MBeanServerFactory.createMBeanServer();

The MBean server is created through the static MBeanServerFactory object, and we store its object reference. Its true type is hidden by the factory object, that casts the returned object as an MBeanServer interface. The MBean server is the only functional class referenced directly in this application.

Then we just need some means of communicating (only the HTML adaptor is shown here):

ObjectInstance html = 
server.createMBean("com.sun.jdmk.comm.HtmlAdaptorServer", null);
server.invoke(html.getObjectName(), "start", new Object[0], new String[0]);

For each of the communications protocols we ask the MBean server to create the corresponding MBean. We must provide the class name of the MBean that we want the MBean server to instantiate. The MBean server instantiates the object, registers it for management, and returns its ObjectInstance reference (see ObjectInstance of an MBean).

When an MBean is created through the MBean server, you can never obtain a direct programmatic reference on an MBean. The object instance is the only handle the calling process has on the MBean. The MBean server hides the MBean object instance and only exposes its management interface.

We then ask the server to invoke the start operation of the HTML adaptor's MBean. The object instance gives us the MBean's object name which we use to identify the MBean (see Object Names). The other parameters correspond to the signature of the start operation, which takes no parameters. This operation activates the adaptor or connector so that it will respond to remote management operations on its default port. We can now connect to the HTML protocol adaptor from a web browser.

Referencing MBeans

As demonstrated by the minimal agent, most agent applications interact with MBeans through the MBean server. It is possible for an object to instantiate an MBean class itself, which it can then register in the MBean server. In this case, it keeps a programmatic reference to the MBean instance. All other objects can only interact with the MBean through its management interface exposed by the MBean server.

In particular, service MBeans and connectivity MBeans rely solely on the MBean server to access resources. The MBean server centralizes the access to all MBeans. It unburdens all other objects from having to keep numerous object references. To ensure this function, the MBean server relies on object names to identify MBean instances uniquely.

Object Names

Each MBean object registered in the MBean server is identified by an object name. The same MBean class can have multiple instances, but each must have a unique name. The ObjectName class encapsulates an object name composed of a domain name and a set of key properties. The object name can be represented as a string in the following format:

DomainName:property=value[,property2=value2]*

The DomainName, the property and value can be any alphanumeric string, as long as it does not contain any of the following characters: : , = * ?. All elements of the object name are treated as literal strings, meaning that they are case sensitive.

Domains

A domain is an abstract category that can be used to group MBeans arbitrarily. The MBean server lets you search easily for all MBeans with the same domain. For example, all connectivity MBeans in the minimal server could have been registered into a domain called Communications.

Since all object names must have a domain, the MBeans in an MBean server necessarily define at least one domain. When the domain name is not important, the MBean server provides a default domain name that you can use. By default, it is called the DefaultDomain, but you can specify a different default domain name when creating the MBean server from its factory.

Key Properties

A key is a property-value pair that can also have any meaning that you assign to it. An object name must have at least one key. Keys and their values are independent of the MBean's attributes. The object name is a static identifier that identifies the MBean, whereas attributes are the exposed, runtime values of the corresponding resource. Keys are not positional and can be given in any order to identify an MBean.

Keys provide the specificity for identifying a unique MBean instance. For example, an object name for the HTML protocol adaptor MBean might be: Communications:protocol=html,port=8082, assuming the port will not change.

Usage

All MBeans must be given an object name that is unique. It can be assigned by the MBean's preregistration method, if the MBean supports preregistration (see the Javadoc API of the MBeanRegistration interface). Or it can be assigned by the object that creates or registers the MBean, which overrides the one given during preregistration. However, if neither of these assign an object name, the MBean server will not create the MBean and will raise an exception. Once an MBean is instantiated and registered, its assigned object name cannot be modified.

You can encode any meaning into the domain and key strings. The MBean server handles them as literal strings. The contents of the object name should be determined by your management needs. Keys can be meaningless serial numbers if MBeans are always handled programmatically. On the other hand, the keys can be human-readable to simplify their translation to the graphical user interface of a management application. With the HTML protocol adaptor, object names are displayed directly to the user.

ObjectInstance of an MBean

An object instance represents the complete reference of an MBean in the MBean server. It contains the MBean's object name and its Java class name. Object instances are returned by the MBean server when an MBean is created or in response to queries about MBeans. Since the object name and class name cannot change over the life of a given MBean, its returned object instance will always have the same value.

You cannot modify the class or object name in an object instance. This information is read-only. The object name is used to refer to the MBean instance in any management operation through the MBean server. The class name can be used to instantiate similar MBeans or introspect characteristics of the class.

Minimalist Agent

Example 5–2 gives the code for one of the smallest agents you can write. It retains all the functionality that we will need to connect to it with a web browser described in Chapter 6, HTML Protocol Adaptor.


Example 5–2 Minimalist Agent

import javax.management.ObjectInstance;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;

public class MinimalistAgent {

    public static void main(String[] args) {
        
        MBeanServer server = MBeanServerFactory.createMBeanServer();
        
        try {

            ObjectInstance html = server.createMBean(
                "com.sun.jdmk.comm.HtmlAdaptorServer", null);
            server.invoke(html.getObjectName(), "start", null, null);

        } catch(Exception e) {
            e.printStackTrace();
            return;
        }
    }
}


Note –

Only three classes are “imported” by this program and needed to compile it. However, the MBean server dynamically instantiates other classes, such as the HtmlAdaptorServer, that are needed at runtime. As a result, the Java DMK runtime JAR files (jdmkrt.jar and jsnmpapi.jar) must be in the classpath of the Java virtual machine that is running the minimal agent (see “Directories and Classpath” in the Preface).


The MinimalistAgent relies on the HTML adaptor, but we could have used any MBean that provides some way of accessing the MBean server. You could even use your own MBean that encodes some proprietary protocol, provided it makes all functionality of the MBean server available remotely.

It is important to realize that this minimalist agent is a fully functional agent that is every bit as powerful as any agent that can be deployed. Because we can connect to this agent, we can dynamically create new MBeans for it to manage, and classes that are not available locally can be downloaded from the network (see Chapter 14, M-Let Class Loader). Because resources, services, and other connections can be added dynamically, this agent can participate in any management scheme.

Of course, it is more efficient for the agent application to perform the initialization, including the creation of all MBeans that are known to be necessary. Typically, an agent application also needs to set up data structures or start specific applications that its resources require. For example, it can establish a database session that an MBean will use to expose stored information. The agent application usually includes everything necessary for making the intended resources ready to be managed within the intended management solution.

However, there is no single management solution. Many different agent applications can perform the same management function, requiring more or less intervention after they are started. And the flexibility of the Java DMK enables many different management solutions to achieve the same goal. For example, an MBean could also establish the database session itself during its preregistration phase.

Running the Minimal Agent Example

The example code for the MinimalAgent application is located in the examplesDir/MinimalAgent directory. This agent application only has minimal output and is intended to be accessed for management through one of its communication MBeans. However, you will need to compile and start the minimal agent before continuing on to the next chapter.

To Run the Minimal Agent Example
  1. Compile the MinimalAgent.java file in the examplesDir/MinimalAgent directory with the javac command.

    For example, on the Solaris platform, type:


    $ cd examplesDir/MinimalAgent/
    $ javac -classpath classpath *.java
    

    When we access the minimal agent through the HTML adaptor, we will instantiate the SimpleStandard and SimpleDynamic classes. Because we do not use a dynamic class loader, the agent will need these classes at runtime. You must compile the standard and dynamic MBean classes as described in Running the Standard MBean Example and Running the Dynamic MBean Example.

  2. To run the example, update your classpath to find the MBeans and start the agent class:


    $ java -classpath classpath:../StandardMBean:../DynamicMBean MinimalAgent
    

Because this is a minimal agent, the example does not have much output. The MBean server is started, and the three communication MBeans are created. It is now possible to connect to this agent. Management applications can connect through the HTTP and RMI connector servers, as described in Connector Servers.

The simplest way to communicate with the agent is through the HTML protocol adaptor. This adaptor provides a view of the agent and its MBeans through standard HTML pages that can be viewed on almost any web browser.

To connect to the agent, load the following URL in your browser:

If you get an error, you might have to switch off proxies in your preference settings or substitute your host name for localhost. Any browser on your local network can also connect to this agent by using your host name in this URL. Chapter 6, HTML Protocol Adaptor details how to manage this agent from its web view.

Chapter 6 HTML Protocol Adaptor

The HTML protocol adaptor provides a view of the agent and its registered MBeans through a basic interface on any web browser. It is the easiest way to access an agent since no further coding is necessary. For this reason, it can be useful for testing and debugging your MBeans in the minimal agent.

In this chapter, we will use your browser to “manage” the minimal agent and its MBeans. The HTML protocol adaptor outputs HTML pages that represent the agent and its MBeans. The adaptor also interprets the commands sent back by the buttons and fields appearing in your browser. It then interacts with the agent's MBean server to get information about the MBeans that it has registered and to operate on them.

The HTML adaptor relies mostly on plain HTML. The only JavaScriptTM that the generated pages contain are pop-up windows for displaying information. Browsers that are not enabled for JavaScript might give an incompatibility message and will not be able to display the information. Otherwise, the generated pages contain no further scripting (JavaScript, Visual Basic or other), no frames and no images that might slow down loading.

This chapter relies on the minimal agent that you will need to start first, as explained in Running the Minimal Agent Example. Once you can connect to the HTML protocol adaptor in the minimal agent, you are ready to begin these topics:

Agent View

The first page displayed by the HTML adaptor is always the agent view. It initially contains a list of all registered MBeans. The following figure shows the agent view for the minimal agent. It contains four MBeans: three communication MBeans, one of which is the HTML adaptor, and the MBean server delegate. The delegate is a special MBean explained in MBean Server Delegate.

Figure 6–1 Initial Agent View of the Minimal Agent

Screen shot showing the initial agent view of the minimal agent

The text field for filtering by object name enables you to modify the list of displayed MBeans. The filter string is initially *:*, which displays all registered MBeans. Filtering MBeans describes how to use the filter. The agent's registered domain indicates the name of the default domain in this agent. The number of MBeans on this page is the count of those listed beneath the separator line.

The Admin button is links to the agent administration page (see Agent Administration).

MBean List

The MBean list contains all MBeans whose object name matches the filter string. Object names can be filtered by their domain name and list of key-value pairs. In this list, MBeans are sorted and grouped by domain name. Each MBean name listed is an active link to the page of the corresponding MBean view.

After its initialization, the contents of an agent are dynamic. New MBeans can be created and registered into new or existing domains and old MBeans can be removed. These changes can also affect the functionality of the agent. New agent services can be registered (or removed) as well. We will demonstrate examples of dynamic management in Instantiating and Managing MBeans.

MBean Server Delegate

The MBean server delegate is an MBean that is automatically instantiated and registered by the MBean server when it is created. It provides information about the version of the Java Dynamic Management Kit (DMK) that is running, and it represents the MBean server when sending notifications.

Notifications are events sent by MBeans. They are covered in detail in Chapter 9, Notification Mechanism. Because the MBean server instance is not an MBean object, it relies on its delegate MBean to send notifications. The MBean server delegate sends notifications to inform interested listeners about such events as MBean registrations and unregistrations.

The exposed attributes of the delegate MBean provide vendor and version information about the MBean server. This can let a remote management application know which agent version is running and which version of the Java runtime environment it is using. The delegate MBean also provides a unique identification string for its MBean server.

To View the MBean Server Delegate Information
  1. Click the name of the delegate MBean to see its attributes.

    Version, vendor and identification information is listed in the table of attributes.

  2. Click the Back to Agent View link or use your browser's “Previous page” function to return to the MBean list in the agent view.

MBean View

The MBean view has two functions: it presents the management interface of the MBean and it enables you to interact with its instance. The management interface of an MBean is given through the name of the attributes, the operation signatures, and a self-description. You can interact with the MBean by reloading its attribute values, setting new values, or invoking an operation.

To Display the MBean View
  1. Display the agent view.

    This is the first page displayed by the HTML adaptor.

  2. In the agent view, click on the object name of the HTML adaptor MBean: name=HTMLAdaptorServer in the default domain.

    This will display its MBean view.

Header and Description

As shown in Figure 6–2, the top part of the page contains the description of the MBean and some controls for managing it:

Figure 6–2 Description in the MBean View

Screen shot showing description of the MBean

The first two lines give the object instance (object name and class name) for this MBean. The MBean name is the full object name of this MBean instance, including the domain. The key-property pairs might or might not identify the MBean to a human reader, depending on the agent developer's intention. The MBean Java class is the full class name for the Java object of which this MBean is an instance.

The reload controls include a text field for entering a reload period and a manual Reload button. Initially, the reload period is set to zero indicating that the contents of the MBean view are not automatically refreshed. Clicking the Reload button forces the page to reload, thereby updating all of the attribute values displayed. If you have entered a reload period, clicking the button will begin automatic reloading with the given period. The reload period must be at least five seconds.


Note –

Use the Reload button of the MBean view instead of the browser's reload page button. After some operations, such as applying changes to attribute values, the browser's button will repost the form data, inadvertently performing the same operation again. To avoid undesirable side effects, always use the Reload button provided in the MBean view.


To Set the Reload Period
  1. Type a reload period of 5 and click the Reload button.

    Every five seconds the page will blink as it reloads.

  2. In another browser window, open another connection to the HTML adaptor at http://localhost:8082/.

  3. Observe the new values for the ActiveClientCount and LastConnectedClient attributes in the original window.

    You might have to try several connections before you see the attribute values change.

The reload period is reset to zero every time you open an MBean view.

The Unregister button is a shortcut for removing this MBean from the agent. Unregistering is covered in Instantiating and Managing MBeans.

The MBean description text provides some information about the MBean. Because standard MBeans are statically defined, they cannot describe themselves and the MBean server provides a generic text. Dynamic MBeans are required to provide their own description string at runtime according to the JMX specification. Except for the class name, this is the only way to tell standard and dynamic MBeans apart in the MBean view.

Table of Attributes

The second part of the MBean view is a table containing all attributes exposed by the MBean. For each attribute, this table lists its name, its Java type, its read-write access, and a string representation of its current value.

While MBean attributes can be of any type, not all types can be displayed in the MBean view. The HTML adaptor is limited to basic data types that can be displayed and entered as strings. Read-only attributes whose type supports the toString method are also displayed. Enumerated types that are concrete subclasses of com.sun.jdmk.Enumerated are displayed as a menu with a pop-up selection list. Boolean attributes are represented as true-false radio buttons. Finally, attributes with array types are represented by a link to a page that displays the array values in a table. If the attribute is writable, you can enter values for the array elements to set them.

For the complete list of supported types, see the Javadoc API of the HtmlAdaptorServer class. If an attribute type is not supported, this is indicated in place of its value. If there was an error when reading an attribute's value, the table shows the name of the exception that was raised and the message it contains.

The name of each attribute is a link that displays a window containing the description for this attribute. Like the MBean description, attribute descriptions can only be provided by dynamic MBeans. The MBean server inserts a generic description for standard MBean attributes. Figure 6–3 shows the attributes of the HTML adaptor with a description of the Active attribute:

Figure 6–3 MBean Attributes With a Description Window

Screen shot showing the attributes of the HTML adaptor and a description window for the Active attribute

To View an Attribute Description
  1. Click on an attribute name in the table of attributes to read its description.

    Because the HTML adaptor is implemented as a dynamic MBean, its attribute descriptions are meaningful.


    Note –

    Due to the use of JavaScript commands in the generated HTML, these pop-up windows might not be available on browsers that are not JavaScript-enabled.


Writable attributes have a text field for entering new values. To set the value of a writable attribute, type or replace its current value in the text field and click the Apply button at the bottom of the attributes table.


Note –

Do not try to modify the attributes of the HTML protocol adaptor here. See Instantiating and Managing MBeans.


Because there is only one Apply button for all the attributes, this systematically invokes the setter for all writable attributes, whether or not their fields have actually been modified. This might affect the MBean if setters have side effects, such as counting the number of modifications, as in the SimpleStandard and SimpleDynamic examples given in Part I

The HTML adaptor detects attributes of the ObjectName type and provides a link to the view of the corresponding MBean. This link is labeled view and is located just under the displayed value of the object name. Because MBeans often need to reference other MBeans, this provides a quick way of navigating through MBean hierarchies.

List of Operations

The last part of the MBean view contains all the operations exposed by the MBean. Each operation in the list is presented like a method signature. There is a return type, then a button with the operation name, and if applicable, a list of parameters, each with their type as well.

As with the table of attributes, the list of operations contains only those involving types that can be represented as strings. The return type must support the toString method and the type of each parameter must be one of basic data types supported by the HTML adaptor. For the complete list, see the Javadoc API of the HtmlAdaptorServer class.

Above each operation name is a link to its description. Parameter names are also active links that display a window with a description. Again, descriptions are only meaningful when provided by dynamic MBeans. The following figure shows some of the operations exposed by the HTML adaptor MBean and a description of the Start operation.

Figure 6–4 MBean Operations With a Description Window (Partial View)

Screen shot giving a partial view of MBean operations, with a description window

We will not perform any operations on this MBean until after a brief explanation in Instantiating and Managing MBeans.

To perform an operation, fill in any and all parameter values in the corresponding text fields and click the operation's button. The HTML adaptor displays a page with the result of the operation, either the return value if successful or the reason why the operation was unsuccessful.

Agent Administration

The agent administration page contains a form for entering MBean information when creating or unregistering MBeans. You can also instantiate an MBean through one of its public constructors. In order to instantiate an MBean, its class must be available in the agent application's classpath. Optionally, you can specify a different class loader if the agent contains other class loader MBeans.

To Display the Agent Administration Page
  1. Go back to the agent view by clicking the link near the top of the MBean view page.

  2. Click on the Admin button in the agent view to display the agent administration page in your browser window.

The first two fields, Domain and Keys are mandatory for all administrative actions. The Domain field initially contains the string representing the agent's default domain. Together, these two fields define the object name, whether for a new MBean to be created or for an existing MBean to unregister. The Java class is the full class name of the object to be instantiated as a new MBean. This field is ignored when unregistering an MBean.

Using the pull-down menu, you can choose one of three actions:

When you click the Send Request button, the HTML adaptor processes the action and updates the bottom of the page with the action results. You might have to scroll down to see the result. The text fields are not cleared after a request, enabling you to perform multiple operations. Clicking the Reset button returns the fields to their last posted value after you have modified them.

Instantiating and Managing MBeans

Sometimes, starting an MBean requires several steps, especially for agent services that require some sort of configuration. For example, you can instantiate another HTML adaptor for connecting to a different port. Usually, this is done programmatically in the agent application, but we must do it through the browser for the minimal agent.

To Create a New HTML Adaptor MBean
  1. On the agent administration page, fill in the fields as follows:

    Domain:Communications
    Keys:protocol=html,port=8088
    Java Class:com.sun.jdmk.comm.HtmlAdaptorServer
    Class Loader:leave blank

    In versions of the Java Dynamic Management Kit (DMK) prior to version 4.0, specifying the port number in the object name would initialize communication MBeans. Now, the names and contents of key properties no longer have any significance for any components of the product. We must set the port in other ways.

  2. Make sure the action selected is Create and send the request. If you scroll down the page, you will see whether your request was successful.

    We cannot connect to this HTML adaptor quite yet. First we need to configure it .

  3. Go to the new HTML adaptor's MBean view with the provided link.

    Remember that we could not modify any of the adaptor's attributes in To View an Attribute Description because the implementation does not allow them to be modified while the HTML adaptor is online. Our new HTML adaptor is instantiated in the stopped state (the StateString attribute indicates OFFLINE), so we can change its attributes.

  4. Set the Port attribute to 8088 and MaxActiveClientCount to 2, then click the Apply button.

    If the page is reloaded and the new values are displayed, the attribute write operation was successful. You can also click the attribute names to get a description them.

  5. Scroll down the MBean view to the Start operation and click the Start button.

    A new page is displayed to tell us the operation was successful. If you go back to the MBean view with the provided link, you can see that the StateString is now indicating ONLINE.

  6. Now you can access your minimal agent through a browser on port 8088. Try going to a different host on the same network and connecting to the following URL:

    In the above URL, agentHostName is the name or IP address of the host where you launched the MinimalAgent. If you reload the MBean view of the new HTML adaptor on either browser, the name of this other host should be the new value of the LastConnectedClient attribute.

Through this new connection, you can stop, modify or remove the HTML adaptor MBean using port 8082. In that case, your original browser will also have to use http://localhost:8088/ to connect. Instead, we will manage the minimal agent from the second host.

To Instantiate MBeans With Constructors
  1. From the browser on the second host, go to the agent administration page. Fill in the fields as follows and request the constructors:

    Domain:Standard_MBeans
    Keys:name=SimpleStandard,number=1
    Java Class:SimpleStandard
    Class Loader:leave blank

    The list of constructors for the SimpleStandard class is displayed at the bottom of the page. The MBean name is also shown. This is the object name that will be assigned to the MBean when using one of the listed constructors. As you can see, the SimpleStandard class only has one constructor that takes no parameters.

  2. Click on the Create button. The result is appended to the bottom of the page. Scroll down and go to the MBean view with the provided link.

    Because it is a standard MBean, all of its description strings are generic. This shows the necessity of programming meaningful attribute names.

  3. In the agent view on the first browser window, click in the filter field and press Return to refresh the agent view.

  4. Click the new MBean's name and set its reload period to 15.

  5. On the second host, type in a different string for the State attribute and click Apply.

    On the first host, you should see the MBean's attributes are updated when the MBean view is periodically reloaded.

  6. On the second host, click the Reset operation button at the bottom of the MBean view page.

    The operation result page is displayed and indicates the success of the operation.

    This page also provides the return value of the operation when it is not void. If you go back to the MBean view, you will see the result of the operation on the attributes. You should also see it on the first host after it reloads.

The browser on the second host is no longer needed and we can remove the HTML adaptor on port 8088.

To Unregister an MBean
  1. Go to the administration page and fill in the object name of the HTML adaptor you want to remove (you do not need its Java class to unregister it):

    Domain:Communications
    Keys:protocol=html,port=8088
    Java Class:leave blank
    Class Loader:leave blank

  2. Choose Unregister from the pull-down menu and click the Send Request button.

    The result is displayed at the bottom of the page.

    You can also unregister an MBean directly from its MBean view. Click the Unregister button on the upper right side of the page.

Filtering MBeans

Because an agent can manage hundreds of MBeans, the agent view provides a filtering mechanism for the list that is displayed. An object name with wildcard characters is used as the filter, and only those MBeans that match are counted and displayed.

Filters restrict the set of MBeans listed in the agent view. This might not be particularly useful for our small agent, but it can help you find MBeans among hundreds in a complex agent. In addition, management applications use the same filter syntax when requesting an agent's MBeans through the programmatic interface of a connector. The filtering enables managers to either get lists of MBean names or find a particular MBean instance.

Filters are typed as partial object names with wildcard characters or as a full object name for which to search, using the syntax domain:key. Here are the basic substitution rules for filtering:

  1. You can search for partial domain names, using the following characters:

    Asterisk (*)

    Replaces any number (including zero) of characters

    Question mark (?)

    Replaces any one character

  2. An empty domain name is replaced by the default domain string. An empty key list is illegal.

  3. Keys are atomic. You must search for the full property=value key. You cannot search for a partial property name or an incomplete value.

  4. An asterisk (*) can be used to terminate the key list, where it replaces any number of any keys (complete property-value pairs).

  5. You must match all keys exactly. Use the form property=value,* to search for one key in names with multiple keys.

  6. Keys are unordered when filtering. Giving one or more keys (and an asterisk) in any order finds all object names that contain that subset of keys.

To Filter MBeans
  1. Go to the administration page and create three more standard MBeans. Modify only the number value in their object names so that they are numbered sequentially.

  2. In the same way as Step 1, create four dynamic MBeans starting with:

    Domain:Dynamic_MBeans
    Keys:name=SimpleDynamic,number=1
    Java Class:SimpleDynamic
    Class Loader:leave blank

  3. Go back to the agent view, that should display all the new MBeans.

  4. Type each of the filter strings given in Table 6–1 to see the resulting MBean list

    Table 6–1 Examples of Filter Strings

    Filter String (domain:key)

    Result 

    Standard_MBeans:*

    Gives all of the standard MBeans we created 

    *_MBeans:*

    Gives all of the standard and dynamic MBeans we created 

    DefaultDomain:

    Not allowed by rule 2 

    :*

    Lists all MBeans in the default domain 

    *:name=Simple*,*

    Not allowed by rule 3 

    *:name=SimpleStandard

    Allowed, but list is empty (rule 5) 

    *:name=*

    Not allowed by rule 3 

    *_??????:number=2,*

    Gives the second standard and dynamic MBean we created 

    Communications:port=8088,protocol=html

    Gives the one MBean matching the domain and both (unordered) keys  

    empty string

    Allowed: special case equivalent to *:*

    Notice how the MBean count is updated with each filter. This count shows the number of MBeans that were found with the current filter, which is the number of MBeans appearing on the page. It is not the total number of MBeans in the agent, unless the filter is *:*.

  5. When you are ready to stop the minimal agent example, go to the window where you started its class and press Control-C.

Chapter 7 MBean Interceptors

An MBean server forwards requests it receives to its default interceptor. A new feature of the Java Dynamic Management Kit (Java DMK) 5.0 enables you to modify this behavior of the MBean server and replace the default interceptor by another object implementing the same interface.

The example provided demonstrates how you can use the concept of MBean interceptors to forward requests to a specific interceptor, and to support “virtual” MBeans. The code samples in this chapter are from the files in the MBeanInterceptor example directory located in the main examplesDir (see “Directories and Classpath” in the Preface).

This chapter covers the following topics:

Overview of MBean Interceptors

The concept of interceptors exploits the proxy design pattern to enable you to modify the behaviour of the MBean server. By default, the MBean server appears from the outside like a hollow shell that simply forwards every operation to the default interceptor. You can replace this default interceptor by another object implementing the same interface, to change the semantics of the MBean server. In most cases, you would use this other object to forward most or all operations to the default interceptor after doing some processing. However, you can also use it to forward some operations to other handlers instead, for instance depending on the object names involved. Figure 7–1 shows schematically how you can insert an interceptor between the MBean server and the default interceptor.

Figure 7–1 Inserting a User Interceptor Between the MBean Server and the Default Interceptor

Diagram showing that a user interceptor is inserted between the MBean server and the default interceptor

Some examples of the uses of interceptors are as follows:

Interceptors can be composed. When an interceptor is added, it is usually inserted between the MBean server shell and the current interceptor. Initially, the current interceptor is the default one. But if another interceptor has already been inserted, this other interceptor is the current one. Hence, a request could pass through several interceptors on its way to the default interceptor, for example a security checker and a logger.

Specifying the Behavior of an MBean Interceptor

The behavior to be implemented by an interceptor is specified by means of the MBeanInterceptor interface. An MBean interceptor has essentially the same interface as an MBean server. An MBean server forwards received requests to its default interceptor, which might handle them itself or forward them to other interceptors.

Changing the Default Interceptor

The default interceptor can be changed by using the setDefaultMBeanInterceptor method of the MBeanServerInt interface. The MBeanServerInt interface provides methods for getting and setting the DefaultMBeanInterceptor used for request treatment.


Note –

Particular care must be taken when replacing the default MBean interceptor with a user interceptor. The MBean server implemented in the Java DMK 5.0 passes requests to its default interceptor without checking the result returned, or the exceptions thrown by the interceptor.

Consequently, user interceptors, which implement most of the methods defined in the MBeanServer interface, must behave as specified for the corresponding MBeanServer methods in the JMX 1.1 specification. In particular, a method in an MBean interceptor must not throw any exceptions apart from the following:

If an MBean interceptor does not respect this condition, and, for example, throws a NullPointerException exception, this might have unexpected effects on calling code, which might not be protected against such behavior.


Running the MBean Interceptor Example

The MBean interceptor example in the examples directory shows you two of the main functions of MBean interceptors, forwarding requests to a specific MBean interceptor, and creating virtual MBeans.

The examplesDir/MBeanInterceptor directory contains the following source files:

To Run the MBean Interceptor Example
  1. Compile all files in the examplesDir/MBeanInterceptor directory with the javac command.

    For example, on the Solaris platform, type:


    $ cd examplesDir/MBeanInterceptor/
    $ javac -classpath classpath *.java
    
  2. Run the example using the classes you have just built, by typing the following command in a terminal window:


    $ java -classpath classpath Agent
    
  3. Interact with the agent through the standard input and output in the window where it was started.

  4. Load the agent's URL in your web browser:


    http://localhost:8082/

    You only see the MBeans registered in the DefaultMBeanInterceptor, namely the connector and adaptor MBeans, and the MBeanServerDelegate.

  5. Press Enter to insert the FileMBeanInterceptor and view the files from the local directory as virtual MBeans.

  6. Reload the agent's URL in your web browser to view the new MBeans:


    http://localhost:8082/
  7. Press Enter to stop the agent.

Chapter 8 Base Agent

The base agent demonstrates the programming details of writing an agent application. We will cover how to access the MBean server, use it to create MBeans, and then interact with those MBeans. Everything that we could do through the HTML adaptor can also be done through the code of your agent application.

Interacting programmatically with the MBean server gives you more flexibility in your agent application. This chapter covers the three main ways to create MBeans through the MBean server, how to interact with MBeans, and how to unregister them.

The base agent is functionally equivalent to the minimal agent, but instead of writing the smallest agent, we will demonstrate good defensive programming with error checking. We will create the same three connectivity MBeans and do some of the same management operations that we did through the browser interface.

The program listings in this tutorial show only functional code: comments and output statements have been modified or removed to conserve space. However, all management functionality has been retained for the various demonstrations. The complete source code is available in the BaseAgent and StandardMBean example directories located in the main examplesDir (see Directories and Classpath in the Preface).

This chapter covers the following topics:

Agent Application

The base agent is a standalone application with a main method, but it also has a constructor so that it can be instantiated dynamically by another class.


Example 8–1 Constructor for the Base Agent

public BaseAgent() {
    // Enable the TRACE level according to the level set 
	  // in system properties
    try {
        TraceManager.parseTraceProperties();
    }
    catch (IOException e) {
        e.printStackTrace();
        System.exit(1);
    }

    echo("\n\tInstantiating the MBean server of this agent...");
    myMBeanServer = MBeanServerFactory.createMBeanServer();

    // Retrieves ID of the MBean server from the delegate
    try {
        System.out.println("ID = "+ myMBeanServer.getAttribute(
            new ObjectName(ServiceName.DELEGATE), "MBeanServerId"));
    } catch (Exception e) {
        e.printStackTrace();
        System.exit(1);
    }
}

In the first statement of the constructor, we enable tracing messages for the agent. The tracing enables us to see internal information at runtime, which is useful for debugging. See Setting Trace Messages for specifying tracing parameters on the command line. Then we create the MBean server through its static factory class (see MBean Server Implementation and Factory). Its reference is stored in a variable with class-wide scope so that all internal methods have access to the MBean server. Finally, we retrieve the MBean server identification string, for informational purposes only.

After the MBean server is initialized, we will create the same three communication MBeans that we created in the minimal agent.

Creating an MBean (Method 1)

The methods of the MBean server enable you to create an MBean in three different ways. The base agent demonstrates all three ways, and we will discuss the advantages of each.

One way of creating an MBean consists of first instantiating its class and then registering this instance in the MBean server. Registration is the internal process of the MBean server that takes a manageable resource's MBean instance and exposes it for management.

Bold text in Example 8–2 and the following code samples highlights the important statements that vary between the three methods.


Example 8–2 Creating an MBean (Method 1)

// instantiate the HTML protocol adaptor object to use the default port
HtmlAdaptorServer htmlAdaptor = new HtmlAdaptorServer();

try {
    // We know that the HTML adaptor provides a default object name
    ObjectInstance htmlAdaptorInstance =
        myMBeanServer.registerMBean(htmlAdaptor, null);
    echo("CLASS NAME  = " + htmlAdaptorInstance.getClassName());
    echo("OBJECT NAME = " + 
    htmlAdaptorInstance.getObjectName().toString());
} catch(Exception e) {
    e.printStackTrace();
    System.exit(0);
}

// Now we need to start the html protocol adaptor explicitly
htmlAdaptor.start();
    
while (htmlAdaptor.getState() == CommunicatorServer.STARTING) {
    sleep(1000);
}
echo("STATE = " + htmlAdaptor.getStateString());
[...]

In this first case, we instantiate the HtmlAdaptorServer class and keep a reference to this object. We then pass it to the registerMBean method of the MBean server to make our instance manageable in the agent. During the registration, the instance can also obtain a reference to the MBean server, something it requires to function as a protocol adaptor.

In the minimal agent, we saw that the HTML adaptor gives itself a default name in the default domain. Its Javadoc API confirms this, so we can safely let it provide a default name. We print the object name in the object instance returned by the registration to confirm that the default was used.

Once the MBean is registered, we can perform management operations on it. Because we kept a reference to the instance, we do not need to go through the MBean server to manage this MBean. This enables us to call the start and getStateString methods directly. The fact that these methods are publicly exposed is particular to the implementation. The HTML adaptor is a dynamic MBean, so without any prior knowledge of the class, we would have to go through its DynamicMBean interface.

In a standard MBean you would call the implementation of its management interface directly. Because the HTML adaptor is a dynamic MBean, the start method is just a shortcut for the start operation. For example, we could start the adaptor and get its StateString attribute with the following calls:

htmlAdaptor.invoke("start", new Object[0], new String[0]);
echo("STATE = " + (String)htmlAdaptor.getAttribute("StateString"));

This type of shortcut is not specified by the Java Management extensions, nor is its functionality necessarily identical to that of the start operation exposed for management. In the case of the HTML adaptor, its Javadoc API confirms that it is identical, and in other cases, it is up to the MBean programmer to guarantee this functionality if it is offered.

Creating an MBean (Method 2)

The second way to create an MBean is the single createMBean method of the MBean server. In this case, the MBean server instantiates the class and registers it all at once. As a result, the caller never has a direct reference to the new object.


Example 8–3 Creating an MBean (Method 2)

ObjectInstance httpConnectorInstance = null;
try {
    String httpConnectorClassName = "com.sun.jdmk.comm.HttpConnectorServer";
    // Let the HTTP connector server provide its default name
    httpConnectorInstance =
        myMBeanServer.createMBean(httpConnectorClassName, null);
} catch(Exception e) {
    e.printStackTrace();
    System.exit(0);
}
// We need the object name to refer to our MBean
ObjectName httpConnectorName = httpConnectorInstance.getObjectName();
echo("CLASS NAME  = " + httpConnectorInstance.getClassName());
echo("OBJECT NAME = " + httpConnectorName.toString());

// Now we demonstrate the bulk getter of the MBean server
try {
    String att1 = "Protocol";
    String att2 = "Port";
    String attNames[]= {att1, att2};
    AttributeList attList =
        myMBeanServer.getAttributes(httpConnectorName, attNames);
    Iterator attValues = attList.iterator();
    echo("\t" + att1 + "\t=" + ((Attribute) attValues.next()).getValue());
    echo("\t" + att2 + "\t=" + ((Attribute) attValues.next()).getValue());

} catch (Exception e) {
    e.printStackTrace();
    System.exit(0);
}

// Now we explicitly start the HTTP connector server
try {
    myMBeanServer.invoke(httpConnectorName, "start",
                         new Object[0], new String[0]);

    // waiting to leave starting state...
    while (new Integer(CommunicatorServer.STARTING).equals
           (myMBeanServer.getAttribute(httpConnectorName,"State"))) {
        sleep(1000);
    }
    echo("STATE = " + 
         myMBeanServer.getAttribute(httpConnectorName, "StateString"));
} catch (Exception e) {
    e.printStackTrace();
    System.exit(0);
}
[...]

One advantage of this method for creating MBeans is that the instantiation and registration are done in one call. In addition, if we have registered any class loaders in the MBean server, they will automatically be used if the class is not available locally. See Chapter 14, M-Let Class Loader for more information on class loading.

The major difference is that we no longer have a reference to our MBean instance. The object instance that was only used for display purposes in the previous example now gives us the only reference we have on the MBean: its object name.

One disadvantage of this method is that all management of the new MBean must now be done through the MBean server. For the attributes of the MBean, we need to call the generic getter and setter of the MBean server, and for the operations we need to call the invoke method. When the agent needs to access the MBean, having to go through the MBean server adds some complexity to the code. However, it does not rely on any shortcuts provided by the MBean, making the code more portable and reusable.

The createMBean method is ideal for quickly starting new MBeans that the agent application does not need to manipulate. In just one call, the new objects are instantiated and exposed for management.

Creating an MBean (Method 3)

The last way of creating an MBean relies on the instantiate method of the MBean server. In addition, we use a nondefault constructor to instantiate the class with a different behavior.


Example 8–4 Creating an MBean (Method 3)

CommunicatorServer rmiConnector = null;
Object[] params = {new Integer(8086)};
String[] signature = {new String("int")};
try {
    String RmiConnectorClassName = "com.sun.jdmk.comm.RmiConnectorServer";
    // specify the RMI port number to use as a parameter to the constructor
    rmiConnector = (CommunicatorServer)myMBeanServer.instantiate(
                       RmiConnectorClassName, params, signature);
} catch(Exception e) {
    e.printStackTrace();
    System.exit(0);
}

try {
    // Let the RMI connector server provides its default name
    ObjectInstance rmiConnectorInstance =
        myMBeanServer.registerMBean(rmiConnector, null);
    
    // Confirm the class and default object name
    echo("CLASS NAME  = " + rmiConnectorInstance.getClassName());
    echo("OBJECT NAME = 
	   " + rmiConnectorInstance.getObjectName().toString());
} catch(Exception e) {
    e.printStackTrace();
    System.exit(0);
}

// Now we explicitly start the RMI connector server
rmiConnector.start();

// waiting to leave starting state...
while (rmiConnector.getState() == CommunicatorServer.STARTING) {
    sleep(1000);
}
echo("STATE = " + rmiConnector.getStateString());

// Check that the RMI connector server is started
if (rmiConnector.getState() != CommunicatorServer.ONLINE) {
    echo("Cannot start the RMI connector server");
    System.exit(0);
}
[...]

As in Example 8–2, we instantiate and register the MBean in separate steps. First, we instantiate the class using the instantiate method of the MBean server. This method lets you specify the constructor you want use when instantiating. Note that we could also have specified a constructor to the createMBean method in the previous example.

To specify a constructor, you must give an array of objects for the parameters and an array of strings that defines the signature. If these arrays are empty or null, the MBean server will try to use the default no-parameter constructor. If the class does not have a public no-parameter constructor, you must specify the parameters and signature of a valid public constructor.

In our case, we specify an integer parameter to set the port through one of the constructors of the RmiConnectorServer class. Then, we register the MBean with the registerMBean method of the MBean server, as in Example 8–2.

One advantage of this creation method is that the instantiate method of the MBean server also supports class loaders. If any are registered in the MBean server, they will automatically be used if the class is not available locally. See Chapter 14, M-Let Class Loader for more information on class loading.

Because we do not take advantage of the class loaders here, we could have just called the class's constructor directly. The main advantage is that, like the first method of MBean creation, we retain a direct reference to the new object. The direct reference again enables us to use the MBean's shortcut methods explicitly.

There are other combinations of instantiating and registering MBeans for achieving the same result. For example, we could use the default constructor and then set the port attribute of the MBean before starting it. Other combinations are left as an exercise to the reader.

Managing MBeans

In Creating an MBean (Method 2), we rely totally on the MBean server to create and access an MBean. The code example in that section demonstrates how to get attributes and invoke operations through the MBean server. Here we will concentrate on the usage of MBean metadata classes when accessing MBeans that represent resources.

We will rely on the StandardAgent and DynamicAgent classes presented in Part I. As mentioned in Comparison With the SimpleStandard Example, the two are nearly identical. The same code works for any registered MBean, whether standard or dynamic. We examine the method for displaying MBean metadata that is common to both.


Example 8–5 Processing MBean Information

private MBeanServer server = null; // assigned by MBeanServerFactory

private void printMBeanInfo(ObjectName name) {

    echo("Getting the management information for  " + name.toString() );
    MBeanInfo info = null;

    try {
        info = server.getMBeanInfo(name);
    } catch (Exception e) {
        e.printStackTrace();
        return;
    }
    echo("\nCLASSNAME: \t"+ info.getClassName());
    echo("\nDESCRIPTION: \t"+ info.getDescription());

    echo("\nATTRIBUTES");
    MBeanAttributeInfo[] attrInfo = info.getAttributes();
    if (attrInfo.length>0) {
        for(int i=0; i<attrInfo.length; i++) {
            echo(" ** NAME: \t"+ attrInfo[i].getName());
            echo("    DESCR: \t"+ attrInfo[i].getDescription());
            echo("    TYPE: \t"+ attrInfo[i].getType() +
                 "\tREAD: "+ attrInfo[i].isReadable() +
                 "\tWRITE: "+ attrInfo[i].isWritable());
        }
    } else echo(" ** No attributes **");

    echo("\nCONSTRUCTORS");
    MBeanConstructorInfo[] constrInfo = info.getConstructors();
    // Note: the class necessarily has at least one constructor
    for(int i=0; i<constrInfo.length; i++) {
        echo(" ** NAME: \t"+ constrInfo[i].getName());
        echo("    DESCR: \t"+ constrInfo[i].getDescription());
        echo("    PARAM: \t"+ constrInfo[i].getSignature().length +
                 " parameter(s)");
    }

    echo("\nOPERATIONS");
    MBeanOperationInfo[] opInfo = info.getOperations();
    if (opInfo.length>0) {
        for(int i=0; i<constrInfo.length; i++) {
            echo(" ** NAME: \t"+ opInfo[i].getName());
            echo("    DESCR: \t"+ opInfo[i].getDescription());
            echo("    PARAM: \t"+ opInfo[i].getSignature().length +
                     " parameter(s)");
        }
    } else echo(" ** No operations ** ");

    echo("\nNOTIFICATIONS");
    MBeanNotificationInfo[] notifInfo = info.getNotifications();
    if (notifInfo.length>0) {
        for(int i=0; i<constrInfo.length; i++) {
            echo(" ** NAME: \t"+ notifInfo[i].getName());
            echo("    DESCR: \t"+ notifInfo[i].getDescription());
        }
    } else echo(" ** No notifications **");
}

The getMBeanInfo method of the MBean server gets the metadata of an MBean's management interface and hides the MBean's implementation. This method returns an MBeanInfo object that contains the MBean's description. We can then get the lists of attributes, operations, constructors, and notifications to display their descriptions. Recall that the dynamic MBean provides its own meaningful descriptions and that the standard MBean descriptions are default strings provided by the introspection mechanism of the MBean server.

Filtering MBeans

The base agent does very little filtering because it does very little management. Usually, filters are applied programmatically to get a list of MBeans to which some operations apply. There are no management operations in the MBean server that apply to a list of MBeans. You must loop through your list and apply the desired operation to each MBean.

Before exiting the agent application, we do a query of all MBeans so that we can unregister them properly. MBeans should be unregistered before being destroyed because they might need to perform some actions before or after being unregistered. See the Javadoc API of the MBeanRegistration interface for more information.


Example 8–6 Unregistering MBeans

public void removeAllMBeans() {

    try {
        Set allMBeans = myMBeanServer.queryNames(null, null);
        for (Iterator i = allMBeans.iterator(); i.hasNext();) {
            ObjectName name = (ObjectName) i.next();

            // Do not unregister the MBean server delegate
            if ( ! name.toString().equals( ServiceName.DELEGATE ) ) {
                echo("Unregistering " + name.toString() );
                myMBeanServer.unregisterMBean(name);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
        System.exit(0);
    }
}

We use the queryNames method because we only need the object names to operate on MBeans. The null object name as a filter gives us all MBeans in the MBean server. We then iterate through the resulting set and unregister each one, except for the MBean server delegate. As described in MBean Server Delegate, the delegate is also an MBean and so it will be returned by the query. However, if we unregister it, the MBean server can no longer function and cannot remove the rest of our MBeans.

We recognized the delegate by its standard name which is given by the static field ServiceName.DELEGATE. The ServiceName class provides standard names and other default properties for communications and service MBeans. It also provides the version strings that are exposed by the delegate MBean. Note that, because the delegate is the only MBean created directly by the MBean server, it is the only one whose name cannot be overridden during its registration. The delegate object name is always the same, so we are always sure to detect it.

Running the Base Agent Example

The examplesDir/BaseAgent/ directory contains the source file of the BaseAgent application.

To Run the Base Agent Example
  1. Compile the BaseAgent.java file in this directory with the javac command.

    For example, on the Solaris platform, type:


    $ cd examplesDir/BaseAgent/
    $ javac -classpath classpath *.java
    

    Again, we do not need the MBean classes at compile time, but they will be needed at runtime, because we do not use a dynamic class loader. You will need to have compiled the standard and dynamic MBean classes as described in Running the Standard MBean Example and Running the Dynamic MBean Example. If you want to load any other class in the base agent, you must include its directory or JAR file in the classpath.

  2. To run the example, update your classpath to find the MBeans and start the agent class:


    $ java -classpath classpath:../StandardMBean:../DynamicMBean BaseAgent
    

Setting Trace Messages

Because the base agent enables internal tracing (see Example 8–1), you can also set the trace level and trace output on the command line. The tracing mechanism is explained in the Java Dynamic Management Kit 5.0 Tools Reference and in the Javadoc API of the com.sun.jdmk.TraceManager class (for receiving traces) and the com.sun.jdmk.trace.Trace class (for producing traces). The simplest way to get the default tracing is to specify the filename for a trace log on the java command line:


$ java -classpath classpath -DTRACE_OUTPUT=filename BaseAgent

Agent Output

In addition to any trace information, this agent displays output for the three types of MBean creation.

When the connection MBeans have been created, it is possible to connect to the agent through one of the protocols. Management applications can connect through the HTTP and RMI connector servers, as described in Connector Servers. If you connect to the base agent through the HTML adaptor, you could follow the same procedures as with the minimal agent.

When you are done, press Enter to remove all MBeans from the agent and exit the agent application.

Chapter 9 Notification Mechanism

This chapter presents the mechanisms for sending and receiving notifications by demonstrating them locally on the agent-side. MBeans for either resources or services are the source of notifications, called broadcasters. Other MBeans or objects that want to receive the notifications register with one or more broadcasters, and they are called listeners.

Notification mechanisms are demonstrated through two sample broadcasters. The MBean server delegate that notifies listeners of MBean creation and unregistration, and an MBean that sends attribute change notifications.

The code samples are from the files in the Notification2 example directory located in the main examplesDir (see Directories and Classpath in the Preface).

This chapter covers the following topics:

Overview

The ability for resources and other entities to signal an event to their managing applications is a key functionality of management architectures. As defined in the Java Management Extensions (JMX) specification, notifications in the Java Dynamic Management Kit (DMK) provide a generic event mechanism whereby a listener can receive all events sent by a broadcaster.

All notifications in the Java DMK rely on the Notification class that itself inherits from Java event classes. A string called the notification type inside a Notification object gives the nature of the event, and other fields provide additional information to the recipient. This ensures that all MBeans, the MBean server, and remote applications can send and receive Notification objects and its subclasses, regardless of their inner type.

You can define new notification objects only by subclassing the Notification class. This ensures that the custom notifications will be compatible with the notification mechanism. New notification classes can be used to convey additional information to custom listeners, and generic listeners will still be able to access the standard Notification fields. However, because there are already fields provided for user data, subclassing is discouraged in the JMX architecture so that notification objects remain as universal as possible.

Listeners usually interact with notification broadcasters indirectly through the MBean server. The interface of the MBean server enables you to associate a listener with any broadcaster MBean, thereby giving you dynamic access to any of the broadcasters that are registered. In addition, the MBean metadata provided through the MBean server contains the list of notification types that the MBean broadcasts.

Figure 9–1 summarizes how listeners register with broadcasters and then receive notifications, in an agent application.

Figure 9–1 Listener Registration and Notification Propagation

Diagram showing how listeners register with broadcasters and receive notifications

MBean Server Delegate Notifications

The MBean server delegate object is an MBean that is automatically created and registered when an MBean server is started. It preserves the management model by serving as the management interface for the MBean server. The delegate exposes read-only information such as the vendor and version number of the MBean server. More importantly for this chapter, it sends the notifications that relate to events in the MBean server: all MBean registrations and unregistrations generate a notification.

NotificationBroadcaster Interface

A class must implement the NotificationBroadcaster interface to be recognized as a source of notifications in the JMX architecture. This interface provides the methods for adding or removing a notification listener to or from the broadcaster. When the broadcaster sends a notification, it must send it to all listeners that are currently registered through this interface.

This interface also specifies a method that returns information about all notifications that might be sent by the broadcaster. This method returns an array of MBeanNotificationInfo objects, each of which provides a name, a description string, and the type string of the notification.

As detailed in the Javadoc API, the MBeanServerDelegate class implements the NotificationBroadcaster interface. We know from the JMX specification that it sends notifications of the following types:


Note –

Although broadcaster objects are almost always MBeans, they should not expose the methods of the NotificationBroadcaster interface. That is, the MBean interface of a standard MBean should never extend the NotificationBroadcaster interface. As we shall see in Chapter 12, Notification Forwarding, the remoteMBeanServer interface of connector clients provides the methods needed to register for and receive notifications remotely.


NotificationListener Interface

Listeners must implement the NotificationListener interface, and they are registered in the notification broadcasters to receive the notifications. The listener interface defines a handler method that receives all notifications of the broadcaster where the listener is registered. We say that a listener is registered when it has been added to the broadcaster's list of notification recipients. This is completely independent of any registration of either object in the MBean server.

Like the broadcaster, the listener is generic, meaning that it can handle any number of different notifications. Its algorithm usually involves determining the type of the notification and taking the appropriate action. A listener can even be registered with several broadcasters and handle all of the notifications that are sent.


Note –

The handler is a callback method that the broadcaster calls with the notification object it wants to send. As such, it runs in the broadcaster's thread and should therefore run rapidly and return promptly. The code of the handler should rely on other threads to execute long computations or blocking operations.


In our example, the listener is a trivial class that has a constructor and the handler method. Our handler simply prints out the nature of the notification and the name of the MBean to which it applied. Other listeners on the agent side might themselves be MBeans that process the event and update the state of their resource or the quality of their service in response. For example, the relation service must know when any MBeans participating in relations are unregistered. It does this by listening to MBean server delegate notifications.


Example 9–1 Listener for MBean Server Delegate Notifications

import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.MBeanServerNotification;

public class AgentListener implements NotificationListener {
    [...]

    // Implementation of the NotificationListener interface
    //
    public void handleNotification(Notification notification,
                                   Object handback) {

        // Process the different types of notifications fired by the
        // MBean server delegate.
        String type = notification.getType();

        System.out.println(
            "\n\t>> AgentListener handles received notification:" +
            "\n\t>> --------------------------------------------");
        try {
            if (type.equals(
                    MBeanServerNotification.REGISTRATION_NOTIFICATION)) {
                System.out.println("\t>> \"" +
                    ((MBeanServerNotification)notification).getMBeanName() +
                    "\" has been registered in the server");
            }
            else if (type.equals(
                    MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) {
                System.out.println("\t>> \"" +
                    ((MBeanServerNotification)notification).getMBeanName() +
                    "\" has been unregistered from the server\n");
            }
            else {
                System.out.println("\t>> Unknown event type (?)\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
}

In most cases, the notification object passed to the handler method is an instance of the Notification class. This class provides the notification type as well as a time-stamp, a sequence number, a message string, and user data of any type. All of these are provided by the broadcaster to pass any needed information to its listeners. Because listeners are usually registered through the MBean server, they only know the broadcaster by its object name, given by the getSource method of the notification object.


Note –

The notification model does not assume that notifications will be received in the same order that they are sent. If notification order is critical to your application, your broadcaster should set the sequence numbers appropriately, and your listeners should sort the received notifications.


The MBean server delegate sends MBeanServerNotification objects that are subclasses of the Notification class. This subclass provides two constants to identify the notification types sent by the delegate and a method that gives the object name of the MBean that was registered or unregistered. Our notification handler uses these to print out the type of operation and the object name to which the operation applies.

Adding a Listener Through the MBean Server

Now that we have identified the objects involved, we need to add the listener to the notification broadcaster. Our example does this in the main method of the agent application:


Example 9–2 Registering for MBean Server Delegate Notifications

AgentListener agentListener = null;
[...]

echo("\nAdding the MBean server delegate listener...");
try {
    agentListener = new AgentListener();
    myAgent.myMBeanServer.addNotificationListener(
        new ObjectName(ServiceName.DELEGATE),
        agentListener, null, null);
} catch(Exception e) {
    e.printStackTrace();
    System.exit(0);
}
echo("done");

In Example 9–2, the agent application adds the AgentListener instance to the delegate MBean, which is known to be a broadcaster. The object name of the MBean server delegate is given by the DELEGATE constant in the ServiceName class. The listener is added through the addNotificationListener method of the MBean server. This method preserves the management architecture by adding listeners to MBeans while referring only the MBean object names.

If an MBean implements the listener interface and needs to receive certain notifications, it can add itself to a broadcaster. For example, an MBean could use its preregistration method in order to add itself as a notification listener or it could expose a method that takes the object name of the notification broadcaster MBean. In both cases, its notification handler method must be designed to process all expected notification types.

The last two parameters of the addNotificationListener methods of both the MBeanServer and the NotificationBroadcaster interfaces define a filter and a handback object, respectively. Filter objects are defined by the NotificationFilter interface and provide a callback method that the broadcaster calls before calling the notification handler. If the filter is defined by the entity that adds the listener, it prevents the handler from receiving unwanted notifications.

Handback objects are added to a broadcaster along with a listener and are returned to the designated handler with every notification. The handback object is completely untouched by the broadcaster and can be used to transmit context information from the entity that adds the listener to the handler method. The functionality of filters and handback objects is beyond the scope of this tutorial; refer to the JMX specification for their full description.

Attribute Change Notifications

In this second part of the notification example, we demonstrate attribute change notifications that can be sent by MBeans. An MBean designer can choose to send notifications whenever the value of an attribute changes or is changed. The designer can implement this mechanism in any manner, according to the level of consistency required by the management solution.

The JMX specification only provides a subclass of notifications to use to represent the attribute change events: the AttributeChangeNotification class.

NotificationBroadcasterSupport Class

The broadcaster in our example is a very simple MBean with only one attribute. The setter method for this attribute triggers a notification whenever the value actually changes. This policy is specific to our example. You might want to design an MBean that sends an attribute change every time the setter is called, regardless of whether or not the value is modified. Similarly, the fact that the reset operation changes the value of the attribute but does not send a notification is specific to our example. Your management needs might vary.

Example 9–3 shows the code for our SimpleStandard MBean class (the code for its MBean interface has been omitted):


Example 9–3 Broadcaster for Attribute Change Notifications

import javax.management.NotificationBroadcasterSupport;
import javax.management.MBeanNotificationInfo;
import javax.management.AttributeChangeNotification;

public class SimpleStandard
    extends NotificationBroadcasterSupport
    implements SimpleStandardMBean {

    /* "SimpleStandard" does not provide any specific constructors.
     * However, "SimpleStandard" is JMX compliant with regards to 
     * constructors because the default constructor SimpleStandard() 
     * provided by the Java compiler is public.
     */ 

    public String getState() {
        return state;
    }

    // The attribute setter chooses to send a notification only if
    // the value is modified
    public void setState(String s) {
        if (state.equals(s))
            return;
        AttributeChangeNotification acn = new AttributeChangeNotification(
            this, 0, 0, null, "state", "String", state, s);
        sendNotification(acn);
        state = s;
        nbChanges++;
    }

    [...]
    // The reset operation chooses not to send a notification even though
    // it changes the value of the state attribute
    public void reset() {
        state = "initial state";
        nbChanges = 0;
        nbResets++;
    }

    // Provide details about the notification type and class that is sent
    public MBeanNotificationInfo[] getNotificationInfo() {
        
        MBeanNotificationInfo[] ntfInfoArray = new MBeanNotificationInfo[1];
        
        String[] ntfTypes = new String[1];
        ntfTypes[0] = AttributeChangeNotification.ATTRIBUTE_CHANGE;
        
        ntfInfoArray[0] = new MBeanNotificationInfo( ntfTypes,
            "javax.management.AttributeChangeNotification", 
            "Attribute change notification for the 'State' attribute.");
        return ntfInfoArray;
    }

    private String      state = "initial state";
    private int         nbChanges = 0;
    private int         nbResets = 0;
}

This MBean sends its notifications and it implements the NotificationBroadcaster interface by extension of the NotificationBroadcasterSupport class.

This class implements the NotificationBroadcaster interface in order to provide all the mechanisms for adding and removing listeners and sending notifications. It manages an internal list of listeners and their handback objects and updates this list whenever listeners are added or removed. In addition, the NotificationBroadcasterSupport class provides the sendNotification method to send a notification to all listeners currently on its list.

By extending this object, our MBean inherits all of this behavior. Subclassing NotificationBroadcasterSupport is a quick and convenient way to implement notification broadcasters. We do not even have to call a superclass constructor because it has a default constructor. We only need to override the getNotificationInfo method to provide details about all of the notifications that might be sent.

Attribute Change Listener

Like our listener for MBean server notifications, our listener for attribute change notifications is a trivial class consisting of just the handler method.


Example 9–4 Listener for Attribute Change Notifications

import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.AttributeChangeNotification;

public class SimpleStandardListener implements NotificationListener {
    [...]

    // Implementation of the NotificationListener interface
    //
    public void handleNotification(Notification notification,
                                   Object handback) {

        // Process the different types of notifications fired by the
        // simple standard MBean.
        String type = notification.getType();

        System.out.println(
            "\n\t>> SimpleStandardListener received notification:" +
            "\n\t>> ---------------------------------------------");
        try {
            if (type.equals(AttributeChangeNotification.ATTRIBUTE_CHANGE)) {

  System.out.println("\t>> Attribute \"" +
      ((AttributeChangeNotification)notification).getAttributeName()
      + "\" has changed");
  System.out.println("\t>> Old value = " +
      ((AttributeChangeNotification)notification).getOldValue());
  System.out.println("\t>> New value = " +
      ((AttributeChangeNotification)notification).getNewValue());

            }                
            else {
                System.out.println("\t>> Unknown event type (?)\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
}

Again, we are handling a subclass of the Notification class, this one specific to attribute change notifications. The AttributeChangeNotification class provides methods for extracting the information about the attribute, notably its name, its type and its values before and after the modification. Our handler does nothing more than display these to the user. If this handler were part of an MBean in a larger management solution, it would probably to take some action, depending upon the change in value of the attribute.

As demonstrated by the broadcaster's code (see Example 9–3), the subclass can easily be instantiated and sent instead of a Notification object. Its constructor provides parameters for initializing all of the attribute-related values. In our example, we do not use significant values for the sequenceNumber and timeStamp parameters because our listener has no need for them. One great advantage of the Java DMK is that you only need to implement the level of functionality that you require for your management solution.


Adding a Listener Directly to an MBean

There is nothing that statically indicates that our MBean sends attribute change notifications. In our case it is a design decision, meaning that we know that the listener will receive attribute change notifications because we wrote the MBean that way. At runtime, the MBean server exposes the list of notifications in this MBean's metadata object, allowing a manager that is interested in attribute changes to register the appropriate listener.

Being confined to the agent, our example is much simpler. First we instantiate and register our simple MBean with the agent's MBean server. Then, because we have designed them to work together, we can add our listener for attribute changes to our MBean. Because we have kept a direct reference to the MBean instance, we can call its addNotificationListener method directly, without going through the MBean server.


Example 9–5 Registering for Attribute Change Notifications

SimpleStandard simpleStd = null;
ObjectName simpleStdObjectName = null;
SimpleStandardListener simpleStdListener = null;
[...]

try {
    simpleStdObjectName =
        new ObjectName("simple_mbean:class=SimpleStandard");
    simpleStd = new SimpleStandard();
    myAgent.myMBeanServer.registerMBean(simpleStd, simpleStdObjectName);
} catch(Exception e) {
    e.printStackTrace();
    System.exit(0);
}

echo("\nAdding the simple standard MBean listener...");
try {
    simpleStdListener = new SimpleStandardListener();
    simpleStd.addNotificationListener(simpleStdListener, null, null);
} catch(Exception e) {
    e.printStackTrace();
    System.exit(0);
}
echo("done");

There are two major implications to adding our listener directly to the MBean instance:

The rest of the agent object's code performs the setup of the agent's MBean server and various input and output for running the example. Similar agents are presented in detail earlier in Part II.

Running the Agent Notification Example

Now that we have seen all of our notification broadcaster objects and all of our listener handlers, we are ready to run the example.

The examplesDir/Notification2 directory contains all of the files for the simple MBean, the listener objects, and the agent. When started, the agent application adds the MBean server delegate listener first, so that a notification can be seen for the creation of the MBean. Attribute change notifications are triggered by modifying attributes through the HTML adaptor.

To Run the Agent Notification Example
  1. Compile all files in this directory with the javac command.

    For example, on the Solaris platform with the Korn shell, type:


    $ cd examplesDir/Notification2/
    $ javac -classpath classpath *.java
    
  2. To run the example, start the agent application:


    $ java -classpath classpath Agent
    
  3. After the agent application has started and added the MBean server delegate listener, press Enter to create the simple MBean.

    Before the next printout of the agent application, you should see the text generated by the AgentListener class. Its handler method has been called with an MBean creation notification, and it prints out the object name of our new MBean.

  4. Now that the simple MBean is registered and the SimpleStandardListener has been added as a listener, trigger attribute change notifications by modifying the State attribute through the HTML adaptor.

    1. Load the following URL in your browser:

      If you get an error, you might have to switch off proxies in your preference settings or substitute your host name for localhost. Any browser on your local network can also connect to this agent by using your host name in this URL.

    2. In the attribute table of our MBean view, type a new value for the State attribute and click the Apply button.

      Every time you do this, you should see the output of the attribute change listener in the terminal window where you launched the agent.

  5. When you have finished with the attribute change notifications, press Enter in the agent's terminal window to remove our simple MBean.

    Again, the output of the MBean server delegate listener is displayed. This time it has detected that our MBean has been unregistered from the MBean server.

  6. Press Enter again to stop the agent application.