Java Dynamic Management Kit 4.2 Tutorial

Chapter 9 MBean Proxies

In the previous topic, we saw how to access a remote agent and interact with its MBeans through connectors. The Java Dynamic Management Kit provides additional functionality which makes the remoteness of an agent and the communication layer even more transparent: proxy objects for registered MBeans.

A proxy is an object instance which represents an MBean, which mirrors the methods of the MBean, and whose methods are invoked directly by the caller. The proxy transmits requests to the MBean, through the MBean server, possibly through a connector, and returns any responses to the caller. Proxy objects can also register listeners for notifications that the MBean may emit.

The advantage of a proxy object is that it allows applications to have an instance of an object which represents an MBean, instead of accessing the MBean's management interface through methods of the MBean server or through a connector client. This can simplify both the conceptual design of a management system and the source code needed to implement that system.


Note -

While the concept of proxy objects remains unchanged from previous versions of the Java Dynamic Management Kit, the binding mechanisms have been modified in version 4.2.

The old proxy creation and binding methods were not thread-safe and are now deprecated. Callers are now responsible for instantiating and binding the proxy objects that they wish to use; see "The Proxy Interface" for details.

In addition, the new design allows proxies to be bound on both the agent and manager sides for a more symmetric usage.


The code samples in this topic are taken from the files in the SimpleClients example directory located in the main examplesDir (see "Directories and Classpath" in the preface).

Contents:

The Proxy Mechanism

Proxy objects simplify the interactions between an application and the MBeans it wants to manage. The purpose of a proxy is to invoke the methods that access the attributes and operations of an MBean, through its MBean server. These method calls can be rather tedious to construct at every invocation, so the proxy performs this task for the caller.

For example to invoke an operation on an MBean, an application must call the invoke method of the MBean server and provide the MBean's object name, the operation name string, an array of parameter objects, and a signature array. The proxy is a class which codes this whole sequence, meaning that the application can call the reset method directly on the proxy instance. And because the code of a proxy class is deterministic, it can be generated automatically using the proxygen tool.

Conceptually, a proxy instance makes the MBean server and a protocol connector completely transparent. Except for MBean registration and connector connection phases, all management requests on MBeans can be fully served through proxies, with identical results. However, all functionality of the Java Dynamic Management Kit is available without using proxies, so their usage is never mandatory.

By definition, a proxy object has the same interface as its MBean: the proxy can be manipulated as if it were the MBean instance, except that all requests are transmitted through the MBean server to the actual MBean instance for processing. A standard MBean proxy exposes getters, setters, and operation methods. It may also register listeners for all notifications broadcast by their corresponding MBean. A dynamic MBean proxy, also known as a generic proxy, exposes generic methods which are identical to those of the DynamicMBean interface.

In addition, standard proxies are commonly called proxy MBeans, because they are themselves MBeans. They are generated with an MBean interface, and can therefore be registered as standard MBeans in an MBean server. This feature allows one agent to expose resources whose MBeans are actually located in another agent. An equivalent functionality is covered in the topic of "Cascading Agents" in the lesson on "Agent Services".

Local and Remote Proxies

Proxies may also be bound to objects called handlers which necessarily implement the ProxyHandler interface. The methods of this interface are a subset of MBean server methods, as listed in Table 8-1 (see "The RemoteMBeanServer Interface"). These are the only methods that a proxy needs to in order to access its corresponding MBean and fulfill all management requests.

In the Java Dynamic Management Kit, the RemoteMBeanServer interface extends the ProxyHandler interface, meaning that proxy objects may be bound to any of the connector clients. These are called remote proxies, because they are instantiated in an application which is distant from the agent and its MBean instances.

As a new feature in version 4.2 of the product, the implementation of the MBeanServer interface also implements the ProxyHandler interface, so that proxy objects may be bound to the MBean server itself. These are called local proxies because they are located in the same application as their corresponding MBeans. Local proxies help preserve the management architecture by providing the simplicity of performing direct method calls on MBeans, while still routing all operations through the MBean server.

The symmetry of remote and local proxies complements the symmetry that allows management components to execute either in an agent or in a management application. Provided that all proxy classes are available, management components which use MBean proxies may be instantiated in an agent and rely on the MBean server or may be instantiated in a remote manager where they interact with a connector client. Except for communication delays, the results of all operations will be identical, and the same notifications will be received, whether obtained through a local proxy or a remote proxy (see "Adding a Listener Through the Connector").

The following diagram shows local proxies which are instantiated in an agent and bound to the MBean server, and the same classes instantiated as remote proxies in a management application and bound to the connector client. Management components located in either the agent or management application can interact with the local or remote proxies, respectively. Management components may also access the MBean server or the connector client directly, regardless of whether proxies are being used.

