Sponsored Link •
|
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
First, of course, it's necessary to bring in the module that provides the bridge to the JVM:
include Java
Some things to note:
include 'java'
require 'java'
include
statement and in the include_class
statement. But they're required in the require
statement, which comes between them. The mnemonic is "Quotes are required, not included."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 require
statements, at least, should allow for wildcards. Something like that would have saved me a lot of trouble:
require "some/path*.jar"
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()
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:
CLASSPATH
(did not work for me. JRuby didn't seem to read the environment)$RUBY_HOME/lib
(haven't tried this)JRUBY_EXTRA_CLASSPATH
(also didn't work for me)-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.)
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 ...
Headius:
http://www.headius.com/jrubywiki/index.php/Calling_Java_from_JRuby
Sun:
http://java.sun.com/developer/technicalArticles/scripting/jruby/
Rob Di Marco's post at Innovation on the Run:
http://www.innovationontherun.com/scraping-dynamic-websites-using-jruby-and-htmlunit/
Have an opinion? Readers have already posted 2 comments about this weblog entry. Why not add yours?
If you'd like to be notified whenever Eric Armstrong adds a new entry to his weblog, subscribe to his RSS feed.
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. |
Sponsored Links
|