Java RMI Server in Docker Container - java

In a server i am running my docker container with an RMI-server jar file.
I have tried several different configurations but i just cant get it working.
My Serverside:
public class Main extends UnicastRemoteObject implements RmiServerIntf {
public static final String MESSAGE = "Hello World from docker in RMI";
public Main() throws RemoteException {
super(0); // required to avoid the 'rmic' step, see below
}
public String getMessage() {
return MESSAGE;
}
public static void main(String args[]) throws Exception {
System.out.println("RMI server started");
System.setProperty("java.rmi.server.hostname", "<host-ip-address>");
try { //special exception handler for registry creation
LocateRegistry.createRegistry(1099);
System.out.println("java RMI registry created.");
} catch (RemoteException e) {
LocateRegistry.getRegistry();
System.out.println("java RMI registry already exists.");
}
//Instantiate RmiServer
Main obj = new Main();
// Bind this object instance to the name "RmiServer"
Naming.rebind("RmiServer", obj);
System.out.println("PeerServer bound in registry");
}
}
My Client:
public class Main {
public Main() {
}
public static void main(String[] args) throws RemoteException, NotBoundException, MalformedURLException {
RmiServerIntf obj = (RmiServerIntf) Naming.lookup("rmi://<my-host-address>:4023/RmiServer");
System.out.println(obj.getMessage());
}
}
And they both share "RmiServerIntf"
My Dockerfile:
FROM ubuntu:latest
RUN echo "Updating ubuntu image"
RUN apt-get update && apt-get install -y \
openjdk-8-jre
EXPOSE 1099
COPY RMIServer.jar /home/RMIServer.jar
CMD ["java", "-jar", "/home/RMIServer.jar"]
I start my container with:
docker run --name rmitest -d -p 4023:1099 rmitestimage
The client throws me :
Exception in thread "main" java.rmi.ConnectException: Connection refused to host: <my-host-address>; nested exception is:
java.net.ConnectException: Connection refused (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:130)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:227)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:179)
at com.sun.proxy.$Proxy0.getMessage(Unknown Source)
at com.company.Main.main(Main.java:19)

If you export the Registry and your remote object on the same port from the same JVM you will overcome your port problem. You don't need to use a socket factory.
static Registry registry;
// ...
registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
// ...
public Main() throws RemoteException
{
super(Registry.REGISTRY_PORT);
// ...
}
// ....
Main main = new Main();
registry.rebind("RmiServer", main);

It have come to my knowledge that rmi only uses the rmiregistry-port for initializing the connection, and the actual data-transfer is happening on random ports.
Since docker only allows connections to ports you have explicit linked to the host-machine, the initialization of the RMI-server side is happening, but the actual data-transfer from method-invokation is "blocked".
This problem should be possible to overcome with the help of a custom RMI socket factory. Will return with answer if I succeed.

Related

Java RMI connection over internet

I'm trying to get remote objects from a server hosted on different network. I'm able to connect on same machine and on same network, but when I try to get it from different network I get:
Connection refused to host: 192.168.1.131; nested exception is: java.net.ConnectException: Connection timed out: connect
It seems that lookup function is searching at wrong network. I tried to use System.setProperty but it doesn't work. Here the code:
Server
public class Main {
public static void main(String[] args) {
try{
System.out.println("Init server...\n");
TestInterface test = new TestImplement();
System.setProperty("java.rmi.server.hostname", "95.247.x.x");
System.out.println("Reg RMI...\n");
Registry rmiRegistry = LocateRegistry.createRegistry(5555);
rmiRegistry.rebind("Test" , test);
System.out.println("Reg completed!\n");
}catch(Exception e){
e.printStackTrace();
}
}
}
Client
...
registryRMI = LocateRegistry.getRegistry("95.247.x.x",5555);
TestInterface testClient = (TestInterface)registryRMI.lookup("Test");
...
Do I need to set java.rmi.server.hostname in client jar as well?
TestInterface test = new TestImplement();
System.setProperty("java.rmi.server.hostname", "95.247.x.x");
You need to set java.rmi.server.hostname before exporting any remote objects. Doing it afterwards is too late.
System.setProperty("java.rmi.server.hostname", "95.247.x.x");
TestInterface test = new TestImplement();

RMI Server uses wrong IP address

I am starting my RMI server while my network card is connected to Network A. Running my client succeeds as expected and prints "Hello World". As soon as I change my Network connection to Network B (without restarting my RMI server!) I am not able to connect to the server anymore.
Server and client always run on the same host, so using localhost address would be sufficient.
Server:
public class HelloImpl extends UnicastRemoteObject implements Hello {
public HelloImpl() throws RemoteException {
}
public String sayHello() {
return "Hello world!";
}
public static void main(String args[]) throws RemoteException {
Registry registry = LocateRegistry.createRegistry(3128);
registry.rebind("HelloServer", new HelloImpl());
}
}
Client:
public class HelloClient {
public static void main(String arg[]) throws Exception{
Registry registry=LocateRegistry.getRegistry("localhost", 3128);;
Hello result = (Hello) registry.lookup("HelloServer");
System.out.println(result.sayHello());
}
}
Exception is:
HelloClient exception: Connection refused to host: 192.168.169.136; nested exception is:
java.net.ConnectException: Connection timed out: connect
Which refers to my IP address that was assigned while being connected to Network A. There Registry lookup works as expected, only the call to result.sayHello() fails with the exception above.
How to tell RMI to use localhost for everything (and not only the registry)?
Set the system property java.rmi,server.hostname to 127.0.0.1 at the server JVM, before exporting any remote objects, including the Registry.

Java RMI client and remote server Connection Refused to localhost

I am not so experienced in Java RMI. I am trying to implement simple RMI communication, but faced with the problem.
Here my RMI client
public class MainClientApplication {
public static final String FILE_NAME_RMI_POLICY = "rmi.policy";
public static final String RMI_DOMAIN = "mydomain.net";
public static final String RMI_ENDPOINT = "/MD5Decrypter";
public static final String PROTOCOL = "rmi://";
public static void main(String[] args) {
System.out.println(System.getProperty("user.dir") + File.separator + FILE_NAME_RMI_POLICY);
System.setProperty("java.security.policy", System.getProperty("user.dir") + File.separator + FILE_NAME_RMI_POLICY);
String remoteHost = String.format("%s%s%s", PROTOCOL, RMI_DOMAIN, RMI_ENDPOINT);
System.setSecurityManager(new SecurityManager());
try {
ComputeEngine computeEngine = (ComputeEngine) Naming.lookup(remoteHost);
SwingUtilities.invokeLater(() -> new MainWindow("MD5 Decryption", computeEngine));
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Communication inerface
public interface ComputeEngine extends Remote {
Object executeTask(Task t) throws RemoteException;
}
My server
public class MainServer {
public static final int PORT = 1099;
public static final String RMI_ENDPOINT = "MD5Decrypter";
public static final String FILE_NAME_RMI_POLICY = "rmi.policy";
public static final String RMI_DOMAIN = "mydomain.net";
public static final String PROTOCOL = "rmi://";
public MainServer() {
try {
System.setProperty("java.security.policy", System.getProperty("user.dir") + File.separator + FILE_NAME_RMI_POLICY);
//System.setProperty("java.rmi.server.hostname", RMI_IP);
System.setProperty("java.rmi.server.hostname", RMI_DOMAIN);
System.setSecurityManager(new SecurityManager());
ComputeEngine computeEngine = new ComputeEngineExecutor();
// Naming.rebind(String.format("%s%s:%d/%s", PROTOCOL, RMI_DOMAIN, PORT, RMI_ENDPOINT), computeEngine);
Naming.rebind(RMI_ENDPOINT, computeEngine);
System.out.println("Successfully started RMI Server");
} catch (RemoteException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
MainServer mainServer = new MainServer();
}
}
And rmi.policy
grant {
permission java.net.SocketPermission "*:1024-65535", "connect, accept";
permission java.io.FilePermission "-", "read";
permission java.net.SocketPermission "*:80", "connect";
};
So I have started rmiregistry on my server rmiregistry 1099. And everything works well until calling remote object method.
Here are my steps
Lets consider that my domain is mydomain.net , I host it on my server at home with static IP.
Run rmiregistry on the server
Run server application on the server
Run client application on any PC
Click button to invoke remote method
So it seems that everything connects and binds successfully, because if force enter invalid domain, endpoint or refused if rmiregistry is not started - java.rmi.ConnectException is thrown.
But when I try to invoke any method on remote object, BTW which has been already retrieved successfully it throws a java.rmi.ConnectException
java.rmi.ConnectException: Connection refused to host: 127.0.1.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:130)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:227)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:179)
at com.sun.proxy.$Proxy0.executeTask(Unknown Source)
at ui.MainWindow.lambda$initGui$0(MainWindow.java:49)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2348)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
As I can understand the problem is that it (server) forces me to connect to my localhost instead of remote.
P.S on localhost everything works perfectly (both client and server on the one PC)
Please help to solve this problem.
EDIT
I have forgotten to tell that I have tried to set property of java.rmi.server.hostname, in this case it takes a lot of time to start the server and ends with exception.
Leaving the 127.0.0.1 issue aside, which is solved by java.rmi.server.hostname, your problem now is connect timeouts. This will be caused by your firewall. You need to export your remote object on a specific port, and you need to open that TCP port in your firewall.

