Thursday, June 25, 2009

Viewing Names Bound to RMI Registry

When working with Java Remote Method Invocation (RMI), there are times when it is helpful to know which names are currently bound to a particular rmiregistry on a particular host/port combination. This is especially true when debugging problems related to getting an RMI client unable to connect to an RMI server either because the server cannot be found (NotBoundException) or because the server port is already bound to the provided name (AlreadyBoundException).

A simple Java application can be written that provides all named bindings for an RMI registry on a particular host and port. The simple application demonstrated in this posting takes advantage of standard Java classes such as LocateRegistry, Registry, and other classes and exceptions in the java.rmi and java.rmi.registry packages. The code for this application is shown next.


RmiPortNamesDisplay.java

package dustin.examples.rmi;

import java.rmi.ConnectException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

/**
* Display names bound to RMI registry on provided host and port.
*/
public class RmiPortNamesDisplay
{
private final static String NEW_LINE = System.getProperty("line.separator");

/**
* Main executable function for printing out RMI registry names on provided
* host and port.
*
* @param arguments Command-line arguments; Two expected: first is a String
* representing a host name ('localhost' works) and the second is an
* integer representing the port.
*/
public static void main(final String[] arguments)
{
if (arguments.length < 2)
{
System.err.println(
"A host name (String) and a port (Integer) must be provided.");
System.err.println(
"\tExample: java dustin.examples.rmi.RmiPortNamesDisplay localhost 1099");
System.exit(-2);
}

final String host = arguments[0];
int port = 1099;
try
{
port = Integer.valueOf(arguments[1]);
}
catch (NumberFormatException numericFormatEx)
{
System.err.println(
"The provided port value [" + arguments[1] + "] is not an integer."
+ NEW_LINE + numericFormatEx.toString());
}

try
{
final Registry registry = LocateRegistry.getRegistry(host, port);
final String[] boundNames = registry.list();
System.out.println(
"Names bound to RMI registry at host " + host + " and port " + port + ":");
for (final String name : boundNames)
{
System.out.println("\t" + name);
}
}
catch (ConnectException connectEx)
{
System.err.println(
"ConnectionException - Are you certain an RMI registry is available at port "
+ port + "?" + NEW_LINE + connectEx.toString());
}
catch (RemoteException remoteEx)
{
System.err.println("RemoteException encountered: " + remoteEx.toString());
}
}
}



To test out the above application, I can start any service exposing an RMI interface. For this example, I have started a GlassFish domain as shown in the next screen snapshot.



The port on which GlassFish exposes its JMX RMI interface for management and monitoring is highlighted in the screen snapshot and is 8686. When I run the simple RMI port names display application shown above on the same host on which I ran GlassFish, I can use "localhost" as the host. When I run the above Java application, I see two bound names on the RMI registry on localhost at port 8686. This is shown in the next screen snapshot.



From the results shown in the above image, we see that GlassFish exposes two named services on port 8686: jmxrmi and management/rmi-jmx-connector.

The simple application shown above uses standard Java libraries and classes, but also has a "script" feel. It seems like what would really work well here is a script language that uses Java classes. In other words, a scripting language that runs on the JVM such as JRuby or Groovy seems like the perfect fit. With that in mind, the next code listing shows a Groovy implementation of the application written above in traditional Java.

rmiPortNamesDisplay.groovy

import java.rmi.ConnectException
import java.rmi.RemoteException
import java.rmi.registry.LocateRegistry
import java.rmi.registry.Registry

if (args.length < 2)
{
println "A host name (String) and a port (Integer) must be provided."
println "\tExample: groovy rmiPortNamesDisplay localhost 1099"
System.exit(-2)
}

host = args[0]
port = 1099
try
{
port = Integer.valueOf(args[1])
}
catch (NumberFormatException numericFormatEx)
{
println "The provided port value '${args[1]}' is not an integer."
System.exit(-1)
}

registry = LocateRegistry.getRegistry(host, port)
boundNames = registry.list()
println "Names bound to RMI registry at host ${host} and port ${port}:"
boundNames.each{println "\t${it}"}


The above Groovy script, like the Java application from which it was adapted, can be run on the command line. This is shown in the next screen snapshot.



Besides the obvious syntactic differences, one advantage of writing something like this in Groovy is that it does not require an explicit compilation step. The Java application above had to be compiled into a Java .class file first and then executed. With Groovy, this is all done implicitly so that to the user it just feels like running a text-based script. I am especially fond of using Groovy in cases like this where I wish to combine scripting features with accessibility to the JVM and standard Java libraries.


Conclusion

When working with RMI, there are times when it is important to know which named services are already bound to an RMI registry at a given host and port. This blog posting has demonstrated use of the LocateRegistry, Registry, and other relevant classes to do this in a fairly easy manner with traditional Java and with Groovy. It is a straightforward process to extend the Java application and Groovy script shown in this blog posting to cover multiple hosts and ports to, in effect, search for RMI registered names.

No comments: