简体   繁体   中英

RMI - Getting a ClassNotFoundException at runtime

I've spent almost five hours trying to resolve this, to no avail. I've created an application that uses RMI. It compiles fine, but I can't it get it to run. All my class files are in C:\\Users\\Benji\\Desktop\\ass2\\build (short for "assignment"; nothing dirty). All source files are in C:\\Users\\Benji\\Desktop\\ass2\\src. I've put everything in a single package to make things more comprehensible (and changed the import statements in the source to reflect this).

I have put a batch file in C:\\Users\\Benji\\Desktop\\ass2\\ . Its contains the execution statement:

java -classpath ./build -Djava.rmi.server.codebase=file:/C:/Users/Benji/Desktop/ass2/build -Djava.security.policy=broker.policy BrokerReception Broker 16890

(The two args "Broker" and "16890" are needed by the program).

The file broker.policy is also in C:\\Users\\Benji\\Desktop\\ass2\\ . Its contents are:

grant
{
    permission java.security.AllPermission;
};

(and yes, I realise this isn't a good security policy. I'll work on this later).

There are actually three Main classes, one for a client, one for a broker (a mediator for the client) and a server. I am trying to start the Broker. Code for the Broker interface is as follows:

import java.io.FileNotFoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.ArrayList;

public interface Broker extends Remote
{
    public boolean getAvailability(int startDate, int endDate) throws FileNotFoundException, RemoteException;

    public ArrayList<CityInfo> getCities() throws FileNotFoundException, RemoteException;

    public ArrayList<HotelInfo> getCityHotels(int cityNumber) throws FileNotFoundException, RemoteException;

    public int getHotelRoomRate(int hotelNumber) throws FileNotFoundException, RemoteException;

    public boolean makeBooking(String firstName, String lastName, String contact, String creditCardNo) throws FileNotFoundException, RemoteException;
}

And the implementation class:

import java.io.FileNotFoundException;
import java.util.ArrayList;

public class BrokerClientLiaison implements Broker
{
    private BrokerDatabase directory;
    private BrokerHotelsLiaison liaison;

    public BrokerClientLiaison(BrokerDatabase directory, int activeHotelNumber)
    {
        this.liaison = new BrokerHotelsLiaison(activeHotelNumber);
        this.directory = directory;
    }

    public boolean getAvailability(int startDate, int endDate) throws FileNotFoundException
    {
        return liaison.getAvailability(startDate, endDate);
    }

    public ArrayList<CityInfo> getCities() throws FileNotFoundException
    {
        return directory.getCities();
    }

    public ArrayList<HotelInfo> getCityHotels(int cityNumber) throws FileNotFoundException
    {
        return directory.getCityHotels(cityNumber);
    }

    public int getHotelRoomRate(int hotelNumber) throws FileNotFoundException
    {
        return liaison.getHotelRoomRate(hotelNumber);
    }

    public boolean makeBooking(String firstName, String lastName, String contact, String creditCardNo) throws FileNotFoundException
    {
        return liaison.makeBooking(firstName, lastName, contact, creditCardNo);
    }
}

And finally, the main class to start the implementation class:

import java.io.FileNotFoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.sql.SQLException;

public class BrokerReception
{
    public static void main(String[] args)
    {
        System.out.println("Args are:");
        for(String arg : args)
        {
            System.out.println(arg);
        }
        System.out.println();

        try
        {
            BrokerDatabase directory = new BrokerDatabase();
            directory.connect(args[0]);

            int activeHotelNumber = Integer.parseInt(args[1]);
            if(directory.checkActiveHotelExists(activeHotelNumber))
            {
                BrokerClientLiaison liaison = new BrokerClientLiaison(directory, activeHotelNumber);
                Broker liaisonStub = (Broker) UnicastRemoteObject.exportObject(liaison, 0);
                Registry registry = LocateRegistry.getRegistry();
                registry.rebind(Protocol.BROKER_INTERFACE_NAME, liaisonStub);
            }
            else
            {
                throw new FileNotFoundException();
            }
        }
        catch(ArrayIndexOutOfBoundsException aioobe)
        {
            System.err.println("Args required:");
            System.err.println("1. Name of database file");
            System.err.println("2. Number of active hotel");
            System.exit(1);
        }
        catch(ClassNotFoundException cnfe)
        {
            System.err.println("Couldn't load database driver");
            System.exit(2);
        }
        catch(SQLException sqle)
        {
            System.err.println("Couldn't establish connection to database");
            System.err.println("Check that the database has been properly registerd,");
            System.err.println("and that you provided the correct name");
            System.exit(3);
        }
        catch(NumberFormatException nfe)
        {
            System.err.println("Second argument must be an integer");
            System.exit(4);
        }
        catch(FileNotFoundException fnfe)
        {
            System.err.println("The database contains no entries with that hotel number");
            System.exit(5);
        }
        catch(RemoteException re)
        {
            System.err.println("Unable to bind as " + Protocol.BROKER_INTERFACE_NAME);
            re.printStackTrace();
            System.exit(6);
        }
    }
}

