javaclassloaderjdodb4odatanucleus

ClassCastException in DataNucleus DAO object when persisting/retrieving an Object using JDO


I've created a simple webapp using Spring & Jetty, and am creating a hello world JDO test using DataNucleus & DB4O.

I can persist a class no problem, but when I go to query for the class I get a ClassCastException, can't cast a.b.c.MyClass to a.b.c.MyClass.

When I examine the classloader of the original object I created, it's [WebAppClassLoader@1592226291], naturally it's springs WebApp classloader.

I am performing both the persist operation and query operation in the same servlet method, when I re-read the object from the DB with a simple query I get back a set of a.b.c.MyClass objects from the DB, but the classloader is [sun.misc.Launcher$AppClassLoader@5acac268], thus the exception.

Following the DataNucleus docs here http://www.datanucleus.org/extensions/classloader_resolver.html

...the JDO2 class-loading mechanism utilises 3 class loaders
* When creating the PersistenceManagerFactory you can specify a class loader. This is used first if specified
* The second class loader to try is the class loader for the current thread.
* The third class loader to try is the class loader for the PMF context.

I covered the first two options documented, and verified that the classloader is the WebAppClassLoader in the Servlet with these debug steps in the servlet:

Thread.currentThread().getContextClassLoader().toString()
((JDOPersistenceManagerFactory)pm.getPersistenceManagerFactory()).getPrimaryClassLoader().toString()

Both yield [WebAppClassLoader@1592226291] as the classloader.

I'm not sure where I'm going wrong here.


Solution

  • My earlier comment as an answer:

    This exception indicates that it is a class loader issue. Compare the class-loader of the object and the class you're using for the cast.

    ClassLoader loaderOfObject = theObject.getClass().getClassLoader();
    ClassLoader loaderOfLocalClass = MyClass.getClassLoader();
    // have to be the same.
    assert loaderOfObject.equals(loaderOfLocalClass);
    

    Btw: If db4o is using the wrong class-loader. You can change that by configuring the class-loader explicit.

        EmbeddedConfiguration configuration = Db4oEmbedded.newConfiguration();
        JdkReflector reflector = new JdkReflector(Thread.currentThread().getContextClassLoader());
        configuration.common().reflectWith(reflector);
        ObjectContainer container = Db4oEmbedded.openFile(configuration, "database.db4o");
    

    When a single class-loader isn't enough: You can also pass a instance of the db4o-interface JdkLoader instead of a class-loader. In there you can implement any class-lookup method. For example to look up in multiple class loaders.