Java Dynamic Management Kit 5.1 Tutorial

Chapter 9 Protocol Connectors

Protocol connectors provide a point-to-point connection between a Java dynamic management agent and a management application. Each connector relies on a specific communication protocol, but the API that is available to the management application is identical for all connectors and is entirely protocol-independent.

A connector consists of a connector server component registered in the agent and a connector client object instance in the management application. The connector client exposes a remote version of the MBean server interface. Each connector client represents one agent to which the manager wants to connect. The connector server replies to requests from any number of connections and fulfills them through its MBean server. Once the connection is established, the remoteness of the agent is transparent to the management application, except for any communication delays.

Connectors rely on the Java serialization package to transmit data as Java objects between client and server components. Therefore, all objects needed in the exchange of management requests and responses must be instances of a serializable class. However, the data encoding and sequencing are proprietary, and the raw data of the message contents in the underlying protocol are not exposed by the connectors.

Java Dynamic Management Kit (Java DMK) version 5.1 implements the new connectors defined by Java Management Extensions (JMX) Remote API. The connectors implemented in previous versions of Java DMK are retained for backwards compatibility, and are described in Part VI. The two new connectors are based on the remote method invocation (RMI) protocol, and a new protocol called the JMX messaging protocol (JMXMP). These new connector protocols allow more sophisticated security than was previously possible to be implemented on the connectors, and are also less complicated to implement.

The code samples in this chapter are taken from the files in the current/Connectors example directory located in the main examplesDir (see Directories and Classpath in the Preface).

This chapter covers the following topics:

9.1 Connector Servers

Connector servers on the agent side listen for management requests issued through a corresponding connector client. The connector server transmits these requests to its MBean server and forwards any response back to the management application. The connector server also forwards notifications, when the management application has registered to receive them through its connector client.

A connector server listens for incoming requests from its corresponding connector client, decodes that request and encodes the reply. Several connector clients can establish connections with the same connector server, and the connector server can handle multiple requests simultaneously. There only needs to be one connector server MBean per protocol to which the agent needs to respond. However, several connector servers for the same protocol can coexist in an agent for processing requests on different ports.

9.1.1 Connector Server Factories

The simplest way to create connector servers is to use the JMXConnectorServerFactory constructor defined by JMX Remote API. No instances of this class are ever created, but its single method, newJMXConnectorServer() is called to create instances of the JMXConnectorServer class. New JMXConnectorServer instances are created when newJMXConnectorServer() is passed the following parameters:

  1. A JMXServiceURL, that serves as the address for the connector server; this URL identifies the connector protocol to be implemented, and the machine name and port number (or path or URL) at which this connector server will be bound.

  2. The environment Map for newJMXConnectorServer(); this can be null.

  3. A reference to the MBean server that will be exposed for remote management through this connector server. If null is given at creation time, the MBean server that will be exposed to remote management will be the MBean server in which the connector server is later registered as an MBean.

    Note that if you supply an MBean server at creation time, you can optionally register the connector server as an MBean. In that case, the MBean server in which you choose to register the connector server can optionally be the same MBean server as the one that was supplied at creation time. However, if you supply a null value for this parameter, you will need to register the connector server as an MBean in the MBean server that you wish to expose through that connector server.

In the JMXServiceURL, the connector protocol specified can be either RMI or JMXMP. Depending which protocol is specified, the connector server created will be either an instance of the RMIConnectorServer or JMXMPConnectorServer classes. Both of these classes inherit from the JMXConnectorServer class.

9.1.2 RMI Connector Server

The Java DMK RMI connector server supports the standard RMI transports, Java Remote Method Protocol (JRMP) and the Internet Inter-Object Request Broker (ORB) Protocol (IIOP). An example of an RMI connector is provided in the examplesDir that demonstrates an RMI connection between a server and a remote client.

The Server class from the RMI connector example is is shown in Example 9–1.


Example 9–1 RMI Connector Server

