This discussion is archived
8 Replies Latest reply: Aug 16, 2010 1:04 PM by 843793 RSS

RMI ClassCastException

843793 Newbie
Currently Being Moderated
Hello, world!


My problem is the following: I'm developping a peer-to-peer application based on RMI. This application includes applets -called peerlets- that can be exposed to and used by other peers. Hence I have two remote interfaces:
public interface Peerlet extends Remote {

    // stuff that throws RemoteException

}
public interface Peer extends Remote {

    public Peerlet getPeerlet(/*some Peerlet ID here*/) throws RemoteException;

    //...

}
As you can see Peer is the broker that passes references on local peerlets to remote peers. There are many possible peerlets, such as:
public interface APeerlet extends Peerlet {

    // stuff that throws RemoteException

}
Creating a Peer instance, retrieve it from another remote Peer instance via RMI is fine. My problem occurs when I try to get references on remote peerlets. Concretely the situation is the following:

- peerA and peerB are two running Peer instances, they have a reference on each other; peerA hosts a APeerlet.
- if peerB tries the following:
//...
Peerlet p = (APeerlet) peerA.getPeerlet(/*appropriate ID to get the desired APeerlet*/);
//...
everything goes fine, peerB can use p normaly. But this is not what I want, since this piece of code needs to have APeerlet defined. If I try to get simply a Peerlet - that would be used latter by another piece of code who knows what to do with a APeerlet - ie:
//...
Peerlet p = (Peerlet) peerA.getPeerlet(/*...*/);
//...
I get a
java.lang.ClassCastException: $Proxy3 cannot be cast to ....Peerlet
I guess it's a class loading issue, but I can't figure out why the cast to Peerlet fails whereas a cast to APeerlet does not. I have found interesting behaviour with this two variations in peerB's code:
import ....APeerlet;

//... 

Class<? extends Peerlet> clazz = APeerlet.class;
Peerlet p = clazz.cast(peerA.getPeerlet(/*...*/););
// no import ....APeerlet;

//... 

Class<? extends Peerlet> clazz = /*something that returns me APeerlet.class*/;
Peerlet p = clazz.cast(peerA.getPeerlet(/*...*/););
The first version is still not what I want since it needs a definition of APeerlet, but it works. The second one throws ClassCastException... I have hidden many details, such as the http server I use for dynamic class loading; it does not receive the same queries when the ClassCastException is thrown. Could it be the source of my trouble?


S

