4

In a project, I need to extend the standard class loader with a custom class loader. The main motivation for this is that the classpath is extended during run time. (For a solution to this problem see: https://stackoverflow.com/a/51584718/231397 ).

Serializing objects via

ObjectOutputStream out;
try {
  FileOutputStream fos = new FileOutputStream(filename);
  out = new ObjectOutputStream(fos);
  out.writeObject(object);
}
catch(FileNotFoundException e) {}
catch (IOException e) { }
finally {
  out.close();
}

works fine, while deserializing objects via

Object object = null;
ObjectInputStream in;
try {
  FileInputStream fis = new FileInputStream(filename);
  ObjectInputStream in = new ObjectInputStream(fis);
  object = in.readObject();
}
catch(FileNotFoundException e) {}
catch (ClassNotFoundException e) {}
finally {
  in.close();
}

fails with a ClassNotFoundException exception. The reason is that the standard class loader is invoked, which does not know about the custom class loader.

Q: What is the correct way to deserialize objects using a custom class loader?

Christian Fries
  • 16,175
  • 10
  • 56
  • 67

1 Answers1

4

(Note: Having searched for solutions on the net, I give an answer to my own question. Some solutions one finds on the net is “almost correct”: it does not work for inner classes).

The solution is to extend ObjectInputStream and override the method resolveClass(ObjectStreamClass) This can be done with an anonymous class. Then use Class.forName inside resolveClass with the custom classloader. That is

Object object = null;
try {
  FileInputStream fis = new FileInputStream(filename);
  ObjectInputStream in = new ObjectInputStream(fis) {
    @Override
    public Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
      try {
        return Class.forName(desc.getName(), true, customClassLoader);
      } catch (Exception e) { }

      // Fall back (e.g. for primClasses)
      return super.resolveClass(desc);
    }
  };

  object = in.readObject();
}
catch(FileNotFoundException e) {}
catch (ClassNotFoundException e) {}
finally {
  in.close();
}

Remark: You may find solutions suggesting to use

customClassLoader.loadClass(desc.getName());

instead of

Class.forName(desc.getName(), true, customClassLoader)

While this may work in many cases, it may have issues. For example I found that customClassLoader.loadClass(desc.getName()) does not work for inner classes (possibly due to difference in naming for an inner class B of a class A in package p resolving to p.A.B or to p.A$B). Also note that loadClass does not initialize the class. See https://stackoverflow.com/a/7099453/231397

Christian Fries
  • 16,175
  • 10
  • 56
  • 67