public static void main(String[] args) {  
      try {  
          // Instantiate the MBean server  
          //  
          MBeanServer mbs = MBeanServerFactory.createMBeanServer();  
 
          // Create an RMI connector server  
          //  
          JMXServiceURL url = new JMXServiceURL(  
            "service:jmx:rmi:///jndi/rmi://localhost:9999/server");  
          JMXConnectorServer cs =  
               JMXConnectorServerFactory.newJMXConnectorServer(url,  
               null, mbs);  
          cs.start();  
        } catch (Exception e) {  
          e.printStackTrace();  
        }  
   }  
}  

Firstly, the Server class creates a new MBean server called mbs by calling the createMBeanServer() method of the MBeanServerFactory class. A call to JMXServiceURL creates a new service URL called url, which serves as an address for the connector server. This service URL defines the following:

Finally, an RMI connector server named cs is created by calling the JMXConnectorServerFactory constructor, 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 the instance of RMIConnectorServer that is created exports its underlying RMI server stub server to the RMI registry.

9.1.3 JMXMP Connector Server

The JMXMP connector protocol defined by Java DMK 5.1 is based on Java serialization over transmission control protocol (TCP) sockets. The JMXMP protocol is a custom protocol for JMX Remote API, and offers a more complete security solution than the RMI connector, as it can implement both the secure sockets layer (SSL) and the simple authentication and security layer (SASL) technologies. These optional security features are described in Chapter 11, Connector Security.

In the JMXMP connector, communication between server and client happens over a single TCP connection, and every message is a serialized Java object. Communication between server and client is performed in two separate streams, one for each direction, allowing multiple concurrent requests over the connection at any given time.

The JMXMP connector example is contained in the directory examplesDir/current/Connectors/jmxmp.

The code for a JMXMP connector server is shown in Example 9–2.


Example 9–2 JMXMP Connector Server

public class Server { 
 
   public static void main(String[] args) { 
      try { 
         // Instantiate the MBean server 
         // 
         MBeanServer mbs = MBeanServerFactory.createMBeanServer(); 
 
         // Create a JMXMP connector server 
         // 
         JMXServiceURL url = 
              new JMXServiceURL("jmxmp", null, 5555); 
         JMXConnectorServer cs = 
              JMXConnectorServerFactory.newJMXConnectorServer(url, 
              null, mbs); 
         cs.start(); 
       } catch (Exception e) { 
         e.printStackTrace(); 
       } 
    } 
} 
 

Firstly, the Server class creates a new MBean server named mbs by calling the createMBeanServer() method of the MBeanServerFactory class.

A call to JMXServiceURL creates a new service URL called url, which serves as an address for the connector server. This service URL defines the following:

  1. The connector will use the JMXMP protocol, denoted by jmxmp.

  2. No environment map is specified, as denoted by null.

  3. The connector server will listen for client connections on port 5555 on the local host.

Finally, a JMXMP connector server named cs is created by calling the constructor JMXConnectorServerFactory, with the service URL url, the null environment map, and the MBean server mbs as parameters. The connector server cs is launched by calling the start() method of JMXConnectorServer, whereupon JMXMPConnectorServer, which inherits from JMXConnectorServer, starts listening for client connections.

9.2 Connector Clients

The manager application interacts with a connector client to access an agent through an established connection. A connector client provides methods to handle the connection and access the agent.

Through the connector, the management application sends management requests to the MBeans located in a remote agent. Components of the management application access remote MBeans by calling the methods of the connector client for getting and setting attributes and calling operations on the MBeans. The connector client then returns the result, providing a complete abstraction of the communication layer.

9.2.1 Connector Factories

The simplest way to create connectors is to use the JMXConnectorFactory constructor defined by JMX Remote API. No instances of this class are ever created, but its method, connect() can be called to create a connected JMX connector using a JMXServiceURL that you pass it as a parameter. New, unconnected, JMXConnector instances can also be created by calling the other JMXConnectorFactory method, newJMXConnector(), and passing it the connector server's JMXServiceURL. An environment map can also be supplied to provide additional parameters.