NoSuchObjectException - No such object in table

I'm try to set up a simple RMI implementation, but I'm having some trouble.
The server starts up fine, but the client can never seem to find the remote object (Naming.lookup fails every time). From reading around people have mentioned storing the remote object (Bank) in a static variable, but that hasn't worked either.
UPDATE: If I remove all references to the port number, the whole thing seems to work fine. Does anyone know why that is?
Server:
public class Bank extends UnicastRemoteObject implements BankInterface {
public static void main(String args[]) throws Exception {
try{
System.setSecurityManager(new SecurityManager());
System.out.println("Security Manager set.");
Bank myBank = new Bank(Integer.parseInt(args[0]));
System.out.println("Bank instance created");
Naming.rebind("Bank", myBank);
System.out.println("Name rebind completed.");
System.out.println("Server ready for requests!");
}catch(Exception e){
System.out.println("Error in main - " + e.toString());
}
}
}
Client
public class ATM {
public static void main (String args[]) throws Exception {
String URL = "//" + args[0] + ":" + args[1] + "/Bank";
System.out.println("Connecting to: " + URL);
BankInterface bank = (BankInterface)Naming.lookup(URL);
System.out.println("Connected!");
}
}
Stacktrace
Exception in thread "main" java.rmi.NoSuchObjectException: no such object in tab
le
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(Stream
RemoteCall.java:276)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:
253)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:379)
at sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source)
at java.rmi.Naming.lookup(Naming.java:101)
at ATM.main(ATM.java:8)
Commands I'm running from cmd.exe are:
rmiregistry
java Bank 7777
java ATM localhost 7777 testMethod
You're running the Registry on its default port, and binding to that Registry, by not using a port number in the bind string, but you're looking up a non-existent Registry on port 7777. The bind string and the lookup string should be the same.
NB lookup isn't the same as connecting. There is no connection to your remote object until you call one of its remote methods.

RMI cannot connect to remote server

I've been plying with RMI recently and while I managed to make it work on locahost I've been having all sorts of problem when trying to use a remote server. Here's the basic code I'm trying to run:
Server:
public class RmiServer extends UnicastRemoteObject implements RmiServerIntf {
public static final String MESSAGE = "Hello world";
public RmiServer() throws RemoteException {
}
public String getMessage() {
return MESSAGE;
}
public static void main(String args[]) {
System.out.println("RMI server started");
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
System.out.println("Security manager installed.");
} else {
System.out.println("Security manager already exists.");
}
try {
LocateRegistry.createRegistry(1099);
System.out.println("java RMI registry created.");
} catch (RemoteException e) {
e.printStackTrace();
}
try {
RmiServer obj = new RmiServer();
Naming.rebind("rmi://localhost/RmiServer", obj);
System.out.println("PeerServer bound in registry");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Remote class interface:
public interface RmiServerIntf extends Remote {
public String getMessage() throws RemoteException;
}
Client:
public class RmiClient {
RmiServerIntf obj = null;
public String getMessage() {
try {
obj = (RmiServerIntf)Naming.lookup("rmi://54.229.66.xxx/RmiServer");
return obj.getMessage();
} catch (Exception e) {
e.printStackTrace();
return e.getMessage();
}
}
public static void main(String args[]) {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
RmiClient cli = new RmiClient();
System.out.println(cli.getMessage());
}
}
rmi.policy file:
grant {
permission java.security.AllPermission;
};
I compiled the classes and created a stub for the server. Then I placed client, stub, interface and policy on my machine and server, stub, interface and policy on the remote machine. The remote server being a Linux machine I made all the files executable. I also added a rule on the local firewall allowing port 1099, and opened all ports on the remote machine
After this I navigated to the server's directory on the remote machine and inserted the following command:
java -Djava.security.policy=rmi.policy RmiServer
This didn't give me problems so I went back to the local machine and entered
java -Djava.security.policy=rmi.policy RmiClient
I wait, and wait and I get the error message:
Connection refused to host: 172.31.xx.xx; nested exception is: java.net.ConnectException: Connection timed out: connect
I've been fighting with these connection errors all day yesterday and this is as far as I got. I'm sure there's only one very small thing I'm still doing wrong but I just can't find what it is.
This may not solve your problem, but I've had similar issues with JPPF (via Java RMI) on Linux. The solution was to ensure that the ephemeral port range on the Client-side machine covered only ports that were allowable by the Client-side's local firewall. E.g., if your firewall allows ports 48000 to 64000 to be connected to by an external machine, ensure that your ephemeral port range also falls within 48000 to 64000. Give that a try and let us know what happens.
System.setProperty("java.rmi.server.hostname","10.0.3.73");
Please use the above statements in your RMIServer side code, and try and connect from remote client again. It worked for me

Categories