利用RJB在Ruby on Rails中使用Java代码的教程
开始之前
关于本教程
RubyonRails(Rails)是用Ruby编写的一个full-stackWeb应用程序框架,而Ruby是一种功能丰富的、免费的、可扩展的、可移植的、面向对象的脚本编制语言。Rails在Web应用程序开发人员之间非常流行。通过它,可以快速有效地开发Web应用程序,并将其部署到任何Web容器中,例如IBM?WebSphere?或ApacheTomcat。
在Rails和类似的Web应用程序开发框架出现之前,用于Web应用程序开发的标准工具是Java语言,因为Java语言是独立于平台的,并且有完整的API集。很多JavaWeb应用程序仍然在运行,这导致很多非常有用的、编写良好的Java代码(在本教程中统称遗留代码)具有良好的可用性。遗留Java代码通常被打包在一组JAR文件。
如果将Web应用程序开发平台改为Rails,那么可以重用遗留Java代码。RubyJavaBridge(RJB)是一个工具包,通过它可以将JAR文件装载到Rails应用程序中,还可以在Rail应用程序中访问其中的方法和变量。本教程解释如何在Rails应用程序中配置和使用RJB。
目标
在本教程中,您将学习如何:
- 下载、编译和安装RJB
- 设置RJB以访问共享Java库
- 将遗留Java代码装载到Rails应用程序中并进行访问
本教程并不深入研究Rails的功能。与其他Web框架相比,Rails有很多优点,其中一个优点就是用于该平台的文档的数量和质量都很高(参见参考资料)。
先决条件
本教程假设读者基本熟悉Java语言、Ruby和RubyonRails。
系统需求
本教程假设您使用Linux?系统(但是,在Windows?上的步骤基本上是相同的)。本教程假设您有一个可以工作的RubyonRails。如果还没有,请在参考资料小节中找到相关文档的链接,以帮助您在自己的系统上安装和配置Rails。
RJB要求系统上安装有JavaSDK。如果需要一个JavaSDK,可以针对您的平台下载最新的JavaSESDK,并马上安装它。
RJB安装和设置
本节带您亲历RJB的下载、安装、编译和设置。
下载RJB
可以下载标准RubyGem包或自己编译的源代码归档文件形式的RJB。为了进行演示,我推荐下载源代码归档文件,所以我将使用这种方法。闲话少说,现在就下载RJB1.1.3source.zip文件(在撰写本教程之际,已经有了最新的RJB版本)。
确保设置或更新了以下环境变量,它们是安装RJB所必需的:
- JAVA_HOME必须指向JavaSDK安装目录。
- PATH必须包括$JAVA_HOME/bin。
例如,在bash(仅用于Linux系统)中,假设已经将JavaSDK安装到/usr/local/jdk60,则执行以下命令:
[root@san]#exportJAVA_HOME=/usr/local/jdk60 [root@san]#exportPATH=$PATH:$JAVA_HOME/bin
编译和安装RJB
下一步是通过执行以下命令编译和安装RJB:
[root@san]#unziprjb-1.1.3.zip [root@san]#cdrjb-1.1.3 [root@san]#rubysetup.rbconfig [root@san]#rubysetup.rbsetup [root@san]#rubysetup.rbinstall
确认安装成功
为了确认RJB安装成功,首先调用Ruby的交互式控制台irb:
[root@san]#irb
然后输入require'rjb':
irb(main):001:0>require'rjb' =>true irb(main):002:0>exit
如果require'rjb'命令返回true,则意味着Ruby安装识别出新安装的rjb库。现在可以在应用程序中开始使用RJB。
通过RJB使用遗留代码
在本节中,您将在Rails应用程序中装载和访问遗留Java代码。
示例项目
JavaTarpackagefromICEEngineering是用Java语言编写的一个很好的工具包,用于处理归档文件。它提供了tar归档实用程序的本地Java实现,当与java.util.zip包相结合时,它可以处理.tar.gz文件。它还利用Java语言的平台独立性,可以不作修改地在所有UNIX?变体和Windows上运行。作为练习,您将使用它来解压一个样例tar文件的内容。通过类似的方法,可以在RubyonRails应用程序中使用任何遗留Java代码。
练习的目标是:
- 将tar.jar文件装载到一个Rails应用程序中。
- 将JAR文件所需的类装载到应用程序中。
- 解压使用这些类的样例test.tar文件的内容。
入门
获取样例文件
首先,需要为系统获取样例tar文件(test.tar)和JavaTar包:
- 将test.tar下载并保存到一个方便的位置。
- 下载和保存javatar-2.5.tar.gz。
- 将javatar-2.5.tar.gz的内容解压到一个方便的位置。这个练习中,这个包中惟一需要用到的文件是tar.jar,它在jars目录中。
访问共享库
RJB使用JavaNativeInterface(JNI)实现它的功能。因此,它需要访问JDK安装中附带的一些共享对象文件(共享库)。您必须使用以下命令将这些文件的位置添加到LD_LIBRARY_PATH环境变量中:
[root@san]#exportJAVA_HOME=/usr/local/jdk60 [root@san]#exportLD_LIBRARY_PATH=$LD_LIBRARY_PATH:$JAVA_HOME/jre/lib/i386 [root@san]#exportLD_LIBRARY_PATH=$LD_LIBRARY_PATH:$JAVA_HOME/jre/lib/i386/client
如果打算在独立的Ruby脚本中使用RJB,那么只需在正在处理的shell中设置这些环境变量。对于您的RubyonRails应用程序,还必须在Rails应用程序的environment.rb文件中设置这些变量。
将RJB装载到Rails应用程序
要将RJB装载到Rails应用程序并将它设置为可以调用Java类,需要执行两个步骤:
- 告诉Ruby将rjb库包含到代码中。
- 装载JVM,设置类路径和其他可选的JVM参数。
首先,使用下面的命令初始化RJB:
require'rjb'
接着,将Rails应用程序中将要使用的所有legacy.jar文件—本例中为tar.jar—添加到classpath变量:
Rjb::load(classpath='.:/path/to/tar.jar',jvmargs=[])
可以将jvmargs留空,除非希望为JVM指定额外的参数。
现在,可以将准备使用的Java类导入到Ruby中,实例化这些类,并调用所需的方法。
将Java类导入到Ruby并实例化
清单1中的Ruby代码从tar.jar包中导入了需要的Java类,并从导入的类中创建了Ruby对象:
清单1.将Java类导入到Ruby并实例化
tararchive=Rjb::import('com.ice.tar.TarArchive') fileinputstream=Rjb::import('java.io.FileInputStream') file=Rjb::import('java.io.File') file_instance=file.new_with_sig('Ljava.lang.String;','.') fileinputstream_instance= fileinputstream.new_with_sig('Ljava.lang.String;','test.tar') tararchive_instance= tararchive.new_with_sig('Ljava.io.InputStream;',fileinputstream_instance) p"Let'sverifythattheobjectscreatedareindeedoftheclasseswe wanted..." p"------------------------------" p"FortheFileinstance...." p"Expecting:java.io.File,itis:"+file_instance._classname p"------------------------------" p"FortheFileInputStreaminstance...." p"Expecting:java.io.FileInputStream,itis:"+ fileinputstream_instance._classname p"------------------------------" p"FortheTarArchiveinstance...." p"Expecting:com.ice.tar.TarArchive,itis:"+ tararchive_instance._classname
导入Java类
清单1中的前三行将调用RJB的import方法,将所需的类分别导入到Ruby变量tararchive、fileinputstream和file中。必须指定类的完整包路径—例如,TarArchive类为com.ice.tar.TarArchive,而FileInputStream类为java.io.FileInputStream—就像使用java命令运行应用程序那样。
实例化导入的类
接着,清单1创建导入类的对象。可以通过调用每个类的new方法创建类,就像创建任何Ruby对象一样(例如,tararchive.new)。但是这样做会调用TarArchive类的默认的构造函数(没有参数),而您并不希望这样做。
当重载了类的构造函数后,需要对上面的对象创建方法进行一些修改。在这种情况下,必须按照下面的方式创建对象:
object=Classname.new_with_sig('signature',parameter[,moreparameters])
第一个参数定义构造函数所需的参数的签名类型。它告诉RJB调用其输入参数匹配指定签名的构造函数。
清单1中的第4个和第5个语句分别创建file和fileinputstream类的对象,它们调用相应的构造函数,参数类型为String。
在清单1的第6个语句中,TarArchive类的其中一个构造函数接受InputStream类型的对象作为参数。该语句的签名类型是一个单独的InputStream输入参数。这些类型签名的详细细节在getNameAPI的JavaSDK文档中做了很好的描述(参见参考资料)。第二个参数是创建的InputStream类型对象。
检验对象创建
清单1中的其余内容将检验RJB创建的对象是否是指定类的对象,方法是调用添加到每个对象的_classname方法。例如,调用tararchive_instance._classname将返回com.ice.tar.TarArchive,这意味着类被正确装载,并成功创建了该类的对象。
调用方法并捕获结果
将类装载到Ruby并从中创建了对象后,下一步是调用需要的方法并查看结果。例如,您希望使用TarArchive类的extractContents方法,将样例文件(test.tar)的内容提取到当前目录中。
和构造函数一样,可以使用两种方式调用方法。一种方式是直接调用方法,例如:
tararchive_instance.extractContents(file_instance)
当方法重载后,使用_invoke调用指定方法的每个参数的类型签名:
tararchive_instance._invoke('extractContents','Ljava.io.File;',file_instance)
这一步可以使RJB知道在方法重载时应该调用哪些方法。
和对待普通Ruby代码一样,您将捕获对象方法返回的结果(如果有的话),并在自己的应用程序中使用结果。方法调用返回的结果被自动转换为相应的对象类型。您只需在对象内直接调用方法。
JavaTarArchive类中实现的功能现在可以用于您的Ruby代码。通过使用相同的方法,Java代码中已实现的任何功能可以不加修改地在您的Ruby和Rails应用程序中重用。
完整的代码
清单2展示了本教程示例的完整Ruby代码(也可以通过下载获得):
清单2.完整的示例Ruby代码
#Include'rjb'libraryintoyourapplication require'rjb' #LoadtheJVMspecifyingthejarfilestoincludeandanyotheroptionalJVMarguments Rjb::load(classpath='.:/path/to/tar.jar',jvmargs=[]) #ImporttheclassesyouwanttouseintoaRubyvariable #specifythefullpackagepathtotheclasses. tararchive=Rjb::import('com.ice.tar.TarArchive') fileinputstream=Rjb::import('java.io.FileInputStream') file=Rjb::import('java.io.File') #Createobjectsoftheclasses.Usethenewmethoddirectlyoruse #the'new_with_sig'calltoinvokeoverloadedconstructorswitharguments #Directoryyouwanttoextractthefilesinthiscase,thecurrentdirectory file_instance=file.new_with_sig('Ljava.lang.String;','.') #inputstreaminstanceofthefiletoextract fileinputstream_instance=fileinputstream.new_with_sig('Ljava.lang.String;','test.tar') #tararchiveinstanceofthefiletobeextracted. tararchive_instance=tararchive.new_with_sig('Ljava.io.InputStream;'\ ,fileinputstream_instance) #Invokethemethodfromtheclassandcapturetheresults. #Useeitherthedirectcallofthemethod, #orthe'_invoke'calltoinvokeoverloadedmethods. p'Extractingfile.....' tararchive_instance.extractContents(file_instance) p'Done...'
尝试这些代码,将清单2中的代码保存到一个文件中,并且扩展名为.rb(或使用下载中的rjb-javatar.rb),然后在Ruby解释程序中运行。
结束语
在全新的Rails应用程序中重用已有的遗留Java代码其实非常简单,方式如下:
- 安装JavaSDK和RJB。
- 将JAVA_HOME和LD_LIBRARY_PATH环境变量导出到您的Rails应用程序的environment.rb文件中。
- 在应用程序中包括rjb库。
- 通过指定希望使用的JAR文件,装载RJB和JVM。
- 从希望使用的JAR文件中将类导入到Ruby变量并创建类的对象。
- 开始在Rails应用程序中使用刚刚创建的类,就像使用任何Ruby对象一样。
如果希望在Rails应用程序中重用已经使用Java代码实现的业务逻辑,RJB非常有用,并且不需要使用Ruby重新实现。它同时提供了RubyonRails和Java编程的优点。
考虑替代方法
还可以使用一种称为JRuby的替代方法,它可以实现与RJB相同的目标。JRuby是使用Java语言实现的完整的Ruby包,使Ruby能够运行在JVM之上(参见参考资料)。使用JRuby,您可以访问所有Java库。JRuby要求安装特定于JRuby的RubyGems,因为针对非Java的Ruby的普通RubyGems不能与JRuby兼容。
RJB和JRuby各有优缺点。对于JRuby,Ruby全部在JVM之上运行,每个Ruby调用将经过JVM,这将使执行变得非常缓慢。同样,如果已经设置了一个Rails应用程序,需要从头开始设置,以便JRuby访问Java代码。作为原生Ruby包,RJB易于安装,并且可以在已有的Rails设置中使用。如果需要在您的Rails应用程序中快速调用一些Java代码片段,那么RJB是最好的选择。
整体而言,在Rails应用程序中重用遗留Java代码的能力非常有用。使用Java语言实现的设计和编写都非常良好的业务逻辑不会搁置不用,相反,可以在新的Web应用程序中继续发挥有用的功能。