背景: 需要实现一个以Ruby开发的控制台程序,并提供RPC功能,以CORBA为规范,而CORBA服务端有可能是JAVA/C/C++等语言来实现。
方案1: 使用Ruby的开源类库Rinn(http://sourceforge.net/projects/rinn/)来直接实现CORBA客户端以实现对服务端的调用。(先用其提供的ridl(idl-to-ruby)来生成IDL的客户端Ruby代码。)
方案2: 使用java实现CORBA客户端,再通过Rjb(http://rubyforge.org/projects/rjb/),调用java的CORBA客户端代码。
比较: 方案1 与 方案2 的实现复杂程度差不多, 方案1的开源代码直接用纯Ruby代码实现对接口定义语言(IDL)的解析并实现了IIOP协议下的ruby ORB对象。 我一开始还是比较倾向使用这种方案,因为纯Ruby的实现,可以减少客户端环境的复杂程度,至少只需要有Ruby的运行环境就可以了。但是,该开源项目Rinn似乎很久没有人维护了(最近一次修改是在2001年7月),也没有正式release过。我下载了该代码,试图用它来实现客户端代码,但是遇到了很大的麻烦,代码是基于老的Ruby版本写的,而且还有运行错误,也没有完整的文档说明。最后,放弃了方案1选择了方案2。方案2也需要借助开源的类库:Rjb该项目比较完善,于是下载了rjb-1.0.3开始了我的Ruby-CORBA之旅。
首先介绍一下我的开发环境:
Ruby: ruby 1.8.5 (2006-12-25 patchlevel 12) [i386-mswin32]
下载地址: http://rubyforge.org/frs/?group_id=167
Java:
java version "1.5.0_06"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_06-b05)
Java HotSpot(TM) Client VM (build 1.5.0_06-b05, mixed mode, sharing)
警告: Rjb-1.0.3还不支持1.4或者之前的版本,否则调用会出现[BUG] Segmentation fault。
我将用一个Demo来说明我是如何使用rjb完成对CORBA服务端调用的。
第一步: 安装 rjb-1.0.3-mswin32.gem
使用命令行: gem install D:/Download/Ruby/rjb-1.0.3-mswin32.gem -y
安装成功将会提示: Successfully installed rjb, version 1.0.3
第二步: 创建 CORBA 的 JAVA 服务端/客户端
创建CORBA应用程序的过程大体如下:
● 编写IDL接口定义文件;
● 将接口定义文件编译为相应高级语言源代码,产生服务器框架与客户端存根;
● 基于服务器框架,编写服务对象实现程序;
● 基于客户端存根,编写客户对象调用程序;
● 分别编译客户对象和服务对象程序;
● 运行服务对象和客户对象程序;
a. 接口定义 (HelloApp.idl)
module HelloApp
{
interface
Hello
{
string sayHello(in string vin);
oneway
void
shutdown();
};
};
通过Sun提供的将IDL文件编译成Java源代码的工具idlj(jdk1.3.0_01以上版本),为接口定义文件生成客户端存根和服务器框架: idlj -fall Hello.idl
这将在idl文件目录下生成一个 HelloApp 文件夹,其中包含以下文件:
Hello.java
HelloHelper.java
HelloHolder.java
HelloOperations.java
HelloPOA.java
_HelloStub.java
b. 接口实现 (HelloImpl.java)
HelloImpl.java是Hello IDL 接口的实现;每个Hello实例都由一个HelloImpl实例来实现。HelloImpl是_HelloImplBase的子类,_HelloImplBase是由 idlj编译器从示例 IDL 中生成的。
//
The servant -- object implementation -- for the Hello
//
example. Note that this is a subclass of HelloPOA, whose
//
source file is generated from the compilation of
//
Hello.idl using j2idl.
package
HelloApp;
import
org.omg.CORBA.ORB;
public
class
HelloImpl
extends
HelloPOA {
private
ORB orb;
public
void
setORB(ORB orb_val) {
orb
=
orb_val;
}
//
implement sayHello(vin) method
public
String sayHello(String vin) {
return
"
Hello
"
+
vin;
}
//
implement shutdown() method
public
void
shutdown() {
orb.shutdown(
false
);
}
}
//
end class
c. 服务端实现 (Server/HelloServer.java)
package
Server;
import
org.omg.CORBA.ORB;
import
org.omg.CosNaming.NameComponent;
import
org.omg.CosNaming.NamingContextExt;
import
org.omg.CosNaming.NamingContextExtHelper;
import
org.omg.PortableServer.POA;
import
HelloApp.Hello;
import
HelloApp.HelloHelper;
import
HelloApp.HelloImpl;
public
class
HelloServer {
public
static
void
main(String args[]) {
try
{
//
create and initialize the ORB
ORB orb
=
ORB.init(args,
null
);
//
get reference to rootpoa & activate the POAManager
POA rootpoa
=
(POA) orb.resolve_initial_references(
"
RootPOA
"
);
rootpoa.the_POAManager().activate();
//
create servant and register it with the ORB
HelloImpl helloImpl
=
new
HelloImpl();
helloImpl.setORB(orb);
//
orb.connect((Object) helloImpl);
//
get object reference from the servant
org.omg.CORBA.Object ref
=
rootpoa.servant_to_reference(helloImpl);
//
and cast the reference to a CORBA reference
Hello href
=
HelloHelper.narrow(ref);
//
get the root naming context
//
NameService invokes the transient name service
org.omg.CORBA.Object objRef
=
orb
.resolve_initial_references(
"
NameService
"
);
//
Use NamingContextExt, which is part of the
//
Interoperable Naming Service (INS) specification.
NamingContextExt ncRef
=
NamingContextExtHelper.narrow(objRef);
//
bind the Object Reference in Naming
String name
=
"
HelloApp
"
;
NameComponent path[]
=
ncRef.to_name(name);
ncRef.rebind(path, href);
System.out.println(
"
HelloServer ready and waiting ...
"
);
//
wait for invocations from clients
orb.run();
}
catch
(Exception e) {
System.err.println(
"
ERROR:
"
+
e);
e.printStackTrace(System.out);
}
System.out.println(
"
HelloServer Exiting ...
"
);
}
//
end main
}
//
end class
d. 客户端实现 (Client/HelloClient.java)
package
Client;
import
java.util.Properties;
import
org.omg.CORBA.ORB;
import
org.omg.CosNaming.NamingContextExt;
import
org.omg.CosNaming.NamingContextExtHelper;
import
HelloApp.Hello;
import
HelloApp.HelloHelper;
public
class
HelloClient {
private
Hello remoteHello
=
null
;
public
HelloClient(String host, String port)
{
Properties props
=
new
Properties();
props.put(
"
org.omg.CORBA.ORBInitialPort
"
, port);
props.put(
"
org.omg.CORBA.ORBInitialHost
"
, host);
String[] args
=
{};
try
{
ORB orb
=
ORB.init(args, props);
//
get the root naming context
//
NameService invokes the transient name service
org.omg.CORBA.Object objRef
=
orb.resolve_initial_references(
"
NameService
"
);
NamingContextExt ncRef
=
NamingContextExtHelper.narrow(objRef);
//
get the Object Reference in Naming
String name
=
"
HelloApp
"
;
remoteHello
=
HelloHelper.narrow(ncRef.resolve_str(name));
}
catch
(Exception e) {
e.printStackTrace(System.out);
}
}
public
java.lang.Object sayHello(String name)
{
return
this
.remoteHello.sayHello(name);
}
}
说明: 客户端通过命名服务器中注册的“HelloApp”查找服务端提供的远程的Hello对象。
e. Ruby 代码实现 (HelloAppClient.rb)
require
"
rjb
"
clientClass
=
Rjb::
import
(
"
Client.HelloClient
"
)
client
=
clientClass.new(
"
localhost
"
,
"
1050
"
)
ret
=
client.sayHello(
"
Ruby
"
)
puts ret.toString
其中Ruby 调用 HelloClient 的 sayHello 方法。还可以用 _invoke 方法来调用该方法。
再编写一个vbs来启动服务:
const
SERVER_PORT
=
"
1050
"
set
sh
=
WScript.CreateObject(
"
WScript.Shell
"
)
sh.run(
"
tnameserv -ORBInitialPort
"
+
SERVER_PORT)
sh.run(
"
java Server/HelloServer -ORBInitialPort
"
+
SERVER_PORT)
运行: