Gaurav Panwar
Gaurav Panwar

Reputation: 283

RMI RemoteException

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

Answers (2)

user2462210
user2462210

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

leedm777
leedm777

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

Related Questions