Details
-
Type:
Bug
-
Status:
Closed
-
Priority:
Critical
-
Resolution: Fixed
-
Affects Version/s: JRuby 1.1.2
-
Fix Version/s: JRuby 1.1.3
-
Component/s: Java Integration
-
Labels:None
-
Environment:MacOS 1.5.2, Java 1.5.0_13, JRuby trunk r6630
-
Testcase included:yes
-
Number of attachments :
Description
Requiring and using a Java library that ends up using a Thread Context classloader doesn't work.
I'm integrating some Java code into my JRuby project and adding the jars to JRuby's classpath using require.
The external Java code uses other libraries that use a Thread Context classloader. When the ThreadContext classloader tries to create a class that is defined in one of the jars I required from JRuby I get a ClassNotFoundException.
If I add the jars to the system CLASSPATH everything works.
This problem came up while trying to integrate our SAIL-OTrunk code however we've created a much simpler test to show the problem.
Trac:
http://trac.cosmos.concord.org/projects/browser/trunk/common/misc/context-classloader-test
Setting up the test:
svn co https://svn.concord.org/svn/projects/trunk/common/misc/context-classloader-test
cd context-classloader-test
mkdir -p lib/test/contextclassloader
javac -sourcepath src/ -d lib/ src/test/contextclassloader/Main.java
cp src/test/contextclassloader/configuration.xml lib/test/contextclassloader/
jar cf context-classloader.jar -C lib test/contextclassloader/
Running the tests:
failing test:
$ jruby main.rb java.lang.ClassNotFoundException: test.contextclassloader.ConfigurableClass Continuing ... java.lang.NullPointerException: target should not be null Continuing ... java.lang.NullPointerException: target should not be null Continuing ... java.lang.NullPointerException: target should not be null Continuing ... nil
passing test:
$ export CLASSPATH=$CLASSPATH:`pwd`/context-classloader.jar $ jruby main.rb test.contextclassloader.ConfigurableClass@f7cdc7 name: really cool name goes here
Here's some of the investigation I did when I was originally looking into the classloading issues running OTrunk from JRuby.
The problem occurs when a Java class I call then calls XMLDecoder to instantiate another Java class – XMLDecoder is not using the JRuby classloader – instead it is using a thread context classloader and can't find the class it's trying to create because only the JRuby classloader has the new jars I've required
In JRuby the thread context classloader is the parent of the jruby classloader. See my experiments here:
When I contacted Charles Nutter on IRC he wrote that JRuby is not setting the threadcontext classloader when the JRuby thread is created to make it easier to create new JRuby thread instances.
the problem we would have setting the jruby loader as thread context classloader is...what happens if the thread calls through to another JRuby instance? since the jaxp libs are loaded by system classloader, in order for them to locate and instantiate classes from lower classloaders it looks at tc classloader – we don't set tc classloader to the jruby classloader because, well, we really can't if we want jruby instances to be able to migrate across threads
I don't know enough about the JRuby threading to know why using a ThreadContext classloader would hinder or prevent migrating JRuby instances across threads.
You can see more of the IRC conversation between me and Charles starting around 01:05:43 here:
Recent threads mentioning related issues:
Chris Evans-8:
"When I add the websphere jars to the classpath in the jruby executable, it works.
If I append the jars in the script, either through $CLASSPATH << or 'require
blah.jar', the script fails."
http://www.nabble.com/Dynamically-adding-to-classpath-td15841856.html
Lenny Marks:
"..trying to use the Spring's ClassPathXmlApplicationContext, but it seems that
internally, Springkeeps trying to load stuff with the Thread's contextClassLoader"
I've encountered another case when
proper thread context classloader is required: loading JDBC drivers
that are not on CLASSPATH.
Luckily, there is a more or less straightforward way to adjust the
context classloader:
With that, I verified that the test case from the bug report pass. And
JDBC drivers are loaded OK too.