RMI Adaptors and RMI Connector

Simone Bordet

Revision History
Revision $Revision: 1.1 $$Date: 2002/02/12 10:57:35 $

Introduction

RMI adaptors allow clients to connect to a JMX Agent from a remote JVM using RMI.

OpenJMX's features two RMI adaptors:

  • JRMP Adaptor (class openjmx.adaptor.rmi.jrmp.JRMPAdaptor)
  • IIOP Adaptor (class openjmx.adaptor.rmi.iiop.IIOPAdaptor)

The JRMP Adaptor supports also SSL for secure connections, and both the adaptors support remote notification listeners and filters.

Note that RMI is not a protocol, and here must be intended as "Remote Method Invocation using Java". The underlying protocol can be anyone, and in fact two protocols are supported by the OpenJMX's RMI adaptors: JRMP and IIOP.

JRMP is the Java Remote Method Protocol, and it's the RMI default protocol.
IIOP is the Internet Inter ORB Protocol, and it's the CORBA default protocol.

Remote Method Invocation in Java can have JRMP or IIOP as underlying protocols, and the remote invocation process is usually referred to, respectively, as "RMI" and "RMI over IIOP".
"RMI" is a short for "RMI over JRMP".

JRMP Adaptor

The JRMP adaptor exposes the JMX Agent to RMI over JRMP calls from remote JVMs.
It needs a naming service such as the rmiregistry or the NamingService MBean (see OpenJMX documentation) to be active before being started, and proper directives to find the naming service (such as a jndi.properties file in the classpath). Below you can see the snippet of the code needed in the server to create, register and start the JRMP adaptor.

Example 3.3. Deploying the JRMP adaptor

		
public class Server
{
	public static void main(String[] args) throws Exception
	{
		// Create a MBeanServer
		MBeanServer server = MBeanServerFactory.createMBeanServer();

		// Create and start the naming service
		ObjectName naming = new ObjectName("Naming:type=rmiregistry");
		server.createMBean("openjmx.tools.naming.NamingService", naming, null);
		server.invoke(naming, "start", null, null);

		// Create the JRMP adaptor
		ObjectName adaptor = new ObjectName("Adaptor:protocol=JRMP");
		m_server.createMBean("openjmx.adaptor.rmi.jrmp.JRMPAdaptor", adaptor, null);
		JRMPAdaptorMBean mbean = (JRMPAdaptorMBean)StandardMBeanProxy.create(JRMPAdaptorMBean.class, server, adaptor);
		// Set the JNDI name with which will be registered
		String jndiName = "jrmp";
		mbean.setJNDIName(jndiName);
		// Register the JRMP adaptor in JNDI and start it
		mbean.start();
	}
}
		
			

Let's look now at the client, and how it can interact with the JRMP Adaptor.
All you need is an RMIConnector (class openjmx.connector.rmi.RMIConnector), and proper directives to find the naming service. To find the naming service, you need a jndi.properties file, or a Hashtable with the suitable naming properties to be passed to the RMIConnector. Below you can see the code snippet necessary to connect to the adaptor.

Example 3.4. Deploying the JRMP adaptor

		
public class Client
{
	public static void main(String[] args) throws Exception
	{
		// Create an RMIConnector, passing it the JNDI name of the JRMP Adaptor
		String jndiName = "jrmp";
		RMIConnector connector = new RMIConnector(jndiName);

		// Use the connector as it is an MBeanServer

		String remoteHostName = connector.getRemoteHostName();

		ObjectName objName = new ObjectName("examples:mbean=MyService");
		ObjectInstance instance = connector.createMBean("examples.mbeans.rmi.MyRemoteServiceObject", objName, null);

		NotificationListener listener = new NotificationListener()
		{
			public void handleNotification(Notification n, Object handback)
			{
				// Do something
			}
		};
		connector.addNotificationListener(objName, listener, null, null);

		// and so on
	}
}
		
			

It is VERY important to use the RMIConnector class in the client, and not to try to lookup directly the adaptor in JNDI.
This because the RMIConnector does some trick to understand which is the underlying transport protocol (JRMP or IIOP), and handles the listeners to be invoked remotely by the server.
It is definitely simpler to use the RMIConnector class.

JRMP Adaptor over SSL

The JRMP adaptor can be setup to use SSL to allow secure RMI connections.
Using SSL with RMI over JRMP requires the use of custom RMI socket factories, and requires the server to setup a keystore where a private key and the corresponding public key wrapped in a X509 self-signed certificate are stored.
The certificate is then sent to the client when the connection is established, and the client must trust the server's certificate by looking it up in a truststore (in the client JVM). If the certificate found in the truststore matches the one sent by the server, the SSL connection is complete and data exchange may begin.

