[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[xmlblaster-devel] Class- and resource-loading



Hi,
this mail may be a little presumptuous; but my intentions are good:
namely to try to explain why class- and resourceloading in XmlBlaster
only works good in standalone mode.

Here's a background, that should be familiar to all of us.

Classloaders in Java are hierarchical. At the top is the JVM
classloader, and then there is the System classloader, which normally is
the classloader holding the classpath wich was specified on the command
line.

A child classloader allways have access to the parent classloader, but
any classes loaded by the child classloader is invisible to the parent.

Here's a pretty typical tree when running standalone:

Class loader tree
        |
sun.misc.Launcher$ExtClassLoader at b169f8
{file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/sunjce_provider.jar:file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/dnsns.jar:file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/localedata.jar:file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/ldapsec.jar:}
        |
sun.misc.Launcher$AppClassLoader at 4b222f {file:/home/pra/src/javatest/:}

However, in application servers, it is very common that this tree is
much deeper. It is very easy to create a new "isolated" child
classloader to hold application server classes and to isolate components
loaded by the server.

In such environments it is important to note how one uses the
classloader.

Generally one can say that that the classloader is available to the
runtime in two flavors:

* as a signature for a loaded class. The classloader is part of the
signature of a loaded class. The same class loaded by different
classloaders is actually not the same class, as instancesof will not
find them equal.

* As the current classloader of the running thread.

Lets see what this means during a method invocation on class A.

During the invocation its possible to lookup two possible different
classloaders:

a) The "signature" classloader, i.e the loader that loaded the class.
b) The context classloader, i.e the loader thats connected to the
current thread.

The first type of classloader is used when the following types of codes
are used:

this.getClass().getClassLoader();
A.class.getClassLoader();
Class.forName("");

The second classloader is accecable through
Thread.currentThread().getContextClassLoader();

When running standalone apps, the differences between these two ways of
getting the classloaders is often not important. Thats because the app
classloader will be loading all app specific classes, and will also be
set as the context classloader of the applications thread. Here's a
typical output (see example at the end of the mail):

System ClassLoader sun.misc.Launcher$AppClassLoader at 4b222f

Class loader tree
        |
sun.misc.Launcher$ExtClassLoader at b169f8
{file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/sunjce_provider.jar:file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/dnsns.jar:file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/localedata.jar:file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/ldapsec.jar:}
        |
sun.misc.Launcher$AppClassLoader at 4b222f
{file:/home/pra/src/javatest/:file:/home/pra/src/javatest/my.jar:}

Context loader tree
        |
sun.misc.Launcher$ExtClassLoader at b169f8
{file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/sunjce_provider.jar:file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/dnsns.jar:file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/localedata.jar:file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/ldapsec.jar:}
        |
sun.misc.Launcher$AppClassLoader at 4b222f
{file:/home/pra/src/javatest/:file:/home/pra/src/javatest/my.jar:}


But when running in an appserver or otherwise managed environment, there
might be a difference. Say we create a component with its own
classloader. That component uses the class loaded by the app
classloader:

         URLClassLoader ucl = new URLClassLoader(pathes,l);         
         Thread.currentThread().setContextClassLoader(ucl);

         printClassLoaderTree();

Now the classloader we got through the class signature is not the same
as the context classloader:

Class loader tree
        |
sun.misc.Launcher$ExtClassLoader at b169f8
{file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/sunjce_provider.jar:file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/dnsns.jar:file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/localedata.jar:file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/ldapsec.jar:}
        |
sun.misc.Launcher$AppClassLoader at 4b222f
{file:/home/pra/src/javatest/:file:/home/pra/src/javatest/my.jar:}

Context loader tree
        |
sun.misc.Launcher$ExtClassLoader at b169f8
{file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/sunjce_provider.jar:file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/dnsns.jar:file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/localedata.jar:file:/usr/local/lib/j2sdk1.4.1_01/jre/lib/ext/ldapsec.jar:}
        |
sun.misc.Launcher$AppClassLoader at 4b222f
{file:/home/pra/src/javatest/:file:/home/pra/src/javatest/my.jar:}
        |
java.net.URLClassLoader at 1cde100 {file:../:}


What does this mean? Which effects could it have on XmlBlaster?

The basic problem is this: When using the "signature" classloader to
load resources or classes only resources and classes available to the
classloader that loaded the class that uses the classloader is
available, not classes or resources in the components classloader.

As I have already pointed out, there is a problem with jacorb:s handling
in this regard. That is: if xmlBlaster.jar is loaded by a component
classloader and not the app classloader, jacorb will not be able to find
jacorb.properties since it uses the system classloader to lookup the
resource.

But there is more to it: if for example Property (jutils.jar) is loaded
by system class loader, but xmlBlaster.jar is not, it will not be able
to find xmlBlaster.properties. 

Well, to make a long story short. If one does not have an exact idea
about why to use the signature classloader (i.ex only resources part of
the jar whee the class is is allowed to be loaded) one should ALLWAY use
the context classloader when loading classes dynamically and looking up
resources. 

In a standalone environment there is no difference between the two
approaches, and in a managed environment it will work much better.


//Peter
Example class:

import java.net.*;
/**
 * TestClassLoader.java
 *
 *
 * Created: Fri Aug 29 11:02:36 2003
 *
 *  at author <a href="mailto:pra at tim.se";>Peter Antman</a>
 *  at version $Revision$
 */

public class TestClassLoader{
   public TestClassLoader () {
      
   }


   public static String getURLs(URL[] urls) {
      if ( urls == null) {
         return "{}";
      } // end of if ()
      StringBuffer b = new StringBuffer("{");
      for ( int i = 0;i<urls.length;i++) {
         b.append( urls[i] ).append(":");
      } // end of for ()
      b.append("}");
      return b.toString();
   }
   
   public static void printClassLoaderTree(ClassLoader l) {
      ClassLoader p = l.getParent();
      if ( p!= null) {
         printClassLoaderTree(p);
      } // end of if ()
      String u = "";
      if ( l instanceof URLClassLoader) {
         u = getURLs(  ((URLClassLoader)l).getURLs());
      } // end of if ()
      
      System.out.println("\t|\n"+l+" " +u);
   } 

   public static void printClassLoaderTree() {
         System.out.println("\nClass loader tree");
         printClassLoaderTree( TestClassLoader.class.getClassLoader());
         System.out.println("\nContext loader tree");
         ClassLoader l = Thread.currentThread().getContextClassLoader();
         printClassLoaderTree(l );
   }
   
   public static void main(String[] args){
      try {
         System.out.println("System ClassLoader " +
ClassLoader.getSystemClassLoader());

         printClassLoaderTree();

         ClassLoader l = Thread.currentThread().getContextClassLoader();
         URL[] pathes = new URL[0];
         if ( args.length > 0) {
            pathes = new URL[] { new URL(args[0])};
         } // end of if ()
         
         URLClassLoader ucl = new URLClassLoader(pathes,l);         
         Thread.currentThread().setContextClassLoader(ucl);

         printClassLoaderTree();

         
         Thread.currentThread().setContextClassLoader(l);


      } catch (Exception e) {
         e.printStackTrace();
      } // end of try-catch
      


   }
} // TestClassLoader
-- 
------------------------------------------------------------
Peter Antman	Chief Technology Officer, Development
Technology in Media, Box 34105 100 26 Stockholm
WWW: http://www.tim.se	WWW: http://www.backsource.org
Email: pra at tim.se	 
Phone: +46-(0)8-506 381 11 Mobile: +46-(0)704 20 58 11
------------------------------------------------------------