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.
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.