JRuby class loader by example

Posted in Original Content by David Cavalero - last update: Dec 30, 2023

If you arrive on this page - you were probably following a link. This page was created by David Cavalera, who no longer owns this domain. It is still linked/indexed - so I decided to keep it online You can find David on Twitter/X or LinkedIn

Original version of this page can still be found on the internet archive.

<Original content below:>

Everybody knows that JRuby lets us to load java jar files using require, but it seems that just a few people know that there are two other methods to do it and how they work under the hood. Our new and shiny wiki page includes a couple of examples but let me go further anyway.

As I said, the first method to load java classes into our ruby application is require. It works like it does with any ruby file but with jar files, so if we invoke a code like this we’ll end up with a bunch of java classes loaded:

require 'java'
require 'hello_world.jar'

This method, just lets us to load jar files, it doesn’t work with java class directories, though.

The second strategy to load java classes is using the variable ‘$CLASSPATH’. It behaves exactly like ‘$LOADPATH’ so we can append or remove paths from the list. The advantage of this strategy is that it also allows to load directories that contain java classes, so these calls are completely valid:

require 'java'
$CLASSPATH << 'hello_world.jar'
$CLASSPATH << 'hello_world_directory'

If we wanted to digg into both methods, ‘require’ and ‘$CLASSPATH’, we’ll find out that at the end when we use require we are actually adding our library to the ‘$CLASSPATH’:

$ irb
jruby-1.6.0.RC2 :001 > require 'java'
 => true
jruby-1.6.0.RC2 :002 > require '/Users/david/dev/jruby/lib/jruby.jar'
 => true
jruby-1.6.0.RC2 :003 > $CLASSPATH
 => ["file:/Users/david/dev/jruby/lib/jruby.jar"]

The third method is for sure the most unknown but it’s actually where everything ends. When we use JRuby one of the constants that we have accessible is ‘JRuby’. This constant, among other things, lets us to reach the runtime itself with ‘JRuby.runtime’’ and from here we also have available the java class loader that JRuby uses, ‘JRuby.runtime.jruby_class_loader’.

The JRuby class loader also lets us to load jar files and class directories but it’s a little bit harder to do it:

If this time we take a look at the JRuby source code itself, we’ll discover that when we append something to the ‘$CLASSPATH’ variable we are actually adding it to the JRuby class loader:

@JRubyMethod(name = {"append", "<<"}, required = 1)
public IRubyObject append(IRubyObject obj) {
    String ss = obj.convertToString().toString();
    try {
        URL url = getURL(ss);
        getRuntime().getJRubyClassLoader().addURL(url);
    } catch (MalformedURLException mue) {
        throw getRuntime().newArgumentError(mue.getLocalizedMessage());
    }
    return this;
}

So now, the question is which one we have to use. For the day to day the best option is ‘require’. Most of the java code we are going to deal with is packed as jar files, and it has a syntax well known. If we need to work with class directories too or we have any other special case, probably our best option is ‘$CLASSPATH’. It gives us what we need, we don’t have to deal with urls and java idioms, and it’s still a syntax that we know. And finally, I’d leave the third option for cases when we need to work with the class loader explicitily. For instance, I use this option in Trinidad because each one of the deployed applications can include their own jar files, so this option lets me create new class loaders for each application and ensure they are completely isolated. <End or original content>

Other articles