This week's book giveaway is in the OCMJEA forum. We're giving away four copies of OCM Java EE 6 Enterprise Architect Exam Guide and have Paul Allen & Joseph Bambara on-line! See this thread for details.
Every class is identified in JVM by a key which is a combination of three thing...[classname, packagename, its loader] and this combination must be unique. Does it mean that when you have duplicate combination for a single class, then ClassCastException comes.
I got this type of exception when I bundled my transfer object in both WAR and EJB-JAR of my application.
My transfer object class is something like this....
Here since my TransferObject class is in both WAR as well as in EJB-JAR, it will be loaded by two class loaders. Then I passed the instance of TransferObject created in WAR in EJB-JAR. There I already have one instance whose class is loaded by EJB class loader. When I call overridden equals() method on these two TransferObject instances, I get ClassCastException.
I think because the two instances are loaded by different classloader I am getting ClassCastException.
I am not sure about that and how to get the same thing by a simple java program.
Can I load same class by two different class loaders?
Here is one quote from onjava.com
we have classes M-A1 (M loaded by class loader A1) and M-A2 (M loaded by classloader A2). Imagine we also have another class, Test-A1, with a method upcastM() that looks like this:
Because the class Test is loaded by A1, its symbolic link M is also loaded by A1. So we are going to upcast a given object to M-A1. When this method is called with an instance of the class M-A1 as an argument, it will return successfully, but if it is called with an instance of M-A2, it will throw a ClassCastException because it is not the same class, according to the JVM.
Yes, OK. You asked about ClassCastExceptions happening during class loading, but that's not really what you're asking.
If you load the same class file twice, in two different ClassLoaders, then the two classes are indeed two different classes, and you can't cast one to the other. The solution is not to put these classes into more than one archive file; you can take them out into a single shared jar and make sure it's loaded by a common ancestor class loader.
Joined: Apr 25, 2005
Originally posted by Ernest Friedman-Hill Yes, OK. You asked about ClassCastExceptions happening during class loading, but that's not really what you're asking.
Thanks for your reply sir. I think the subject line is not correctly displaying my problem.
So now I am going to edit the subject line...
Now lets take a different case.
In the above code when I execute Test.main(), Test class will be loaded by System classpath classloader.
At line 2, will the same class be loaded again by loader1? I am sure whether Test class will be loaded by two class loader.
Can I get same ClassCastException by simple java program?. Well the whole point is loading same class by two different class loader and then casting one reference type in another.
In your simple program, the same classloader handles everything; the class is loaded only once. The forName() call returns the same Class object from a cache.
You could reproduce the ClassCastException problem in a relatively small program, but it's not just the code, but how you deploy it that counts. Here's a recipe that would work:
1) Write a little class "Test", something like your original class with an equals() method that casts the argument to "Test". Compile it, then move Test.class to some location that is definitely NOT on your class path. This is important: if you don't do this, then you won't get the exception later.
2) Create two instances of URLClassLoader. Give each instance a constructor argument with a URL pointing to the location of Test.class. Note that now both these ClassLoaders can find Test.class, but the system classloader cannot.
3) Load the class once with each URLClassLoader to create two Class objects by using the three-argument form of Class.forName().
4) Create an instance from each Class object using newInstance(). Just store each instance in Object variables o1 and o2.
There are, at least, two general ways to generate a ClassCastException under the conditions described. One is with a branched ClassLoader hierarchy, and the second is with a ClassLoader that does not preferentially delegate to its parent. Read on for the long winded explanation ...
In a simple J2SE application (like "Hello World") there are at least three ClassLoader instances involved. In order (and order is very significant) these are: 1.) bootstrap classes, 2.) extension classes 3.) classpath (user) classes. These form a hierarchy (parent-child relationship) where the child (higher number in the above list) class loaders delegate to their parent before trying to load a class themselves. The delegation behavior is by convention and is not enforced. As long as this is a simple chain (one child per parent) and the contract for delegation is followed then ClassCastExceptions due to ClassLoader differences are impossible. If the ClassLoader hierarchy branches (i.e. 2 or more children for any parent) then ClassCastExceptions may be thrown even if the class names are identical. This is the scenario illustrated in the earlier post by Mr Friedman-Hill where there are 2 instances of URLClassLoader, each a child of the classpath classloader. Also, if a ClassLoader does not follow the convention of delegating first to its parent then the potential exists for ClassCastException due to different ClassLoader instances. The simplest way to do this would be to write a ClassLoader that loads a class before delegating to its parent, in which case it could load something that the parent already has loaded. J2EE app servers use a ClassLoader hierarchy to enforce some security, e.g. not allowing EARs to access other EARs classes. The behavior depends on the app server. Some app servers use one ClassLoader per EAR and a child ClassLoader per WAR. Some use a sibling arrangement where a ClassLoader for a WAR delegates to a sibling classloader for an EJB-JAR.
"As of JDK 1.2, a bootstrap class loader that is built into the JVM is responsible for loading the classes of the Java runtime. This class loader only loads classes that are found in the boot classpath, and since these are trusted classes, the validation process is not performed as for untrusted classes. In addition to the bootstrap class loader, the JVM has an extension class loader responsible for loading classes from standard extension APIs, and a system class loader that loads classes from a general class path as well as your application classes.
Since there is more than one class loader, they are represented in a tree whose root is the bootstrap class loader. Each class loader has a reference to its parent class loader. When a class loader is asked to load a class, it consults its parent class loader before attempting to load the item itself. The parent in turn consults its parent, and so on. So it is only after all the ancestor class loaders cannot find the class that the current class loader gets involved. In other words, a delegation model is used."
Joined: Apr 25, 2005
Thanks Friedman. Thanks Chris.
I really apreciate the effort which you have made in solving the problem.