views:

256

answers:

1

I've written a custom classloader 'JarClassLoader', which itself works ok, since the following test case is green:

public void testJarClassLoader() throws Exception
{
    JarClassLoader cl = new JarClassLoader();
    cl.addURLsByClassPath("lib/a-lot-of-jars.jar|lib/more-jars.jar", "\\|");

    Class c = cl.loadClass("com.packagepath.classname");

    assertNotNull(c);

    System.out.println("Class: "+ c);
}

But, the following test case would not work:

public void testSetThreadClassLoader() throws Exception
{
    JarClassLoader cl = new JarClassLoader();
    cl.addURLsByClassPath("lib/a-lot-of-jars.jar|lib/more-jars.jar", "\\|");

    Thread t = new Thread() {
        public void run()
        {
            TestCase.assertEquals("com.packagepath.JarClassLoader", 
                    Thread.currentThread().getContextClassLoader().getClass().getName());
            //this assertion passed


            try
            {
                Class c = Class.forName("com.packagepath.classname");

                //it doesn't work, throws ClassNotFoundException

                TestCase.assertNotNull(c);
            }
            catch (ClassNotFoundException e)
            {
                e.printStackTrace();
            }

            **com.packagepath.classname.methodname("params");**
            //it doesn't work, throws java.lang.ClassNotFoundException
        }
    };

    t.setContextClassLoader(cl);
    t.start();
}

notice the stared line, I wanted to use thread context classloader when I literally call a method.

I've read dozens of web pages and docs, none of them tells me certainly whether the bold line should work or not. I can't understand where went wrong.

Can literally called method use classloader to load the corresponding class? If not, why can't? I need it to work, since the calling in the jars that vendors provides are literally, not using reflection or anything else, and I have to assign different classloader for different threads to avoid name collision in the vendor jars.

Can anybody help me? Thanks a million!

+1  A: 

Each class links to other classes using its own class loader. Class loaders usually delegate to their parents. You can't change the thread context class loader and expect the class to be unlinked and relinked to different classes.

So you need to make sure that the linked class is in the same class loader instance (or a parent) as the linking class.

Tom Hawtin - tackline
Thanks for your answer. I wasn't expecting to unlink it, I've set a classpath with basically nothing, so the class was never loaded before entering the thread. I just wanted to link it for the first time.
Utensil
So you were expecting the link to happen depending upon which thread it happened to be running on at the time? That wouldn't work very well either.
Tom Hawtin - tackline
Thanks, but what I need is not a "wouldn't work very well" evaluation but a certain answer about can and cant, based on extensive knowlege of java thread context classloader. Actually I've seen codes in Tomcat and other mature thing that can unlink and link, not mention just link it for one time.
Utensil
@Utensil You unlikely to have seen unlinking. You've probably seen reloading, where the relationship is reversed. Polymorphism can help. Thread context class loader is just a `ClassLoader` field in `Thread`.
Tom Hawtin - tackline
yeah, it's just a field or say reference. But what I need to know is when would thread use its context classloader? would it use it when the class.method is literally written? That's the key. I've seen codes get the context classloader, assign to a local variabl, set another loder,enter a code area, and then reset the classloader to the local variable. The reason the coder do this must be that they knwo for sure, it will work for a way of calling class.method. The question is : how many ways are there and which are they?
Utensil
As I say, classes are linked using their own class loader. The thread context class loader is available through `Thread.getContextClassLoader`. Many libraries features use that call to get a relevant class loader. Many library use those libraries, and so on. I would suggest avoiding thread context class loader, or anything else using global/thread-local state.
Tom Hawtin - tackline