ClassCastException when casting to the same class
Asked Answered
S

11

69

I have 2 different Java projects, one has 2 classes: dynamicbeans.DynamicBean2 and dynamic.Validator.

On the other project, I load both of these classes dynamically and store them on an Object

class Form {
    Class beanClass;
    Class validatorClass;
    Validator validator;
}

I then go ahead and create a Validator object using validatorClass.newInstance() and store it on validator then I create a bean object as well using beanClass.newInstance() and add it to the session.

portletRequest.setAttribute("DynamicBean2", bean);

During the lifecycle of the Form project, I call validator.validate() which loads the previously created bean object from the session (I'm running Websphere Portal Server). When I try to cast this object back into a DynamicBean2 it fails with a ClassCastException.

When I pull the object back out of the session using

faces.getApplication().createValueBinding("#{DynamicBean2}").getValue(faces);

and check the class of it using .getClass() I get dynamicbeans.DynamicBean2. This is the class I want to cast it to however when I try I get the ClassCastException.

Any reason why I'm getting this?

Secure answered 5/5, 2009 at 18:38 Comment(0)
L
67

I am not quite following your description of the program flow, but usually when you get ClassCastExceptions you cannot explain you have loaded the class with one classloader then try to cast it to the same class loaded by another classloader. This will not work - they are represented by two different Class objects inside the JVM and the cast will fail.

There is an article about classloading in WebSphere. I cannot say how it applies to your application, but there are a number of possible solutions. I can think of at least:

  1. Change the context class loader manually. Requires that you can actually get a reference to an appropriate class loader, which may not be possible in your case.

    Thread.currentThread().setContextClassLoader(...);
    
  2. Make sure the class is loaded by a class loader higher in the hierarchy.

  3. Serialize and deserialize the object. (Yuck!)

There is probably a more appropriate way for your particular situation though.

Licentiate answered 5/5, 2009 at 18:38 Comment(4)
My guess (I don't have experience with the portal server) is that you are loading the class in different portlets, each with its own classloader. You then place the instance into a session object, and when you retrieve it from the wrong portlet (from which it was created) you have your exception. I would guess (based on WAS experience) that you need to place the jar with your class somewhere higher in the classloading hierarchy so that it is loaded by a parent of both portlets classloaders.Nicobarese
i.e. Number 2, with details.Nicobarese
@Licentiate This has been quite some time. But, would you mind elaborating the first possible solution you've stated? I feel that an issue I'm having can be solved by this. Thanks in advance!Summon
I had almost the same problem, and serialize and deserialize is somehow a solution because the object was cached and serialized from a previous run, so not compatible with current classloader.Oslo
T
38

I was getting this problem after adding a dependency to spring-boot-devtools in my Springboot project. I removed the dependency and the problem went away. My best guess at this point is that spring-boot-devtools brings in a new classloader and that causes the issue of class casting problems between different classloaders in certain cases where the new classloader is not being used by some threads.

Reference: A dozer map exception related to Spring boot devtools

Tevet answered 12/3, 2019 at 22:32 Comment(3)
Spot on! Thanks for the answer. This was my problem my too. It was resolved after removing devtools dependancy.Medovich
Awesome, this saved me. Thanks for the link too, that lead me to the 'real' solution rather than having to disable devtools.Haematoid
Thanks! This was still an issue in 2022. Not sure why they haven't fixed it yetGabrila
N
16

The class objects were loaded in different classloaders, therefore the instances created from in each of classes are seen as 'incompatible'. This is a common issue in a an environment where there are many different classloaders being used and objects are being passed around. These issues can easily arise in Java EE and portal environments.

Casting an instance of a class requires that the Class linked to the object being casted is the same as the one loaded by the current thread context classloader.

Nicobarese answered 5/5, 2009 at 18:47 Comment(3)
Is there a right way I should load the classes to stop this from happening?Secure
The mysterious part is why two class loaders should load the same class. In any Java application any class should always be loaded by only one class loader. Whenever you get this behavior something really weird is going on with your class loaders.Heydon
That depends on the environment you are operating in. If you use a singleton to cache information, and you put data into this cache from two different webapps that are in the same EAR, they can each have their own classloader creating instances, and when an item is retrieved from this cache from the other webapp, it will result in a ClassCastException. Sharing of in memory data can be very tricky in these environments, so you have to be aware of where classes are being loaded from for it to work.Nicobarese
J
2

I got the A2AClassCastException problem when trying to create a List of objects from XML using Apache Commons Digester.

List<MyTemplate> templates = new ArrayList<MyTemplate>();
Digester digester = new Digester();
digester.addObjectCreate("/path/to/template", MyTemplate.class);
digester.addSetNext("/path/to/template", "add");
// Set more rules...
digester.parse(f); // f is a pre-defined File

for(MyTemplate t : templates) { // ClassCastException: Cannot cast mypackage.MyTemplate to mypackage.MyTemplate
    // Do stuff
}

As stated above, the cause is that the digester doesn't use the same ClassLoader as the rest of the program. I ran this in JBoss, and it turned out that commons-digester.jar was not in JBoss's lib directory, but rather in a webapp's lib directory. Copying the jar into mywebapp/WEB-INF/lib also solved the problem. Another solution was to casll digester.setClassLoader(MyTemplate.class.getClassLoader()), but that feels like quite an ugly solution in this context.

Julian answered 25/11, 2011 at 15:21 Comment(0)
S
1

Had the same my.package.MyClass cannot be cast to my.package.MyClass on WildFly 10.1 and, as I understand, I did the opposite to what @Emil Lundberg described in his answer.

I have added the module (which contains my.package.MyClass) to my.war/WEB-INF/jboss-deployment-structure.xml as a dependency

<dependencies>
    ...
    <module name="my.package"/>
</dependencies>

and removed the corresponding jar from my.war/WEB-INF/lib, re-deployed the WAR and then the code worked as expected.

Thus, we made sure it solves the issue. Now, we need to make sure the issue won't come back, for example, when the updated version of WAR will be assembled and deployed.

For this, in the sources of those WAR, it is required to add <scope>provided</scope> for those jar in pom.xml, so that when my.war is re-assembled next time with the fix/enhancement code injected, it will not bundle this jar into my.war/WEB-INF/lib.

Sang answered 10/8, 2018 at 10:0 Comment(0)
A
0

I had the same issue while using several JBoss instances on different machines. To bad I didn't stumble across this post earlier.
There were artifacts deployed on different machines, two of them declared class loaders with identical name.I changed one of the classloader names and everything worked fine => Beware of Copy&Paste!

Why doesn't the ClassCastException thrown mention the involved class loaders? - I think that would be very useful information.
Does anyone know if there will be anything like this available in the future? Needing to check the class loaders of 20-30 Artifacts is not that pleasant. Or is there something I missed in the exception text?

EDIT: I edited the META-INF/jboss-app.xml file and changed the name of the loader, the idea is to have a unique name. At work we use the artifact id(unique) combined with the version inserted by maven({$version}) during the build.
Using dynamic fields is only optional but helps if you want to deploy different versions of the same application.

<jboss-app>
   <loader-repository> 
   com.example:archive=unique-archive-name-{$version}
   </loader-repository> 
</jboss-app>

You can find some info here: https://community.jboss.org/wiki/ClassLoadingConfiguration

Adamant answered 23/2, 2012 at 12:40 Comment(0)
A
0

I had the same issue, and I finally found a workaround on java.net :

Copy all org.eclipse.persistence jar files from glassfish4/glassfish/modules to WEB-INF/lib. Then go in your glassfish-web.xml, and set class-delegate to false.

Worked for me !

Alleluia answered 17/11, 2014 at 13:27 Comment(0)
C
0

I had a similar issue with JAXB and JBoss AS 7.1. The issue and solution are described here: javax.xml.bind.JAXBException: Class *** nor any of its super class is known to this context. The exception that was given was org.foo.bar.ValueSet cannot be cast to org.foo.bar.ValueSet

Clause answered 7/1, 2017 at 21:23 Comment(0)
L
0

I had the same issue on a wildfly EJB, The EJB was returning a list of Objects and has an remote and a local interface. I used the Local interface by mistake what was working just fine up until the point you try to cast the objects in the list.

Local/Remote interface:

public interface DocumentStoreService {

    @javax.ejb.Remote
    interface Remote extends DocumentStoreService {
    }

    @javax.ejb.Local
    interface Local extends DocumentStoreService {
    }

The EJB bean:

@Stateless
public class DocumentStoreServiceImpl implements DocumentStoreService.Local, DocumentStoreService.Remote {

The correct spring wrapper around the EJB:

<bean id="documentStoreService" class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
    <property name="jndiName" value="java:global/dpc/dpc-ejb/DocumentStoreServiceImpl!santam.apps.dpc.service.DocumentStoreService$Remote"/>
    <property name="businessInterface" value="santam.apps.dpc.service.DocumentStoreService$Remote"/>
    <property name="resourceRef" value="true" />
</bean>

Note the $Remote, You can change this to $Local and it will find the Local interface just fine, and also execute the methods without any issue (from a separate application on the same container), but the model objects are not marshaled and are from a different class loader if you use the local interface by mistake.

Livingston answered 21/2, 2017 at 8:25 Comment(0)
C
0

Another option:

Happened to me in weblogic, but I guess it can happen in other servers as well - if you do (just) "Publish" and therefor some of your classes are re-loaded. Instead do "Clean" so all the classes will re-loaded together.

Counterplot answered 28/2, 2017 at 11:22 Comment(0)
F
0

I had same problem with an EJB lookup from another EJB. I solved adding @Remote(MyInterface.class) to EJB class configuration

Freberg answered 21/6, 2017 at 15:16 Comment(1)
DONT do this. I don't know what it is about regular Java developers dipping their toes into the JavaEE world and deciding all of the sudden they don't want to read the documentation anymore. Remote is used for a very specific purpose where 95% of JavaEE applications won't ever need this, or even want it. What this person is suggesting is violating the EJB3.0 Spec and using an annotation that changes the way an EJB is injected to do it. Never do this unless you know what Remote means and you actually need it for how its intended. Understand your application and stop hacking around the spec.Langdon

© 2022 - 2024 — McMap. All rights reserved.