RubyJavaBridge
rjb is my new bridge.
ASR-1.8.7 also contains rjb-1.2.6 binary
How to install rjb
Execute gem install command, and select number. If select 1(not mswin32), add PATH environment `javah' and `javac' containts directory before execute gem command.
#example #bash export PATH=$PATH:$JAVA_HOME/bin #tcsh setenv PATH=$PATH:$JAVA_HOME/bin
If you install to Cygwin, set JAVA_HOME environment make consistent with Cygwin before execute gem command.
#example export JAVA_HOME=$(cygpath -ua $JAVA_HOME)
Execute gem command.
# gem install rjb
Beware! Install incomple if no set environment variables before install. back then execute 'gem uninstall rjb' and 'gem install rjb'.
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 AMD64 Linux
Java for AMD64 Linux has one issue to use Rjb. The shared object that came with JRE causes 'unknown exception'.
The workaround is to put LD_PRELOAD environment variable before invoking the script.
For example to run Rails sciprt/server as:
% LD_PRELOAD=/opt/jdk1.5.0_09/jre/lib/amd64/libzip.so script/server
The pathname '/opt/jdk1.5.0_09/jre/lib/amd64' is varied.
(26 Dec. 2006 added by arton: id:odz has helped to explore this issue, thanks)
in RoR
Both LD_LIBRARY_PATH and JAVA_HOME setting required in environment.rb file of RoR.
Setting them in the apache conf didn't seem to work.
This tip was contributed by Ruban Phukan, Thanks Ruban.
(03 Nov. 2006 - Wes Gamble added:
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.)
(Thanks Wes, I updated RubyForge? top page with your text. http://rjb.rubyforge.org/ - arton)
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
- 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. (rjb-0.1.8)
jvmargs is a string array. Its elements are the jvm arguments. (rjb-0.2.0)
ex)
Rjb::load(nil, ['-verbose:gc', '-Dfoo.bar=FooBar'])
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 name | encoded name |
boolean | Z |
byte | B |
char | C |
class or interface | Lclassname; |
double | D |
float | F |
int | I |
long | J |
short | S |
auto type matching rules
Since rjb-0.1.4, it supposes the method by the arguments.
- match the number of the arguments.
- if the argument instanceof Object and the parameter type instanceof Object, it matches.
- FIXNUM matches any one of BCDFIJS.
- STRING matches java.lang.String.
- TRUE/FALSE match Z.
- ARRAY matches any types of the array.
- Rjb imported object matches java.lang.String, the class or the subclass. If String, Object#toString will be called to create String object.
- 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.
rubyize method name (since rjb-1.0.6)
Rjb treats Java's method name as ruby's to change capitalize letter to an underscode and a small letter.
ex)
s = instance.replace_all('hiki', 'rwiki')
as
s = instance.replaceAll('hiki', 'rwiki')
call overloaded method (with type informations)
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.
return value conversion (since rjb-1.0.8)
Natively Rjb treats Java's returned values as they are.
If the value is primitive, then convert it to Ruby's native. For example Java's int to Ruby's Fixnum, Java's boolean as Ruby's TrueClass? or FalseClass etc.
In the same matter, Rjb don't convert if the return value was an object. So, if Java's method returned java.lang.Integer object, then Rjb passes the value as Java_Lang_Integer object.
You can change this behavior with setting Rjb::primitive_conversion psuedo variable to true.
ex)
jInteger = Rjb::import 'java.lang.Integer' obj = jInteger.valueOf '19' # obj is an instance of Java_Lang_Integer class puts obj.intValue # => 19 Rjb::primitive_conversion = true obj = jInteger.valueOf '20' # obj is an instance of Ruby's Fixnum puts obj # => 20
The default value of Rjb::primitive_conversion is false.
accessing fields
- static field is ok (rjb-0.1.2)
>ruby -rrjb -e "Rjb::import('java.lang.System').out.println('Just Another Ruby Hacker')" Just Another Ruby Hacker >
- instance field is ok too (rjb-0.1.2)
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 (rjb-0.1.9)
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
The encoding that is guessed by $KCODE
Ruby supports a character code of EUC-JP,Shift_JIS,UTF-8,NONE and changes it in $KCODE. $KCODE in the case of NONE, guess encoding. Rules changed in rjb-0.1.2(trunk(rev.31)). Rules as is follow.
$KCODE | guess rule | original encoding |
E | euc-jp | |
S | cp932 | |
U | not conversion | |
N | Windows && GetACP() == 932 | cp932 |
Windows && GetACP() != 932 | not conversion | |
Locale && sjis | shift_jis | |
Locale && euc-jp | euc-jp | |
Locale && utf-8 | not conversion | |
Locale && other | not conversion | |
other | not conversion |
If match case of "not conversion", Ruby string must be converted to UTF-8 before binding. In this case, Rjb calls "NewStringUTF" of JNI API so it must point the UTF-8 chars.
String encoding conversion rule for ruby 1.9.x
m17n was included in Ruby1.9.0. Handling of the string was changed.
Ruby to Java
When String#encoding return Shift_JIS/EUC-JP/ISO-2022-jp, convert to utf-8. Otherwise string is not conversion.
Java to Ruby
Don't convert it automatic. Set Encoding::UTF_8 to String#encoding.
Keyword(s):
References: