Reputation:
I want to make rmi server on port and network interfaces I choose programmatically (without jvm settings). For example I want the rmi server listen interface 127.0.0.1 and port 2525. I've read information in internet and this is the solution I finally came to.
class ServerSocketFactory implements RMIServerSocketFactory, Serializable {
public ServerSocket createServerSocket(int port) throws IOException
{
ServerSocket server = new ServerSocket(2525, 0, InetAddress.getByName("127.0.0.1"));
return server;
}
}
And this is how I create my registy
registry = LocateRegistry.createRegistry(2525,null,new ServerSocketFactory());
However, I get exception:
java.rmi.server.ExportException: Port already in use: 2525; nested exception is:
java.net.BindException: Address already in use
at sun.rmi.transport.tcp.TCPTransport.listen(TCPTransport.java:341)
I can't understand why if I use ServerSocketFactory in createRegistry there is also a port argument. What is my mistake?
P.S. This is not duplicate of Remote method invocation port in use because it is about selecting network interface but not port.
Upvotes: 1
Views: 1251
Reputation: 310850
That BindException
means the port is already in use. So you're running this code twice, or something else is listening at the port.
You don't have to specify the port number twice. Just use the port number that is provided as a parameter to createServerSocket()
. It will be the port number you specified when exporting, or zero.
If you are using multiple instances of this socket factory, you need to implement equals()
in your socket factory class, such that it returns true
for all instances of this class.
NB RMIServerSocketFactory
implementations do not need to be Serializable
. As a matter of fact you don't really need your RMIServerSocketFactory
implementation class at all other than for binding to 127.0.0.1. Unclear why you would want to do that, as it would mean you are doing all your RMI within the same host, which is pretty futile.
So in summary you need:
registry = LocateRegistry.createRegistry(2525);
and
myObject = new MyRemoteObject(2525);
and
// constructor
public MyRemoteObject(int port) throws RemoteException
{
super(port);
}
If you need the remote object(s) (including the Registry) to listen at only one local IP address instead of all of them, that's when you need an RMIServerSocketFactory
:
public class MyRMIServerSocketFactory implements RMIServerSocketFactory
{
private InetAddress address;
public MyRMIServerSocketFactory(InetAddress address)
{
this.address = address;
}
@Override
public ServerSocket createServerSocket(int port) throws IOException
{
return new ServerSocket(port, 0, address);
}
@Override
public boolean equals(Object that)
{
return that != null && this.getClass() == that.getClass() && this.address.equals((MyServerSocketFactory)that).address);
}
}
To use it:
Registry registry = LocateRegistry.createRegistry(2525, null, new MyServerSocketFactory(address));
and
public MyRemoteObject(int port, InetAddress address) throws RemoteException
{
super(port, null, new MyServerSocketFactory(address);
}
The rules of this game are that ports are shared between remote objects exported from the same JVM (which can include the Registry) if and only if:
equals()
Upvotes: 2