Figure 9-1 Interacting with Local and Remote Proxies

Graphic

This diagram shows all possible relations between management components, proxies and their MBeans. Standard proxies can only represent a specific standard MBean, and generic proxies may represent any standard or dynamic MBean. In a typical management scenario, the management components will be located in only one application, and for simplicity, they will rarely instantiate more than one type of proxy.

Throughout the rest of this topic, we do not distinguish between local and remote proxies. A proxy object, either standard or generic, is used in exactly the same way regardless of whether it is instantiated locally or remotely.

The Proxy Interface

In addition to the methods for accessing the attributes and operations of its MBean, all proxies implement the methods of the Proxy interface. This interface contains the methods for binding the proxy instance to the proxy handler which can fulfill its requests. The setServer method binds the proxy to the handler object. Setting the server to null effectively unbinds the proxy object. The result of the getServer method can indicate whether or not a proxy is bound and if so, it will return a reference to the handler object.

Because of the new design of the proxy mechanism, many methods in the Proxy interface are deprecated as of version 4.2 of the product. The functionality of the deprecated methods is preserved in the three non-deprecated methods, listed in the following table. See the Javadoc API of the Proxy interface for details.

Table 9-1 Non-Deprecated Methods of the Proxy Interface
ObjectInstancegetMBeanObjectInstance()
ProxyHandlergetServer()
voidsetServer(ProxyHandler server)

Standard proxies may also implement the NotificationBroadcasterProxy interface if their corresponding MBean is a notification proxy. This interface contains the same addNotificationListener and removeNotificationListener methods that the MBean implements from the NotificationBroadcaster interface.

Applications that use proxies therefore have two ways to detect notification broadcasters. The first way relies on the implementation of the NotificationBroadcasterProxy interface which can be detected in the proxy's class inheritance. The second and more standard way is to look at the notifications listed in the MBean's metadata obtained by the getMBeanInfo method either from the server or through the proxy.

Generic proxies do not implement the NotificationBroadcasterProxy interface, so callers must use the MBean metadata for detecting broadcasters. In addition, generic proxies cannot register notification listeners, callers must do this directly through the server.

Standard MBean Proxies

A standard MBean proxy class is specific to its corresponding MBean class. Furthermore, a proxy instance is always bound to the same MBean instance, as determined by the object name passed to the proxy's constructor. Binding the proxy to its ProxyHandler object can be done through a constructor or set dynamically through the methods of the Proxy interface.

The methods of a standard proxy have exactly the same signature as those of the corresponding standard MBean. Their only task is to construct the complete management request, which necessarily includes the object name, and to transmit it to the MBean server or connector client. They also return any result directly to the caller.

Because the contents of all proxy methods are determined by the management interface of the MBean, the proxy classes can be generated automatically.

Generating Proxies for Standard MBeans

The proxygen tool provided with the Java Dynamic Management Kit takes the class files of an MBean and its MBean interface, and generates the Java source code files of its corresponding proxy object and proxy MBean interface. You then need to compile the two proxy files with the javac command and include the resulting class files in your application's classpath.

The proxygen command is fully documented in the Java Dynamic Management Kit 4.2 Tools Reference guide, and in the Javadoc API for the ProxyGen class. Its command line options allow you to generate read-only proxies where all setter methods are suppressed and to define a package name for your proxies. For the purpose of the examples, we generate the default proxies without any of these options: see the section on "Running the Standard Proxy Example".

The following code sample shows part of the code generated for the SimpleStandard MBean used in the SimpleClients examples.


Example 9-1 Code Generated for the SimpleStandardProxy Class

  public java.lang.String getState()
    throws InstanceNotFoundException, AttributeNotFoundException,
    ReflectionException, MBeanException {

      return ((java.lang.String)server.getAttribute(
                  objectInstance.getObjectName(), "State"));
    }

  public  void setState(java.lang.String value)
    throws InstanceNotFoundException, ReflectionException,
    AttributeNotFoundException,InvalidAttributeValueException,
    MBeanException {

      server.setAttribute(objectInstance.getObjectName(),
                          new Attribute("State",value));
  }

  public void reset()
    throws InstanceNotFoundException, ReflectionException,
    MBeanException {

    Object result;
    result= server.invoke(objectInstance.getObjectName(), "reset",
                          null, null);
  }

You are free to modify the generated code if you wish to customize the behavior of your proxies. However, customization is not recommended if your MBean interfaces are still evolving, because all modifications will need to be redone every time the proxies are generated.

