The Artima Developer Community
Sponsored Link

Weblogs Forum
Using Java classes in JRuby

2 replies on 1 page. Most recent reply: Sep 17, 2007 5:57 PM by Eric Armstrong

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 2 replies on 1 page
Eric Armstrong

Posts: 207
Nickname: cooltools
Registered: Apr, 2003

Using Java classes in JRuby (View in Weblogs)
Posted: Sep 17, 2007 5:57 PM
Reply to this message Reply
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:

  • Capitalization is significant.
  • This version generates an error, saying a module name is required but a string has been supplied:
    include 'java'
  • This pre 1.0 form is still out there on some web pages. It doesn't generate any obvious errors, but it doesn't work:
    require 'java'
  • Quotes are an error in this 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 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/


Elizabeth Wiethoff

Posts: 89
Nickname: ewiethoff
Registered: Mar, 2005

Re: Using Java classes in JRuby Posted: Sep 19, 2007 1:41 PM
Reply to this message Reply
Nice to see someone else figuring out how to use Java classes in JRuby.

Last month I wrote some experimental JRuby programs and posted them to http://c2.com/cgi/wiki?JayRuby. They provided enough proof-of-concept to convince the hobby XP group I'm in (http://xpnyc.org/) to switch our DupDetector project (http://xpnyc.org/index.php?title=DupDetector) from conventional Ruby to JRuby so that we can implement our "in-house customer's" GUI in Java Swing.

Eric Armstrong

Posts: 207
Nickname: cooltools
Registered: Apr, 2003

Re: Using Java classes in JRuby Posted: Sep 22, 2007 9:38 AM
Reply to this message Reply
Very cool, Elizabeth. Ruby + Java classes have a great future. In fact, someone with language skills could probably write a language that looked very much like JavaFX Script--and have all the benefits of Ruby, in addition to a DSL for Swing.

Flat View: This topic has 2 replies on 1 page
Topic: Using Java classes in JRuby Previous Topic   Next Topic Topic: Reply to Guido's Reply

Sponsored Links



Google
  Web Artima.com   

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