java.rmi.UnmarshalException: java.lang.ClassNotFoundException. Can't get acces to class from another physical JVM using RMI

StackOverflow https://stackoverflow.com/questions/19886814

Domanda

I have client and server on different machines.

When i'm trying to deserialize object on client i get this exception.

java.rmi.UnmarshalException: error unmarshalling return; nested exception is: 
java.lang.ClassNotFoundException: task.MyTask

Here's my server code:

System.setProperty("java.security.policy", "all_policy.policy");
System.setSecurityManager(new RMISecurityManager());
        try
        {
            mngr = new Manager();
            registry = LocateRegistry.createRegistry(15120);

            TaskDistributor stub = (TaskDistributor) UnicastRemoteObject.exportObject(mngr, 0);
            registry.rebind("Manager", stub);
        }
catch (Exception e)
        {
            System.out.println ("Ошибка при инициалиазации RMI: " + e.getMessage() + "\n");
            e.printStackTrace();
        }
...

try
    {
        //when creating a new task
        mngr.setTask(task);
    }
    catch (Exception e)
    {
        System.out.println ("Ошибка при передаче задания: " + e.getMessage() + "\n");
        e.printStackTrace();
    }

And here's the client:

try
{
    //msg.getMessage() = MyTask.class.getClassLoader().getResource("").getPath() called on server machine
    System.setProperty("java.rmi.server.codebase", "file://" + server + msg.getMessage());
    Registry registry = LocateRegistry.getRegistry(server, 15120);
    TaskDistributor mngr = (TaskDistributor) registry.lookup("Manager");
    cg.append("Задание получено\n");

    MatrixMultiplier task = mngr.getTask(); //<------------Exception here
    task.multiply();

    mngr.setTask(task);

    client.sendMessage(new ChatMessage(ChatMessage.TASK_COMPLETED, ""));
}
catch (Exception e)
{
    System.out.println ("Ошибка: " + e.getMessage());
    e.printStackTrace();
}

It does work if i use it on the same local machine so i think remote client JVM can't get access to server class files or i'm using wrong path.

msg.getMessage() returns this: /C:/Projects/IDEA/KachNetworkLab/out/production/server/

Any suggestions would be much appreciated.

Solution:

This HttpServer worked for me. Maybe someone else will find this useful...

HttpServer httpServer = HttpServer.create(address, 10);
httpServer.createContext("/", new HttpHandler() {
            @Override
            public void handle(HttpExchange t) throws IOException {
                try {
                File f = new File(t.getRequestURI().toString().substring(1).replace("\\", "/"));
                    System.out.println(t.getRequestURI().toString().substring(1).replace("\\", "/"));
                    if (!f.exists())
                    {
                        StringBuilder response = new StringBuilder().append("No luck :(");
                        t.sendResponseHeaders(HttpURLConnection.HTTP_OK, response.toString().getBytes().length);
                        t.getResponseBody().write(response.toString().getBytes());
                        t.getResponseBody().close();
                        t.close();
                        return;
                    }

                    t.sendResponseHeaders(HttpURLConnection.HTTP_OK, f.length());
                    InputStream file = new FileInputStream(f);
                    sendFile(file, t.getResponseBody());
                    t.getResponseBody().close();
                    t.close();
                }
                catch(Exception e)
                {
                    e.printStackTrace();
                }
            }
        });

        httpServer.setExecutor(null);
        httpServer.start();

Calling System.setProperty("java.rmi.server.codebase", "http://" + IP + ":8000" + project root path on server); on client will provide RMI with remote codebase path.

You can get root path on server by calling MyClass.class.getProtectionDomain().getCodeSource().getLocation().getPath() for example.

You should be careful though. This way anyone can get access to any file on your server machine. It would be wise to write some security conditions in your handle or modify policy file.

È stato utile?

Soluzione

The problem is that on the client, you're specifying a file: URI, which tells the JVM to look for classes in the local file system. This works when you're running the client JVM on the same actual machine as the server, but if you want the client to be able to download the classes from a different machine, you have to give it some sort of URI it can access over the network. You could use a mounted network share to do this, but the standard way is to make the jars (or bare class files) available via HTTP. The Oracle tech notes on dynamic code downloading have a thorough explanation and several examples.

If you're wanting the RMI server to be able to run "standalone" without talking to any other services, you could embed a Web server in it that binds to a random port, and then have an RMI method for the client to retrieve a URI from the server for downloading.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top