1

Context

This is an application using Java RMI, with:

  • A client on host A and a server on host B.
  • A HTTP server on host A, it holds a copy of all classes used by the client and the server.
  • The RMI registry is created from the server code, it shares server JVM and CLASSPATH.

For the server and the client applications:

  • The java.rmi.server.codebase property is set (in the code) to the HTTP server URL.
  • A security manager is in place with the appropriate policies.
  • The java.rmi.server.hostname is set to the LAN address.

Method invocation:

  • A server method is defined with a parameter of type WorkRequest (abstract class).
  • The client invokes this method using a subclass WorkRequestSquare.
  • WorkRequestSquare is never mentioned on the server code

Server code

Object execute(WorkRequest work) throws RemoteException { return work.execute(); }

Client code:

try { servant.execute(new WorkRequestSquare(123)); }
catch (RemoteException e) {
    System.out.println("Error while submitting work request:");
    e.printStackTrace();
}

This code works when the server has all related classes in the CLASSPATH and no dynamic loading is necessary.

Problem

Removing WorkRequestSquare from the CLASSPATH on the server side throws an exception on the client side (nothing is thrown on the server side):

Error while submitting work request:
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
    java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: 
    java.lang.ClassNotFoundException: WorkRequestSquare
    at sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
    ...

WorkRequestSquare being never explicitly mentioned on the server code, it should be dynamically loadable, but this doesn't happen.

Bypass

I was made aware by Stuart Marks that the property java.rmi.server.useCodebaseOnly should be set to false.

The default value for this property has been changed from false to true in JDK 7u21. useCodebaseOnly set to true prevents the JVM from dynamically loading classes from any location, except the one defined locally using java.rmi.server.codebase. This means that the codebase value set by the client JVM when marshalling the WorkRequestSquare parameter is ignored by a server which useCodebaseOnly is true.

When useCodebaseOnly is set to false on the server, and WorkRequestSquare class is removed from the server CLASSPATH, the server now gets the class definition from the HTTP server. This is a valid bypass.

Question

There is still something abnormal as the client and the server have the same value for the codebase property.

When useCodebaseOnly was defaulted to true the server JVM should have ignored client codebase added to the RMI stream, but it should have anyway used its own codebase to retrieve the WorkRequestSquare class.

This should have been successful anyway because client and server codebase values are identical.

Could someone cast some light on that matter?

mins
  • 6,478
  • 12
  • 56
  • 75
  • 1
    Make sure you set the `java.rmi.server.useCodebaseOnly` property to `false` in both your client and server. – Stuart Marks Aug 15 '14 at 14:24
  • @StuartMarks: Thanks a lot, this solved the problem. Appreciate your help. The default value is `true` since JRE 7. However, I don't understand why this worked: I have the same value for `java.rmi.server.codebase` set by client and server code. `useCodebaseOnly` seems to limit the server to using its own value of `codebase` and forget `codebase` value added by the client to the object sent in the rmi stream. As both `codebase` properties are equal... ?! (Could you please create an answer, so that I can select it) – mins Aug 16 '14 at 10:45
  • Thank you for posting this. This has saved me a lot of time debugging why the server fails to load class from the client codebase. – Kiwi May 13 '15 at 03:59

1 Answers1

0

What is the minimal set of classes that need to be in the classpath?

The classes that are actually used by name in the client code. For example, the remote interface, the types of the formal arguments and the result, exception types named in remote method signatures.

/ what are the classes that can be missing in the classpath, provided they are available using the codebase server?

Classes that implement classes or interfaces above and that aren't used by name in the client code.

Is there a tool to debug RMI communication (e.g. Eclipse plugin) and avoid using a network sniffer?

There certainly was an eclipse plugin, I saw it many years ago, but I've never actually needed one.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Any class used by name in the code has to be on the CLASSPATH. In this case you are sending WorkRequestSquare *to* the server, so the *client* JVM must also be started with the codebase property set, and the server must run under a security manager. – user207421 Aug 14 '14 at 23:55
  • Sorry to keep asking... in my case I can understand WorkRequestSquare is required in the classpath for the client (because it is required to compile the client code), but why is it required in the classpath for the server too (this class is not named in the server code)? Can't the server just download it from the codebase HTTP server when it received a parameter with this type? – mins Aug 15 '14 at 00:21
  • @mins See my previous comment. It isn't required in the CLASSPATH for the server if it is in the codebase *of the client,* i.e. the URL set defined in `java.rmi.server.codebase` system property *of the client JVM.* – user207421 Aug 15 '14 at 00:51
  • EJP, thanks for your patience. Client and server codes set the `codebase` property to a url where (all) the classes are located. They also set a security manager. The problem is elsewhere. You wrote: "*classes [can] be missing in the classpath* [if they] *aren't used by name in the client code*"). `WorkRequestSquare` satisfies this requirement, but contrary to my expectation the server requires `WorkRequestSquare` to be in **its** classpath or an exception is thrown. What is wrong in that case? – mins Aug 15 '14 at 12:25
  • It must be mentioned by name in either the client or the server code, otherwise it wouldn't get used at all. Did you check your sever logs as suggested? – user207421 Aug 16 '14 at 12:42
  • Now, Stuart Marks provided a clue with `java.rmi.server.useCodebaseOnly` which value is now defaulted to `true`. Set back to `false` on the **server**, the problem disappears. But see my comment to his suggestion. There is still something I don't understand. – mins Aug 16 '14 at 13:27
  • 1
    Are the client and server codebase set to the same URL? If not, is the client URL really accessible from the server? – user207421 Aug 17 '14 at 00:03
  • Same URL (using a constant field). URL accessible, this is confirmed in the HTTP server log when `useCodebaseOnly` is set to false on the server. – mins Aug 17 '14 at 07:52