Using Standard MBean Proxies

Once the proxies are generated and available in your application's classpath, their usage is straightforward. For each of the proxy objects it wishes to use, the application needs to instantiate its proxy class and then bind it to a ProxyHandler object. The application is responsible for creating and binding all of the proxies that it wishes to use, and it must unbind and free them when they are no longer needed.


Note -

In previous versions of the Java Dynamic Management Kit, the connector client handled the creation of proxy instances and insured that only one proxy object could exist for each MBean. As of this version (4.2) of the product, connector clients no longer instantiate nor control proxy objects. The corresponding methods of the RemoteMBeanServer interface are now deprecated.

Similarly, the previous binding methods in the Proxy interface are deprecated in favor of the new setServer and getServer methods. This change is necessary so that proxies may be bound to any ProxyHandler object, to allow for both local and remote proxies.


All parameters for binding the proxy can be given in its constructor, which makes it very simple to instantiate and bind a proxy in one step.


Example 9-2 Instantiating and Binding a Proxy In One Step

String mbeanName = "SimpleStandard";

// build the MBean ObjectName instance
ObjectName mbeanObjectName = null;
String domain = connectorClient.getDefaultDomain();
mbeanObjectName = new ObjectName( domain + ":type=" + mbeanName );

// create the MBean in the MBeanServer of the agent
String mbeanClassName = mbeanName;
ObjectInstance mbeanObjectInstance =
    connectorClient.createMBean( mbeanClassName, mbeanObjectName );

// create and bind a proxy MBean on the client side
// that corresponds to the MBean just created in the agent
Proxy mbeanProxy = new SimpleStandardProxy(
                           mbeanObjectInstance, connectorClient );

echo("\tPROXY CLASS NAME  = " +
      mbeanProxy.getClass().getName() );
echo("\tMBEAN OBJECT NAME = " +
      mbeanProxy.getMBeanObjectInstance().getObjectName() );
echo("\tCONNECTOR CLIENT  = " +
      mbeanProxy.getServer().getClass().getName() );

If the class name of your proxy is not known at compile time, you will have to instantiate its class dynamically. In the following code, we obtain the proxy class name which corresponds to an MBean, and we call its first constructor. This must be the constructor which takes an ObjectInstance identifying the MBean, and we must dynamically build the call to this constructor. We then call the setServer method to bind the new proxy instance.


Example 9-3 Instantiating and Binding a Proxy Class Dynamically

// Get the class name of the MBean's proxy
Class proxyClass = Class.forName(
    connectorClient.getClassForProxyMBean( mbeanObjectInstance ));

// Find the constructor with takes an ObjectInstance parameter
Class[] signature = new Class[1];
signature[0] = Class.forName("javax.management.ObjectInstance");
Constructor proxyConstr = proxyClass.getConstructor( signature );

// Call the constructor to instantiate the proxy object
Object[] initargs = new Object[1];
initargs[0] = mbeanObjectInstance;
Proxy proxy2 = (Proxy) proxyConstr.newInstance( initargs );

// Bind the proxy
proxy2.setServer( connectorClient );

echo("\tPROXY CLASS NAME  = " +
      proxy2.getClass().getName());
echo("\tMBEAN OBJECT NAME = " +
      proxy2.getMBeanObjectInstance().getObjectName());
echo("\tCONNECTOR CLIENT  = " +
      proxy2.getServer().getClass().getName());

// We no longer need proxy2, so we unbind it
proxy2.setServer( null );

Once a proxy is bound, you can access the attributes and operations of its MBean through direct calls to the proxy object, as shown in the following example.


Example 9-4 Accessing a Standard MBean Through Its Proxy

try {
    // cast mbeanProxy to SimpleStandardProxy, so we can
    // call its MBean specific methods
    SimpleStandardProxy simpleStandardProxy =
        (SimpleStandardProxy) mbeanProxy;

    [...]

    // Change the "State" attribute
    simpleStandardProxy.setState("new state from client");

    // Get and display the new attribute values
    echo("\tState     = \"" + simpleStandardProxy.getState() + "\"");
    echo("\tNbChanges = " + simpleStandardProxy.getNbChanges());

    // Invoke the "reset" operation
    simpleStandardProxy.reset();
    [...]

    // We are done with the MBean, so we
    // unbind the proxy and unregister the MBean
    simpleStandardProxy.setServer( null );
    connectorClient.unregisterMBean( mbeanObjectName );

} catch (Exception e) {
    echo("\t!!! Error accessing proxy for " + 
          mbeanProxy.getMBeanObjectInstance().getObjectName() );
    e.printStackTrace();
}

