3

I was having an issue with deserializing an object. The current project has a plugin-style architecture, so I have jars that contain class files that are loaded at runtime. I was unable to deserialize an object that contained a class that was found in one of those jars, so I wrote a quick test method that gets called mid-stream that just loaded the plugins, instantiates the correct one (the object implements a particular interface so I can identify it via .isAssignableFrom(..) ), serializes it (which happens fine), and then immediately tries to deserialize it.

I still get a 'ClassNotFoundException'.

Stacktrace:

Jul 21, 2014 4:02:11 PM com.newspinrobotics.auth.MainFrame loadPlugins
SEVERE: null
java.lang.ClassNotFoundException: com.newspinrobotics.auth.plugin.tcpserver.TCPServer
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:270)
at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:625)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1612)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1517)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
at com.newspinrobotics.auth.MainFrame.loadPlugins(MainFrame.java:79)
at com.newspinrobotics.auth.MainFrame.<init>(MainFrame.java:43)
at com.newspinrobotics.auth.MainFrame$6.run(MainFrame.java:539)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:733)
at java.awt.EventQueue.access$200(EventQueue.java:103)
at java.awt.EventQueue$3.run(EventQueue.java:694)
at java.awt.EventQueue$3.run(EventQueue.java:692)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:703)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)

Now, before you ask. The TCPServer class has no non-serializable fields in it. It has Strings and primitives in it. There is one field that does not fall into those categories but it is marked transient. There is even a no-arg constructor (though that is not needed, correct?). And yes it implements Serializable (the serialization goes fine).

I am confused how the classloader could possibly NOT have it since it instantiates the object only a few lines before it deserializes it.

I do use a custom-made ClassLoader (extends URLClassLoader) to load the jar files at runtime.

I do provide a public static final long serialVersionUID = XXXXXXXL; field.

EDIT:

The Class loader I am referring to extends URLClassLoader. It's located inside a utility class written by another person that upon further inspection actually is not really even a modification (for some reason he felt like extending it and not really doing anything of substance to it). All the utility does is pick apart jar files, and use the URLClassLoader to add the jar files into the URLClassLoader via addURL(..) and also load the classes via loadClass(..). So it does not seem like there's anything nefarious in this utility. I am no ClassLoader ninja however, so I can certainly give further info on it if needed. It's really just a few utility functions for loading Jar files and picking out class files and loading them.

Help me StackOverflow, you're my only hope (maybe).

Aaron Marcus
  • 153
  • 2
  • 13
  • So you've implemented a custom ClassLoader, you're having a problem with class loading, and you don't post any of your class-loading code (neither the custom ClassLoader nor the code that uses it, including the code that builds the TCPServer object in the first place)... – Tim Jul 21 '14 at 20:57
  • Well. Considering that ClassLoader seems to work for instantiating the objects (in such a way that they are completely usable) it didn't seem like the problem was in the classloader. – Aaron Marcus Jul 21 '14 at 20:58
  • Your custom Classloader doesn't appear to be in your stack trace, which makes it look like it's not being used when you deserialize. So although it might be fine, the code that attempts to use it (via the ObjectInputStream) doesn't appear to be. Can you post the relevant code? – Tim Jul 21 '14 at 21:15
  • What do you consider to be relevant? The entire class loader utility? or the definition of 'modified class loader' which really in the end isn't modified at all. It's extended, but only overrides the addURL and in that simply call the super. So not really extended at all. – Aaron Marcus Jul 21 '14 at 21:19
  • I'd be less concerned with the ClassLoader itself (because as you say, it works elsewhere) and more about the code where you're invoking it. So if our discussion about extending `ObjectInputStream` in the comments on my answer doesn't bear fruit, I'd suggest you post the code where you're successfully instantiating a `TCPServer` from your classloader, as well as the code where you're deserializing it unsuccessfully from the `ObjectInputStream` (including the code where you instantiate and configure that `ObjectInputStream`). But hopefully extending `ObjectInputStream` will be all you need. – Tim Jul 21 '14 at 21:47

1 Answers1

2

Did you subclass ObjectInputStream to override resolveClass() to use your custom Classloader? (For an example of someone else doing this, though not a direct answer to your question, see ObjectInputStream custom classloader deserialization issue: resolveClass() not called.)

Community
  • 1
  • 1
Tim
  • 2,027
  • 15
  • 24
  • No I did not subclass ObjectInputStream, I just used the default. I did not think about that interaction at all. – Aaron Marcus Jul 21 '14 at 21:20
  • I haven't done this myself, but the small amount of Googling I did seems to indicate that that's what's most commonly done. (There might be other ways to do it, I'm not sure.) My guess is that the standard `ObjectInputStream` simply calls the standard classloader, giving you no way to inject your custom classloader in the middle of the call. I'm a little surprised there isn't an API for passing a set of custom `ClassLoaders` to a standard `ObjectInputStream`, but maybe that'll come in the future. – Tim Jul 21 '14 at 21:44
  • 1
    Sorry. Forgot to click it. Yes, it was a classloader issue. It took making a custom ObjectInputStream to fix it. – Aaron Marcus Jul 30 '14 at 19:19
  • Hi, @AaronMarcus, how and where did you implement this custom ObjectInputStream? We're facing the exact same problem here: https://stackoverflow.com/questions/66663334/why-cant-i-retrieve-data-from-my-chroniclemap – Mario Marinato Mar 17 '21 at 15:06
  • Apologies @MarioMarinato I wasn't on here for a really long time, hope you figured it out! TBH I don't remember details of this anymore. I'm assuming there is a spot in instantiation or a specific call for using a custom OIS? – Aaron Marcus Feb 21 '23 at 21:02