Mauricio Ortiz
Mauricio Ortiz

Reputation: 33

Error executing client RMI

Sorry for writing but use a translator , I hope you can help me with an example of an RMI I'm testing.

I see the following error when running the client:

java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
    java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
    java.lang.ClassNotFoundException: client.Pi

The project structure is as follows.

|-Cliente\
|   |-client\  ComputePi (main class) and Pi(Task)
|      |-->ComputePi.java
|      |-->ComputePi.class
|      |-->Pi.java
|      |-->Pi.class
|   |-compute\
|      |-->Compute.java
|      |-->Compute.class
|      |-->Task.java
|      |-->Task.class
|   |-public\
|      |-classes\
|         |-->compute.jar 
|         |-client\
|            |--> Pi.class
|-->client.policy

|-Servidor\
|   |-compute\
|      |-->Compute.java
|      |-->Compute.class
|      |-->Task.java
|      |-->Task.class
|   |-engine\
|      |-->ComputeEngine.java
|      |-->ComputeEngine.class
|   |-public\
|      |-classes\
|         |-->compute.jar 
|-->server.policy

The server starts fine, but when I start the client it sends me the error shown above. When I run the client I do as follows:

java -cp c:\Users\Mauricio\Documents\RMI\Cliente;c:\Users\Mauricio\Documents\RMI\Cliente\public\classes\compute.jar
   -Djava.rmi.server.codebase=file:/c:/Users/Mauricio/Documents/RMI/Cliente/public/classes/
   -Djava.security.policy=c:/Users/Mauricio/Documents/RMI/Cliente/client.policy 
   client.ComputePi 127.0.0.1 45

Can you help me understand what is causing this error?

Sorry for not putting before the code.

Class Compute

package compute;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Compute extends Remote {
    <T> T executeTask(Task<T> t) throws RemoteException;
}

Class Task

package compute;

public interface Task<T> {
    T execute();
}

Class ComputePi

package client;

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.math.BigDecimal;
import compute.Compute;

public class ComputePi {
    public static void main(String args[]) {
        if (System.getSecurityManager() == null) {
            System.setSecurityManager(new SecurityManager());
        }
        try {
            String name = "Compute";
            Registry registry = LocateRegistry.getRegistry(args[0]);
            Compute comp = (Compute) registry.lookup(name);
            Pi task = new Pi(Integer.parseInt(args[1]));
            BigDecimal pi = comp.executeTask(task);
            System.out.println(pi);
        } catch (Exception e) {
            System.err.println("ComputePi exception:");
            e.printStackTrace();
        }
    }    
}

Class Pi

package client;

import compute.Task;
import java.io.Serializable;
import java.math.BigDecimal;

public class Pi implements Task<BigDecimal>, Serializable {

    private static final long serialVersionUID = 227L;

    /** constants used in pi computation */
    private static final BigDecimal FOUR =
        BigDecimal.valueOf(4);

    /** rounding mode to use during pi computation */
    private static final int roundingMode = 
        BigDecimal.ROUND_HALF_EVEN;

    /** digits of precision after the decimal point */
    private final int digits;

    /**
     * Construct a task to calculate pi to the specified
     * precision.
     */
    public Pi(int digits) {
        this.digits = digits;
    }

    /**
     * Calculate pi.
     */
    public BigDecimal execute() {
        return computePi(digits);
    }

    /**
     * Compute the value of pi to the specified number of 
     * digits after the decimal point.  The value is 
     * computed using Machin's formula:
     *
     *          pi/4 = 4*arctan(1/5) - arctan(1/239)
     *
     * and a power series expansion of arctan(x) to 
     * sufficient precision.
     */
    public static BigDecimal computePi(int digits) {
        int scale = digits + 5;
        BigDecimal arctan1_5 = arctan(5, scale);
        BigDecimal arctan1_239 = arctan(239, scale);
        BigDecimal pi = arctan1_5.multiply(FOUR).subtract(
                                  arctan1_239).multiply(FOUR);
        return pi.setScale(digits, 
                           BigDecimal.ROUND_HALF_UP);
    }
    /**
     * Compute the value, in radians, of the arctangent of 
     * the inverse of the supplied integer to the specified
     * number of digits after the decimal point.  The value
     * is computed using the power series expansion for the
     * arc tangent:
     *
     * arctan(x) = x - (x^3)/3 + (x^5)/5 - (x^7)/7 + 
     *     (x^9)/9 ...
     */   
    public static BigDecimal arctan(int inverseX, 
                                    int scale) 
    {
        BigDecimal result, numer, term;
        BigDecimal invX = BigDecimal.valueOf(inverseX);
        BigDecimal invX2 = 
            BigDecimal.valueOf(inverseX * inverseX);

        numer = BigDecimal.ONE.divide(invX,
                                      scale, roundingMode);

        result = numer;
        int i = 1;
        do {
            numer = 
                numer.divide(invX2, scale, roundingMode);
            int denom = 2 * i + 1;
            term = 
                numer.divide(BigDecimal.valueOf(denom),
                             scale, roundingMode);
            if ((i % 2) != 0) {
                result = result.subtract(term);
            } else {
                result = result.add(term);
            }
            i++;
        } while (term.compareTo(BigDecimal.ZERO) != 0);
        return result;
    }
}

Class ComputeEngine

package client;

import compute.Task;
import java.io.Serializable;
import java.math.BigDecimal;

public class Pi implements Task<BigDecimal>, Serializable {

    private static final long serialVersionUID = 227L;

    /** constants used in pi computation */
    private static final BigDecimal FOUR =
        BigDecimal.valueOf(4);

    /** rounding mode to use during pi computation */
    private static final int roundingMode = 
        BigDecimal.ROUND_HALF_EVEN;

    /** digits of precision after the decimal point */
    private final int digits;

    /**
     * Construct a task to calculate pi to the specified
     * precision.
     */
    public Pi(int digits) {
        this.digits = digits;
    }

    /**
     * Calculate pi.
     */
    public BigDecimal execute() {
        return computePi(digits);
    }

    /**
     * Compute the value of pi to the specified number of 
     * digits after the decimal point.  The value is 
     * computed using Machin's formula:
     *
     *          pi/4 = 4*arctan(1/5) - arctan(1/239)
     *
     * and a power series expansion of arctan(x) to 
     * sufficient precision.
     */
    public static BigDecimal computePi(int digits) {
        int scale = digits + 5;
        BigDecimal arctan1_5 = arctan(5, scale);
        BigDecimal arctan1_239 = arctan(239, scale);
        BigDecimal pi = arctan1_5.multiply(FOUR).subtract(
                                  arctan1_239).multiply(FOUR);
        return pi.setScale(digits, 
                           BigDecimal.ROUND_HALF_UP);
    }
    /**
     * Compute the value, in radians, of the arctangent of 
     * the inverse of the supplied integer to the specified
     * number of digits after the decimal point.  The value
     * is computed using the power series expansion for the
     * arc tangent:
     *
     * arctan(x) = x - (x^3)/3 + (x^5)/5 - (x^7)/7 + 
     *     (x^9)/9 ...
     */   
    public static BigDecimal arctan(int inverseX, 
                                    int scale) 
    {
        BigDecimal result, numer, term;
        BigDecimal invX = BigDecimal.valueOf(inverseX);
        BigDecimal invX2 = 
            BigDecimal.valueOf(inverseX * inverseX);

        numer = BigDecimal.ONE.divide(invX,
                                      scale, roundingMode);

        result = numer;
        int i = 1;
        do {
            numer = 
                numer.divide(invX2, scale, roundingMode);
            int denom = 2 * i + 1;
            term = 
                numer.divide(BigDecimal.valueOf(denom),
                             scale, roundingMode);
            if ((i % 2) != 0) {
                result = result.subtract(term);
            } else {
                result = result.add(term);
            }
            i++;
        } while (term.compareTo(BigDecimal.ZERO) != 0);
        return result;
    }
}

Upvotes: 2

Views: 226

Answers (2)

user207421
user207421

Reputation: 311023

java.lang.ClassNotFoundException: client.Pi

The server doesn't have client.Pi available on its CLASSPATH or via the client's codebase either. file: codebases don't work unless both server and client are on the same machine, in which case there is little or no point in using the codebase feature at all.

See also @Mario's answer re java.rmi.server.useCodebaseOnly.

Upvotes: 0

Mario
Mario

Reputation: 2515

I believe the error you're getting is because you're missing the following flag when you launch the server:

-Djava.rmi.server.useCodebaseOnly=false

The problem is with the server, not the client. The client is reporting the server's error.

At the time of my writing this, the Oracle RMI tutorial is missing this flag; so, it's not your fault.

https://docs.oracle.com/javase/tutorial/rmi/running.html

If you're missing the flag, the server will not load any code that it doesn't have in its own code base. In other words, if the client is on a different machine than the server, and the Pi class exists only on the client, the server will not be able to load it. The error you're seeing is the exact error I was getting until I added the flag.

My understanding is that from JVM v1.7 on, the default value for useCodebaseOnly is true. (A true value allows for greater security, but makes for a broken tutorial.)

In short, add the flag set to false to the server's startup command. Good luck!

Upvotes: 1

Related Questions