JRuby saves the day

Posted by andy

So I'm rewriting yet another subsystem which consists of a mismash of several languages and programmer ego's (hardcore C being the largest one, aargh) to what else .. Ruby. Everythings going smoothly. Every line of Ruby code replaces about 10 lines of "put other language here" cruft. Life couldn't be more beautiful. But then I hit the wall, the Java wall. Here I'm confronted with a full enterprisy Service Manager complete with dependencies on Java-only libs. Now what? I could rewrite the whole thing in Ruby. But then there would be 2 implementations of the same thing to maintain, not to mention reading through Java code, bad.

Enter JRuby. Since the main code blob of this project is captured in a Mongrel plugin I thought about just deploying the whole of Mongrel on JRuby. Unfortunately JRuby Mongrel support was not there yet (Mongrel 1.1 supports JRuby). So the next best thing was to build some kind bridge between JRuby and Ruby + Mongrel + Plugin. Distributed Ruby (DRb) is a perfect fit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#
# server.rb (this)
# jars/big-bad-service.jar
#
APP_ROOT = File.join(File.dirname(__FILE__), '.')

require 'java'
require 'drb'

require "#{APP_ROOT}/jars/big-bad-service.jar"

BigBadService = com.blah.BigBadService

class JRubyServer
  def initialize
    @bbs = BigBadService.new
    @bbs.initialize_service
  end

  def bbs_call(param) 
     @bbs.bbs_call(param)
  end
end

if __FILE__ == $0
  DRb.start_service 'druby://127.0.0.1:6666', JRubyServer.new
  DRb.thread.join
end

Execute like ruby server.rb, and then you'll have the server listening on port 6666 of localhost. Nice, we can now call our Java service from other Ruby code with this simple snippet:

1
2
3
4
5
6
require 'drb'
DRb.start_service

java_bbs = DRbObject.new(nil, 'druby://127.0.0.1:6666')

puts  java_bbs.bbs_call("Whack!") #=> "Whacked!"

Kewl! Except for one big caveat. As of JRuby 1.0.x Java objects cannot be marshalled correctly so passing them to your Ruby code will cause all sorts of interesting hangs and crashes when you access them concurrently (see JRUBY-1235). Untill JRuby 1.1 is out you can synchronize all your calls to JRuby and making sure you convert any results to proper Ruby objects before using them elsewhere in your Ruby code.

This hack saved me loads of (Java hacking) time!