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!




