The Context Classpath: Getting Java to see our added classpath.

Pat <[email protected]>
Newsgroups gmane.comp.java.beanshell.devel,gmane.comp.java.beanshell.user
Message-ID <[email protected]>
You may have noticed that when adding classes using addClassPath() those
classes aren't available to external Java code that you invoke from your
BeanShell scripts.  They are effectively local to the script itself.

I've been experimenting with a version of the addClassPath() command that
adds classpath to the current thread's context classloader.  This is the
general mechanism in Java that allows system and framework classes to see
classes loaded by application specific classloaders.  For example, it allows a
J2EE container to work with classes supplied in an EAR or WAR.

BeanShell has for a long time *read* from the context classloader in order to
see classes provided by applications, but it has not previously *set* the
context classloader with its own classpath extensions.  This has meant that
added classes could be referred to in BeanShell scripts, but would not be seen
by Java code called by those scripts.

The example below shows a simple EJB lookup requiring the Weblogic JNDI factory
and some application classes in an EJB client jar.  This script adds the
weblogic.jar and client jar to the classpath and works without any external
classpath being provided:

  import javax.naming.*;

  addContextClassPath("/home/pat/weblogic.jar");
  addContextClassPath("/home/pat/ejb-client.jar");

  // Lookup JNDI context using the Weblogic factory
  String initialContextFactory = "weblogic.jndi.WLInitialContextFactory";
  String localContextURL = "t3://172.16.75.139:7001";
  Properties p = new Properties(); 
  p.put( Context.INITIAL_CONTEXT_FACTORY, initialContextFactory );
  p.put( Context.PROVIDER_URL, localContextURL );
  ctx = new InitialContext(p);

  // Look up an EJB requiring the ejb-client classes to be seen by Weblogic
  home = ctx.lookup ("MyEJB");
  bean = home.create(); 
  bean.test();

Using the current addClassPath() command BeanShell would be able to see the
added classes, but system classes - for example InitialContext - would not and
so it would fail to load the weblogic JNDI factory.  Similarly, weblogic would
not be able to see the necessary EJB home class without the context classpath.

I expected this new command to make it easier to do testing using the BeanShell
Servlet... being able to add client jars directly in the script rather than
having to deploy the bsh servlet with every app you want to test.  However I'm
getting a ClassCastException that I can't explain when data objects are passed
between the two classloaders.

Anyway, for your amusement I've attached a copy of the new command if anyone
wants to experiment.

At minimum I will add this as a new command in an upcoming release.  If it
proves to be generally useful and harmless I may make this the default
functionality of addClassPath().

Note that this doesn't solve the problem entirely, as not all Java code obeys
the context classloader.  For example, code that calls Class.forName() directly
will not see our added classpath unless we load both BeanShell and the
application through our own classloader and this is something I plan to provide
a means for later.  I'm not sure why Sun doesn't have Class.forName() defer to
the context classloader if no class is found.  There must be a reason.


Thanks,
Pat
addContextClassPath.bsh (text/plain, 631 B)
/**
    Add classpath to the current Thread's context classloader.
    This allows the added classpath to be seen not only by BeanShell but by any
    system classes that honor the context classloader.

    @method void addClassPath( string | URL )
*/
void addContextClassPath( path )
{
    URL url;
    if ( path instanceof URL )
        url = path;
    else
        url = pathToFile( path ).toURL();

    // Add to the context classloader
    ccl = Thread.currentThread().getContextClassLoader();
    urls=new URL[] { url };
    urlc = new URLClassLoader( urls, ccl );
    Thread.currentThread().setContextClassLoader( urlc );
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.