Appendix B. JRuby – Ruby in Practice

Appendix B. JRuby

JRuby is an implementation of the Ruby language that runs on the Java Virtual Machine (JVM). The main benefits of using JRuby are the ability to mix Ruby and Java code, the performance benefits gained from running on the JVM, and the ability to deploy web applications to Java servers like Tomcat and J2EE. This appendix provides an overview that will help you get started exploring the many facets of JRuby.

B.1. Installing and using JRuby

The steps required to install JRuby are the same on all operating systems. First, make sure you have Java installed and that the java command is accessible from the path. Next, download the most recent version of JRuby available from dist.codehaus.org/jruby, expand the archive to a directory of your choice, and set the path to point to the bin directory that contains the various JRuby executables (jruby, jirb, etc.). Verify that JRuby is installed correctly by running jruby --version from the command line.

For example, on Linux you could download and install JRuby 1.1.2 like this:


$ curl -OL http://dist.codehaus.org/jruby/jruby-bin-1.1.2.tar.gz
$ tar -xz < jruby-bin-1.1.2.tar.gz
$ mv jruby-1.1.2 /opt/jruby
$ echo "export PATH=\$PATH:/opt/jruby/bin" >> .profile

To run Ruby programs using JRuby, use the jruby command:


$ jruby myprog.rb

You can also use jirb, the JRuby Interactive Interpreter, the same way you would use irb (see appendix A for instructions on how to install Wirble).

JRuby includes a recent version of RubyGems, and you can start installing and managing gems using the gem command. Make sure to read the next section to avoid conflicts between the Ruby and JRuby gem repositories.

If you need specific JVM settings, you can pass them along to JRuby using command-line options with the prefix -J, or by setting the environment variable JAVA_OPTS. For example,


$ export JAVA_OPTS=-client
$ jruby -J-Xmx512m myapp.rb

Next, we’ll look at how to use JRuby and Ruby side by side on the same machine.

B.2. JRuby and Ruby side by side

Ruby and JRuby maintain separate gem repositories, and any gem you install for one will not be available for the other. While most gems are identical across platforms, gems that use C extensions install different code depending on the target platform. On Windows machines (mswin32), they will use JRs, and on most other platforms, they will compile shared libraries during installation. Gems that use C extensions will not install on JRuby. Fortunately, many popular gems, such as Mongrel and Hpricot, were ported over to JRuby and install using Java libraries instead of C extensions.

RubyGems handles this for you by installing the right gem for the target platform, so gem install mongrel will work the same way for all Ruby platforms and for JRuby. There are a few cases where you will be using different gems, such as mysql for Ruby but jdbc-mysql for JRuby.

Many gems install command-line scripts; for example, Rake provides rake while RSpec uses spec. Installing the gem on both platforms will result in two command-line scripts in two different locations, both available from the path. This may be a source of confusion at first, but it is easy to work around using the -S command-line argument, as the following example illustrates:


