Different classloaders cause ClassCastException when persisting data via Spring - java

I'm creating an MVC Spring webapp.
Using: Jetty (servlet container), DataNucleus (dao platform), DB4O (embedded datastore).
When I persist an object (done from within a Spring Controller) using JDO from DataNucleus, it stores to the DB fine.
#PersistenceCapable
public class Test {
#Persistent
private String testString;
//getter-setters implemented
}
When I do a simple query for the objects I previously added I get a ClassCastException on my Test class (can't cast a.b.c.Test to a.b.c.Test).
The classloader of Test returned by JDO is (toString) [sun.misc.Launcher$AppClassLoader#5acac268], the classloader of the Test class before I persisted it to the DB is [WebAppClassLoader#1593275665]
I've gotten this far, but I don't really know what to do with a classloader issue like this, I've never spent much thought on classloaders before. Any direction is helpful.

There doesn't need to be two different versions of the class for a class cast exception to appear. Even the same class definition is seen as two different classes when loaded by two distinct classloaders. Which seems to be the case here.
Unfortunately I am not familiar with the platforms you use, so I can't give more concrete advice than this: try to experiment with moving the jar containing your Test class to different places on your web app classpath, and/or reconfiguring the Spring and Jetty classloaders so that both delegate the loading of Test to the same parent classloader.

I think that your problem might be similar to the one described here.
If so, the cure would appear to be to make sure that the jdo jarfile is loaded by a common ancestor of the classloaders.

Related

Class cast exception with MSSQL drivers [duplicate]

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?
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:
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(...);
Make sure the class is loaded by a class loader higher in the hierarchy.
Serialize and deserialize the object. (Yuck!)
There is probably a more appropriate way for your particular situation though.
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
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.
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.
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.
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
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 !
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
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.
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.
I had same problem with an EJB lookup from another EJB.
I solved adding #Remote(MyInterface.class) to EJB class configuration

getting java.lang.ClassCastException: com.app.LoginInfo cannot be cast to com.app.LoginInfo during jboss migration

We are migrating our server from Jboss 5 to Wildfly 9.
During migration, one of the issue which we are unable to solve is this classcastexception. Its happening while logging to the system. Issue is occurring inconsistently and mostly occurring after a restart.
We are getting this exception while querying using Hibernate hql and castin
LoginInfo userInfo = (LoginInfo) session.createQuery(hql).uniqueResult();
I have reconfirmed that we have only one version of hibernate jar.
Classes are not compatible if they are loaded by different class loaders. Even though you might only have a single copy of a JAR file on disk it might still be loaded by different class loaders whose classes would not be compatible.
If you are fiddling around with class loaders yourself you have to make sure that you set the parent class loader correctly to avoid situations like these.

Java ClassLoader to deal with "Instance" classes

As we know java class loading works like in picture below.
When we have a notion of plugin in our application (like app servers) we sometimes need some classes to be loaded in the instances class loader rather than in parent, so that each new instance (plugin, webapp or whatever) loads that particular class not delegating the parent...
For example Log4j classes, we need them to be loaded for each instance.
So in order to do this the best approach that came into my mind is to write custom classloader that will take a list of class names which shall be prevented from being delegated to parent classloader (ideally we want instances to be in complete isolation).
So the the application that will load other "instances" will use that custom classloader while loading those "instances"...
Is there an implementation of such classloader that solves this issue? (given that we dont want to know what OSGi is and we work with pure java no frameworks etc...)
My searches end up pointing some frameworks or some web container specific solutions, yet this is quite a simple task that is solved with one class, I'd like to find out if i'm missing something before i start implementing it.
Update (digging deeper) : suppose there is a class loaded by bootstrap which has static state that can be shared between instances (and we really badly want to make sure instances are completely isolated), now that class is obviously not included in our classpath, but it is loaded, and if we copy it instead of reffering it we will achieve the required isolation.
So do we have the notion of copying or cloneing a class from one classloader to other?

Hacking a singleton with classloaders - Multiton in a Java web app

I am creating a web front end using an existing back end containing several singleton classes. The DataStore is initialized by passing a user object into it, which is fine in a desktop application environment where the app is launched once on each machine, but will not work in a server side application designed to cater for multiple users.
The database guys are reluctant to change the service layer and remove these singletons to allow an instance per user, or allow a single instance of a service layer object to serve multiple users. This is with good reason, the desktop app has been in use for 10 years and changes could have serious side effects for the desktop app.
I have been asked to investigate using classloaders to create multiple instances of the singletons. I am not comfortable with this idea at all, hacking singletons seems like bad practice, but changing the service layer could take months of work.
I have tested this out already by putting two identical WAR files of my app (with different file names) into Tomcat. Tomcat creates a classloader for each webapp and they worked just fine separately. I only encountered problems when the singletons used System.setProperty/System.getProperty, which is to be expected as the System class comes from a classloader much higher up in the tree.
To get this separation within a single webapp, it starts to get a bit complicated. It seems I would have to create a different classloader for each session, and use the classloader to load either all the classes in the whole service layer or just the ones which are singletons and their dependencies.
The problem comes when I'm thinking about how to use these objects in a session in a servlet. Because each Servlet has a single instance within a Tomcat, getting objects from the session and casting them will not be straightforward. Eg, to get a DataStore object from the session, I would have to cast it to the correct DataStore class loaded by the correct ClassLoader, since a single class loaded by two different ClassLoaders counts as two completely separate classes.
I have read that using ClassLoaders can cause all sorts of problems with memory leaks if they are not used carefully, and from the sounds of this, if I have 500 users, that is a lot of classloaders and classes loaded by classloaders. Won't I then have also have issues with PermGen?
I suppose from this large explanation, I really have 4 questions:
Hacking a singleton with classloaders in a webapp. Creating potentially hundreds of instances of the same classes designed to be singletons. Is that a terrible idea? So terrible I shouldn't contemplate it?
What is the best way to implement this if I absolutely have to?
How do I cater for casting in a Servlet, if I want to get and set objects into a session?
Will I end up with issues with memory leaks, and PermGen space?
I would really appreciate any suggestions. Thanks :)
It's not ideal, but I don't think it's terrible. In practice, it's not much worse than loading multiple versions of the same class (even without singletons), and that's becoming increasingly common in complex application server environments, particularly those with OSGi.
It's hard to say which approach is best, but to begin with, I would start by creating child class loaders of your web application class loader. Arrange to load implementation classes in the child class loaders (i.e., the ones with the singleton), and it possible, have the implementation class implement an interface that is loaded from the web application class loader.
By loading the interface from the web application class loader. This is basically the same approach that the application server itself is using to invoke your HttpServlet: the interface is loaded by the server, so it can refer to it directly, but the implementation is in a child class loader for your application. You're just creating a secondary layer of interface/impl split for your own convenience.
You'll end up with memory leaks if you store references to the child class loader (or its loaded classes, or instantiated objects from those classes) in a "parent" class loader (e.g., if the singleton registers an MBean, which causes it to get referenced in a JVM-wide object), but that's no different from if you weren't creating child class loaders. If you're dynamically creating/destroying these singletons (thus child class loaders), you'll have to take care that you don't retain references to those child class loaders (or classes/objects) longer than necessary. PermGen is probably more problematic. If you can run with Java 8, that's gone away; otherwise, you might have to increase the default PermGen size depending on how many of these class loaders/singletons you need to create.

Can I run code before servlet class loading on Tomcat?

I'm doing some runtime bytecode manipulation on some of my business objects, and it's very important that they be loaded in the right order. Currently I'm simply calling Class.getSimpleName() on them in the right order in my Startup servlet. This has been working just fine, but if there's a better way, I'm all ears.
Now, however, I need a method in one of my servlet filters to return a concrete business object type. This is causing the classloader to load that particular business object class first (out of order) and things break.
What I'd like is to be able to run my getSimpleName() hack before any of my servlets or filters are loaded. Is there some place I can put code that runs before the classloader even loads my filters?
Yes, you can.
Look at ServletContextListener.
The tomcat class loader works in this fashion.
if you want to load any classes before any of you web-app classes load, then you can put those classes in a jar file and deploy it to tomcat common library, these classes will be loaded before your web application classes are loaded by the class loader.
You can check the documentation of how apache tomcat class loader works here

Categories

Resources