Cast exception?

#1

I'm getting a weird exception:

java.lang.ClassCastException: database.entity.Player cannot be cast to database.entity.Player

From just fetching a single result:

TypedQuery<Player> query = em.createNamedQuery("Player.getPlayer", Player.class);
Player player = query.setParameter("id", uid).getSingleResult();

Where the query is a NamedQuery store in Player class file:

@NamedQuery(name="Player.getPlayer", query="SELECT c FROM Player c WHERE c.uid = :id")

Any thoughts on how this error even make sense? :L

#2

Since it is a casting from a type to itself it probably fails because that class is loaded by two different class loaders.

An instance of a class that is loaded by one class loader cannot be cast to the same class that is loaded by another class loader.

ObjectDB Support
#3

That seem to be the issue here, but I'm not doing any casting in the code! Is it some casting done by the query? If so how can I work around it?

#4

You do cast implicitly by using TypedQuery with a Player.class (which might be loaded by a different class loader than the one that in use with ObjectDB).

Please always post error messages with full stack traces.

 

ObjectDB Support
#5

I don't think the stack trace is very useful here, that's why I didn't include it...

Exception: java.lang.ClassCastException
Message: database.entity.Player cannot be cast to database.entity.Player
Description: Error during event handling: java.lang.ClassCastException: database.entity.Player cannot be cast to database.entity.Player, Listener: { Ext: TestServer, Type: JAVA, Lev: ZONE, { Zone: server }, {} }
+--- --- ---+
Stack Trace:
+--- --- ---+
extension.LoginEventHandler.handleServerEvent(LoginEventHandler.java:97)
com.smartfoxserver.v2.extensions.SFSExtension.handleServerEvent(SFSExtension.java:258)
com.smartfoxserver.v2.entities.managers.SFSExtensionManager.dispatchEvent(SFSExtensionManager.java:764)
com.smartfoxserver.v2.entities.managers.SFSExtensionManager.dispatchZoneLevelEvent(SFSExtensionManager.java:685)
com.smartfoxserver.v2.entities.managers.SFSExtensionManager.handleServerEvent(SFSExtensionManager.java:882)
com.smartfoxserver.v2.core.SFSEventManager$SFSEventRunner.run(SFSEventManager.java:64)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
java.lang.Thread.run(Thread.java:722)

So basically the query fetched the persistent fields and built a Player instance, but this built class is loaded by a different class loader than what the current method is using.

What are the ways to go around this?

#6

This seems to be a partial stack trace, since ObjectDB related methods are not shown (maybe the cause stack trace was removed).

Please post details about both class loaders. Run the query as Query rather than TypedQuery and then check the result type (Player) and its class loader. Check also the class loader of Player.class.

ObjectDB Support
#7

I've tried using dynamic query instead of Named, and also Query instead of TypedQuery, but none worked.

I thought the stack trace is incomplete at first, but then some testing revealed that there is no error on the fetching request. I could have written:

Object player = query.getSingleResult();

Without any error. So the only error occurring is the casting, which doesn't have anything to do with any method from ObjectDB.

So I looked into the class loaders and found out:

Getting class loader 1: java.net.URLClassLoader@40834bca
Getting class loader 2: com.objectdb.o.TYM@16b62091

They...look different, but what should I do?

#8

Maybe ObjectDB cannot find the class because the URLClassLoader is not accessible for it.

Where do you install the objectdb.jar file? Is it in the same location as your entity classes (e.g. as part of the web application)?

ObjectDB Support
#9

That sounds the most likely.

After a bit of research I've came across a bit of documentation on the middleware which I didn't quite understand the first time I've read it:

Libraries deployed under your Extension folder will be loaded in the Extension Class Loader. This means that you can change that specific library without affecting other Extensions.
Libraries shared under the __lib__ folder are loaded in the parent Class Loader. If you change any of these dependencies it will affect all Extensions that use them.

So they are indeed loaded by two class loaders!

However, this defeats my previous workaround and I am back with the problem of unable to load the emf:

javax.persistence.PersistenceException: No Persistence provider for EntityManager named xxx

