11 JMX Connectors
This chapter introduces the concepts of standard and dynamic management beans (MBeans) and shows how to use JMX technology to perform operations on MBeans, locally and remotely.
Accessing Standard and Dynamic MBeans By Using the RMI Connector
This example demonstrates standard and dynamic MBeans .
As seen in Essentials of the JMX API, a standard MBean statically defines its management interface through the names of the methods it contains. A dynamic MBean implements a specific Java interface and reveals its attributes and operations at run time.
The JMX technology defines a connector based on Remote Method Invocation (RMI). The RMI connector supports the Java Remote Method Protocol (JRMP) transport. This connector allows you to connect to an MBean in an MBean server from a remote location, and perform operations on it, exactly as if the operations were performed locally.
The purpose of this example is to demonstrate the implementation of a standard MBean and a dynamic MBean. It shows how to perform operations on them, both locally, and remotely through an RMI connection between a server and a remote client.
When you run this example:
- 
                        The server: - 
                              Creates an MBean server 
- 
                              Registers a SimpleStandardand aSimpleDynamicMBean in the local MBean server
- 
                              Performs local operations on the MBeans 
- 
                              Creates an RMI connector server 
 
- 
                              
- 
                        The client: - 
                              Creates an RMI connector 
- 
                              Registers a SimpleStandardand aSimpleDynamicMBean on the remote MBean server
- 
                              Performs remote operations on both MBeans 
 
- 
                              
Analyzing the Classes Used in the Basic MBean Example
- 
                        Copy the source code contained in the JMX Connectors section and create corresponding files in the work_dir/jmx_examples/Basicdirectory. The files inside this directory should then include the following:- Server.java
- SimpleStandardMBean.java
- SimpleStandard.java
- SimpleDynamic.java
- ClientListener.java
- Client.java
- README
 
- 
                        Open each *.javafile in your IDE or a text editor.
The following sections analyze each of the classes used in the basic MBean example, and explain how the classes perform the operations described in the preceding section.
Server.java in the MBean Example
Due to its size, the Server.java class is analyzed in the following
            series of code excerpts:
                     
- 
                           CODE EXAMPLE 11-1 MBean Example Class Server.java (Excerpt 1) 
- 
                           CODE EXAMPLE 11-2 MBean Example Class Server.java (Excerpt 2) 
- 
                           CODE EXAMPLE 11-3 MBean Example Class Server.java (Excerpt 3) 
- 
                           CODE EXAMPLE 11-4 MBean Example Class Server.java (Excerpt 4) 
- 
                           CODE EXAMPLE 11-5 MBean Example Class Server.java (Excerpt 5) 
CODE EXAMPLE 11-1 MBean Example Class Server.java (Excerpt 1)
 
public class Server { 
 