The operations needed to setup the JRMP adaptor over SSL are the following:

  • Generate a keystore file using JDK's keytool
  • Export the X509 certificate in a truststore file using JDK's keytool
  • Deploy a SSL ServerSocket factory MBean with the information about the keystore you just generated
  • Deploy the JRMP adaptor MBean passing it the information about the just deployed SSL ServerSocket factory MBean
  • Start the JRMP adaptor

The operations needed to setup the client are the following:

  • Copy the generated trust.store file containing the server certificate to the client
  • Invoke the java interpreter with the following system property: javax.net.ssl.trustStore=<path>/trust.store

Below you can find the code snippets for the server; the client is exactly equal as before.

Example 3.5. Deploying the JRMP adaptor over SSL

		
// Generate the keystore
keytool -genkey -v -keystore key.store -storepass storepwd -keypass keypwd -dname "CN=Simone Bordet, OU=Project Administrator, O=OpenJMX, L=Torino, S=TO, C=IT" -validity 365

// Export the X509 certificate
keytool -export -v -storepass storepwd -keystore key.store | keytool -import -v -storepass storepwd -keystore trust.store -noprompt

// Deploy the MBeans
public class Server
{
	public static void main(String[] args) throws Exception
	{
		// Create a MBeanServer
		MBeanServer server = MBeanServerFactory.createMBeanServer();

		// Create the SSL ServerSocket factory
		ObjectName ssl = new ObjectName("Adaptor:service=SSLServerSocketFactory");
		server.createMBean("openjmx.adaptor.ssl.SSLAdaptorServerSocketFactory", ssl, null);
		SSLAdaptorServerSocketFactoryMBean factory = (SSLAdaptorServerSocketFactoryMBean)StandardMBeanProxy.create(SSLAdaptorServerSocketFactoryMBean.class, server, ssl);
		factory.setKeyStoreName("key.store");
		factory.setKeyStorePassword("storepwd");
		factory.setKeyManagerPassword("keypwd");

		// Create and start the naming service
		ObjectName naming = new ObjectName("Naming:type=rmiregistry");
		server.createMBean("openjmx.tools.naming.NamingService", naming, null);
		server.invoke(naming, "start", null, null);

		// Create the JRMP adaptor
		ObjectName adaptor = new ObjectName("Adaptor:protocol=JRMP");
		m_server.createMBean("openjmx.adaptor.rmi.jrmp.JRMPAdaptor", adaptor, null);
		JRMPAdaptorMBean mbean = (JRMPAdaptorMBean)StandardMBeanProxy.create(JRMPAdaptorMBean.class, server, adaptor);
		// Set the JNDI name with which will be registered
		String jndiName = "jrmp";
		mbean.setJNDIName(jndiName);
		// Set the SSL ServerSocket Factory
		mbean.setSSLFactory(ssl.toString());
		// Register the JRMP adaptor in JNDI and start it
		mbean.start();
	}
}
		
			

IIOP Adaptor

The IIOP adaptor exposes the JMX Agent to RMI over IIOP calls from remote JVMs.
It needs a naming service such as tnameserv to be active before being started, and proper directives to find the naming service (such as a jndi.properties file in the classpath). Below you can see the snippet of the code needed in the server to create, register and start the IIOP adaptor.

Example 3.6. Deploying the IIOP adaptor

		
public class Server
{
	public static void main(String[] args) throws Exception
	{
		// Create a MBeanServer
		MBeanServer server = MBeanServerFactory.createMBeanServer();

		// Remember to start tnameserv !

		// Create the IIOP adaptor
		ObjectName adaptor = new ObjectName("Adaptor:protocol=IIOP");
		m_server.createMBean("openjmx.adaptor.rmi.iiop.IIOPAdaptor", adaptor, null);
		IIOPAdaptorMBean mbean = (IIOPAdaptorMBean)StandardMBeanProxy.create(IIOPAdaptorMBean.class, server, adaptor);
		// Set the JNDI name with which will be registered
		String jndiName = "jrmp";
		mbean.setJNDIName(jndiName);
		// Register the IIOP adaptor in JNDI and start it
		mbean.start();
	}
}
		
			

The client is exactly equal as before, as the RMIConnector is smart enough to understand the underlying protocol and act properly.