i'm trying to implement a basic distributed application using the Java RMI framework.
Implementing it locally, I can do it, but when I want to manage dynamic class loading remotely, by loading the respective .class files of the classes on an HTTP Server in "Remote" (using virtual machines in the same local network), the Client fails to correctly download the .class for the stub:
Exception in thread "main" java.lang.NoClassDefFoundError: Example1 / RemoteAdd).
I have tried to run the Server in a Windows machine together with the RMI Registry, and in another virtual machine with Ubuntu (connected through a LAN) the Client (also in this machine there is also the HTTP Web Server with files. class).
But nothing, I get the exception:
Exception in thread "main" java.lang.NoClassDefFoundError: Example1 / RemoteAdd.
IP Window Machine (Server and RMI Registry) : 192.168.56.1
IP Ubuntu Machine (Client and HTTP Server) : 192.168.1.70
This is the Server class:
package Esempio1;
import java.rmi.Naming;
import java.security.Policy;
public class ServerAdd {
public ServerAdd() {
try {
System.setProperty("java.rmi.server.hostname","192.168.56.1");
System.setProperty("java.rmi.server.useCodebaseOnly","false");
System.setProperty("java.security.policy","policy.txt");
System.setProperty("java.rmi.server.codebase","http://192.168.1.70/Root/");
System.out.println(System.getProperty("java.rmi.server.codebase"));
System.out.println(System.getProperty("java.rmi.server.useCodebaseOnly"));
System.setSecurityManager(new SecurityManager());
RemoteAdd a = new ImpAdd();
Naming.rebind("add", a);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main (String[] args) {
new ServerAdd();
}
}
And this is Client:
package Esempio1;
import java.rmi.Naming;
public class ClientAdd {
public ClientAdd() {
try {
System.setProperty("java.security.policy","policy.txt");
System.setProperty("java.rmi.server.useCodebaseOnly","false");
System.out.println(System.getProperty("java.rmi.server.useCodebaseOnly"));
System.out.println(System.getProperty("java.security.policy"));
if(System.getSecurityManager()==null) {
System.setSecurityManager(new SecurityManager());
}
RemoteAdd a = (RemoteAdd) Naming.lookup("rmi://192.168.56.1:1099/add");
Integer x = new Integer(8);
Integer y = new Integer(2);
Integer c = a.sommaIng(x, y);
System.out.print(c.getClass()+" "+c);
} catch (Exception e) {
System.out.println(e);
}
}
public static void main (String[] args) {
new ClientAdd();
}
}
I send the RMI Registry first like this:
start rmiregistry -J-Djava.rmi.server.useCodebaseOnly=false
Then I run the server on the same machine as the Registry, and it runs smoothly...
Finally I run the Client on the Ubuntu machine, and I get this error back:
Exception in thread "main" java.lang.NoClassDefFoundError: Esempio1/RemoteAdd
at Esempio1.ClientAdd.<init>(ClientAdd.java:23)
at Esempio1.ClientAdd.main(ClientAdd.java:37)
Caused by: java.lang.ClassNotFoundException: Esempio1.RemoteAdd
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 2 more
Also this is the policy file used :
grant
{
permission java.security.AllPermission ;
};
The HTTP Server runs on Ubuntu through the Apache2 service and contains a Root folder with Esempio1 inside and all the .class files (192.168.1.70/Root/Esempio1)
Does anyone know where I'm wrong?
Related
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.
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.
Before you said that, yes, I know, there are a lot of threads which cover the same issue here on Stackoverflow, but any of those solved my problem. My problem is using the RMI interface (that is mandatory for my purposes) in two distinct computers, where one provides the RMI object (Server) and one asks for the Stub and obtains a Proxy. My code is the exact copy of the one provided by the Java 7 Reference Manual by Oracle and edited by Oracle Press:
IRmi.java
import java.rmi.*;
public interface IRmi extends Remote {
double add() throws RemoteException;
}
RmiImpl.java
import java.rmi.*;
import java.rmi.server.*;
public class RmiImpl extends UnicastRemoteObject implements IRmi {
public RmiImpl() throws RemoteException {}
public double add() { double d = 5.0; return d; }
}
Server.java
import java.net.*;
import java.rmi.*;
public class Server {
public static void main(String s[]) {
try {
RmiImpl ri = new RmiImpl();
Naming.rebind("Server",ri);
} catch (Exception e) {
System.err.println("Err");
}
}
}
Client.java
import java.rmi.*;
public class Client {
public static void main(String s[]) {
try {
IRmi itf = (IRmi)Naming.lookup("rmi://192.168.0.8/Server");
System.out.println(itf.add());
} catch (Exception e) {
e.printStackTrace();
}
}
}
After that, I compile with:
javac IRmi.java
javac RmiImpl.java
rmic RmiImpl
javac Client.java
javac Server.java
After that passage, I copy all the classes on both the client and the server, and then I run rmiregistry on the same folder where the classes were transfered. Assuming that in my local lan (192.168.0.0/255) there are two machines, where the client is 192.168.0.3 and the server 192.168.0.8 I run on those machine respectively java client and java Server, where the Client returns me the following error:
java.rmi.ConnectException: Connection refused to host: 127.0.1.1; nested exception is:
java.net.ConnectException: Connessione rifiutata
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:129)
at RmiImpl_Stub.add(Unknown Source)
at Client.main(Client.java:8)
Caused by: java.net.ConnectException: Connessione rifiutata
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:579)
at java.net.Socket.connect(Socket.java:528)
at java.net.Socket.<init>(Socket.java:425)
at java.net.Socket.<init>(Socket.java:208)
at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(RMIDirectSocketFactory.java:40)
at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(RMIMasterSocketFactory.java:147)
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:613)
... 5 more
For instance, In another test I've also tried to implement the server with this following code:
try {
RemImpl obj = new RemImpl(this.serv);
if (this.ob_list.size()>0) {
for (Observer ob: ob_list) {
obj.addObserver(ob);
}
}
this.myrec = (Rem) UnicastRemoteObject.exportObject(obj, 9999);
Registry registry = LocateRegistry.createRegistry(9999);
registry.rebind(this.serv, this.myrec);
//this.has_error = false;
System.out.println("Binded as "+this.serv);
} catch (RemoteException e) {
System.err.println("Remote exception catched: " + e.getMessage());
//this.has_error = true;
this.myrec = null;
}
and the client with the other following code:
try {
Registry registry = LocateRegistry.getRegistry(host);
this.myrec = (Rem) registry.lookup(service);
} catch (Exception e) {
e.printStackTrace();
}
and, in this case, the client returns me the following and different error:
java.rmi.NoSuchObjectException: no such object in table
ERROR
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:275)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:252)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:161)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:194)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:148)
at com.sun.proxy.$Proxy0.send(Unknown Source)
at rmi.lowlevel.NullSenderPolicy.send(NullSenderPolicy.java:81)
at message.policy.old.BroadcastSenderPolicy.single_send(BroadcastSenderPolicy.java:104)
at message.policy.old.AtomicBroadcastSender.fifo_send(AtomicBroadcastSender.java:54)
at message.policy.old.AtomicBroadcastNode.fifo_send(AtomicBroadcastNode.java:131)
at elements.testunit.TestPairBroadcastNodes.main(TestPairBroadcastNodes.java:20)
At this point I don't know which way to turn. Thanks in advance for any other kind suggestion.
The connection refusal seems to be a case of item A.1 in the RMI FAQ.
The NoSuchObjectInTable problem is because you're looking up the wrong Registry. You created it on port 9999 but you're looking up a different one. This can be cured by calling getRegistry(serverHost, 9999) in the client.
You should also make the Registry reference static in the server JVM.
Actually, the problem was lately solved by adding the -Djava.rmi.server.hostname=192.168.0.x argument on both client and server. Thanks for all the advices.
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
I am trying to get a rmi connection going. I have ran into many security issues but have been unable to find a way past all this. I execute my jar file with:
java -Djava.security.policy=java.security.AllPermission -jar "myjarfile"
The code I have been using to create this is:
public class server
{
public static void main(String args[])throws Exception
{
if (System.getSecurityManager() == null)
System.setSecurityManager ( new RMISecurityManager() {
public void checkConnect (String host, int port) {}
public void checkConnect (String host, int port, Object context) {}
});
try
{
sampleserverimpl server = new sampleserverimpl();
System.out.println("SERVER IS WAITING");
LocateRegistry.createRegistry(2020);
//Runtime.getRuntime().exec("rmiregistry 2020");
Naming.rebind("//localhost:2020/SERVER", server);
}
catch(Exception e)
{
System.out.println(e);
}
}
};
The error trace I am receiving is:
Exception in thread "RMI TCP Connection(idle)" java.security.AccessControlExcept
ion: access denied (java.net.SocketPermission 127.0.0.1:31199 accept,resolve)jav
a.rmi.UnmarshalException: Error unmarshaling return header; nested exception is:
java.io.EOFException
I have tried different ways to get around this, can anyone see the issue here?
Thanks
-Djava.security.policy accepts a URL which points to a policy file which in turn contains the permissions. So you should have: -Djava.security.policy=/some/path/my.policy as the JVM argument where the my.policy file contains:
grant {
permission java.security.AllPermission;
};
Also, in order to avoid the NULL check present in your code and the manual creation of a SecurityManager, you can request a SecurityManager be automatically installed for your application by passing the JVM switch: -Djava.security.manager.
Your final JVM invocation should look like:
java -Djava.security.manager -Djava.security.policy=/some/path/my.policy
This is two separate exceptions. The first is a permission problem. The second one, the EOFException, could have any of a number of causes. I would need to see java -version and the complete stack trace to assist further.