Reputation: 283
Any idea why do I get RemoteException while trying to invoke methods on Unix machine from Windows?
I am inside the network and don't think this is because of firewall problem as I can do "telnet" from Windows to Unix box after starting the RMI server at the unix box. I also could not understand why is it going to local loopback IP?
Stack Trace:
RemoteException occured, details java.rmi.ConnectException: Connection refused to host: 127.0.0.1; nested exception is:
java.net.ConnectException: Connection refused: connect
java.rmi.ConnectException: Connection refused to host: 127.0.0.1; nested exception is:
java.net.ConnectException: Connection refused: connect
Many thanks in advance.
Upvotes: 6
Views: 13327
Reputation: 39
I suggest a solution based on customized RMISocketFactory.
Like explained on Sun Site, you can provide your own SocketFactory : http://docs.oracle.com/javase/7/docs/technotes/guides/rmi/socketfactory/
My solution use this mecanism for intercept client socket creation, and replace the host received (127.0.0.1) by the good IP, well known by the client.
Th rest of the communication mechanism is still based on java rmi standards.
With this implementation, the exporter does not have to know it's own IP, which is sometimes no easy (multiple network interfaces ...)
Here are the tree classes, the Factory, the Server and the Client. The Hello class and interface are also uploaded to be exhaustive.
Hope it should be utile
SocketFactory:
import java.io.IOException;
import java.io.Serializable;
import java.net.ServerSocket;
import java.net.Socket;
import java.rmi.server.RMISocketFactory;
/**
* Socket Factory for RMI calls.
*
* This classe, instanciated from server when RMI objects are exported, is send
* to the client who use it (transparently) for create sockets which call remote objects.
*
* This implementation give the ability to modify dynamically the target host cible.
*
* The host will not be aware of it's own IP.
*/
public class MySocketFactory extends RMISocketFactory implements Serializable {
/**Target host for RMI calls, setted by caller. */
private static String server = "localhost";
/**
* Create a client socket, replacing required host by the host setted when the service is called,
* via {@link #setServer(String)}.
* The host received is usually 127.0.0.1, depending on property java.rmi.server.hostname on the exporter.
*/
@Override
public Socket createSocket(String host, int port) throws IOException {
System.out.println("change host from " + host + " to " + server);
return getFactory().createSocket(server, port);
}
/**
* Create a server socket.
*/
@Override
public ServerSocket createServerSocket(int port) throws IOException {
return getFactory().createServerSocket(port);
}
/**
* Use default RMI factory.
*/
private RMISocketFactory getFactory() {
return RMISocketFactory.getDefaultSocketFactory();
}
/**
* Save the target host. This method must be called before use of a service (before Naming.lookup).
*/
public static void setServer(String host) {
server = host;
}
}
Exporter :
import java.io.IOException;
import java.rmi.Naming;
import java.rmi.RMISecurityManager;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RMISocketFactory;
import java.rmi.server.UnicastRemoteObject;
/**
* RmiExport
*/
public class MyRmiExporter {
/**
* java -Djava.security.policy=java.policy MyRmiExporter
*/
public static void main(String[] args) throws RemoteException, IOException {
System.setSecurityManager(new RMISecurityManager());
Hello export = new HelloImpl();
RMISocketFactory sf = new MySocketFactory();
UnicastRemoteObject.unexportObject(export, true);
Remote stub = UnicastRemoteObject.exportObject(export, 0, sf, sf);
String url = "rmi://0.0.0.0:" + Registry.REGISTRY_PORT + "/Hello";
LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
Naming.rebind(url, stub);
System.out.println("Exported " + url);
}
}
Client :
import java.io.IOException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.registry.Registry;
public class MyClient {
/**
* java MyClient localhost
*/
public static void main(String[] args) throws IOException, NotBoundException, InterruptedException {
String host = args[0];
MySocketFactory.setServer(host);
String url = "rmi://" + host + ":" + Registry.REGISTRY_PORT + "/Hello";;
System.out.println("look up " + url);
Hello proxy = (Hello) Naming.lookup(url);
System.out.println("OK, remote getted !");
System.out.println(proxy.hello("bonjour"));
}
}
Bean :
import java.io.Serializable;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Hello extends Remote, Serializable {
String hello(String mess) throws RemoteException;
}
Impl :
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl extends UnicastRemoteObject implements Hello {
public HelloImpl() throws RemoteException {
}
@Override
public String hello(String mess) throws RemoteException {
return "hello : " + mess;
}
}
last and least, java.policy :
grant {
permission java.security.AllPermission;
};
Upvotes: -1
Reputation: 24062
You probably don't have your hostname configured properly on your Linux box. I bet if you ping $(hostname)
from your Linux box, it will ping 127.0.0.1
. Usually this is because of an entry in your /etc/hosts
file.
There's a couple of ways to solve the problem. The hard way would be to get your Linux box to resolve its own hostname to its IP address properly. You can edit your /etc/hosts
file, setup your DNS server, whatever you've got to do. The challenge is that while this may make things more technically correct, you run the risk of breaking things that relied on the old behavior.
The path of least change would be to set the system property java.rmi.server.hostname
to the hostname or IP address of your Linux box. (i.e. java -Djava.rmi.server.hostname=$(hostname) ...
).
Why?
The Java RMI registration server is actually a network wide registration server. Objects on other machines can bind themselves to this registry.
When a remote object is registered, the registration includes the network address as part of the registration. By default, the address it uses is 'the IP address of the local host, in "dotted-quad" format.' In your setup, this address is 127.0.0.1
.
When your Windows box contacts the registration service for the address of the remote object, it gets back 127.0.0.1
. It then tries to contact the remote object at that address. That's why it's going to the loopback address.
Upvotes: 12