RMI implementing Session management

2.2k views Asked by At

I want to build system so that : 1. Client connects to Server 2. Client asks for a port to server him 3. Server creates a remote object to serve the Client and binds it to a port 4. return the port number to client 5. Client connects to the port

My session/connection manager

public class ConnectionManager extends UnicastRemoteObject implements Server
{
public List<ClientHandler> clients = Collections.synchronizedList(new ArrayList<>());
public ConnectionManager() throws RemoteException 
{
    super();
}

public static final String RMI_ID = "Server";

@Override
public boolean checkConnection() throws RemoteException 
{
    return true;
}

@Override
public int getPort(String ip) throws RemoteException
{
    int i = 10000+clients.size()*2;
    clients.add(new ClientHandler(ip, i));
    return i;
}
}

Session implementation

public class ClientHandler extends UnicastRemoteObject implements Transfer
{
Registry rmi;
Registry reg;
PrintWriter log;
public Client client;
public ClientHandler(String ip, int port) throws RemoteException
{
    super();
    try
    {
        File f = new File(ip+" "+new Date()+".txt");
        log = new PrintWriter(f);
    }catch (Exception e)
    {

        e.printStackTrace();
    }
    rmi = LocateRegistry.createRegistry(port);
    try
    {
        rmi.bind(String.valueOf(port),this);
    }catch(Exception e)
    {
        e.printStackTrace();
    }
}

The problem that if the object is created in a remote call , it is considered of remote origin and so unless you are on the same host it is not allowed to bind one to the LocalRegistry. and server throws java.rmi.AccessException Registry.Registry.bind disallowed : origin IP address is non-local host.

1

There are 1 answers

2
user207421 On

The problem that if the object is created in a remote call , it is considered of remote origin

Untrue. This is simply not correct. You've just made this up. Don't do that.

and so unless you are on the same host it is not allowed to bind one to the LocalRegistry.

But you are on the same host. The constructor ClientHandler runs in the same host as the ConnectionManager and it creates a Registry, also in the same host. Indeed all this takes place within the same JVM.

and server throws java.rmi.AccessException Registry.Registry.bind disallowed : origin IP address is non-local host.

No it doesn't. I tested your code. After fixing a compilation error it worked. Cannot reproduce.

HOWEVER

You don't need the part about the port, or the extra bind. All remote objects can share the same port. The server only needs to return a new instance of the remote session object to the client, directly.

A simple implementation of your requirement, using your classnames, looks like this, with a certain amount of guesswork as you didn't provide the Server interface:

public interface Server extends Remote
{
    boolean checkConnection() throws RemoteException;
    Transfer getSession(String ip) throws RemoteException;
}

public class ConnectionManager extends UnicastRemoteObject implements Server
{
    public List<ClientHandler> clients = Collections.synchronizedList(new ArrayList<>());
    public ConnectionManager() throws RemoteException 
    {
        super();
    }

    public static final String RMI_ID = "Server";

    @Override
    public boolean checkConnection() throws RemoteException 
    {
        return true;
    }

    @Override
    public Transfer getSession(String ip) throws RemoteException
    {
        ClientHandler ch = new ClientHandler(ip);
        clients.add(ch);
        return ch;
    }
}

public class ClientHandler extends UnicastRemoteObject implements Transfer
{
    PrintWriter log;
    public Client client;
    public ClientHandler(String ip) throws RemoteException
    {
        super();
        try
        {
            File f = new File(ip+" "+new Date()+".txt");
            log = new PrintWriter(f);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

EDIT

It is clear from your post and your comment below that you are suffering from a number of major misconceptions about Java RMI:

  • if a remote object is created in a remote call , it is not 'considered of remote origin'
  • The port on which an object is exported has nothing to do with the Registry.

    It is determined when you construct an object that extends UnicastRemoteObject, via the super(), call, or when you call UnicastRemoteObject.exportObject() for other objects. If you supply a non-zero port number, that port is used, and can normally shared with other remote objects. Otherwise if there is already an RMI port in use it is shared with this object, otherwise a system-allocated port number is obtained.

    Note that the export step precedes the bind step, so it is quite impossible for your misconception to be correct.

  • You don't need multiple Registries running in the same host. You can use a single one and bind multiple names to it.
  • If you first call LocateRegistry.createRegistry(), all other remote objects you export from that JVM can share the port with the Registry.
  • Remote methods can return remote objects.

    The Registry is an example: it is actually very little more than a remote hash-map. Your methods can do it too. The objects are replaced by their stubs during the return process.

For those reasons, your intended design is completely fallacious and your comment below complete nonsense.

Further notes:

  • A do-nothing method doesn't really check a connection. It is just as likely to create a new connection and check that. Connections don't really exist in RMI, or at least they are very well hidden from you, pooled, expired, etc.
  • You don't need to pass the client's IP address. You can get it at the server from RemoteServer.getClientHost().
  • The constructor for ClientHandler should not catch IOExceptions internally: it should throw them to the caller, so the caller can be aware of the problem.