Running the Standard Proxy Example

The examplesDir/SimpleClients directory contains all of the files for the ClientMBeanProxy application which demonstrates the use of standard MBean proxies.

If you haven't done so already, compile all files in this directory with the javac command. For example, on the Solaris platform with the Korn shell, you would type:


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

Before running the example, you must also generate the proxy MBeans classes and compile them as well. From the same directory as above, type the following commands:


$ installDir/SUNWjdmk/jdmk4.2/JDKversion/proxygen SimpleStandard
$ javac -classpath classpath SimpleStandardProxy.java
Instructions
  1. Launch the base agent in a terminal window with the following command:


    $ java -classpath classpath BaseAgent
    

    The agent creates the RMI connector server to which the client application will establish a connection, and then it waits for management operations.

  2. Wait for the agent to be completely initialized, then, in another window on the same host, launch the management application with the following command:


    $ java -classpath classpath ClientMBeanProxy
    
  3. Press <Enter> in the manager window to step through the example.

    As seen in the code examples, the client application instantiates the proxy objects to access the MBean it has created in the base agent.

  4. Press <Enter> one last time to exit the manager application, but leave the base agent running for the next example.

Generic Proxies

Because dynamic MBeans only expose their management at runtime, it is impossible to generate a specific proxy object for them. Instead, we use the GenericProxy object which can be bound to any dynamic MBean, and whose generic methods take the name of the attribute or operation being accessed. Therefore, to access a dynamic MBean through generic proxy you invoke exactly the same methods as those of the DynamicMBean interface.

Just as the MBean server's generic methods can access both standard and dynamic MBeans, generic proxies can also be bound to standard MBeans. You lose the specificity and simplicity of a standard proxy, but a generic proxy is always available in any Java Dynamic Management application, and it never needs regenerating.

The management application in this example shows how generic proxies can be used to access both standard and dynamic MBeans. The application contains the following subroutine which takes the class name of an MBean, creates that MBean in the agent, and instantiates a generic proxy to access the MBean.

In fact, the subroutine instantiates two generic proxies for the MBean, one using the GenericProxy class constructor which also binds the proxy, the other bound in a second, separate call to its setServer method. This demonstrates that it is possible to have two distinct proxy instances coexisting simultaneously for the same MBean.


Example 9-5

private void doGenericProxyExample( String mbeanName ) {

    try {
        // build the MBean ObjectName instance
        ObjectName mbeanObjectName = null;
        String domain = connectorClient.getDefaultDomain();
        mbeanObjectName = new ObjectName( domain +
                                          ":type=" + mbeanName);

        // create the MBean in the MBeanServer of the agent
        String mbeanClassName = mbeanName;
        ObjectInstance mbeanObjectInstance =
            connectorClient.createMBean( mbeanClassName, mbeanObjectName );

        // create and bind a generic proxy instance for the MBean
        Proxy proxy = new GenericProxy(
                              mbeanObjectInstance, connectorClient );

        echo("\tPROXY CLASS NAME  = " +
              proxy.getClass().getName());
        echo("\tMBEAN OBJECT NAME = " +
              proxy.getMBeanObjectInstance().getObjectName());
        echo("\tCONNECTOR CLIENT  = " +
              proxy.getServer().getClass().getName());

        // An alternate way is to first instantiate the generic proxy,
        // and then to bind it to the connector client:
        Proxy proxy2 = new GenericProxy( mbeanObjectInstance );
        proxy2.setServer( connectorClient );

        echo("\tPROXY CLASS NAME  = " +
              proxy2.getClass().getName());
        echo("\tMBEAN OBJECT NAME = " +
              proxy2.getMBeanObjectInstance().getObjectName());
        echo("\tCONNECTOR CLIENT  = " +
              proxy2.getServer().getClass().getName());

        // we no longer need proxy2, so we unbind it
        proxy2.setServer(null);

        [...] // Accessing the MBean through its generic proxy (see below)

        // When done with the MBean, we unbind the proxy
        // and unregister the MBean
        //
        proxy.setServer(null);
        connectorClient.unregisterMBean( mbeanObjectName );

    } catch (Exception e) {
        echo("\t!!! Error instantiating or binding proxy for " + 
              mbeanName );
        e.printStackTrace();
    }
}

The standard and dynamic MBean classes used in this example have exactly the same management interface, and therefore, we can use the same code to access both of them. The manager application does this by calling the above subroutine twice, once with the class name of the standard MBean, once with that of the dynamic MBean:

manager.doGenericProxyExample("SimpleStandard");
manager.doGenericProxyExample("SimpleDynamic");

