user2770791 user2770791 - 2 months ago 15
Java Question

Java RMI ClassNotFoundException on the client side

There have been several questions with this same Title, but none of them have helped me solve my issue.

I have been trying to follow the tutorial here:
https://docs.oracle.com/javase/tutorial/rmi/overview.html

I was driving myself crazy by putting everything in one project, so I've created 3 separate projects:


  1. Server -> It has Main (Driver), and class:ServerNode implements Compute

  2. Client -> It has Main (Driver), and class:TestTask implements Task

  3. Shared -> It has interface:Task, and interface:Compute



The idea is... both applications need to know about the Interfaces Task (The unit of work the client sends to the server), and Compute (Which is the interface of ServerNode that the client uses to give commands to).

Server Code:

System.setProperty("java.security.policy","file:server.policy");
System.out.println(System.getProperty("java.security.policy"));

if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}
int port = 1099;
try {
LocateRegistry.createRegistry(port);
String name = "Compute"; // Name of the interface
Compute engine = new ServerNode(); // Implementor of Compute interface, ComputeEngine
Compute stub =
(Compute) UnicastRemoteObject.exportObject(engine, 0); // Create the stub the client will use
Registry registry = LocateRegistry.getRegistry(port);
registry.bind(name, stub); // Bind the "Compute" to the stub, so client can access it by "Compute"
System.out.println("ComputeEngine bound");
} catch (Exception e) {
System.err.println("ComputeEngine exception:");
e.printStackTrace();
}


Client Code:

System.out.println("Entering client");
String lookupName = "localhost";
System.setProperty("java.security.policy","file:client.policy");
System.out.println(System.getProperty("java.security.policy"));
System.setProperty("rmi.server.codebase", "file:/c:/Users/Brian/Documents/RMI_ClientSide/TestTask.class");
System.out.println(System.getProperty("rmi.server.codebase"));
if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}
try {
String name = "Compute";



Registry registry = LocateRegistry.getRegistry("localhost", 1099);

Compute comp99 = (Compute)registry.lookup("Compute");

System.out.println(comp99.executeTask(new TestTask()));

} catch (Exception e) {
System.err.println("ComputePi exception:");
e.printStackTrace();
}


I am led to believe, from the tutorial
That only the CLIENT needs to know about TestTask... It doesn't/shouldn't need to go in Shared. The server just needs to know about the interface Task and the client gives it an implementor of that interface... I believe in the background the server downloads the concrete code from the clients codebase.

My directory structure is
1. Documents/RMI_ServiceSide
2. Documents/RMI_ClientSide
3. Documents/RMI_Shared

Right now I am programmatically starting up the rmiregister in the server code, instead of running it on the command line. I don't think that's my problem, but at this point (after 4 hours of debugging) I have no idea. I am able to start the server though with that code.

The problem is on the client side. When I try to run the client code, I get:

Entering client
file:client.policy
file:/c:/Users/Brian/Documents/RMI_ClientSide/TestTask.class
ComputePi exception:
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: com.company.TestTask
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:355)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:568)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:276)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:253)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:162)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:227)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:179)
at com.sun.proxy.$Proxy0.executeTask(Unknown Source)
at com.company.Main.main(Main.java:46)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: com.company.TestTask
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:315)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:568)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ClassNotFoundException: com.company.TestTask


Alright, it appears that when I try to execute the TestTask, the Registry comes back that it can't find it. I have tried every possible combination of paths for the -Djava.rmi.server.codebase=file:/c:/Users/Brian/Documents/RMI_ClientSide/ command line argument AND/OR the System.setProperty("rmi.server.codebase", "file:/c:/Users/Brian/Documents/RMI_ClientSide/TestTask.class");

Absolutely nothing seems to work... I don't know if this is an issue with the rmiregistry or if I just didn't hit the correct permutation on my codebase. This is all on one machine, so I don't know why it is having problems finding it.

To be explicit... The files that would be necessary:
1. Client -> RMI_ClientSide/bin/.../Main.class and TestTask.class
2. Server -> RMI_ServerSide/bin/..Main.class and ServerNode.class
3. Shared -> RMI_Shared/bin/.../RMI_Shared.jar -> contains Task.class and Compute.class

As I said, these are three separate projects, BUT the package name is the same... package com.company; Just by coincidence, I didn't rename the default when creating each project. I don't know if that matters either. Certainly didn't when importing RMI_Shared.jar into server and client projects... they both compiled fine.

One other answer I found gave the impression Shared would need to contain the TestTask too... but that doesn't make sense as the registry should pull the class definition up to the server when needed... That way a client can execute arbitrary code up to the server (Which is intended in this simple insecure test).

I would appreciate any help to get this running. Thanks!

EDIT: Added a link to my codebase folder image
Codebase directory

Note that the directories for my projects themselves are just documents/. Client, server, and helper.

EJP EJP
Answer
java.rmi.ServerException: RemoteException occurred in server thread

The class is not found at the server. You sent an instance of com.company.TestTask but the server doesn't have that class on its CLASSPATH and cannot find it via the codebase feature.

System.setProperty("rmi.server.codebase", "file:/c:/Users/Brian/Documents/RMI_ClientSide/TestTask.class");

Your codebase string is wrong. It should not include the class file name or any directory that is part of its package. A codebase is a list of URLs that are either JAR files or directories that are heads of package structures.

Comments