Start RJB

26 Mar 2014

让一个ruby和java同时不熟悉的人搞这个,真是很晕啊!!!

Rjb - Ruby Java Bridge

rjb是一个桥梁程序,连接ruby和java的。

下载Rjb

ASR-1.8.x package也包含了rjb的最新版本。

已知的限制:Rjb只支持JVM的主线程。至少你不能从JVM工作线程中调用ruby的垃圾处理器。它会引起一些错误。

Rjb的使用场景

  • Rake + Rjb 是 比Maven 和 Ant 更强大和有用的构建工具
  • 你可以使用Rjb模拟来测试java业务逻辑类
  • 在Ruby on Rails 程序中移植结构对象有用
  • 但是小心构建连接程序,Ruby(和Rjb)不考虑JVM的线程处理

如何使用rjb

jvm是必须的

例如,如果你使用带有Sun j2se的linux, 你需要设置LD_LIBRARY_PATH来明确的指定j2se的共享对象。

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

在Ruby on Rails

需要设置LD_LIBRARY_PATH和JAVA_HOME.

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"

(但是不确定设置LD_LIBARY_PATH是不是有用)

在apache中设置是没用的, 这个提示是由Ruban Phukan提供,谢谢Ruban.

在Ruby on Rails 带有 Apache + FastCGI

使用Apache + FastCGI, 如果你要在FastCGI的配置文件中设置JAVA_HOME和LD_LIBRAY_PATH可以使用-initial-env选项。如:

-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

这样Rjb在environment.rb不指定环境变量的情况下页可以工作,这个提示由Wes Gamble提供,感谢 Wes.

在windows

不需要设置LD_LIBRARY_PATH, 因为他们会被动态的载入到进程中。

在OS X

这次, rjb的test.rb一直测试失败,我也不知道哪里出了问题

请忽略这个问题(我会持续在这方面下工夫)

  • 在OS X中,rjb不找JAVA_HOME的环境变量,它直接找/System/Library/Frameworks/JavaVM.framework/Libraries/libjvm_compat.dylib。 所以你需要使用/System/Library/Frameworks/JavaVM.framework/Libraries symbolic link设置正确的java版本。

rjb是必须的

require 'rjb'

载入jvm

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

ClassPath是客户端提供的运行时类目录。Rjb使用PATH_SEP字符串连接到ENV[‘CLASSPATH’]之前。

jvmargs是字符串数组,是jvm的参数

ex.

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

如果你不需要指定类目录和JVM的参数,这部分可以跳过。

导入java Class到ruby

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

调用了这行代码, str就是java.langString类了。

实例化对象

instance = str.new

意思等同于

String instance = new String();

对于java.lang.String. 你可以调用带参数的构造函数像这样:

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

使用RJB,你需要为不同的重载方法指定类型信息。

instance = str.new_with_sig('Ljava.lang.String;', 'hiki is a wiki engine')

klass#new_with_sig(sig, arg[, more args]) : 调用带类型信息的构造函数

sig意思是signature, 你可以在J2SE的Class#getName API文档中找到这些类别的名字。作为数组的编码过后的类型名称。

type Name encoded name
boolean Z
byte B
chart C
class or interface Lclassname;
double D
float F
int I
long J
short S

自动类型匹配规则

  • 参数数目
  • 如果实参是Object的实例, 形参也是Object的实例 。则匹配成功
  • FIXNUM 匹配所有的BCDFIJS (bcdfijs)
  • STRING 匹配java.lang.String
  • TRUE/FALSE 匹配Z
  • ARRAY 匹配 任意类型的数组
  • Rjb导入的对象匹配java.lang.String, 类,子类。Object#toString用来创建String对象。
  • 任意的都匹配到Object的类型。

更多的例子

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>

调用实例的方法

在Java

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

在Ruby

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

在rjb, 返回的字符串会强制转换成Ruby的字符串,而不是java.lang.String的实例。

调用重载的方法(带类型信息)

Rjb检查参数的类型,来确定调用哪个方法。但是它不完整(假设有这种情况,如果有三个方法 void foo(int), void foo(short), void foo(long), 当参数时30),这种情况你需要使用ojb#_invoke来调用。例如:

instance2 = instance._invoke('replaceAll', 'Ljava.lang.String;Ljava.lang.String;', 'hiki, 'rwiki')

obj#_invoke(name, sig, arg[, more args]) : 调用一个带name的方法,name是类型信息。

name: 调用的方法名称

sig: 类型标签,见上面。

访问字段

静态字段

>ruby -rrjb -e "Rjb::import('java.lang.System').out.println('Just Another Ruby Hacker')"
Just Another Ruby Hacker
>

实例字段

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

绑定Ruby对象到java的接口

你可以把Ruby的对象绑定到java的接口上,只要这个对象有责任返回来自java世界的方法。

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): 绑定ruby对象到java 接口

obj, ruby对象
name, java的接口名称
return 绑定到特定接口的新对象。

在ruby中抛出java异常。

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

这段代码抛出一个带“test exception”消息的NoSuchElementException异常,当调用者调用Iterator#next的时候

throw(classname, message)
抛出一个异常对象。

classname
可以抛出的类的名字

message
原因的描述

检查对象的类

rjb为每个实例增加了叫_classname的方法。 这个方法返回它的类的名称

obj#_classname
返回java类名。

例如

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

可以帮助指正这里英文错误么

comments powered by Disqus