Search code examples
javaserializationclassloaderdynamic-class-loaders

Java custom class loader issue


I'm sending a Class object from client to server side. Every time the server needs to load the Class object sent by the client instead of reusing it by parent delegation model (when it was loaded during the 1st iteration).

I'm trying to use a custom class loader on the server side whose loadClass(String) simply calls findClass() instead of checking with parent hierarchy. To achieve this, I'm doing following:

  1. Generate byte[] by reading the .class file on the client side as following:
Class cl = com.example.XYZ.class;
String path = cl.getName().replace('.', '/') + ".class";
InputStream is = cl.getClassLoader().getResourceAsStream(path);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int data = -1;
while((data=is.read())!=-1)
  bos.write(data);
byte[] classBinaryData = bos.toByteArray();

I'm sending classBinaryData to the server side.

  1. On the server side, every time I retrieve the byte[], verify if it's the same as on client side by matching MD5 checksum, then I create a new instance of my custom class loader and pass the byte array so it could be used in calling defineClass from within findClass.

However, I'm getting either of the errors (depending on the way I create byte[] out of .class)

Incompatible magic value ..... in class file <Unknown>

OR

com/example/XYZ (wrong name: com/example/XYZ) coming from defineClass

I need help in figuring out the mistake in my approach/code.


Solution

  • Your byte[] generation code looks fine.

    When I used the byte array generated form your code to load the class with following class loader code, it was able to load the class successfully.

    class CustomClassLoader extends ClassLoader {
    
        public Class loadTheClass(String name, byte[] bytes) {
    
            return defineClass(name, bytes, 0, bytes.length);
        }
    }
    

    Using this classloader like this

    CustomClassLoader ccl = new CustomClassLoader();
            Class cz = ccl.loadTheClass("com.example.XYZ", classBinaryData);
            Object o = cz.newInstance();
    
    1. I think you must use '.' instead of '/' in the name when you are loading the class at server side.
    2. And ensure that the byte array data is not changed in your other code.