Upon closer examination, isn't javax.persistence package name inside the objectdb.jar file? So if there is a problem loading objectdb.jar at runtime, how can we get the exception contained inside the package?

Is it perhaps only part of the jar is being loaded? Is that even possible? Or maybe it has to do with different class loaders?

Here is the stack trace again:

javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:85)
javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:54)
extension.LoginEventHandler.handleServerEvent(LoginEventHandler.java:103)
...

I also tried to confirm that jars under the extension folder loads properly, so I put the ObjectDB points tutorial (exported) jar into my current web app, and I created a Point object successfully in code. So I think objectdb.jar is getting loaded alright, but parts of it is not working somehow...

Edit: I took a look at the Persistence.java source code and extracted the method that is throwing the exception:

/*     */   public static EntityManagerFactory createEntityManagerFactory(String paramString, Map paramMap)
/*     */   {
/*  73 */     EntityManagerFactory localEntityManagerFactory = null;
/*  74 */     PersistenceProviderResolver localPersistenceProviderResolver = PersistenceProviderResolverHolder.getPersistenceProviderResolver();
/*     */
/*  76 */     List localList = localPersistenceProviderResolver.getPersistenceProviders();
/*     */
/*  78 */     for (PersistenceProvider localPersistenceProvider : localList) {
/*  79 */       localEntityManagerFactory = localPersistenceProvider.createEntityManagerFactory(paramString, paramMap);
/*  80 */       if (localEntityManagerFactory != null) {
/*     */         break;
/*     */       }
/*     */     }
/*  84 */     if (localEntityManagerFactory == null) {
/*  85 */       throw new PersistenceException("No Persistence provider for EntityManager named " + paramString);
/*     */     }
/*  87 */     return localEntityManagerFactory;
/*     */   }

It looks like either the list of local PersistenceProviders is 0 or none of them is able to create an emf with the given string...

This is quite strange since we are able to create one when we switched class loaders...not sure how to make sense out of it...

#10

The "No Persistence provider" exception is thrown by JPA and not by ObjectDB, so it can be thrown if JPA is loaded but cannot find ObjectDB. Possibly JPA is available in the parent class loader separately from ObjectDB (for example, it is available as part of Java EE), in addition to being part of objectdb.jar.

If JPA is loaded by class loader A, your entity classes are loaded by class loader B, and class loader A cannot see class loader B, there is a problem. If ObjectDB is in class loader A you will have the ClassCastException. If ObjectDB is in class loader B you will have the "No Persistence provider" exception.

A solution in that case depends on the framework that you use.

ObjectDB Support
#11

I did a few more tests and traces and found that Persistence is loaded by the same class loader as the current web app and the objectdb package. I've also checked that objectdb is actually loaded by:

trace("$$$$$");
trace(JpaQuery.class);     // Stuff in objectdb.jar
trace(Activator.class);
trace(ABT.class);
trace(Persistence.class.getClassLoader());
trace(ABT.class.getClassLoader());
trace(getClass().getClassLoader());
trace("$$$$$");

With results:

$$$$$
class com.objectdb.jpa.JpaQuery
class com.objectdb.Activator
class com.objectdb.o.ABT
java.net.URLClassLoader@8e53cf0
java.net.URLClassLoader@8e53cf0
java.net.URLClassLoader@8e53cf0
$$$$$

So it seems it is something to do with something inside objectdb.jar?

Edit: workaround solution found. I separated all my db classes and put them in a jar under the same directory as objectdb, both in the previous directory where persistence isn't a problem but cast exception happened. But since now I ensured db classes are loaded by the same loader as objectdb.jar, everything is working now.

However, I still think something weird is happening - persistence exception shouldn't have been thrown there...maybe someone else might come across this problem later :L

#12

Please check also:

    ClassLodaer classLoader = Thread.currentThread().getContextClassLoader();

and if it is not the same as java.net.URLClassLoader@***, set it before first access to ObjectDB:

    Thread.currentThread().setContextClassLoader(Player.class.getClassLoader());
ObjectDB Support

Reply