Rjb - Ruby Java Bridge

rjb is a bridge software. It connects Ruby and Java.

Download Rjb

ASR-1.8.x Package also contains the latest version of rjb binary

Known Restriction: Rjb only supports JVM's main thread. At least you can never invoke Ruby's GC from JVM's worker thread, it causes something bad or die.

Rjb's Use Case

  • Rake + Rjb is the more powerful and useful build tool than both Maven and Ant, I believe.
  • You can test your Java business logic class itself with Rjb's mock.
  • It helps to migrate Struts's Model Object into your RoR application.
  • But beware to build Swing application, Ruby (and Rjb) doesn't consider JVM's native threads handling.

How to use rjb

jvm is required

For example, if you use Linux with Sun j2se, you need to set LD_LIBRARY_PATH points j2se shared objects explicitly.

sh, bash:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$JAVA_HOME/jre/lib/i386:$JAVA_HOME/jre/lib/i386/client

csh, tcsh:

setenv LD_LIBRARY_PATH $LD_LIBRARY_PATH:$JAVA_HOME/jre/lib/i386:$JAVA_HOME/jre/lib/i386/client

in RoR

Both LD_LIBRARY_PATH and JAVA_HOME setting required in environment.rb file of RoR.

ENV['JAVA_HOME'] = "/usr/java/jdk1.5.0_09"
ENV['LD_LIBRARY_PATH'] = "#{ENV['LD_LIBRARY_PATH']}:#{ENV['JAVA_HOME]}/jre/lib/i386:#{ENV['JAVA_HOME']}/jre/lib/i386/client"
(but I'm not sure that setting LD_LIBRARY_PATH at environment.rb is really effective)

Setting them in the apache conf didn't seem to work.

This tip was contributed by Ruban Phukan, Thanks Ruban.

in RoR with Apache + FastCGI

Using Apache + FastCGI, if you set JAVA_HOME and LD_LIBRARY_PATH in the FastCGI module configuration file using the -initial-env option, like so:

-initial-env RAILS_ENV=production \
-initial-env JAVA_HOME=/usr/java/jdk1.5.0_09 \
-initial-env LD_LIBRARY_PATH=${JAVA_HOME}/jre/lib/i386
then RJB will work without specifying these environment variables in environment.rb.

This tip was contributed by Wes Gamble, Thanks Wes.

in Windows

you never need to set LD_LIBRARY_PATH, because they can be loaded and bound into the process dynamically. (it may cause any vulnelability ?)

in OS X

At this time, rjb test.rb fails test_underscored_constant, but I can't figure out what's wrong.

Please ignore this issue (I'm continuously working).

  • on OS X, rjb does not look up JAVA_HOME environment variable, but /System/Library/Frameworks/JavaVM.framework/Libraries/libjvm_compat.dylib directly. So you need to set appropriate Java version using /System/Library/Frameworks/JavaVM.framework/Libraries symbolic link.
    • thanks Adam.

rjb is required

require 'rjb'

load jvm

Rjb::load(classpath = '.', jvmargs=[])

classpath is a client supplied runtime classpath. Rjb appends this string before ENV['CLASSPATH'] using PATH_SEP character.

jvmargs is a string array. Its elements are the jvm arguments.

ex)

Rjb::load(nil, ['-verbose:gc', '-Dfoo.bar=FooBar'])

If you don't need to specify both classpathes and JVM argments, then you can skip to call Rjb::load.

import java class into ruby

str = Rjb::import('java.lang.String')  # import String class into the varibale 'str'

After this call, the variabe 'str' contains java.lang.String class.

instanciate an object

instance = str.new

This means that

String instance = new String();

For java.lang.String, you'd like to call the constructor with some arguments like

String instance = new String("hiki is a wiki engine");
// in Java, this call was nonesence because String is imutable...

With rjb, you need to specify type informations for theses overloaded methods.

instance = str.new_with_sig('Ljava.lang.String;', 'hiki is a wiki engine')
klass#new_with_sig(sig, arg[, more args])
invoke constructor with type informations
sig
type signature. You can find the type names at J2SE's Class#getName API documentation as arrays's encoded element type names.
type nameencoded name
booleanZ
byteB
charC
class or interfaceLclassname;
doubleD
floatF
intI
longJ
shortS

auto type matching rules

  1. match the number of the arguments.
  2. if the argument instanceof Object and the parameter type instanceof Object, it matches.
  3. FIXNUM matches any one of BCDFIJS.
  4. STRING matches java.lang.String.
  5. TRUE/FALSE match Z.
  6. ARRAY matches any types of the array.
  7. Rjb imported object matches java.lang.String, the class or the subclass. If String, Object#toString will be called to create String object.
  8. Any matches the Object type.

more example

irb(main):001:0> require 'rjb'
=> true
irb(main):002:0> Str = Rjb::import('java.lang.String')
=> #<Rjb::Java_lang_String:0x2c64ba0>
irb(main):003:0> s = Str.new_with_sig('[BLjava.lang.String;', [48, 49, 50], 'Windows-31j')
=> #<#<Class:0x2c6a2b8>:0x2c5c3f8>
irb(main):004:0> p s.toString
"012"
=> nil
irb(main):005:0>

call instance method (none overloaded)

in Java

String instance2 = instance.replaceAll("hiki", "rwiki");

in rjb

s = instance.replaceAll('hiki', 'rwiki')

in rjb, returned String coerces to ruby's String, not java.lang.String instance.

call overloaded method (with type informations)

Rjb inspect type of arguments, and then decides which method to be called. But it's not complete (suppose the case; if there are three methods like void foo(int), void foo(short), void foo(long), and the argument is 30), in this case you need to call with obj#_invoke as

instance2 = instance._invoke('replaceAll', 'Ljava.lang.String;Ljava.lang.String;', 'hiki, 'rwiki')
obj#_invoke(name, sig, arg[, more args])
invoke a method with name 'name' with type informations
name
the name of the method to be called
sig
type signature. You can find the type names at J2SE's Class#getName API documentation as arrays's encoded element type names.

accessing fields

  • a static field :
>ruby -rrjb -e "Rjb::import('java.lang.System').out.println('Just Another Ruby Hacker')"
Just Another Ruby Hacker
>
  • instance field :
require 'rjb'
pnt = Rjb::import('java.awt.Point')
p = pnt.new(0, 0)
p.y = 80
puts "x=#{p.x}, y=#{p.y}"
=>
x=0, y=80

bind Ruby object to Java interface

You can bind Ruby object to Java interface as long as the object has responsable to respond the method call from Java world.

class Comparable
  def initialize(val)
    @value = val
  end
  def compareTo(oponent)
    return @value - oponent.to_i
  end
end
cp = Comparable.new(3)
cp = Rjb::bind(cp, 'java.lang.Comparable')
bind(obj, name)
bind ruby object and Java interface
obj
ruby object
name
Java's interface name
return
new object that's bound to the specified interface

throws java exception in ruby-bounded-object

You can throw java exception from the bounded object.

class Iterator
  def hasNext()
    true
  end
  def next()
    Rjb::throw('java.util.NoSuchElementException', 'test exception')
  end
end

This code throws NoSuchElementException? with a message 'test exception' while the caller calls Iterator#next.

throw(classname, message)
throw an exception object.
classname
string that represents throwable class.
message
string that describes the cause.

inspect object's class

Rjb adds a method named _classname for each instance. This method returns the name of its class.

obj#_classname
return the java class name

ex)

require 'rjb'
out = Rjb::import('java.lang.System').out
p out._classname
out.println('jarh')

result)

"java.io.PrintStream"
jarh

Couldn't you help to correct English in this page ?

Rjb Project Page