5 Things to Look for in JRuby 1.4

The team has finally wrapped up the long summer of JRuby-related travel, and it's time to set our sights on a new version of JRuby for the community to lovingly embrace.

The release itself is tentatively planned to enter a round of release candidates at the end of September. Please prepare yourselves, try the release candidates when they are available, and most importantly, report bugs!

So what can you expect in the next release, other than the usual array of bug, performance, and compatibility fixes? Here are the five areas where we've been focusing efforts.

1. 1.8.7 is the New Baseline

When Ruby 1.8.7 was first released, we had a false start where we eagerly started to add some of the new additions, only to find that Rails (version 2.0 at the time) was rendered useless by the changes. Now, finally, three minor releases of Rails 2.x later, Rails on Ruby 1.8.7 has finally stabilized, and is now the recommended version of Ruby to use with Rails. Additionally, 1.8.7 is showing up as the default version of Ruby in many operating systems, and we began to see more bug reports about missing 1.8.7 features.

So, in JRuby 1.4, we'll finally report compatibility with Ruby 1.8.7p174:

$ jruby -v
jruby 1.4.0dev (ruby 1.8.7p174) (2009-09-14 b09f382) (Java HotSpot(TM) Client VM 1.5.0_19)  \[i386-java\]

The patch-level (p174) is based on the version of Ruby where we pulled the updated standard library. We always maintain that patch-level is a “best effort” number, and as such, we cannot promise full feature-for-feature (or bug-for-bug!) compatibility. However, as the RubySpec project has matured, it's gotten much easier for us to feel confident about this claim. As always, if you spot an area where we differ from Ruby 1.8.7, please do file a bug on the discrepancy, or even better, make sure that RubySpec covers the issue by submitting a patch to RubySpec.

2. Improving 1.9 Compatibility

Even though Ruby 1.9 is still a moving target, the RubySpec project has recently begun to cover many of the new 1.9 features, which gives JRuby an easier, more robust mechanism for tracking how far along the path to 1.9 we are.

We've also been following our tried-and-true approach of just trying to get stuff working. In particular, IRB, RubyGems, Rake, and even some simple Rails applications are working with JRuby in 1.9 mode.

Other than a couple of major new features remaining (Multilingualization/encoding, IO transcoding, Fibers), JRuby 1.9 mode should just work. To try out JRuby in 1.9 mode, just pass --1.9 as a leading argument on the command-line:

$ jruby --1.9 -v
jruby 1.4.0dev (ruby 1.9.1p0) (2009-09-14 b09f382) (Java HotSpot(TM) Client VM 1.5.0_19)  \[i386-java\]

If you've been writing 1.9 code, we'd love to hear if you can get it working on JRuby: do comment!

3. New YAML Parser

Ok, so a new YAML parser is something you'd probably have to give more than a cursory glance to notice. But our good friend Ola Bini has gone and done it again with Yecht, a new YAML engine. Ola re-implemented YAML support in JRuby to be much closer to the idiosyncracies of Syck, the C YAML parser that MRI uses. With this change, a number of long-standing YAML compatibility issues are fixed. Hopefully this is the last YAML engine that JRuby will ever need!

4. Java Method Selection and Coercion API

For a long time, JRuby Java integration features have been something of a black box, especially the way Java methods are selected and invoked and arguments are coerced. This black box has hindered understanding of how JRuby calls Java, and in the case of overloaded methods, has made some Java methods unreachable. Consider this Java class:

public class Overloaded {
    public static void use(int i) { /* ... */ }
    public static void use(long l) { /* ... */ }
}

And this JRuby code:

Java::Overloaded.use 10

Which Overloaded method is called? It turns out it's use(long). JRuby behavior is to coerce Ruby Fixnums into Java longs. Thus, with current versions of JRuby, it's impossible to call the version that takes an int.

We'd like to make this part of the Java integration layer more transparent and accessible. Here are a few APIs we're considering:

a. Object#java_send(name, signature, *args)

Like Object#send except it would allow you to identify and invoke a specific Java method by its name and signature. A signature for these purposes is an array of classes, where the first element in the array specifies the return type.

b. Object#java_method(name, signature)

Like Object#method, except it would take an array of Java classes representing a method signature as an additional argument and return a Method object that can be #call‘ed.

c. #coerce_to?(type)

Method convention to allow an object to decide whether it can be coerced to a Java type. JRuby will call this method if it's available to get hints to aid in choosing a method in the presence of several overloaded methods.

d. #to_java(type)

Method convention to allow arbitrary Ruby objects to control how they are coerced to Java. If a Ruby object responds to #to_java, JRuby will use it to convert the argument before passing to a Java method invocation.

e. #to_ruby(type)

Like #to_java except to be applied to return types and Java objects coming back to Ruby.

All of these APIs are still yet to be implemented, and we could use some good real-world examples of where JRuby falls down to help ensure that we solve those issues.

5. Generate Real Java Classes from Ruby

Since we started writing Ruby2java as an add-on library, we've been looking for a way to expose the functionality of creating a real Java class from Ruby in the JRuby core. JRuby 1.4 will have some new experimental APIs for doing this. The example below explains it in code:

require 'java'
require 'jruby/core_ext'

class SimpleTest
  def equals
    raise "not equal" unless 1.0 == 1
  end
end

SimpleTest.add_method_signature("equals", [java.lang.Void::TYPE])
SimpleTest.add_method_annotation("equals", org.junit.Test => {})
SimpleTest.become_java!

The effect of this code is the same as writing the following Java code:

package ruby;
import org.junit.Test;
public class SimpleTest {
    @Test
    public void equals() {
        // dispatch back to Ruby code here
    }
}

We are also providing #add_class_annotation and #add_parameter_annotation methods as well, rounding out the ability to shape a real Java class.

These APIs are admittedly verbose and low-level, but they do allow you to easily build higher level constructs using Ruby metaprogramming techniques.

Here's a more realistic example of implementing a JAX-RS/Jersey resource using Ruby (full source available):

class Hello < RubyJersey::Resource
  GET()
  Produces("text/plain")
  Returns(java.lang.String)
  def hello
    "Hello Ruby Jersey!"
  end
end

Feedback

The release is still a couple of weeks away, so none of this is by any means final. Please send us feedback by commenting here or use the JRuby mailing list for longer discussions, and let us know how the 1.4 release candidates work for you!