"Directory" in the above code is a class that accesses a database.

I don't know what other information I need to give. Can anybody tell me what I'm doing wrong? Incidentally, I went back and did Oracle's RMI tutorial at http://download.oracle.com/javase/1.5.0/docs/guide/rmi/hello/hello-world.html , to see if I could figure out what's wrong. The tutorial makes no mention of codebase or security policy, but provides all the code and precise instructions for compilation and execution. I followed these instructions to the letter, and yet even this didn't work!

Stack trace:

java.rmi.ServerException: RemoteException occurred in server thread; nested exce
ption is:
        java.rmi.UnmarshalException: error unmarshalling arguments; nested excep
tion is:
        java.lang.ClassNotFoundException: Broker
        at sun.rmi.server.UnicastServerRef.oldDispatch(UnicastServerRef.java:396
)
        at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:250)
        at sun.rmi.transport.Transport$1.run(Transport.java:159)
        at java.security.AccessController.doPrivileged(Native Method)
        at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
        at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:5
35)
        at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTranspor
t.java:790)
        at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport
.java:649)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExec
utor.java:886)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor
.java:908)
        at java.lang.Thread.run(Thread.java:662)
        at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(Stream
RemoteCall.java:255)
        at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:
233)
        at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:359)
        at sun.rmi.registry.RegistryImpl_Stub.rebind(Unknown Source)
        at BrokerReception.main(BrokerReception.java:32)
Caused by: java.rmi.UnmarshalException: error unmarshalling arguments; nested ex
ception is:
        java.lang.ClassNotFoundException: Broker
        at sun.rmi.registry.RegistryImpl_Skel.dispatch(Unknown Source)
        at sun.rmi.server.UnicastServerRef.oldDispatch(UnicastServerRef.java:386
)
        at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:250)
        at sun.rmi.transport.Transport$1.run(Transport.java:159)
        at java.security.AccessController.doPrivileged(Native Method)
        at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
        at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:5
35)
        at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTranspor
t.java:790)
        at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport
.java:649)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExec
utor.java:886)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor
.java:908)
        at java.lang.Thread.run(Thread.java:662)
Caused by: java.lang.ClassNotFoundException: Broker
        at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:247)
        at sun.rmi.server.LoaderHandler.loadProxyInterfaces(LoaderHandler.java:7
11)
        at sun.rmi.server.LoaderHandler.loadProxyClass(LoaderHandler.java:655)
        at sun.rmi.server.LoaderHandler.loadProxyClass(LoaderHandler.java:592)
        at java.rmi.server.RMIClassLoader$2.loadProxyClass(RMIClassLoader.java:6
28)
        at java.rmi.server.RMIClassLoader.loadProxyClass(RMIClassLoader.java:294
)
        at sun.rmi.server.MarshalInputStream.resolveProxyClass(MarshalInputStrea
m.java:238)
        at java.io.ObjectInputStream.readProxyDesc(ObjectInputStream.java:1530)
        at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1492)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1
731)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
        ... 12 more

if you are running your registry as a second process, it needs access to your remote classes. the easiest way to do this is to add the appropriate classpath argument to the commandline when starting the registry.

if you are trying to use remote class loading, i believe you need to setup an rmi security manager in your application, either on the commandline or in the main method. (personally, distributing the classes usually works for 99% of situations and is 100 times easier to get right).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM