The Artima Developer Community
Sponsored Link

Cool Tools and Other Stuff
Using Java classes in JRuby
by Eric Armstrong
September 18, 2007
Summary
I put several pieces of information together and experimented to fill in the missing bits. When I was done, I had a program that implemented Java interfaces and accessed external classes, as well as core classes.

Advertisement

I recently had occasion to write a JRuby script that used Java APIs in the XDocs CMS. I found most of the information I needed scattered around the web (the URLs are listed in the Resources section at the end).

I decided to collect the relevant stuff in one place, leaving out the stuff that seemed extraneous, and adding the additional little bits that turned out to be necessary. This post contains the results. (But things may well change, so let me know if there are errors or it needs to be brought up to date!)

Note:
The original of this article (along with any updates) is here: http://blogs.sun.com/coolstuff/entry/using_java_classes_in_jruby

Requiring JAR Files

First, of course, it's necessary to bring in the module that provides the bridge to the JVM:

include Java

Some things to note:

The program then needed to require every jar file that the program eventually used, even if it wasn't directly referenced in the JRuby script.

Many of the jar files I wound up requiring were implicit. Given a code sample like this:

x = someMethod().someOtherMethod.aThirdMethod()

then every intermediate class had to be required into the code. (Since I was converting a Groovy script, I often didn't know what those classes were.) But even when those objects accessed classes internally, those classes had to be required into the program so the JVM knew where to find them.

So I kept running the program, getting a missing class error, looking for it in the 30 or 40 jar files that make up the CMS, and then adding a new require statement when I found it. I eventually wrote a script to search the jars. But it was an interesting lesson. The API documents tell me what package a class is in, but it would be delightful if there were a cross-reference to the JAR it's contained in.)

As a side-note, wildcards in the CLASSPATH setting would be cool. But the requirestatements, at least, should allow for wildcards. Something like that would have saved me a lot of trouble:

require "some/path*.jar"

Including Class Names and Referencing Them

A lot of the time, requiring the jar files is pretty much all you need to do. JRuby can generally figure out what the type is by inspection (the return value from a method, say), so you don't have to explicitly include those classes. But there are times when you do need to specify the class name in the code (for example, to access a static method). In those cases, you need to add an include_class statement to your code.

When including classes, and when referencing classes in the code, package names that start with java, javax, org, and com are "magic". You can use those package prefixes as you would any other variable. Other packages referenced in the code need to be prefixed with Java::

The "magic" prefixes are "magic" for external packages, as well as for core JVM classes. If you forget the prefix, JRuby gives you an "invalid method" error, because it assumes that the first part of the package is a method that returns an object.

You can also set up constants to shorten the paths:

JFrame = javax.swing.JFrame
...
_frame = JFrame.new()

Accessing External Classes

So far, so good. But somehow I couldn't find quite enough information on the web to access 3rd party classes. After examining the writeups listed in the Resources and doing a lot of experimenting, I was able to make things work by doing the following:

1. Tell JRuby where to find the JAR files. Options:

  1. Put them in the CLASSPATH (did not work for me. JRuby didn't seem to read the environment)
  2. Put them in $RUBY_HOME/lib (haven't tried this)
  3. For NetBeans on Windows, in JRUBY_EXTRA_CLASSPATH (also didn't work for me)
  4. Give up and specify the jar files with a full path, as I did in Step #2.
  5. Follow the example Rob Di Marco's example and put -I<directory> on the command line for every directory that contains a jar file.

AND

2. Require each jar by name, specifying the full path if the directory it's in hasn't been specified on the command line, as in Step #1.

To my surprise, the CLASSPATH setting in the environment wasn't picked up when the JRuby script was running on Solaris. So this worked:

require "/some/path/MyStuff.jar"

But not this:

require "MyStuff.jar"

Without the -I entries on the command line, the latter fails with "no such file to load".

AND

3. Do an include_class on each class that needs to be named in the code, using a fully qualified package name. Specify the Java:: prefix if the package name doesn't start with one of the "magic" packages.

So if a static method returns a Foo object, the code will look like this:

include_class Java::some.package.MyClass
...
x = MyClass.staticGetMethod()

Note that Foo does not need an include_class statement, since it is not explicitly named in the code.)

Implementing a Java Interface

As per the Nabble page (http://www.nabble.com/What-is-the-current-syntax-for-defining-a-class-in-JRuby-that-implements-a-Java-interface--t4277539.html), include interfaces into the class as though they were modules:

class SomeClass
  include java.lang.Runnable
  include java.lang.Comparable
  def run
  ...
  end
  ...

Or group them together into a single module and include that:

module RunCompare
    include java.lang.Runnable
    include java.lang.Comparable
end

class SomeClass
    include RunCompare
    def run
    ...    
    end
    ...

Resources

Headius:
http://www.headius.com/jrubywiki/index.php/Calling_Java_from_JRuby

Sun:
http://java.sun.com/developer/technicalArticles/scripting/jruby/

Nabble:
http://www.nabble.com/What-is-the-current-syntax-for-defining-a-class-in-JRuby-that-implements-a-Java-interface--t4277539.html

Rob Di Marco's post at Innovation on the Run:
http://www.innovationontherun.com/scraping-dynamic-websites-using-jruby-and-htmlunit/

Talk Back!

Have an opinion? Readers have already posted 2 comments about this weblog entry. Why not add yours?

RSS Feed

If you'd like to be notified whenever Eric Armstrong adds a new entry to his weblog, subscribe to his RSS feed.

About the Blogger

Eric Armstrong has been programming and writing professionally since before there were personal computers. His production experience includes artificial intelligence (AI) programs, system libraries, real-time programs, and business applications in a variety of languages. He works as a writer and software consultant in the San Francisco Bay Area. He wrote The JBuilder2 Bible and authored the Java/XML programming tutorial available at http://java.sun.com. Eric is also involved in efforts to design knowledge-based collaboration systems.

This weblog entry is Copyright © 2007 Eric Armstrong. All rights reserved.

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use