Version 21

    Overview of ClassLoading Issues

     

    Why another page on classloading? Because sometimes when something is explained in a different way with different emphasis it is exactly what somebody needs for their problem.

     

    THIS PAGE IS INTENDED TO BE A CHECKLIST OF COMMON PROBLEMS WITH SOLUTIONS REFERENCED ELSEWHERE

     

    PROBLEMS

     

    I get ClassNotFoundException

     

    This means that some dynamic classloading, i.e. classloader.loadClass() cannot find the class. This FAQ deals with the problem.

     

    I get NoClassDefFoundError

     

    This means that some JVM initiated classloading failed. Usually because of an import on an another class that was in the process of being loaded. Again this FAQ deals with the problem.

     

     

    I get ClassCastException

     

    There are some places where it reports this when it really means ClassNotFoundException, e.g. the CORBA

    PortableRemoteObject.narrow()

     

    Assuming it is the same class name, this problem relates to classes getting loaded from different

    classloaders. At compile time your class has only one identity, its name. If you "deploy" a class many times in different jars, it has multiple identities. They are not the same. See below for more information on the solutions to this problem.

     

    This can also be caused by you only redeploying part of your application. e.g. a webapp uses an EJB

    and you only redeploy the EJB. The webapp will still have the old versions of the classes.

     

    I get LinkageError

     

    Assuming you are running the version of the class you compiled over, this is a subtle form of ClassCastException when classes are passed between classloaders without doing a cast. e.g. you have a class in two separate deployments and you invoke a method that passes an object from one to the other but using an interface that the class implements. (See chapter 2 of the admin docs for more information of this subtle problem).

     

    I get ClassCircularityError

     

    Assuming this isn't a real circular reference, this is caused by a JDK bug. The solution is to avoid concurrent classloading for this class and probably related classes

     

     

     

    ISSUES AND DEFINITIONS

     

    What are the types of things you want to do with classloading?

     

    Sharing and isolation

     

    Sharing

     

    The most common thing to do is to share classes. i.e. choose one version of the classes, test it and let everybody use it. This is usually the easiest to manage, understand and it is certainly the most efficient in terms of resources. It also directly mirrors your build environment.

     

    Isolate

     

    You don't want to share your classes with others but you do want to share common classes and use application server provided classes.

     

    JavaSE Spec compliance

     

    Related to sharing and isolation. Do you want to use application server classes where provided?

     

    Non Spec compliance (except the servlet spec

     

    You don't want to use application server classes or any shared classes. You want your own version.

     

    Caller semantics

     

    Call by reference

     

    This is probably what you are familiar with one java class invokes a method on another.

     

    Call by value

     

    This simulates a remote invocation over the network. Parameters are "marshalled" - turned into a byte stream - then "unmarshalled" with destinations classloader. Any return value or exception is again "marshalled" then "unmarshalled" with the origin's classloader.

     

    NOTE: You cannot use call-by-value for EJB local invocations. It simulates a remote call.

     

     

     

    PROBLEMS WITH THESE ISSUES

     

    Isolation

     

    If you choose not to share you will have problems talking to other applications that don't have the same version of the class. The solution is to use call-by-value to "translate" the classes between the classloaders.

     

    Spec compliance

     

    If you choose not to use the application server classes, you will have problems talking to the application server itself.

     

    Call by value

     

    The extra work of object to bytestream translation is a cpu overhead. Typically you will need 10 times the cpu power (or 10 times the number of machines to run compared to an application that uses call-by-reference.

     

    Servlet configuration

     

    The servlet spec says that you shouldn't use JavaSE spec compliance. This essentially means it will use anything you put in

    WEB-INF/classes

    or

    WEB-INF/lib

    . Since these are usually "dumping grounds" for all sorts of "rubbish", this is usually a bad idea.

     

    This isn't quite true since there is a configurable filter to ignore certain packages.

     

    If you do want isolation, then configure that and share the application server classes.

     

    Redeployment

     

    If you share classes with another application, make sure you either don't redeploy the shared classes or you redeploy everybody when they change.

     

     

     

    COMMON CONFIGURATIONS

     

    Share everything

     

    This is the default as long as you turn off the servlet spec classloader.

     

    EAR Deployer - ease of use

     

    Setting

    isolated=true

    and

    call-by-value=true

    on the

    ear.deployer

    and

    enabling call-by-value for jndi will give you isolation and class translation for any applications deployed as ears.

     

    Listing an deployment's contents

    If you are relying on a jars ClassPath manifest header to pickup classes, its useful to display an overview of the archive contents including the ClassPath references. The attached ListJar ListJar.java, ListJar.class can be used for this. Run the ListJar program passing in the archive to list. For example:

    [starksm@banshee9100 main]$ java -cp . ListJar cpmanifest.ear
    /cvs/JBoss4.0/jboss-4.0/testsuite/output/lib/cpmanifest.ear
    +- META-INF/MANIFEST.MF
    +- abstract.jar (archive)
    |  +- META-INF/MANIFEST.MF
    |  +- org/jboss/test/jmx/loading/AbstractBean.class
    +- concrete.jar (archive)
    |  +- META-INF/MANIFEST.MF Class-Path: ./abstract.jar
    |  +- org/jboss/test/jmx/loading/Concrete.class
    |  +- org/jboss/test/jmx/loading/ConcreteBean.class
    |  +- org/jboss/test/jmx/loading/ConcreteHome.class
    |  +- META-INF/ejb-jar.xml
    +- META-INF/application.xml
    Done
    

    This shows that the cpmanifest.ear/concrete.jar includes the abstract.jar via its Class-Path.

     

    Other

     

    The mechanisms for each deployment type are explained in the main classloading configuration link

     

    More complicated

     

    Sometimes you want isolation, but you still want to share amongst a select number of applications. To do this, assign them to same classloader repository with a specific name of your choosing, rather than using the automatic one created by

    isolated=true

    on the EARDeployer. This way you have your own little world to "play in".

     

    Related