views:

214

answers:

2

I'm seeing a weird error message and am looking for some ideas as to what the problem could be. I'm sort of new to using the JPA.

I have an application where I'm using Spring's Entity Manager Factory (LocalContainerEntityManagerFactoryBean), EclipseLink as my ORM provider, connected to a MySQL DB and built with Maven. I'm not sure if any of this matters.....

When I deploy this application to Glassfish, the application works as expected.

The problem is, I've created a set of stand alone unit tests to run outside of Glassfish that aren't working correctly. I get the following error (I've edited the class names a little)

com.xyz.abc.services.persistence.entity.MyEntity cannot be cast to com.xyz.abc.services.persistence.entity.MyEntity

The object cannot be cast to a class of the same type? How can that be?

Here's a snippet of the code that is in error

Query q = entityManager.createNamedQuery("MyEntity.findAll");
List entityObjects = q.getResultList();
for (Object entityObject: entityObjects) {
   com.xyz.abc.services.persistence.entity.MyEntity entity = (com.xyz.abc.services.persistence.entity.MyEntity) entityObject;

Previously, I had this code that produced the same error:

CriteriaQuery cq = entityManager.getCriteriaBuilder().createQuery();
cq.select(cq.from(com.xyz.abc.services.persistence.entity.MyEntity.class));
List entityObjects = entityManager.createQuery(cq).getResultList();
for (Object entityObject: entityObjects) {
   com.xyz.abc.services.persistence.entity.MyEntity entity = (com.xyz.abc.services.persistence.entity.MyEntity) entityObject;

This code is question is the same that I have deployed to the server.

Here's the innermost exception if it helps

Caused by: java.lang.ClassCastException: com.xyz.abc.services.persistence.entity.MyEntity cannot be cast to com.xyz.abc.services.persistence.entity.MyEntity
    at com.xyz.abc.services.persistence.entity.factory.MyEntityFactory.createBeans(MyEntityFactory.java:47)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:115)
    ... 37 more

I'm guessing that there's some jar I'm using in Glassfish that is different than the ones I'm using in test. I've looked at all the jars I have listed as "provided" and am pretty sure they are all the same ones from Glassfish.

Let me know if you've seen this weird issue before, or any ideas for correcting it.

+4  A: 

This could also be a class loading problem. The same class definition, loaded by two different class loaders, is seen as different classes by the JVM.

You could try this to get information about the class loaders in the game:

Query q = entityManager.createNamedQuery("MyEntity.findAll");
List entityObjects = q.getResultList();

ClassLoader loader1 = 
    com.xyz.abc.services.persistence.entity.MyEntity.getClass().getClassLoader();
System.out.println("MyEntity's class loader is " + loader1);
for (Object entityObject: entityObjects) {
  ClassLoader loader2 = entityObject.getClass().getClassLoader();
  System.out.println("Class loader of entity " + entityObject + " is " + loader2);
}

Instead of System.out.println you could of course use calls to your preferred logging framework.

Here is a series of articles with more details about class loading.

Péter Török
Yes OK, this seems to be an issue. Here is the output:MyEntity's class loader is sun.misc.Launcher$AppClassLoader@1ea2dfeMyEntity Object's class loader is org.springframework.instrument.classloading.SimpleInstrumentableClassLoader@16089a5Now how to fix it??? I'm going to dig into those articles you linked to.
Vinnie
@Vinnie Unfortunately I am not familiar with the tools you use, so I can't offer more concrete help. Good luck - you will need it...
Péter Török
OK, this is probably a rookie mistake, but your suggestion (and the revelation about using different Classloaders locally) led to a workable solution. Seeing that there were different Classloaders in play made me think that Spring's LocalContainerEntityManagerFactoryBean was not the right choice to use during my unit tests. I switched to LocalEntityManagerFactoryBean (which also meant I needed a new persistence unit since I couldn't inject the DataSource) and things seem to be working locally now. Thanks for the help!
Vinnie
+1  A: 

This clearly smells a ClassLoader issue (maybe due to the weaving needed by EclipseLink). Do you use the LoadTimeWeaver? Do you have any javaagent stuff configured? Does the problem occur on the command line under Maven? in your IDE? Please clarify.

Pascal Thivent
Good point about the weaving, I meant to mention this in the question. I'm using org.springframework.context.weaving.DefaultContextLoadTimeWeaver in Glassfish and org.springframework.instrument.classloading.SimpleLoadTimeWeaver in my tests. I wasn't sure which to use, so I just picked one that seemed to work.
Vinnie
BTW - I don't have any javaagent configured. And the problem occurs both from the maven command line as well was from the IDE. The Glassfish server is the only area where I don't see the problem.
Vinnie