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 presents the connector servers defined by Java DMK.
9.2 Connector Clients presents the connector clients.
9.3 Examples of Connector Servers describes how to run the two connector server examples.
9.4 Remote Notifications and Heartbeat Mechanism explains how a connector performs notifications and monitors a connection.
9.5 Wrapping Legacy Connectors explains how you can wrap legacy connectors so they can be used through the new connector infrastructure.
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.
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:
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.
The environment Map for newJMXConnectorServer(); this can be null.
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.
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.
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:
The connector will use the default RMI transport, JRMP, denoted by rmi.
The RMI registry in which the RMI connector stub will be stored will be running on port 9999 on the local host, and the server will be registered under the name server. The port 9999 specified in the example is arbitrary; you can use any available port.
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.
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.
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:
The connector will use the JMXMP protocol, denoted by jmxmp.
No environment map is specified, as denoted by null.
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.
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.
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.
The RMI connector Client example is shown in Example 9–3.
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.
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.
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.
The connector server examples demonstrate the following:
The server:
Creates an MBean server
Creates an RMI or a JMXMP connector server
The remote client:
Creates an RMI or a JMXMP connector
Creates a simple standard MBean in the MBean server, via the RMI connection
Creates a generic notification listener.
Basic operations are performed on the MBean registered in the MBean server, via the RMI connection
The RMI connector server example is found in the examplesDir/current/Connectors/rmi directory.
Compile the Java classes.
$ javac -classpath classpath *.java |
Start an RMI registry on port 9999 of the local host.
$ export CLASSPATH=.:classpath ; rmiregistry 9999 & |
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.
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.
The JMXMP connector server example is found in the examplesDir/current/Connectors/jmxmp directory.
Compile the Java classes.
$ javac -classpath classpath *.java |
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.
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.
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.
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.
jdmk-http, for the legacy HTTP connector.
jdmk-https, for the legacy HTTPS connector.
jdmk-rmi, for the legacy RMI connector.
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.
com.sun.jdmk.http.server.authinfo.list, specifying the list of AuthInfo[] for an HTTP or HTTPS server.
com.sun.jdmk.client.localhost, specifying a String object as a local host name for an HTTP, HTTPS or RMI client.
com.sun.jdmk.http.client.authinfo, specifying an AuthInfo[] object for an HTTP or HTTPS client to connect to its server.
com.sun.jdmk.http.server.authinfo.list, a list of AuthInfo for an HTTP or HTTPS connector server.
com.sun.jdmk.http.server.local.address, specifying the local InetAddress the HTTP/HTTPS connector server will bind to.
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.
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.
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.
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.
The following example uses the classes in the examplesDir/current/Connectors/wrapping directory.
Compile the Java classes
$ javac -classpath classpath *.java |
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.
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.
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.
Not all of the methods supported by standard JMXConnector and JMXConnectorServer are supported by the wrapped legacy connectors.
The following are the only methods supported by the wrapped legacy connector clients.
addNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter, Object handback)
removeNotificationListener(ObjectName name, ObjectName listener)
removeNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter, Object handback)
removeNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter, Object handback)
The wrapped legacy connector servers only support the getConnectionIds() method.
A legacy connector can only use the class loader that was used to created that connector.
Legacy servers do not send out events relating to client connections or failures, but only about state changes: offline, starting, online.
A client can connect to a legacy connector server created by the JMX Remote API factory only after the server has been registered in an MBean server.
A legacy connector client has no way of showing its local port number. Calling Client.getConnectionId() will return:
protocol://host:server_port count |
Where the count is an integer ID that is unique within a Java virtual machine on the client side. For example:
jdmk-http://host_name:6666 1 |
A legacy connector server must be registered as an MBean in the MBean server which is exposed through that connector server, before the server can be started.