$ ruby -S spec specs/*
$ jruby -S gem install rspec
$ jruby -S spec spec/*

This is also the suggested way to install gems when using Ruby and JRuby side by side. If Ruby’s gem command shows up first on the path, you can run it directly to install gems in the Ruby repository and use jruby -S gem to install gems in the JRuby repository, or vice versa.

When working on Rails applications, many of the scripts you will be using (e.g., script/server, script/plugin) are used from the current directory, not the path. In this case, you cannot use the -S option nor do you need to. Simply run the script with the right interpreter, like this:


$ ruby script/server
$ jruby script/server

If you’re writing Ruby code that behaves differently on each platform, you can detect when it runs in JRuby by looking at the value of the RUBY_PLATFORM constant. This constant holds a string that depends on the target platform (e.g., i386-mswin32 or universal-darwin9.0). Since Java code behaves the same way on all operating systems, JRuby always sets this constant to java. You can still determine the underlying operating system by requiring rbconfig and checking the value of Config::CONFIG['host_os'].

Next, let’s see how we can use JRuby to build Ruby applications that use Java libraries and tightly integrate with Java code.

B.3. Mixing Ruby and Java

As you can imagine, JRuby is all about easy Java and Ruby integration. You can access any Java class or interface available in the class path through the Java module. Here’s an example:


>> foo = Java.java.lang.String.new('foo')
=> #<Java::JavaLang::String:0xe704bd @java_object=foo>

>> bar = Java.java.util.HashMap.new
=> #<Java::JavaUtil::HashMap:0xcd022c @java_object={}>

Calling Java methods is just as easy:


>> foo.toString
=> "foo"
>> foo.to_string
=> "foo"
>> foo.to_s
=> "foo"

As you can see from this example, you can call methods using either Java or Ruby naming conventions (either toString or to_string). Since these are also Ruby objects, you can call their Ruby methods as well, in this case to_s.

The conversion works both ways. Here’s an example of passing Ruby objects to Java:


>> array = Java.java.util.ArrayList.new([1, "foo", Object.new])
=> #<Java::JavaUtil::ArrayList:0xc7c7bc @java_object=[1, foo,
#<Object:0xa0a9a>]>

>> array.to_s
=> "[1, foo, #<Object:0xa0a9a>]"

Primitive types like strings and integers automatically convert to the right Java types. Since Ruby objects are also Java objects, you can define Ruby classes that extend Java classes. Java interfaces are exposed as modules, so to implement an interface, you simply include it:


class MyIterator
include Java.java.util.Iterator
def hasNext
false
end
end

If you need to cast arrays, use the to_java method, as this example illustrates:


>> ['foo', 'bar'].to_java
=> #<#<Class:01x96f94>:0x7a00b @java_object=[Ljava.lang.Object;@9c5304>

>> ['foo', 'bar'].to_java(Java.java.lang.String)
=> #<#<Class:01xf3941>:0x8fc43e @java_object=[Ljava.lang.String;@9cce04>

As with Java, you can use import to save yourself typing the full package name:


>> import Java.java.util.Hashtable
=> Java::JavaUtil::Hashtable
>> Hashtable.new
=> #<Java::JavaUtil::Hashtable:0x3584f9 @java_object={}>

To access Java libraries, add them to the class path using the -J-cp command-line argument, by setting the CLASSPATH environment variable, or simply by requiring them:


>> require '/usr/share/ant/lib/ant.jar'
>> Java.org.apache.tools.ant.Project
=> Java::OrgApacheToolsAnt::Project

The Java system properties are always available from ENV_JAVA:


>> ENV_JAVA['java.runtime.version']
=> "1.5.0_13"

You’ve now seen how to use Java code from Ruby. Next we’ll show you how to mix Ruby code into Java applications using the scripting support provided by Java 6.

B.4. Scripting with Ruby

With JRuby you can also run Ruby scripts and use Ruby libraries inside your Java applications. You will need Java 6, which adds scripting support, the JRuby libraries in the class path, and javax.script.EngineManager to create a ScriptEngine that can evaluate Ruby code.

This simple example shows how you can load a Ruby file and call one of its methods:


ScriptEngine jruby = new ScriptEngineManager().getEngineByName("jruby");
jruby.eval(new FileReader("myscript.rb"));
String hello = (String) jruby.eval("hello_world");
System.out.println("Ruby says " + hello);

The last JRuby feature we’re going to cover is the ability to package Ruby applications as WAR files and deploy them directly into your Java web server.

B.5. Deploying web applications

You can use JRuby to deploy Rails applications to Java web servers like Tomcat or JBoss. Start by installing the Warbler gem (jruby -S gem install warbler) and head over to the root of your Rails project to package it as a WAR file:


$ jruby -S warble war

Warbler creates a WAR file that places the core of your Rails application in the WEBINF directory, including Rails itself, any plugins you use, and any gem dependencies. Public files go in the root of the WAR file, and you can use additional Java libraries that are copied over from the lib directory to WEB-INF/lib. The result is a self-contained, ready-to-run application.

If you need to customize the WAR file to your specific needs, start by creating a new configuration using the warble config command, and edit the config/warble.rb file before running warble war again.