Using Java Libraries with JRuby

Credit: Thomas Enebo

Problem

Java offers many class libraries that would be useful to a Ruby programmer; youd like to use one of those libraries from within Ruby. A Java JDBC database may allow you to connect to a database for which Ruby has no connector. Or perhaps you need to use an obscure Java library that has no Ruby counterpart.

Solution

JRuby provides an alternate implementation of the Ruby programming language that runs atop the Java Virtual Machine. When you interpret a Ruby program with JRuby instead of using the default Ruby interpreter, you can load and use Java classes from within the Ruby code.

The first step to using JRuby is to install it:

  1. Download the latest copy of JRuby (see below for the address).

  2. Unzip the JRuby package into the directory where youd like to install it.

  3. Add to your PATH environment variable the bin/ subdirectory of your JRuby installation.

  4. Unless youve already installed it, download the Java Runtime Environment from Suns Java web site and install it. Youll need the JRE version 1.4.x or higher.

Now you can invoke the JRuby interpreter with the jruby command and use it to run Ruby code. Heres a simple example that imports and uses Javas built-in Random class:

#!/usr/bin/env jruby # random.jrb require java include_class java.util.Random r = Random.new(123) puts "Some random number #{r.nextInt % 10}" r.seed = 456 puts "Another random number #{r.nextInt % 10}"

Heres a run of this program:

$ jruby random.jrb Some random number 9 Another random number 0

Discussion

JRuby generally behaves like Ruby. The jruby interpreter supports a common subset of Rubys command-line options, and includes a subset of common core libraries. As JRuby is developed, it will eventually end up with all of Rubys options and libraries.

The first step in a JRuby program is to load the Java support classes. If you don do this, you can still use the JRuby interpreter, but youll be limited to a subset of the Ruby core libraries: you might as well just use the C implementation.

The statement require java updates Rubys Object class with an include_class method, which you can use to import Java classes. When we call include_class to include a class like java.util.Random, Ruby inserts a class called Random into the current namespace. This class is really a Ruby class that proxies method calls to the underlying Java class.

The Random class proxies a constructor call to the java.util.Random constructor. Random#nextInt becomes a call to java.util.Random#nextInt. Random#seed= becomes a call to java.util.Random#setSeed; JRuby creates seed= as a Ruby convenience method, to make the Java classes feel more like Ruby.

If you e including a Java class whose name conflicts with an existing constant in your namespace, then include_class will throw a ConstantAlreadyExistsError. This is problematic if you want to use Java classes like java.lang.String, whose names conflict with the names of built-in Ruby classes. Fortunately, you can customize the name of the proxy class created by include_class. This piece of code loads java.lang.String as the class JString instead of String:

include_class(java.lang.String) { |package,name| "J" + name }

Its worth noting that JRuby implicitly translates primitive types between Ruby and Java. In the Random constructor, the Fixnum argument 123 gets implicitly converted to a Java primitive long, since thats what the java.util.Random constructor takes.

Table 22-1.

Ruby type

Java type

String

char, String

Fixnum

long, int, java.lang.Long, java.lang.Integer

Float

float, double, Java.lang.Float, java.lang.Double

Boolean

java.lang.Boolean, boolean

This automatic conversion creates some amount of ambiguity, because Java supports method overloading and Ruby doesn . Suppose you have a Java class which defines two methods with the same name:

class Foo { public void bar(int arg) {…} public void bar(long arg) {…} }

If you import that class into JRuby and call Foo#bar, to which method should the proxy class dispatch your call?

Foo.new.bar(5)

In JRuby, the exact heuristic is undefined. In practice, this is not a huge problem, since methods that define same-named methods are semantically equivalent. If you do encounter an ambiguous case, you can work around ambiguity using Javas reflection APIs.

Convenience methods

JRuby tries to make Java classes and objects seem as unobtrusive to Ruby as it can. In our earlier example, we saw how a setter:

setSeed(value);

Can be called from Ruby as:

seed = value

JRuby supports the following additional Ruby method name shortcuts:

Table 22-2.

Java

Ruby

obj.getFoo( )

obj.foo

obj.setFoo(value)

obj.foo = value

obj.isFoo(value)

obj.foo? value

The original name still exists, so if you like you can use getFoo and setFoo from Ruby. Of course, if Java already has a method by the same shorthand name (e.g., obj.foo), Ruby won create the shorthand name.

JRuby also provides some Ruby methods that make Java classes seem more like Ruby classes. Here is a list as of Ruby 0.8.3:

JRuby is still a project under development, so expect to see more added as developers discover more candidates.

See Also

Категории