JBoss.org Community Documentation
A java.lang.ClassCastException
results whenever an attempt is made to cast an instance to an incompatible type. A simple example is trying to obtain a String
from a List
into which a URL
was placed:
ArrayList array = new ArrayList(); array.add(new URL("file:/tmp")); String url = (String) array.get(0); java.lang.ClassCastException: java.net.URL at org.jboss.book.jmx.ex0.ExCCEa.main(Ex1CCE.java:16)
The ClassCastException
tells you that the attempt to cast the array element to a String
failed because the actual type was URL
. This trivial case is not what we are interested in however. Consider the case of a JAR being loaded by different class loaders. Although the classes loaded through each class loader are identical in terms of the bytecode, they are completely different types as viewed by the Java type system. An example of this is illustrated by the code shown in Example 3.1, “The ExCCEc class used to demonstrate ClassCastException due to duplicate class loaders”.
package org.jboss.book.jmx.ex0; import java.io.File; import java.net.URL; import java.net.URLClassLoader; import java.lang.reflect.Method; import org.apache.log4j.Logger; import org.jboss.util.ChapterExRepository; import org.jboss.util.Debug; /** * An example of a ClassCastException that * results from classes loaded through * different class loaders. * @author Scott.Stark@jboss.org * @version $Revision: 1.9 $ */ public class ExCCEc { public static void main(String[] args) throws Exception { ChapterExRepository.init(ExCCEc.class); String chapDir = System.getProperty("j2eechapter.dir"); Logger ucl0Log = Logger.getLogger("UCL0"); File jar0 = new File(chapDir+"/j0.jar"); ucl0Log.info("jar0 path: "+jar0.toString()); URL[] cp0 = {jar0.toURL()}; URLClassLoader ucl0 = new URLClassLoader(cp0); Thread.currentThread().setContextClassLoader(ucl0); Class objClass = ucl0.loadClass("org.jboss.book.jmx.ex0.ExObj"); StringBuffer buffer = new StringBuffer("ExObj Info"); Debug.displayClassInfo(objClass, buffer, false); ucl0Log.info(buffer.toString()); Object value = objClass.newInstance(); File jar1 = new File(chapDir+"/j0.jar"); Logger ucl1Log = Logger.getLogger("UCL1"); ucl1Log.info("jar1 path: "+jar1.toString()); URL[] cp1 = {jar1.toURL()}; URLClassLoader ucl1 = new URLClassLoader(cp1); Thread.currentThread().setContextClassLoader(ucl1); Class ctxClass2 = ucl1.loadClass("org.jboss.book.jmx.ex0.ExCtx"); buffer.setLength(0); buffer.append("ExCtx Info"); Debug.displayClassInfo(ctxClass2, buffer, false); ucl1Log.info(buffer.toString()); Object ctx2 = ctxClass2.newInstance(); try { Class[] types = {Object.class}; Method useValue = ctxClass2.getMethod("useValue", types); Object[] margs = {value}; useValue.invoke(ctx2, margs); } catch(Exception e) { ucl1Log.error("Failed to invoke ExCtx.useValue", e); throw e; } } }
Example 3.1. The ExCCEc class used to demonstrate ClassCastException due to duplicate class loaders
package org.jboss.book.jmx.ex0; import java.io.IOException; import org.apache.log4j.Logger; import org.jboss.util.Debug; /** * A classes used to demonstrate various class * loading issues * @author Scott.Stark@jboss.org * @version $Revision: 1.9 $ */ public class ExCtx { ExObj value; public ExCtx() throws IOException { value = new ExObj(); Logger log = Logger.getLogger(ExCtx.class); StringBuffer buffer = new StringBuffer("ctor.ExObj"); Debug.displayClassInfo(value.getClass(), buffer, false); log.info(buffer.toString()); ExObj2 obj2 = value.ivar; buffer.setLength(0); buffer = new StringBuffer("ctor.ExObj.ivar"); Debug.displayClassInfo(obj2.getClass(), buffer, false); log.info(buffer.toString()); } public Object getValue() { return value; } public void useValue(Object obj) throws Exception { Logger log = Logger.getLogger(ExCtx.class); StringBuffer buffer = new StringBuffer("useValue2.arg class"); Debug.displayClassInfo(obj.getClass(), buffer, false); log.info(buffer.toString()); buffer.setLength(0); buffer.append("useValue2.ExObj class"); Debug.displayClassInfo(ExObj.class, buffer, false); log.info(buffer.toString()); ExObj ex = (ExObj) obj; } void pkgUseValue(Object obj) throws Exception { Logger log = Logger.getLogger(ExCtx.class); log.info("In pkgUseValue"); } }
package org.jboss.book.jmx.ex0; import java.io.Serializable; /** * @author Scott.Stark@jboss.org * @version $Revision: 1.9 $ */ public class ExObj implements Serializable { public ExObj2 ivar = new ExObj2(); }
package org.jboss.book.jmx.ex0; import java.io.Serializable; /** * @author Scott.Stark@jboss.org * @version $Revision: 1.9 $ */ public class ExObj2 implements Serializable { }
Example 3.2. The ExCtx, ExObj, and ExObj2 classes used by the examples
The ExCCEc.main
method uses reflection to isolate the classes that are being loaded by the class loaders ucl0
and ucl1
from the application class loader. Both are setup to load classes from the output/jmx/j0.jar
, the contents of which are:
[examples]$ jar -tf output/jmx/j0.jar ... org/jboss/book/jmx/ex0/ExCtx.class org/jboss/book/jmx/ex0/ExObj.class org/jboss/book/jmx/ex0/ExObj2.class
We will run an example that demonstrates how a class cast exception can occur and then look at the specific issue with the example. See Appendix A, Book Example Installation for instructions on installing the examples accompanying the book, and then run the example from within the examples directory using the following command:
[examples]$ ant -Dchap=jmx -Dex=0c run-example ... [java] java.lang.reflect.InvocationTargetException [java] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [java] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) [java] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl .java:25) [java] at java.lang.reflect.Method.invoke(Method.java:585) [java] at org.jboss.book.jmx.ex0.ExCCEc.main(ExCCEc.java:58) [java] Caused by: java.lang.ClassCastException: org.jboss.book.jmx.ex0.ExObj [java] at org.jboss.book.jmx.ex0.ExCtx.useValue(ExCtx.java:44) [java] ... 5 more
Only the exception is shown here. The full output can be found in the logs/jmx-ex0c.log
file. At line 55 of ExCCEc.java
we are invoking ExcCCECtx.useValue(Object)
on the instance loaded and created in lines 37-48 using ucl1
. The ExObj
passed in is the one loaded and created in lines 25-35 via ucl0
. The exception results when the ExCtx.useValue
code attempts to cast the argument passed in to a ExObj
. To understand why this fails consider the debugging output from the jmx-ex0c.log
file shown in Example 3.3, “The jmx-ex0c.log debugging output for the ExObj classes seen”.
[INFO,UCL0] ExObj Info org.jboss.book.jmx.ex0.ExObj(f8968f).ClassLoader=java.net.URLClassLoader@2611a7 ..java.net.URLClassLoader@2611a7 ....file:/Users/orb/proj/jboss/jboss-docs/jbossas/j2ee/examples/output/jmx/j0.jar ++++CodeSource: (file:/Users/orb/proj/jboss/jboss-docs/jbossas/j2ee/examples/output/ jmx/j0.jar <no signer certificates>) Implemented Interfaces: ++interface java.io.Serializable(41b571) ++++ClassLoader: null ++++Null CodeSource [INFO,ExCtx] useValue2.ExObj class org.jboss.book.jmx.ex0.ExObj(bc8e1e).ClassLoader=java.net.URLClassLoader@6bd8ea ..java.net.URLClassLoader@6bd8ea ....file:/Users/orb/proj/jboss/jboss-docs/jbossas/j2ee/examples/output/jmx/j0.jar ++++CodeSource: (file:/Users/orb/proj/jboss/jboss-docs/jbossas/j2ee/examples/output/ jmx/j0.jar <no signer certificates>) Implemented Interfaces: ++interface java.io.Serializable(41b571) ++++ClassLoader: null ++++Null CodeSource
Example 3.3. The jmx-ex0c.log debugging output for the ExObj classes seen
The first output prefixed with [INFO,UCL0]
shows that the ExObj
class loaded at line ExCCEc.java:31
has a hash code of f8968f
and an associated URLClassLoader
instance with a hash code of 2611a7
, which corresponds to ucl0. This is the class used to create the instance passed to the ExCtx.useValue
method. The second output prefixed with [INFO,ExCtx]
shows that the ExObj
class as seen in the context of the ExCtx.useValue
method has a hash code of bc8e1e
and a URLClassLoader
instance with an associated hash code of 6bd8ea
, which corresponds to ucl1
. So even though the ExObj
classes are the same in terms of actual bytecode since it comes from the same j0.jar
, the classes are different as seen by both the ExObj
class hash codes, and the associated URLClassLoader
instances. Hence, attempting to cast an instance of ExObj
from one scope to the other results in the ClassCastException
.
This type of error is common when redeploying an application to which other applications are holding references to classes from the redeployed application. For example, a standalone WAR accessing an EJB. If you are redeploying an application, all dependent applications must flush their class references. Typically this requires that the dependent applications themselves be redeployed.
An alternate means of allowing independent deployments to interact in the presence of redeployment would be to isolate the deployments by configuring the EJB layer to use the standard call-by-value semantics rather than the call-by-reference JBoss will default to for components collocated in the same VM. An example of how to enable call-by-value semantics is presented in Chapter 11, EJBs on JBoss