Sunday, September 12, 2010

Java RMI is a PITA

My background is mainly in Java desktop applications, not "EE server stuff".  But I had an idea for a simple server app, and, having recently attended an excellent pair of meetups sponsored by the SF Java User's Group, I was psyched to write a server app.  The app involves the instrument software sending status messages to a central server.  So first, how to do that?

In the past, I've often done this by opening a ServerSocket on the server, listening to connection requests from the client on a known port, creating a short lived Socket, and streaming over the data.  It works and I know how to code it.  But it's not particularly robust, nor scalable, and it's "so 1980s".

Since the client (at least initially) is written in Java, I next thought to use RMI.  From the little I knew it was "simple".  The drawback, that it only worked for Java programs, wasn't a deal breaker.  Sun/Oracle provide some good documentation.

As you can see from their docs, basic coding is simple.  Define an interface for the service, implement it on the server, and call it from the client.  Since the messages I was passing were basic Strings, not any fancy business classes, no extra classes needed to be declared.  In my case, the interface was one method

public interface MyInterface extends Remote {
   public void setStatus(String instrumentID, String status) throws RemoteException;

and the implementation was also quite simple, storing the message in a HashMap.  Well, simple once I realized that the call needed to declare that it throws a RemoteException.  Forget that and it won't work.  Also, both server and client need a main() to do basic registration.  But there's much more.  The Devil is in the details.

Setting up the classes and .jar files to compile is a minor PITA.  Even in my very trivial case.  Mainly getting everything onto the right classpath.  As you can imagine from the tutorial, this could be complex for a complex RMI call, or if the classes depend on numerous other classes or jar files.

On the server, somebody must manually start the registry, rmiregistry.  This seems a needless step, because in main the server class calls

MyImpl theServer = new MyImpl();
Naming.rebind(NAME, myImpl);

Why can't the java.rmi.Naming check to see that rmiregistry is running?  There's probably some complex cases where you don't want this, or a security issue, but why not handle the simple case?  BTW, just like a ServerSocket, the client needs to know the host and port for the rmiregistry.  No big advantage to RMI here. (yes, once you have multiple RMI calls, it's simpler cause you need only assign one port, not many)

There's a possible classpath issue on the server, where you may have to set java.rmi.server.codebaseWhy?  In my case the one interface that my client uses, MyInterface, has already been included and resolved in the code - it gets compiled!  Isn't the idea to code to an interface and try not to think about the implementations?  Sure, there's probably a good reason, but another hassle.

Much more tedious is the SecurityManager stuff.  In your main, you have to provide a SecurityManager. 

System.setSecurityManager(new RMISecurityManager());

 Why?  Why have security and force people to put in their own, especially when the code examples all grant AllPermissions?  Seems like saying "we have a lock, but you must replace it with an open door".  And, frankly, I never even got this to work.  Probably got some of the -Ds wrong in the runtime arguments.  Luckily, I found this RMI tutorial  where they override key security checks to do nothing.

System.setSecurityManager (new RMISecurityManager() {
public void checkConnect (String host, int port) {}
  public void checkConnect (String host, int port, Object context) {}

By doing this I got RMI to work.  But the security hassles seem designed to lead the user to provide no security, and, IMO, the whole RMI setup issue is a PITA.  Besides, RMI is so "2000".  If you Google "Java RMI sucks", you'll get a lot of hits.  This one was useful.

I next looked into REST, much more trendy and cutting edge, for which JavaEE 6 provides support.  It worked for me.  More in a later blog.

No comments:

Post a Comment