I'm stuck with that for at least 5 hours now and have no other resort but to ask here. I'm writing an RMI application. I'd like the server to bind a remote object (NoteBoardImpl
here), that will be looked-up by the client. Client passes its listener (NoteBoardListener
here) to the server, the listener is also a remote object exported by the client.
I've prepared a simple SSCCE here, so I really hope somebody can look into it. All the classes are in the same folder the default package. I know it's discouraged and I should've split the application in three jars, but I wanted to keep it as simple as possible here.
Remote interfaces:
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface INoteBoard extends Remote {
public void test(INoteBoardListener listener) throws RemoteException;
}
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface INoteBoardListener extends Remote {
public void onNewText(String text) throws RemoteException;
}
Interfaces implementations:
import java.rmi.RemoteException;
public class NoteBoardImpl implements INoteBoard {
@Override
public void test(INoteBoardListener listener) throws RemoteException {
listener.onNewText("server call the listener");
}
}
import java.rmi.RemoteException;
public class NoteBoardListener implements INoteBoardListener {
@Override
public void onNewText(String text) throws RemoteException {
System.out.println(text);
}
}
Client and server:
import java.rmi.Naming;
import java.rmi.server.UnicastRemoteObject;
public class Client {
public static void main(String[] args) {
if (args.length < 2) {
System.out.println("2 arguments required:\nRMI_IP RMI_port");
return;
}
System.setProperty("java.rmi.server.hostname", args[0]);
try {
INoteBoard nb = (INoteBoard) Naming.lookup(String.format("rmi://%s:%s/note", args[0], args[1]));
INoteBoardListener l = (INoteBoardListener) UnicastRemoteObject.exportObject(new NoteBoardListener(), 0);
nb.test(l);
l.onNewText("client invokes listener");
} catch (Exception e) {
e.printStackTrace();
}
}
}
import java.rmi.Naming;
import java.rmi.server.UnicastRemoteObject;
public class Server {
public static void main(String[] args) {
if (args.length < 2) {
System.out.println("2 arguments required:\nRMI_IP RMI_port");
return;
}
System.setProperty("java.rmi.server.hostname", args[0]);
try {
INoteBoard noteBoard = (INoteBoard) UnicastRemoteObject.exportObject(new NoteBoardImpl(), 0);
Naming.rebind(String.format("rmi://%s:%s/note", args[0], args[1]), noteBoard);
} catch (Exception e) {
e.printStackTrace();
}
}
}
I've tried to simulate a distributed system for testing purposes and ran the client on a virtual machine. The host-VM network has the following specs - host IP = 192.168.56.1, VM IP = 192.168.56.101.
First I ran the client and the server locally, using the following commands (having started rmiregistry 1099
beforehand). The working directory is the project's root and the compiled classes are in bin
directory:
java -cp bin -Djava.rmi.server.codebase=http://student.agh.edu.pl/~grajewsk/bin/ Server 192.168.56.1 1099
java -cp bin Client 192.168.56.1 1099
And it worked.
Then I ran the client program on the VM using the same command and here's the exception I got:
java.rmi.ConnectException: Connection refused to host: 192.168.56.1; nested exception is:
java.net.ConnectException: Connection refused
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:619)
at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:216)
at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:202)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:128)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:194)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:148)
at sun.proxy.$Proxy0.test(Unknown Source)
at Client.main(Client.java:14)
Caused by: java.net.ConnectException: Connection refused
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:327)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:193)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:180)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:384)
at java.net.Socket.connect(Socket.java:546)
at java.net.Socket.connect(Socket.java:495)
at java.net.Socket.<init>(Socket.java:392)
at java.net.Socket.<init>(Socket.java:206)
at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(RMIDirectSocketFactory.java:40)
at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(RMIMasterSocketFactory.java:146)
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:613)
... 7 more
Notice how the object is successfully looked-up in the server's registry, then the client-side remote object is exported (also with success) and the execution breaks in the 14-th line, where I'm trying to invoke a method on the server-side object passing the client-side object.
I have no firewalls on either of the systems, pings in both directions go flawlessly. I know that there must be some conceptual problem here and certainly I misunderstood something about the RMI. I'd very much appreciate your help.
The binary codebase is on my student's server, as well as the source code. Thank you in advance!
Ok, finally solved it. The problem was, that I was passing the same java.rmi.server.hostname
argument in both programs, that means that both client and server got the address of the server here. It turned out, that if the client wants to export its own objects, it has to provide its own IP to the java.rmi.server.hostname
. This way everything works fine.
So, to sum it up, I had to give 3 arguments to the server:
RMI_IP RMI_port server_hostname
And 3 analogous arguments to the client:
RMI_IP RMI_port client_hostname