Because the two MBeans have the same behavior, they will produce the same results when accessed through their proxy. The only difference is that the dynamic MBean can expose a description of its management interface in its MBeanInfo object. As expected, accessing a standard MBean through a generic proxy also produces the same result as when it is accessed through a standard proxy (compare the following with Example 9-4).


Example 9-6 Accessing an MBean Through its Generic Proxy

try {

    // cast Proxy to GenericProxy
    GenericProxy genericProxy = (GenericProxy) proxy;

    // Get the MBean's metadata through the proxy
    MBeanInfo info = genericProxy.getMBeanInfo();

    // display content of the MBeanInfo object
    echo("\nCLASSNAME: \t"+ info.getClassName() );
    echo("\nDESCRIPTION: \t"+ info.getDescription() );
    [...] // extract all attribute and operation info

    // Change the "State" attribute
    Attribute stateAttr = new Attribute( "State", "new state from client");
    genericProxy.setAttribute( stateAttr );

    // Get and display the new attribute values
    String state =
        (String) genericProxy.getAttribute("State");
    Integer nbChanges =
        (Integer) genericProxy.getAttribute("NbChanges");
    echo("\tState     = \"" + state + "\""); 
    echo("\tNbChanges = " + nbChanges); 

    // Invoke the "reset" operation
    Object[] params = new Object[0];
    String[] signature = new String[0];
    genericProxy.invoke("reset", params, signature );

} catch (Exception e) {
    echo("\t!!! Error accessing proxy for " + 
          proxy.getMBeanObjectInstance().getObjectName() );
    e.printStackTrace();
}

The above code listing shows how the generic methods are called with the names of attributes and operations, and how required parameters can be constructed.


Running the Generic Proxy Example

The ClientGenericProxy application, also in the examplesDir/SimpleClients directory, demonstrates the use of generic proxies.

If you haven't done so already, compile all files in this directory with the javac command. For example, on the Solaris platform with the Korn shell, you would type:


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

Because generic proxies do not need to be generated, this example does not need the proxygen tool. The GenericProxy class is available in the usual classpath for the product's runtime libraries.

Instructions
  1. If it is not already running on your host, launch the base agent in a terminal window with the following command:


    $ java -classpath classpath BaseAgent
    

    The agent creates the RMI connector server to which the client application will establish a connection, and then it waits for management operations.

  2. Wait for the agent to be completely initialized, then launch the management application in another window on the same host:


    $ java -classpath classpath ClientGenericProxy
    
  3. Press <Enter> in the manager window to step through the example.

    As seen in the code examples, the client application instantiates generic proxy objects to access both a standard and dynamic MBean that it creates in the base agent. The only difference between the two is the user-provided information available in the dynamic MBean's metadata.

  4. Press <Enter> in both windows to exit the base agent and manager applications.

Proxies for Java DMK Components

Most components of the Java Dynamic Management Kit product are MBeans and can therefore also be managed through local or remote proxies. Nearly all are standard MBeans, so their corresponding standard proxies are provided with the product. The Java source code for all component proxy classes can be found in the JdmkProxyMBeans directory located in the main examplesDir (see "Directories and Classpath" in the preface).


Note -

The HTML protocol adaptor is implemented as a dynamic MBean and therefore cannot have a standard proxy. You must use a generic proxy if you wish to access the HTML adaptor through a proxy object.


Of course, all other Java DMK MBean components may also be accessed through generic proxies, although their standard proxies provide more abstraction of the MBean server and a greater simplification of your application's code.

The proxy classes have been generated by the proxygen tool with full read-write access of all attributes. See the chapter on the proxygen tool in the Java Dynamic Management Kit 4.2 Tools Reference guide.

Proxy Packages

Like all other classes, proxies may contain a package statement. The package for a component proxy class depends upon the package of the component:

Compiling the Proxy Classes

To use the standard proxies for the product components, you must first compile them with the javac command and then place the classes you need in the classpath of your agent or management application. If you compile the proxy classes individually, be sure to compile the proxy's MBean interface before its corresponding proxy class.

Because of the package statement in proxy classes, we recommend using the following commands:


$ cd examplesDir/JdmkProxyMBeans/
$ javac -d packageRoot -classpath classpath *ProxyMBean.java *Proxy.java

In this command, the classpath must contain the current directory and the classpath of the Java DMK runtime libraries (usually in installDir/SUNWjdmk/jdmk4.2/JDKversion/lib/jdmkrt.jar). The -d option of the javac compiler creates the necessary directories in the given packageRoot for each class's package. The packageRoot is usually the current directory (.), or you may directly specify a target directory in your application's classpath.