Blog

Using Java from Ruby with JRuby IRB

By | August 9th, 2011 at 2:08PM

Last month I visited a joint meeting for the Richmond Java User Group and Central Virginia Ruby Enthusiasts’ Group. The audience was evenly split between Java and Ruby developers, which made for an ideal setting for my presentation about Java-Ruby interoperability that JRuby facilitates. On the tails of another great JRubyConf last week, I was inspired to share a summary of my presentation and my slides with interested folks who weren’t able to attend the talk.

Using a Java library from Ruby

In this article, I’ll focus on using a Java library from Ruby. You see, if you were to tinker with a single Java method, you would need to write a complete program with “public static void main(String[] args)”. As we will demonstrate here, with JRuby IRB, it becomes almost trivial to play with Java methods.

We will use the unique Java library Akka here. According to the Akka website, “using the Actor Model together with Software Transactional Memory [Akka raises] the abstraction level and provides a better platform to build correct concurrent and scalable applications.”

The Akka Getting Started Tutorial

To begin, let us look at the Akka Getting Started Tutorial written in Java, and translate it into Ruby.

The example computes π using the Madhava-Leibniz series.

Note that in the series, the neighboring terms can be gathered into a smaller work unit without disturbing the identity, or other work units. We created a worker and a few additional workers that communicate by passing a message–Plain Old Java/Ruby Object with a little extra information–along with the message router acting as a broker.

For comparison, here is the Ruby version, and the Java version. The Ruby version is shorter, and requires less ceremony in setting up various classes involved in the program.

Exploring the code

Now let us walk through the key points in the Ruby version.

Preliminary stuff

require "java"

Line 1: Enable Java integration support in JRuby.

$: << File.join(File.dirname(__FILE__), 'lib')

Line 3: Add the lib directory (that resides in the same directory as the current file) to JRuby’s library search paths.

java_import 'akka.actor.Actors'
java_import 'akka.actor.ActorRef'
java_import 'akka.actor.UntypedActor'
java_import 'akka.actor.UntypedActorFactory'
java_import 'akka.routing.CyclicIterator'
java_import 'akka.routing.InfiniteIterator'
java_import 'akka.routing.Routing'
java_import 'akka.routing.UntypedLoadBalancer'
java_import java.util.concurrent.CountDownLatch

Lines 8 through 16: Import the Java library’s classes into the current namespace so we don’t have to prefix them with Java::…. This is similar to Java’s import statements.

def actorOf(&code)
  Actors.actorOf(Class.new do
                   include UntypedActorFactory
                   define_method(:create) do |*args|
                     code[*args]
                   end
                 end.new)
end

Lines 18 through 25: This is a convenient method to create an Akka actor instance through the use of Ruby’s Class.new, which is analogous to anonymous class declaration in Java. This greatly reduces the clutter when used on lines 81 and 125.

class Calculate; end
class Work < Struct.new(:start, :nrOfElements); end
class Result < Struct.new(:value); end

Lines 27 through 29: Define Plain Old Ruby Objects acting as messages between Akka actors. Note there is a lot less noise for these classes than there is for the Java counterparts.

Worker class

class Worker < UntypedActor
  # needed by actorOf
  def self.create(*args)
    new *args
  end
 
  # define the work
  def calculatePiFor(start, nrOfElements)
    ((start * nrOfElements)...((start + 1) * nrOfElements)).inject(0) do |acc, i|
      acc + 4.0 * (1 - (i.modulo 2) * 2) / (2 * i + 1)
    end
  end
 
  # message handler
  def onReceive(message)
    if message.kind_of? Work
      work = message
 
      # perform the work
      result = calculatePiFor(work.start, work.nrOfElements)
 
      # reply with the result
      context.replyUnsafe(Result.new(result))
 
    else
      raise IllegalArgumentException.new "Unknown message [#{message + b}]"
    end
  end
end

Lines 31 through 59 define the Worker class. The part that actually performs the calculation calculatePiFor is much more compact with the use of Enumerable#inject. When this actor is sent a message, it executes the methodonReceive.

Following the Java example, we are examining the class of the message passed; this is admittedly not characteristic of Ruby.

PiRouter class

class PiRouter < UntypedLoadBalancer
  attr_reader :seq
 
  def initialize(workers)
    super()
    @seq = CyclicIterator.new(workers)
  end
end

Lines 61 through 68 define the PiRouter class. Besides having an instance variable :seq, there isn’t much code, much of it is done in the Akka library itself.

Master class

class Master < UntypedActor
  def initialize(nrOfWorkers, nrOfMessages, nrOfElements, latch)
    super()
    @nrOfMessages, @nrOfElements, @latch = nrOfMessages, nrOfElements, latch
    @nrOfResults, @pi = 0, 0.0
 
    # create the workers
    workers = java.util.ArrayList.new
    nrOfWorkers.times { workers << Actors.actorOf(Worker).start }
 
    # wrap them with a load-balancing router
    @router = actorOf { PiRouter.new(workers) }.start
  end
 
  # message handler
  def onReceive(message)
    if message.kind_of? Calculate
      # schedule work
      @nrOfMessages.times do |start|
        @router.sendOneWay(Work.new(start, @nrOfElements), context)
      end
 
      # send a PoisonPill to all workers telling them to shut down themselves
      @router.sendOneWay(Routing::Broadcast.new(Actors.poisonPill))
 
      # send a PoisonPill to the router, telling him to shut himself down
      @router.sendOneWay Actors.poisonPill
    elsif message.kind_of? Result # handle result from the worker
      @pi += message.value
      @nrOfResults += 1
      context.stop if @nrOfResults == @nrOfMessages
    else
      raise IllegalArgumentException.new "Unknown message [#{message}]"
    end
  end
 
  def preStart
    @start = java.lang.System.currentTimeMillis
  end
 
  def postStop
    # tell the world that the calculation is complete
    puts format("\n\tPi estimate: \t\t%s\n\tCalculation time: \t%s millis",
        @pi, (java.lang.System.currentTimeMillis - @start))
    @latch.countDown
  end