 public static void main(String[] args) { 
     try { 
          
         MBeanServer mbs = MBeanServerFactory.createMBeanServer(); 
         waitForEnterPressed(); 
 
         String domain = mbs.getDefaultDomain(); 
         waitForEnterPressed(); 
 
         String mbeanClassName = "SimpleStandard"; 
         String mbeanObjectNameStr = 
             domain + ":type=" + mbeanClassName + ",name=1"; 
         ObjectName mbeanObjectName = 
             createSimpleMBean(mbs, mbeanClassName, mbeanObjectNameStr); 
         waitForEnterPressed(); 
 
         printMBeanInfo(mbs, mbeanObjectName, mbeanClassName); 
         waitForEnterPressed(); 
 
         manageSimpleMBean(mbs, mbeanObjectName, mbeanClassName); 
         waitForEnterPressed(); 
 
         mbeanClassName = "SimpleDynamic"; 
         mbeanObjectNameStr = 
             domain + ":type=" + mbeanClassName + ",name=1"; 
         mbeanObjectName = 
             createSimpleMBean(mbs, mbeanClassName, mbeanObjectNameStr); 
         waitForEnterPressed(); 
 
         printMBeanInfo(mbs, mbeanObjectName, mbeanClassName); 
         waitForEnterPressed(); 
 
         manageSimpleMBean(mbs, mbeanObjectName, mbeanClassName); 
         waitForEnterPressed(); 
 
         [...] 
 
Examining this class, you can see that the following occurs:
First, the Server.java class creates a new MBean server called mbs by calling the createMBeanServer() method of the MBeanServerFactory class.
                        
Then, the default domain in which the MBean server is registered is obtained with a call to the getDefaultDomain() method of the MBeanServer interface. The domain is identified by the string domain.
                        
The MBean class named SimpleStandard is also identified by a variable, in this case the string mbeanClassName. SimpleStandard is the name of the Java class for the Java object of which this MBean is an instance. The SimpleStandard.java object is examined in SimpleStandard.java in the Fine-Grained Security Example.
                        
Another variable, the string mbeanObjectNameStr, is defined as the combination of the domain, plus the following key=value pairs:
                        
- The type, which in this case is thembeanClassName.
- A name, to differentiate this MBean from other MBeans of the same type that might be created subsequently. In this case the name number is1.
The purpose of mbeanObjectNameStr is to give the MBean a human-readable identifier.
                        
A call to createSimpleMBean() creates and registers the SimpleStandard MBean in the local MBean server, with the given object name.
The operations printMBeanInfo(), and manageSimpleMBean() are then performed on the SimpleStandard MBean. Like createSimpleMBean(), these methods are defined later in the Server.java code, and are shown in CODE EXAMPLE 11-4 MBean Example Class Server.java (Excerpt 4) and CODE EXAMPLE 11-5 MBean Example Class Server.java (Excerpt 5).
                        
In code that is not shown here, a second MBean of the type SimpleDynamic is created and registered in the MBean server in exactly the same way as the SimpleStandard MBean. As the name suggests, this MBean is an instance of the SimpleDynamic Java object, which is examined in SimpleDynamic.java in the MBean Example.
                        
CODE EXAMPLE 11-2 MBean Example Class Server.java (Excerpt 2)
 
[...] 
 
JMXServiceURL url = 
  new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/server"); 
JMXConnectorServer cs = 
  JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); 
cs.start(); 
waitForEnterPressed(); 
cs.stop(); 
 
[...] 
 
In CODE EXAMPLE 11-2 MBean Example Class Server.java (Excerpt 2), an RMI
                connector server is created so that operations can be performed on the MBeans
                remotely. A call to the class JMXServiceURL creates a new service
                URL called url, which serves as an address for the connector
                server. In this example, the service URL is given in JNDI form, rather than
                in encoded form (see the API documentation for the
                    javax.management.remote.rmi package for an explanation of JNDI
                form). This service URL defines the following:
                        
- The connector will use the default RMI transport, denoted by rmi.
- The RMI registry in which the RMI connector stub are stored will be running on port
                        9999on the local host, and the server address will be registered under the nameserver. The port9999specified in the example is arbitrary; you can use any available port.
An RMI connector server named cs is created by calling the constructor JMXConnectorServerFactory, with the service URL url, a null environment map, and the MBean server mbs as parameters. The connector server cs is launched by calling the start() method of JMXConnectorServer, whereupon RMIConnectorServer exports the RMI object server to the RMI registry. The connection will remain open until the Enter key is pressed, as instructed by the simple method waitForEnterPressed, that is defined later in the Server code.
                        
CODE EXAMPLE 11-3 MBean Example Class Server.java (Excerpt 3)
 
[...] 
 
private static ObjectName createSimpleMBean(MBeanServer mbs, 
                                              String mbeanClassName, 
                                              String mbeanObjectNameStr) { 
     echo("\n>>> Create the " + mbeanClassName + 
          " MBean within the MBeanServer"); 
     echo("ObjectName = " + mbeanObjectNameStr); 
     try { 
          ObjectName mbeanObjectName = 
             ObjectName.getInstance(mbeanObjectNameStr); 
          mbs.createMBean(mbeanClassName, mbeanObjectName); 
             return mbeanObjectName; 
          } catch (Exception e) { 
            echo(       "!!! Could not create the " +  
                  mbeanClassName + " MBean !!!"); 
            e.printStackTrace(); 
            echo("\nEXITING...\n"); 
            System.exit(1); 
        } 
        return null; 
     } 
 
[...] 
 
CODE EXAMPLE 11-3 MBean Example Class Server.java (Excerpt 3) shows the
                definition of the createSimpleMBean() method. In this method, the
                MBean instance with the object name mbeanObjectNameStr is passed to
                the getInstance() method of the ObjectName
                interface to create a new object name for registering the MBean inside the MBean
                server. The resulting object name instance is named
                mbeanObjectName. A call to the MBeanServer method
                    createMBean() then instantiates an MBean defined by the
                combination of the Java object identified by mbeanClassName and the
                MBean instance mbeanObjectName and registers this MBean in the
                MBean server mbs.
                        
CODE EXAMPLE 11-4 MBean Example Class Server.java (Excerpt 4)
[...] 
 
private static void printMBeanInfo(MBeanServer mbs, 
                                     ObjectName mbeanObjectName, 
                                     String mbeanClassName) { 
     MBeanInfo info = null; 
     try { 
         info = mbs.getMBeanInfo(mbeanObjectName); 
     } catch (Exception e) { 
         echo(  "!!! Could not get MBeanInfo object for " + 
         mbeanClassName +" !!!"); 
         e.printStackTrace(); 
         return; 
     } 
 
     MBeanAttributeInfo[] attrInfo = info.getAttributes(); 
     if (attrInfo.length > 0) { 
         for (int i = 0; i < attrInfo.length; i++) { 
        echo(" ** NAME:    " + attrInfo[i].getName()); 
        echo("    DESCR:   " + attrInfo[i].getDescription()); 
        echo("    TYPE:    " + attrInfo[i].getType() + 
                "READ: "+ attrInfo[i].isReadable() + 
                "WRITE: "+ attrInfo[i].isWritable()); 
        } 
     } else echo(" ** No attributes **"); 
 
[...] 
In CODE EXAMPLE 11-4 MBean Example Class Server.java (Excerpt 4) , we see the
                definition of the method printMBeanInfo(). The
                    printMBeanInfo() method calls the MBeanServer
                method getMBeanInfo() to obtain details of the attributes and
                operations that are exposed by the MBean mbeanObjectName.
                    MBeanAttributeInfo defines the following methods, each of which
                is called in turn to obtain information about the mbeanObjectName
                MBean’s attributes:
                        
- getName: Obtains the attribute’s name.
- getDescription: Obtains the human readable description of the attribute.
- getType: Obtains the class name of the attribute.
- isReadable: Determines whether or not the attribute is readable.
- isWritable: Determines whether or not the attribute is writable.
In code that is not shown here, calls are made to obtain information about the mbeanObjectName MBean’s constructors, operations and notifications:
                        
- MBeanConstructorInfo: Obtains information about the MBean’s Java class.
- MBeanOperationInfo: Learns what operations the MBean performs, and what parameters the MBean takes.
- MBeanNotificationInfo: Finds out what notifications the MBean sends when its operations are performed.
CODE EXAMPLE 11-5 MBean Example Class Server.java (Excerpt 5)
[...] 
 
private static void manageSimpleMBean(MBeanServer mbs, 
                                        ObjectName mbeanObjectName, 
                                        String mbeanClassName) { 
        try { 
            printSimpleAttributes(mbs, mbeanObjectName); 
 
            Attribute stateAttribute = new Attribute("State", 
                                                     "new state"); 
            mbs.setAttribute(mbeanObjectName, stateAttribute); 
 
            printSimpleAttributes(mbs, mbeanObjectName); 
             
            echo("\n    Invoking reset operation..."); 
            mbs.invoke(mbeanObjectName, "reset", null, null); 
 
            printSimpleAttributes(mbs, mbeanObjectName); 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } 
    } 
 
    private static void printSimpleAttributes( 
                                        MBeanServer mbs, 
                                        ObjectName mbeanObjectName) { 
        try { 
            String State =  
               (String) mbs.getAttribute(mbeanObjectName, "State"); 
            Integer NbChanges = 
               (Integer) mbs.getAttribute(mbeanObjectName, 
                                          "NbChanges"); 
        } catch (Exception e) { 
            echo(       "!!! Could not read attributes !!!"); 
            e.printStackTrace(); 
        } 
   } 
 
[...]  
CODE EXAMPLE 11-5 MBean Example Class Server.java (Excerpt 5) demonstrates a method for managing a simple MBean.
The manageSimpleMBean() method first of all calls the printSimpleAttributes() method that is also defined by Server. The printSimpleAttributes() method obtains an MBean attribute called state from the MBean mbeanObjectName, as well as another MBean attribute called NbChanges. Both of these attributes are defined in the SimpleStandard class, shown in SimpleStandard.java in the Fine-Grained Security Example.
                        
The manageSimpleMBean() method then defines an attribute called stateAttribute, which is an instance of the Attribute class. The stateAttribute attribute associates a value of new state with the existing attribute state, defined by SimpleStandard. A call to the MBeanServer method setAttribute() then sets the mbeanObjectName MBean’s state to the new state defined by stateAttribute.
                        
Finally, a call to the MBeanServer method invoke() invokes the mbeanObjectName MBean’s reset operation. The reset operation is defined in the SimpleStandard class.
                        
SimpleStandardMBean.java in the MBean Example
The SimpleStandardMBean.java class is shown in the following code
                     example.
                     
CODE EXAMPLE 11-6 MBean Example Class SimpleStandardMBean.java
 
public interface SimpleStandardMBean { 
 
       public String getState(); 
       public void setState(String s); 
       public int getNbChanges(); 
       public void reset(); 
 
} 
 
The SimpleStandardMBean.java class is a straightforward JMX
                            Specification management interface for the MBean
                                   SimpleStandard. This interface exposes the four
                            operations defined by SimpleStandard for management
                            through a JMX agent.
                        
SimpleStandard.java in the MBean Example
The SimpleStandard.java class is shown in the following code
            example.
                     
CODE EXAMPLE 11-7 MBean Example Class SimpleStandard.java
 
public class SimpleStandard 
    extends NotificationBroadcasterSupport 
    implements SimpleStandardMBean { 
    public String getState() { 
       return state; 
    } 
    public void setState(String s) { 
       state = s; 
       nbChanges++; 
    } 
     
    public int getNbChanges() { 
        return nbChanges; 
    } 
     
    public void reset() { 
       AttributeChangeNotification acn =  
           new AttributeChangeNotification(this, 
                                          0, 
                                          0, 
                                          "NbChanges reset", 
                                          "NbChanges", 
                                          "Integer", 
                                          new Integer(nbChanges), 
                                          new Integer(0)); 
       state = "initial state"; 
       nbChanges = 0; 
       nbResets++; 
       sendNotification(acn); 
    } 
     
    public int getNbResets() { 
       return nbResets; 
    } 
 
    public MBeanNotificationInfo[] getNotificationInfo() { 
        return new MBeanNotificationInfo[] { 
          new MBeanNotificationInfo( 
          new String[] { 
            AttributeChangeNotification.ATTRIBUTE_CHANGE }, 
            AttributeChangeNotification.class.getName(), 
            "This notification is emitted when the reset()  
             method is called.") 
        }; 
    } 
     
    private String state = "initial state"; 
    private int nbChanges = 0; 
    private int nbResets = 0; 
 
}         
 
The SimpleStandard class defines a straightforward JMX
                Specification standard MBean. The SimpleStandard MBean exposes
                operations and attributes for management by implementing the corresponding
                    SimpleStandardMBean interface, shown in SimpleStandardMBean.java in the Subject Delegation Example.
                        
The simple operations exposed by this MBean are to:
- 
                              Define a state 
- 
                              Update this state 
- 
                              Count the number of times the state is updated 
- 
                              Reset the values of the state and the number of changes to their original value of zero 
- 
                              Send a notification whenever the reset operation is invoked 
The notification emitted by the reset operation is an instance of the class AttributeChangeNotification, which collects information about the number of changes carried out on the State attribute before calling reset. The content of the notification sent is defined by the MBeanNotificationInfo instance.
                        
SimpleDynamic.java in the MBean Example
The SimpleDynamic class is shown in the following code example.
                     
CODE EXAMPLE 11-8 MBean Example Class SimpleDynamic.java
 
public class SimpleDynamic 
    extends NotificationBroadcasterSupport 
    implements DynamicMBean { 
 
    public SimpleDynamic() { 
        buildDynamicMBeanInfo(); 
    } 
 
[...] 
The SimpleDynamic dynamic MBean shows how to expose attributes and operations for management at runtime, by implementing the DynamicMBean interface. It starts by defining a method, buildDynamicMBeanInfo(), for obtaining information for the MBean dynamically. The buildDynamicMBeanInfo() method builds the MBeanInfo for the dynamic MBean.
                        
The rest of the code of SimpleDynamic corresponds to the implementation of the DynamicMBean interface. The attributes, operations and notifications exposed are identical to those exposed by the SimpleStandard MBean.
                        
ClientListener.java in the MBean Example
The ClientListener.java class is shown in the following code
            example.
                     
CODE EXAMPLE 11-9 MBean Example Class ClientListener.java
 
 
public class ClientListener implements NotificationListener {  
 public void handleNotification(Notification notification, Object handback)  
     {  
         System.out.println("\nReceived notification: " + notification);  
     }  
} 
 
The ClientListener class implements a straightforward JMX
                Specification notification listener. The handleNotification()
                method of the NotificationListener interface is called upon
                reception of a notification, and prints out a message to confirm that a notification
                has been received.
                        
Client.java in the MBean Example
The Client.java class is shown in the following code example.
                     
CODE EXAMPLE 11-10 MBean Example Class Client.java
 
public class Client { 
 
  public static void main(String[] args) { 
    try { 
      // Create an RMI connector client 
      // 
      JMXServiceURL url = new JMXServiceURL( 
         "service:jmx:rmi:///jndi/rmi://localhost:9999/server"); 
      JMXConnector jmxc = JMXConnectorFactory.connect(url, null); 
      ClientListener listener = new ClientListener(); 
      MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); 
      waitForEnterPressed();       
       
      // Get domains from MBeanServer 
      // 
      String domains[] = mbsc.getDomains(); 
      for (int i = 0; i < domains.length; i++) { 
          System.out.println("Domain[" + i + "] = " + domains[i]); 
      } 
      waitForEnterPressed();       
 
      String domain = mbsc.getDefaultDomain();       
   
      // Create SimpleStandard MBean  
      ObjectName mbeanName =  
             new ObjectName(domain +":type=SimpleStandard,name=2"); 
      mbsc.createMBean("SimpleStandard", stdMBeanName, null, null); 
      waitForEnterPressed();       
             
      // Create SimpleDynamic MBean 
      ObjectName dynMBeanName = 
          new ObjectName(domain +":type=SimpleDynamic,name=2"); 
      echo("\nCreate SimpleDynamic MBean..."); 
      mbsc.createMBean("SimpleDynamic", dynMBeanName, null, null); 
      waitForEnterPressed(); 
       
      // Get MBean count 
      echo("\nMBean count = " + mbsc.getMBeanCount()); 
 
      // Query MBean names 
      echo("\nQuery MBeanServer MBeans:"); 
      Set names = mbsc.queryNames(null, null); 
      for (Iterator i = names.iterator(); i.hasNext(); ) { 
      echo(     "ObjectName = " + (ObjectName) i.next()); 
      } 
      waitForEnterPressed(); 
       
      mbsc.setAttribute(stdMBeanName, 
                        new Attribute("State", "changed state")); 
 
      SimpleStandardMBean proxy = JMX.newMBeanProxy( 
          mbsc, stdMBeanName, SimpleStandardMBean.class, true); 
      echo("\nState = " + proxy.getState()); 
 
      ClientListener listener = new ClientListener(); 
      mbsc.addNotificationListener(stdMBeanName, listener, null, null); 
 
      mbsc.invoke(stdMBeanName, "reset", null, null); 
 
      mbsc.removeNotificationListener(stdMBeanName, listener); 
      mbsc.unregisterMBean(stdMBeanName); 
       
      [...] 
       
      jmxc.close(); 
    } catch (Exception e) { 
      e.printStackTrace(); 
    } 
  } 
} 
[...] 
The Client.java class creates an RMI connector client that is configured to connect to the RMI connector server created by Server.java. Client.java defines the same service URL url as that defined by Server.java. This allows the connector client to retrieve the RMI connector server stub named server from the RMI registry running on port 9999 of the local host, and to connect to the RMI connector server.
                        
With the RMI registry identified, the connector client can be created. The connector client, jmxc, is an instance of the interface JMXConnector, created by the connect() method of JMXConnectorFactory. The connect() method is passed the parameters url and a null environment map when it is called.
                        
The Client also creates an instance of ClientListener, to listen for notifications, as shown in ClientListener.java in the MBean Example.
                        
An instance of a JMX Specification MBeanServerConnection, named
          mbsc, is then created by calling the
          getMBeanServerConnection() method of the JMXConnector
        instance jmxc.
                        
The connector client is now connected to the MBean server created by Server.java, and can register MBeans and perform operations on them with the connection remaining completely transparent to both ends.
                        
The client creates and registers the SimpleStandard MBean and the
        SimpleDynamic MBean in the MBean server with a call to the createMBean()
        method of MBeanServerConnection, and performs the operations defined by
          SimpleStandard and SimpleDynamic as if they were local
        JMX Specification MBean operations.
                        
MBean proxies allow you to access an MBean through a Java interface, allowing you to make calls on the proxy rather than having to write lengthy code to access a remote MBean. An MBean proxy for SimpleStandardMBean is created here by calling the method newMBeanProxy() in the javax.management.JMX class, passing it the MBean’s MBeanServerConnection, object name, the class name of the MBean interface and true, to signify that the proxy must behave as a NotificationBroadcaster. You can make proxies for MXBeans in exactly the same way as for standard MBeans, by simply calling newMXBeanProxy() instead of newMBeanProxy().
                        
The code for the different operations performed on SimpleDynamic is not shown here, because the operations are the same as those performed on SimpleStandard.
                        
Finally, the client unregisters the SimpleStandard MBean and closes the connection. The final removeNotificationListener is optional, as listeners registered by a remote client are removed when that client is closed.
                        
Running the MBean Example
Having examined the example classes, you can run the example. To run the example:
- Compile the Java classes. 
                        $ javac *.java
- Start an RMI registry on port 9999of the local host.The RMI registry is used by the Serverclass to register the RMI connector stub.$ rmiregistry 9999 &
- Start the Serverclass.$ java -classpath . ServerYou will see confirmation of the creation of the MBean server and the creation of the SimpleStandardMBean in the MBean server. You will then be prompted to press the Enter key to obtain information about, and then to perform operations on, theSimpleStandardMBean.After the operations on the SimpleStandardare completed, the process is repeated for theSimpleDynamicMBean.After both the MBeans are created and their operations performed, you see the creation of an RMI connector server, to allow operations to be performed on the MBeans from the remote Client.
- Start the Clientclass in another terminal window.$ java -classpath . ClientYou will see confirmation of the creation of the RMI connector client and of the connection with the connector server. You will also be informed of the domain name, and the creation and registration of SimpleStandardand SimpleDynamic MBeans. The client will perform operations onSimpleStandardand SimpleDynamic MBeans, before unregistering them.