5

So right now, I have a server that is running with ObjectInputStream and ObjectOutputStream.

The problem I am having is that I have a custom (anonymous) class that extends java.lang.Date that I am trying to send to the client and then compile.

So nowhere do I ever define the class on the client side, but I want to compile the class programmatically. I have tried many different methods, but every time I get a ClassNotFoundException because the class isn't on the client side initially.

Class<?> dateClass = (Class<?>) in.readObject(); //This is where the CNF Exception occurs
Compiler.compileClass(dateClass);
Avogadro
  • 101
  • 7

1 Answers1

7

The Java Serialization mechanism assumes the classes are known to the deserializing JVM, it doesn't send the class definitions. In particular, when you serialize a Class object, you don't send the byte code for that class but only instruct the receiving VM to look up the Class object for a class with a particular name.

Also note that a Class object represents a class defined in the JVM, i.e. the class' bytecode has already been loaded. It makes little sense to try to compile to class to generate that bytecode after loading the class.

So, we need to somehow get the class definition to the client. The easiest approach is to do this like any other class the client needs (by packing it in the client's jar file, or whatever means you use to install the client programm). If that is not possible, you could load the class definition over the network, for instance by using a URLClassLoader, or you could send the classfile through the serialization stream, and upon receiving it on the client use ClassLoader.defineClass to load the class.

PS: This issue is completely indepedent of whether the class is named or not. The following test code shows that objects of anonymous classes can be serialized and deserialized just fine (if the receiving VM has the class definition):

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
        Serializable payload = new Serializable() {
            @Override
            public String toString() {
                return "hello from the anonymous class";
            }
        };
        oos.writeObject(payload);
        oos.writeObject(payload.getClass());
    }

    try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) {
        System.out.println(in.readObject());
        System.out.println(in.readObject());
    }
meriton
  • 61,876
  • 13
  • 96
  • 163
  • 1
    This is a far better answer than the pithy tosh about nonymising, although that is a funny word. Like "jelly" and "wibble". So if we're voting on funny words then this answer should pull in those votes now. – davidfrancis Feb 26 '12 at 00:13
  • That's what I was originally thinking. Sending the File for the class over the network then just installing it to the DIR when it gets there. Thanks for the reassurance :D – Avogadro Feb 26 '12 at 00:16
  • You should be fine sending the anon class over a network classloader - eg using rmi classloading is the native java way of doing this – davidfrancis Feb 26 '12 at 00:22
  • Is it not enough to serialize `payload.getClass()` and send that over the network to reconstruct on the other side? Why the need to serialize `payload` instance? – raffian Oct 31 '13 at 21:48