The Client in these examples uses the JMXConnectorFactory.connect() method.

9.2.2 RMI Connector Client

The RMI connector Client example is shown in Example 9–3.


Example 9–3 RMI Connector Client

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); 
      MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); 
       
      // Get domains from MBeanServer 
      // 
      String domains[] = mbsc.getDomains(); 
      for (int i = 0; i < domains.length; i++) { 
          System.out.println("Domain[" + i + "] = " + domains[i]); 
      } 
 
      String domain = mbsc.getDefaultDomain();       
   
      // Create SimpleStandard MBean and perform simple MBean operations 
      // 
      ObjectName mbeanName =  
             new ObjectName("MBeans:type=SimpleStandard"); 
      mbsc.createMBean("SimpleStandard", mbeanName, null, null); 
      System.out.println("\nMBean count = " + mbsc.getMBeanCount()); 
      System.out.println("\nState = " + 
             mbsc.getAttribute(mbeanName, "State")); 
      mbsc.setAttribute(mbeanName, 
             new Attribute("State", "changed state")); 
             
      SimpleStandardMBean proxy = (SimpleStandardMBean) 
          MBeanServerInvocationHandler.newProxyInstance( 
                                       mbsc, 
                                       mbeanName, 
                                       SimpleStandardMBean.class, 
                                       false); 
      System.out.println("\nState = " + proxy.getState()); 
 
      ClientListener listener = new ClientListener(); 
      mbsc.addNotificationListener(mbeanName, listener, null, null); 
 
      mbsc.invoke(mbeanName, "reset", null, null); 
 
      mbsc.removeNotificationListener(mbeanName, listener); 
      mbsc.unregisterMBean(mbeanName); 
      jmxc.close(); 
    } catch (Exception e) {      e.printStackTrace(); 
    } 
  } 
} 

In this example, the Client creates an RMI connector client that is configured to connect to the RMI connector server created by Server in 9.1.2 RMI Connector Server.

As you can see, the Client defines the same service URL url as that defined by Server. 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 thus identified, the connector client can be created. The connector client, jmxc, is an instance of the JMX Remote API 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.

An instance of 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, and can create MBeans and perform operations on them with the connection remaining completely transparent to both ends.

In the examples directory, there is an MBean interface and a class to define an MBean called SimpleStandard. As the name suggests, this is a very basic MBean of the type described in Chapter 1, Standard MBeans. The connector client creates an instance of this SimpleStandard MBean and registers it in the MBean server with a call to the createMBean() method of MBeanServerConnection. The client then activates notifications by calling addNotificationListener(), and performs the operations defined by SimpleStandard as if they were local MBean operations.

Finally, the client unregisters the SimpleStandard MBean and closes the connection.

9.2.3 JMXMP Connector Client

The Client.java class is shown in Example 9–4. The only difference between the client in this example and that used in the RMI connector example is in the JMX service URL. The operations this example performs on the SimpleStandard MBean are identical to those performed in the RMI connector example. Consequently, the code has been abridged.


Example 9–4 JMXMP Connector Client

public class Client { 
 
   public static void main(String[] args) { 
     try { 
        // Create a JMXMP connector client 
        // 
        System.out.println("\nCreate a JMXMP connector client"); 
        JMXServiceURL url = 
            new JMXServiceURL("service:jmx:jmxmp://localhost:5555"); 
        JMXConnector jmxc = JMXConnectorFactory.connect(url, null); 
        MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); 
 
        // Get domains from MBeanServer, create SimpleStandard MBean 
        // 
        String domains[] = mbsc.getDomains(); 
        for (int i = 0; i < domains.length; i++) { 
            System.out.println("Domain[" + i + "] = " + domains[i]); 
        ObjectName mbeanName = new ObjectName(
             "MBeans:type=SimpleStandard"); 
        mbsc.createMBean("SimpleStandard", mbeanName, null, null); 
 
       // Perform simple MBean operations, add listener, reset MBean, 
       // remove listener, unregister MBean 
 
       [...] 
        
       // Close MBeanServer connection 
       // 
       jmxc.close(); 
     } catch (Exception e) { 
       e.printStackTrace(); 
     } 
   } 
} 

The Client.java class creates a JMXMP connector client that is configured to connect to the JMXMP connector server created by Server.

As you can see, Client defines a service URL url that allows the connector client to find the connector server.

The connector client, jmxc, is an instance of the JMXConnector interface, created by the connect() method of JMXConnectorFactory. The connect() method is passed the parameter url when it is called.

An instance of 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, and can create MBeans and perform operations on them with the connection remaining completely transparent to both ends.

The client creates and registers the SimpleStandard MBean in the MBean server with a call to the createMBean() method of MBeanServerConnection, and performs the operations defined by SimpleStandard as if they were local MBean operations.

Finally, the client unregisters SimpleStandard and closes the connection.

9.3 Examples of Connector Servers

The connector server examples demonstrate the following:

  1. The server:

    1. Creates an MBean server

    2. Creates an RMI or a JMXMP connector server

  2. The remote client:

    1. Creates an RMI or a JMXMP connector

    2. Creates a simple standard MBean in the MBean server, via the RMI connection

    3. Creates a generic notification listener.

  3. Basic operations are performed on the MBean registered in the MBean server, via the RMI connection

9.3.1 RMI Connector Server Example

The RMI connector server example is found in the examplesDir/current/Connectors/rmi directory.

To Run the RMI Connector Server Example
  1. Compile the Java classes.


    $ javac -classpath classpath *.java 
  2. Start an RMI registry on port 9999 of the local host.


    $ export CLASSPATH=.:classpath ; rmiregistry 9999 &
  3. Start the RMI connector server:


    $ java -classpath .:classpath Server &

    You will see confirmation of the creation of the MBean server and the RMI connector server.

  4. Start the RMI connector client:


    $ java -classpath .:classpath Client

    You 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 SimpleStandard MBean. The client will then perform operations on SimpleStandard MBean, before unregistering it.

9.3.2 JMXMP Connector Server Example

The JMXMP connector server example is found in the examplesDir/current/Connectors/jmxmp directory.

To Run the JMXMP Connector Server Example
  1. Compile the Java classes.


    $ javac -classpath classpath *.java 
  2. Start the JMXMP connector server:


    $ java -classpath .:classpath Server &

    You will see confirmation of the creation of the MBean server and the JMXMP connector server.

  3. Start the JMXMP connector client:


    $ java -classpath .:classpath Client

    You 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 SimpleStandard MBean. The client will then perform operations on SimpleStandard MBean, before unregistering it.

9.4 Remote Notifications and Heartbeat Mechanism

In previous versions of Java DMK, notification forwarding over remote connectors and the heartbeat mechanism to monitor the health of the connections were performed by custom implementations written specifically for Java DMK. These mechanisms have now been standardized by the implementation of JMX Remote API in Java DMK 5.1, and consequently are integrated into the RMI and JMXMP connectors described in the preceding sections of this chapter.

9.5 Wrapping Legacy Connectors

Although it is recommended that you use the new RMI and JMXMP connector protocols defined by the JMX Remote API, it is possible for you to continue to use your existing legacy connectors alongside the new ones. This is achieved by wrapping the legacy connector so that it appears in a form that is compatible with the new standard connectors. Wrapping your Java DMK 5.0 RMI and HTTP(S) connectors allows applications created using Java DMK 5.1 to interoperate with existing Java DMK applications. In addition, if you want to use HTTP(S) connectors, you must wrap them.

The JmxConnectorServerFactory and JmxConnectorFactory classes are used to create wrapped legacy Java DMK connector servers and clients. These connector servers and clients expose the same interfaces as standard JMX connectors. For the JmxConnectorServerFactory to create a wrapped connector, you must ensure that the jdmkrt.jar is either in your CLASSPATH environment variable, or in the context of the thread that is used to create the wrapped connector.

Java DMK 5.1 defines a new interface, JdmkLegacyConnector, that is used to obtain the wrapped connectors.

The JdmkLegacyConnector interface specifies a new protocol name for each of the legacy connectors. These protocol names are passed into the JMXServiceURL when it is created, in the same way the RMI connector and JMXMP connectors are identified as rmi and jmxmp respectively in the service URLs. The new protocol names are listed below.

The JdmkLegacyConnector also specifies a list of properties to allow the factory to obtain information defined by the user to create the legacy connectors, as follows.

The creation of wrapped legacy RMI and HTTP connector servers is shown in Example 9–5. The code extracts in this section are taken from the classes in the examplesDir/current/Connectors/wrapping directory.


Example 9–5 Wrapping Legacy Connector Servers

public class Server {

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

           JMXServiceURL httpURL = new JMXServiceURL("jdmk-http", 
                                                      null, 6868);
           JMXConnectorServer httpCS =
              JMXConnectorServerFactory.newJMXConnectorServer(httpURL, 
                                                              null, mbs);
           ObjectName httpON = 
               new ObjectName("legacyWrapper:protocol=jdmk-http,port=6868");
	           mbs.registerMBean(httpCS, httpON);

            httpCS.start();

            JMXServiceURL rmiURL = 
               new JMXServiceURL("jdmk-rmi", null, 8888, "/myRMI");
            JMXConnectorServer rmiCS =
                JMXConnectorServerFactory.newJMXConnectorServer(rmiURL, 
                                                                null, mbs);

	            ObjectName rmiON = 
                new ObjectName("legacyWrapper:protocol=jdmk-rmi,port=8888");
	            mbs.registerMBean(rmiCS, rmiON);
	
            rmiCS.start();

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

In Example 9–5, we see that the connector servers are created in a similar way to standard connector servers defined by the JMX Remote API. The only difference is that the connector protocols used to create the JMX service URLs are jdmk-rmi and jmdk-http. For simplicity, this example does not create an HTTPS connector.


Note –

As you can see above, the wrapped connector server is registered in the MBean server before it is started. This order of events must always be respected, otherwise the wrapped connector will not work.



Example 9–6 Wrapping Legacy Connector Clients

public class Client {

  public static void main(String[] args) {
	   try {
	       JMXServiceURL url = new JMXServiceURL("jdmk-http", 
                                              null, 6868);
	       connect(url);
	    
	       url = new JMXServiceURL("jdmk-rmi", null, 8888, 
                                "/myRMI");
	       connect(url);
    } catch (Exception e) {
        e.printStackTrace();
    }
	   System.exit(0);
  }
  
  [...]

As for the creation of the connector servers, in Example 9–6, the legacy connector clients are created by passing the jdmk-http and jdmk-rmi property strings to the JMX service URLs. The rest of the Client example is identical to a standard JMX Remote API connector client.

To Run the Legacy Connector Wrapping Example

The following example uses the classes in the examplesDir/current/Connectors/wrapping directory.

  1. Compile the Java classes


    $ javac -classpath classpath *.java
  2. Start the Server


    $ java -classpath .:classpath Server &

    You will see confirmation of the creation of the wrapped connector servers, and will be prompted to start the Client.

  3. When prompted, start the Client


    $ java -classpath .:classpath Client

    You will see the wrapped HTTP connector client connect to the HTTP server, and perform various MBean operations via the connection. Once the MBean operations have completed, the Client closes the HTTP connection, before opening a connection to the legacy RMI connector server, and performing further MBean operations.

9.5.1 Limitations of Wrapped Legacy Connectors

The wrapped legacy connectors do not provide the full functionality of either their JMX Remote API or their Java DMK 5.0 counterparts. The limitations of the wrapped legacy connectors are listed below.