Edited by: sylf on Aug 14, 2010 3:09 PM
  • 1. Re: RMI ClassCastException
    EJP Guru
    Currently Being Moderated
    It sounds like a class loading issue, but I don't understand why you're casting to APeerlet at all in that code when you're assigning the result to a Peerlet.
  • 2. Re: RMI ClassCastException
    843793 Newbie
    Currently Being Moderated
    I don't understand either why the cast to APeerlet works, but I've been stuck with this for quite a lot of time, and this is the only solution I have found...

    If I understand well the principles of dynamic class loading, the code should work directly without any cast (Peerlet p = peerA.getPeerlet(...)). There should not be dynamic class loading since the Peerlet interface is well known by both peers. But, I can see that Peerlet.class and APeerlet.class are downloaded by peerB from the RMI codebase (in both versions, with and without ClassCastException).

    There are many parts of my application which I have not talked about, and I can't really see why and where class loading is not performed properly... What could I test in this code that would give me a hint on the direction to look at?



    S
  • 3. Re: RMI ClassCastException
    EJP Guru
    Currently Being Moderated
    Those two classes shouldn't be in the codebase JAR file in the first place, they should be in a common Jar deployed at both the client and the server. The codebase should only contain classes that aren't referred to be name in the client or server, e.g. the stubs if any and any Serializable classes that are passed as actual parameters or return values where the formal arguments are interfaces or abstract classes, that you might want the freedom to change without redeployment to the client.
  • 4. Re: RMI ClassCastException
    843793 Newbie
    Currently Being Moderated
    I see your point. Only Peer should be in the codebase, since it is the only class bound to the RMIRegistry, Peerlet and APeerlet should NOT be in the codebase but only in the classpath of both peers.

    However, if Peerlet is not in the codebase, I get a ClassNotFoundException : Peerlet at startup, since the Peer interface uses Peerlet as a return type of Peer.getPeerlet(...). So I had no choice but to keep Peerlet in the codebase, and now peerB gets a ClassNotFoundException : APeerlet when trying to get a APeerlet from peerA...

    java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
            java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: 
            java.lang.ClassNotFoundException: ....APeerlet
            at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:336)
            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:535)
            at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
            at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
            at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
            at java.lang.Thread.run(Thread.java:619)
            at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:273)
            at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:251)
            at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:160)
            at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:194)
            at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:148)
            at $Proxy0.getPeerlet(Unknown Source)
            at ...Peer.getPeerlet(Peer.java:112)
            at //...
    Caused by: java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: 
            java.lang.ClassNotFoundException: ...APeerlet
            at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:296)
            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:535)
            at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
            at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
            at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
            at java.lang.Thread.run(Thread.java:619)
    Caused by: java.lang.ClassNotFoundException: ...APeerlet
            at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
            at java.security.AccessController.doPrivileged(Native Method)
            at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
            at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
            at java.lang.Class.forName0(Native Method)
            at java.lang.Class.forName(Class.java:247)
            at sun.rmi.server.LoaderHandler.loadClass(LoaderHandler.java:434)
            at sun.rmi.server.LoaderHandler.loadClass(LoaderHandler.java:165)
            at java.rmi.server.RMIClassLoader$2.loadClass(RMIClassLoader.java:620)
            at java.rmi.server.RMIClassLoader.loadClass(RMIClassLoader.java:247)
            at sun.rmi.server.MarshalInputStream.resolveClass(MarshalInputStream.java:197)
            at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1575)
            at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1496)
            at java.io.ObjectInputStream.readClass(ObjectInputStream.java:1462)
            at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1312)
            at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1947)
            at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1871)
            at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1753)
            at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
            at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
            at sun.rmi.server.UnicastRef.unmarshalValue(UnicastRef.java:306)
            at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:290)
            ... 9 more
    It looks like it's the RMIServer who can't find APeerlet... but why?

    I get the same Exception after having changed the return type of Peer.getPeerlet() to Remote so that Peerlet has not to be in the codebase anymore.


    S
  • 5. Re: RMI ClassCastException
    EJP Guru
    Currently Being Moderated
    I'm sorry, I needed to think this through more carefully, it's a long time since I did anything serious with codebases.

    (a) You need to put the stubs (if any), all the remote interfaces, and everything they depend on, recursively until closure, into the codebase, and also serializable implementation classes for any arguments or results that are specified by interface or base class, if you do that. You need all that so that Registry.bind() will work.
    (b) You need to put the remote interfaces that are actually named by the client code into the client's classpath directly. I don't quite see why remote interfaces that aren't named directly in the code need to be there too, but I would put them there anyway. I think this is the root of the problem.
    (c) to preserve the codebase between the Registry and the client, the Registry needs to either (i) not have any codebase classes direy available in its own classpath, or (i) be started in the same JVM as the remote object(s) (which I think is a better solution for many reasons).
  • 6. Re: RMI ClassCastException
    843793 Newbie
    Currently Being Moderated
    I'm trying that, and still getting a ClassNotFound... Is there any ordering on the three steps? I do the three in the same method call, as follows: loading classes from jars with a URLCLassLoader, setting the codebase (to these same jars, actually...) then starting an RMIRegistry. I also tried non-dynamic alternatives, with explicit classpath and codebase in the command line and a separate rmiregistry &.

    I'm wondering if OSGi is not messing things up... My application is based on Apache Felix/iPOJO and I have found some records of dynamic proxies and classloader issues when using RMI. Is it a well known problem?

    Thanks a lot for your help,


    S
  • 7. Re: RMI ClassCastException
    EJP Guru
    Currently Being Moderated
    That's a logical order, although I would always set the codebase system property first. Why do you have to load the classes explicitly? why aren't they in your server's classpath?

    I don't know anything about your framework.
  • 8. Re: RMI ClassCastException
    843793 Newbie
    Currently Being Moderated
    ejp wrote:
    Why do you have to load the classes explicitly? why aren't they in your server's classpath?
    In OSGi the code is bundled and can be (re)deployed at runtime. There is a whole proxy system for managing dynamic service binding, featuring just-in-time class loading and object instantiation. I guess that for some reason, APeerlet is not in the classpath when the ClassNotFound is thrown. When I cast Peer.getPeerlet() to APeerlet, the code contains an explicit reference to APeerlet at compile time, and the class is known.

    I have found this which strongly suggests that in OSGi your classloader is not under control.

    However, ClassNotFound occurs in the RMIServer, which is more likely a codebase error. I have tried to reload the bundles just before the call to Peer.getPeerlet(), reset the codebase, I even tried their weird doWithClassLoader() stuff, and still get the exception -_-'


    S

    Edited by: sylf on Aug 16, 2010 8:01 PM: a bit of search have given a lots of examples... It seems that [the problem is far beyond my abilities|http://www.eclipsecon.org/2007/index.php?page=sub/&id=3987], I will stick with a static cast and cry for dynamic peerlet deployment... Anyway, thank you a lot ejp.