end

Lines 70 through 116 define the Master class. Master responds to two kinds of messages. Calculate sets everything in motion. Result messages are sent from the PiRouter to the Master, which adds up the values of the Result messages until the set number of them were sent its way.

Pi class

class Pi
  def self.calculate(nrOfWorkers, nrOfElements, nrOfMessages)
    # this latch is only plumbing to know when the calculation is completed
    latch = CountDownLatch.new(1)
 
    # create the master
    master = Actors.actorOf do
      Master.new(nrOfWorkers, nrOfMessages, nrOfElements, latch)
    end.start
    master.sendOneWay(Calculate.new) # start the calculation
    latch.await # wait for master to shut down
  end
end
 
Pi.calculate(4, 1000, 1000)

Lines 119 through 133 include the top-level class with one class method that sets up the concurrency latch and sends the Calculate message to the Master.

Conclusion

This example shows the basics of Java-Ruby interaction in a complete program. These fundamentals are applicable to your first explorations. If you come up with your own harmony of Java and Ruby, be sure to share it with us in the comments section.

Thank you, Richmond! (And beyond?)

I enjoyed sharing JRuby with the user group members I met in Virginia. Thanks again to the Richmond Java User Group and Central Virginia Ruby Enthusiasts’ Group for hosting me. We are always excited to share JRuby with a larger audience and to hear your thoughts. If you are interested in arranging a JRuby presentation, drop us a line, and we’ll chat!

  • http://profiles.google.com/jamesthepiper James Moore

    What tools do you use for JRuby?  In particular, have any suggestions for Eclipse + JRuby + Java?

  • Anonymous

    James,

    I use NetBeans for developing JRuby (writing Java code). When I write Ruby code, I tend to use Vim. I am not too familiar with Eclipse, unfortunately.

  • Anonymous

    While this is a nice example of cooperation with java (and scala, since most of akka is written in scala, but that doesnt matter), do you really think it is worth to do so? From my personal experience, ruby is great when using with ruby libraries. Using ruby syntax to write programs that could mostly be written the exactly same way in java doesn’t make much sense. Because of ruby’s *very* dynamic nature and java’s being the oposite there are many problem with java libraries under control of ruby code (as far as I can tell from my own experience).  

  • Anonymous

    Yes, I do think it is worth it. I don’t have examples here, but with JRuby, you can write tests for Java libraries in Ruby (e.g., RSpec, Cucumber).

    As you point out, JRuby is a great Ruby implementation in its own right. It is perfectly fine to use it as such, but do keep in mind that Java-Ruby interoperability is another great feature of JRuby.

    You say that you’ve run into problems working with Java libraries. Do you have concrete examples? If you are having problems, we would like to help. Be sure to file a ticket at http://bugs.jruby.org.

  • Anonymous

    Yes, tests are quite good example :)

    I don’t have any examples now, I’ve beed playing with JRuby about a year ago (many could have changed since that).

    Personally, I just find using scala+java much easier and less error prone than ruby+java. No language flames please, I just wanted to know if it is really used “on production” or just long term, well know yet still PoC.

  • http://freemusicformormons.com/lds-ward-choir-music roger pack

    Unfortunately I had a similar reaction…it was like “if you’re looking for tons of raw distributed processing speed using akka…isn’t it an odd combination to couple it with ruby (jruby)?” but I could be proven wrong in the future, or perhaps it’s just that the culture of rubyists hasn’t historically been toward speed so most ruby users don’t care much about it…

  • Tom Enebo

    I think the missing part to this entry may be that it is showing the integration with Akka in the most direct way.  A little extra glue in Ruby can make this example look like a natural Ruby library.  Adding that extra glue though, would make the entry that much longer. 

    Perhaps a good follow-up entry would be to show an example of how Akka can get wrapped with a little extra Ruby to make it look like a nice Ruby API?

  • http://twitter.com/iconara Theo

    Check out Mikka, it’s a wrapper for Akka that takes care of the Java-isms for you: github.com/iconara/mikka

  • http://twitter.com/iconara Theo

    Did you know that you can shorten lines 18-25 to just this: 

    def actor_of(&block)
      Actors.actor_of(&block)
    end

    JRuby takes care of wrapping the block in a proxy that implements the right interface. The JRuby guys have really made it ridiculously easy to interoperate with Java.

  • http://blog.8thcolor.com/2011/08/why-we-and-others-still-need-jvms/ Why we (and others) still need JVMs » 8th color

    [...] Engineyard working on several Ruby implementations [...]

  • Anonymous

     
    Ich mag lesen Ihre Post Sir. Ich fühle, dass ich ein Sachverst?ndigen auf diesem Gebiet zu einem bestimmten Zeitpunkt zu werden. Vielen Dank für die mir die dringend ben?tigte Inspiration.

  • Anonymous

     
    Ich mag lesen Ihre Post Sir. Ich fühle, dass ich ein Sachverst?ndigen auf diesem Gebiet zu einem bestimmten Zeitpunkt zu werden. Vielen Dank für die mir die dringend ben?tigte Inspiration.

    Christian Louboutin Schuhe