CORBA 程序设计指南(入门)
Author: 龙湘明
Company: 北京邮电大学国家重点实验室
Date : 2001-2-28
Update : 2008-11-26
这里假设你对CORBA毫无所知。对JAVA略有所知,因为这里使用JAVA作为程序设计语言。学习了本书,你将对CORBA有个初步了解,并能编写一个简单完整的CORBA应用程序。
1.CORBA 简介
CORBA(Common Object Request Broker Architecture)是为了实现分布式计算而引入的。为了说明CORBA在分布计算上有何特点,我们从它与其它几种分布计算技术的比较中进行说明。
与过去的面向过程的RPC(Remote Procedure Call)不同,CORBA是基于面向对象技术的,它能解决远程对象之间的互操作问题。MicroSoft 的DCOM (Distributed Component Object Model)也是解决这一问题的, 但它基于Windows操作系统,尽管到本书编写时,DCOM已有在其他操作系统如Sun Solaris, Digital Unix, IBM MVS 上的实现,但毫无疑问,只有在微软的操作系统上才会实现得更好。而只有CORBA是真正跨平台的,平台独立性正是CORBA的初衷之一。另一种做到平台无关性的技术是Java RMI(Remote Method Invocation),但它只能用JAVA实现。CORBA与此不同,它通过一种叫IDL(Interface Definition Language)的接口定义语言,能做到语言无关,也就是说,任何语言都能制作CORBA组件,而CORBA组件能在任何语言下使用。
因此,可以这样理解CORBA:CORBA一种异构平台下的语言无关的对象互操作模型。
1.1 CORBA体系结构
CORBA的体系结构如下:
图1.1 CORBA体系结构
CORBA上的服务用IDL描述,IDL将被映射为某种程序设计语言如C++或Java,并且分成两分,在客户方叫IDL Stub(桩), 在服务器方叫IDL Skeleton(骨架)。两者可以采用不同的语言。服务器方在Skeleton的基础上编写对象实现(Object Implementation),而客户方要访问服务器对象上的方法,则要通过客户桩。而双方又要通过而ORB(Object Request Broker,对象请求代理)总线通信。
与传统的Client/Server模式(我们称为Two-tier client/server)不同,CORBA是一种multi-tier client/server architecture,更确切的说,是一种three-tier client/server模式。双重客户/服务器模式存在的问题是两者耦合太紧,它们之间采用一种私有协议通信,服务器的改变将影响到客户方。多重客户/服务器与此不同,两者之间的通信不能直接进行,而需要通过中间的一种叫代理的方式进行。在CORBA中这种代理就是ORB。通过它,客户和服务器不再关心通信问题,它们只需关心功能上的实现。从这个意义上讲,CORBA是一种中间件(Middleware)技术。
下面列出CORBA中的一些重要概念,或者说CORBA中的几个重要名词,有助于读者了解CORBA的一些重要的方面。
1.2 CORBA中的几个概念
1.2.1 ORB(Object Request Broker)
CORBA体系结构的核心就是ORB。可以这样简单理解:ORB就是使得客户应用程序能调用远端对象方法的一种机制。
图1.2 ORB模型
具体来说就是:当客户程序要调用远程对象上的方法时,首先要得到这个远程对象的引用,之后就可以像调用本地方法一样调用远程对象的方法。当发出一个调用时,实际上ORB会截取这个调用(通过客户Stub完成,“提高”篇中会详细解释),因为客户和服务器可能在不同的网络、不同的操作系统上甚至用不同的语言实现,ORB还要负责将调用的名字、参数等编码成标准的方式(称Marshaling)通过网络传输到服务器方(实际上在同一台机器上也如此),并通过将参数Unmarshaling的过程,传到正确的对象上(这整个过程叫重定向,Redirecting),服务器对象完成处理后,ORB通过同样的Marshaling/Unmarshaling方式将结果返回给客户。
因此,ORB是一种功能,它具备以下能力:
1.对象定位(根据对象引用定位对象的实现)
2.对象定位后,确信Server能接受请求
3.将客户方请求通过Marshaling/Unmarshing方式重定向到服务器对象上
4.如果需要,将结果以同样的方式返回。
1.2.2 IDL(Interface Definition Language)
IDL,接口定义语言,是CORBA体系中的另一个重要概念。如果说ORB使CORBA做到平台无关,那么IDL, 则使CORBA做到语言无关。
正像其名字中显示的那样,IDL仅仅定义接口,而不定义实现,类似于C中的头文件。实际上它不是真正的编程语言。要用它编写应用,需要将它映射它相应的程序设计语言上去,如映射到C++或JAVA上去。映射后的代码叫Client Stub Code和 Server Skeleton Code。
IDL的好处是使高层设计人员不必考虑实现细节而只需关心功能描述。IDL可以说是描述性语言。设计IDL的过程也是设计对象模型的过程。它是编写CORBA应用的第一步,在整个软件设计过程中至关重要。
IDL的语法很像C++,当然也像Java。很难想像一个程序设计人员是不懂C或Java的,所以,几乎所有的程序设计人员都能迅速理解IDL。而这正是IDL设计者所希望的。
下面是一个IDL定义的简单例子:
// grid.idl
// IDL definition of a 2-D grid:
module simpleDemo{
interface grid {
readonly attribute short height; // height of the grid
readonly attribute short width; // width of the grid
// IDL operations
// set the element [row,col] of the grid, to value:
void set(in short row, in short col, in long value);
// return element [row,col] of the grid:
long get(in short row, in short col);
};
};
This IDL defines an interface for a grid
CORBA object that maintains a grid or 2-D array of data values, which a client can access or modify remotely.
Module类似于Java中包(Package)的概念,实际上module simpleDemo映射到JAVA正是package simpleDemo。而Interface类似于C++中的类(classs)声明,或是Java中的Interface 定义。
附录中列出了IDL的全部语法。
1.2.3 Stub Code 和 Skeleton Code
Stub code 和Skeleton Code 是由IDL Complier自动生成的,前者放在客户方,后者放在服务器方。不同厂商的IDL complier生成的Stub 和Skeleton会略有区别,但影响不大。
如上面的grid.idl, 编译后,Stub Code包含以下文件:
grid.java
_gridStub.java
gridHelper.java
gridHolder.java
gridOperations.java
Skeleton Code则包含以下文件:
gridOperations.java
gridPOA.java
gridPOATie.java
(在Stud Code 也包含gridOperations.java, 是因为在使用Call back机制时会用到。)
这些文件的用途后面会讲到。
1.2.4 GIOP 和 IIOP
我们知道,客户和服务器是通过ORB交互的,那么,客户方的ORB和服务器方的ORB又是通过什么方式通信呢?通过GIOP(General Inter-ORB Protocol)。也就是说,GIOP是一种通信协议,它规定了两个实体:客户和服务器ORBs间的通信机制。
图1.3 ORBs通信机制
GIOP在设计时遵循以下目标:
Ø Widest possible availability
Ø Simplicity
Ø Scalability
Ø Low cost
Ø Generality
Ø Architectural neutrality
也是说,GIOP设计的尽可能简单,开销最小,同时又具有最广泛的适应性和可扩展性,以适应不同的网络。
GIOP定义了以下几个方面:
1.The Common Data Representation (CDR) definition.
通用数据表示定义。它实际上是IDL数据类型在网上传输时的编码方案。它对所有IDL数据类型的映射都作了规定。
2.GIOP Message Formats.
它规定了Client和Server两个角色之间要传输的消息格式。主要包括Request和Reply两种消息。
一个Request 消息有以下几部分组成:
A GIOP message header
A Request Header
The Request Body
相应的,一个Reply消息则包括
A GIOP message header
A Reply Header
The Reply Body
GIOP1.1规定 GIOP message header格式如下:
// GIOP 1.1
struct MessageHeader_1_1 {
char magic [4];
Version GIOP_version;
octet flags; // GIOP 1.1 change
octet message_type;
unsigned long message_size;
};
Request Header格式如下:
// GIOP 1.1
struct RequestHeader_1_1 {
IOP::ServiceContextList service_context;
unsigned long request_id;
boolean response_expected;
octet reserved[3]; // Added in GIOP 1.1
sequence <octet> object_key;
string operation;
Principal requesting_principal;
};
Request Body则按CDR规定的方式编码,它主要对方法调用的参数进行编码, 如方法:
double example (in short m, inout Principal p);
可表示成:
struct example_body {
short m; // leftmost in or inout parameter
Principal p; // ... to the rightmost
};
3.GIOP Transport Assumptions:
主要规定在任何面向连接的网络传输层上的一些操作规则。如:Asymmetrical connection usage,Request multiplexing,Overlapping requests,Connection management等。
另外,因为CORBA是基于对象的,GIOP还需定义一套Object Location的机制。
GIOP因为是一种通用协议,所以不能直接使用。在不同的网络上需要有不同的实现。目前使用最广的便是Internet上的GIOP,称为IIOP(Internet Inter-ORB Protocol)。IIOP基于TCP/IP协议。IIOP消息格式定义如下:
module IIOP { // IDL extended for version 1.1
struct Version {
octet major;
octet minor;
};
struct ProfileBody_1_0 { // renamed from ProfileBody
Version iiop_version;
string host;
unsigned short port;
sequence <octet> object_key;
};
struct ProfileBody_1_1 {
Version iiop_version;
string host;
unsigned short port;
sequence <octet> object_key;
sequence <IOP::TaggedComponent> components;
};
};
1.2.5 Dynamic Invocation Interface (DII) 和 Dynamic Skeleton Interface (DSI)
动态调用接口(DII)和动态骨架接口(DSI)是用来支持客户在不知道服务器对象的接口的情况下也能调用服务器对象。
一个增加了DII和DSI的CORBA调用模型如下:
图1.4 增加了DII和DSI的调用模型
我们在“提高篇”中再详细讲述DII和DSI。
1.2.6 Object Adapter (对象适配器)
对象适配器是ORB的一部分。它主要完成对象引用的生成、维护,对象定位等功能。对象适配器有各种各样。Basic Object Adapter(BOA, 基本对象适配器)实现了对象适配器的一些核心功能。而Portable Object Adapter(POA,可移植对象适配器)则力图解决对象实现在不同厂商的ORBs下也能使用的问题。最新的ORB产品一般都支持POA。
还有其他一些专有领域的对象适配器如Database Object Adapter等。
1.3 CORBA历史
CORBA是由OMG(Object Management Group)负责制定和维护的一组规范。OMG成立于1989年,是一个非营利的国际性软件组织,主要致力于为分布式计算提供解决方案并制定规范。除CORBA外,OMG还制定了如UML(United Modeling Language, 统一建模语言)、CWM等其他重要规范。OMG目前已有世界上760多个成员,东南大学是中国唯一的OMG成员。
CORBA自1990提出并被OMG采纳以来,已历经多个版本。分别称为 CORBA 1、CORBA 2和CORBA 3。其中CORBA 1是对CORBA 1.x的统称,CORBA 2是对CORBA 2.x的统称。目前CORBA 3规范还在制订中,不久便可面世。
下面是CORBA版本的更新历史。
CORBA1 :
CORBA 1.0 90-12, 提出CORBA框架
CORBA 1.1 91早期,定义了IDL及ORB Interface。
CORBA 1.2 92-93, 修订
CORBA定义了ORB的一些基本特性,但是没有定义而ORB间的通用协议。CORBA 2主要解决这一问题。
CORBA 2:
CORBA 2.0 94-12, 定义了GIOP和IIOP
CORBA 2.1 97-08
CORBA 2.2 98-02, 增加了POA。
CORBA 2.3 98-12
CORBA 2.3.1 99-10, 一些Change bars.
CORBA 2.4 00-10, 具备CORBA 3的雏形,包括:QoS Specification, Asynchronous Messaging, Minimum CORBA, Real-Time CORBA, CORBA Components, Notification Services, Firewall Specification等。
CORBA 2.4.1 00-11
CORBA 2.4.2 01-02
因此,可以说,CORBA 1提出ORB和IDL等CORBA基本概念,CORBA 2主要贡献是IIOP,而在制订中CORBA 3 则集中于CORBA Components技术及Real-time特性等。
对CORBA的简要介绍就到这里。关于OMG及CORBA的更多信息,可访问OMG站点:
下面我们开始编写第一个CORBA程序。
2 使用Orbix 2000编写第一个CORBA程序
在编写CORBA程序前,需要安装CORBA环境。提供CORBA产品的厂商有很多,这里我们采用IONA公司的Orbix 2000。IONA公司在CORBA领域一直处于领先地位,其产品几乎总是和CORBA规范同时出台。Orbix 2000是IONA公司2001年2月发布的最新产品,它具有Real-time特性,而这正是CORBA在电信领域应用的最关键的特性之一。
而程序设计语言采用JAVA。其开发环境为JBuilder 4. JBuilder 4 是Enterprise 公司(原著名的Boland公司)推出的最新JAVA集成开发环境,其内嵌JDK 1.3。
2.1 安装环境
你需要安装JAVA和CORBA ORB环境,前者使用JBuilder 4, 后者使用Orbix 2000。操作系统建议使用Windows 2000(Professional or Server),因为我们后面所讲的一些配置工作是基于Windows 2000,和Windows 98稍有区别。另外,Windows 2000也更为稳定。
2.1.1 安装Java
Orbix 2000需要一个JDK环境。如果你安装了JBuilder 4,可以直接使用其所带的JDK1.3。否则,你需要单独安装一个JDK。JDK是免费的,你可以下载一个较新的版本。
JBuilder 4不是必需的。但我们建议你使用它,因为JBuilder 4 支持CORBA程序的编写,除了能使用其自身的Visibroker(Enterprise公司自己的ORB)外,通过配置,还可直接使用Orbix。这对提高我们的开发效率是很有帮助的。
2.1.2 安装Orbix 2000
Orbix 2000安装很简单,按照安装步骤进行即可。但要注意以下几点:
尽量按缺省方式,这样可以省去不少配置上的麻烦。如安装路径使用C:/Program Files/IONA;使用典型安装等。
Orbix 2000同时支持JAVA和C,还带有一个COMet Development Kit。只需安装Orbix 2000 v1.2 Java Development Kit即可。
图 2.1 安装 Orbix 2000 for Java
安装过程中,会提示输入JDK的路径,所以,最好先安装JAVA。假设JBuilder 4安装在C:/JBuilder 4 目录下,那么,在提示框中输入C:/JBuilder 4/jdk1.3。
Orbix 2000需要license。License.txt文件中包含有所需的license.txt, 告诉安装程序这个文件所在的位置,安装程序会自动读取并安装所需的license。
图 2.2 安装 license
最后,安装程序会做一些配置工作,为使配置生效,你需要重启机器。
2.1.3 配置Orbix 2000
为使Orbix 2000能正常工作,还需要做一些额外的配置工作,主要是设置一些必需的环境变量。这里以Windows 2000为例。(Windows 98中需要在autoexec.bat中用相应命令设置。)
打开Windows的控制面板,找到“System”-“Advanced”-“Environment Variables”,如图2.3所示。
图 2.3 配置环境变量
“Environment Variables”分为两部分:“User variables”和“System variables”。
(以下标*的环境参数是需要用户自己配置的。其他参数安装程序会自动配置,但由于安装不得当或其他原因,也可能不正确,这里列出来,对照检查一遍是否都正确。另外,
我们假设程序安装在C:/Program Files/IONA目录下,如果不是,作相应改变。)
1. User variables:
1)CLASSPATH(*)
在CLASSPATH的末尾添加以下路径:
C:/Program Files/IONA/orbix_art/1.2/localhost; ./ ; ./classes
(添加的部分与前面部分用分号;隔开,下同)
it_javac, it_java工具会在运行时添加C:/Program Files/IONA/orbix_art/1.2/classes, 所以不必在这里设置。增加当前目录 ./ 和./classes会对你工作带来便利。
2)PATH
在末尾增加 C:/Program Files/IONA/bin
这样你可以在任何目录运行Orbix2000的命令了。
2.System variables:
1)IT_PRODUCT_DIR (*)
如果没有IT_PRODUCT_DIR,单击”new”按钮,添加它,其值设置为 C:/Program Files/IONA,也即程序安装路径。这个参数非常重要,Orbix自动设置其他关于目录的参数时都基于它。
2)JAVA_HOME (*)
添加JAVA_HOME,设置为C:/JBuilder4/jdk1.3,或其他JDK路径。it_javac和it_java的运行需要它。
3)PATH
增加C:/Program Files/IONA/bin
User variables 和System variables中只需设置一个就行,当然都设置也无妨。
4)IT_IDL_CONFIG_FILE(*)
idl compiler工具需要这个参数,添加这个参数,设置为C:/Program Files/IONA/orbix_art/1.2/etc
在开始编程之前,请确保上述设置是正确的。当你使用命令行方式时,如果你改变了设置,为使改变生效,需要重新开一个DOS窗口,然后在此窗口下操作,但不必重启机器。
2.2 编写第一个CORBA程序
2.2.1 命令行方式
我们先介绍在命令行方式下的开发步骤,编写一些小型的测试程序可以使用这种方式,
因为没有必要启动庞大的JBuilder 4。 后面我们再介绍在JBuilder 4集成开发环境下的使用方法。实际上两者步骤是类似的,只是方便程度不一样。
通常,开发一个CORBA应用的大致步骤如下:
1)定义 IDL。
2)用IDL complier 将IDL编译成 Client Stub 代码和 Server Skeleton代码。
3)编写Server方的实现(Implementation)代码。
4)编写 Server。
5)编写 Client。
6)编译和调试。
7)运行程序。
编写Server Implementation和编写Server不是一回事。Server和Client都是带有main()函数的可执行的程序。但Server Implementation 是对interface的实现,它可以单独放在一个.java文件中,也可以和Server放在同一个文件中。
下面以一个简单的例子详细讲解各个步骤。
第一步:定义IDL
首先建立一个工作目录,这里假设为D:/mywork/simpleDemo。
然后编写IDL。这个例子我们使用1.2.1 节中grid.idl,一个对二维数组操作的例子。可以使用任何文本编辑器编写,如UltraEdit等。之后保存到D:/mywork/simpleDemo下。
grid.idl定义了一个接口interface grid。其中定义两个方法get()和set(),用以获取和设置数组元素值。注意属性(attribute)的定义,这是C和JAVA中没有的概念(在Delphi中有这个概念),实际上它就相当于类的数据定义,但在映射到Java时被映射成方法。
第二步:编译IDL 为Java Stub和Skeleton.
打开DOS窗口,进入到D:/mywork/simpleDemo目录,在Dos命令提示符下键入:
idl –jbase –jpoa grid.idl
参数-jbase表示生成java stub code,参数–jpoa 表示生成java skeleton code,在Orbix 2000中,骨架代码使用POA方式。
缺省情况下idl编译器会创建一个java_output的目录,如果idl中有module定义,还会生成与此module同名的子目录,并将编译后生成的.java程序放在此目录中。
你可以开关参数控制编译方式,例如,如果想将客户stub和服务器skeleton代码分开,可以键入:
idl –jbase:-Oclient grid.idl
idl –jpoa:-Oserver grid.idl
-O 参数控制输出目录。编译器会自动生成client和server目录,并将代码分别放在这目录中。
我们知道,idl编译器会将module映射成package,缺省情况下,module simpleDemo映射成package simpleDemo。如果想自己控制package的生成,例如要生成package demos.simpleDemo 形式的包,可以:
idl –jbase:-Oclient –jbase:-Pdemos grid.idl
编译器会在module指定的包前再加上-P参数指定的包前缀,即生成package demos.simpleDemo。注意不要写成-Pdemos.simpleDemo,否则会生成package demos.simpleDemo.simpleDemo。
附录B中列出了idl命令的用法。
注意,
我们看看idl生成的stub和skeleton代码。
Stub Code包含以下文件:
grid.java 定义了接口 interface grid,
public interface grid
extends gridOperations,
org.omg.CORBA.Object,
org.omg.CORBA.portable.IDLEntity
{
}
直接由grid.idl中的interface grid 映射而来。
_gridStub.java 桩代码,定义了class _gridStub
public class _gridStub
extends org.omg.CORBA.portable.ObjectImpl
implements grid{
public void set(){…} ;
pubic int get(){..};
…}
class _gridStub中实现了客户方的set()和get()等方法,它们封装了ORB的功能,截取客户的调用,执行Marshaling/Unmarshing等操作,最终得到调用结果。也就是说,在客户方声明一个grid类型的对象,实际上使用gridHelper操作得到的是一个_gridStub类型的实例,调用grid上的方法实际上是调用_gridStub上的方法。
gridHelper.java 定义 public class gridHelper{ }
帮助客户方grid对象生成一个_gridStub类型的实例,同时得到服务器对象的引用。
gridHolder.java 定义public final class gridHolder
implements org.omg.CORBA.portable.Streamable{}
当需要处理out,inout类型的参数时,需要这个类。
gridOperations.java 定义public interface gridOperations{
void set()
int get() }
这个接口实际上是Server使用的。当客户方需要使用callback功能时,也需要这个接口。
Skeleton Code则包含以下文件:
gridPOA.java 定义类abstract public class gridPOA
extends org.omg.PortableServer.Servant
implements org.omg.CORBA.portable.InvokeHandler, gridOperations
{
}
Server方的Implementation代码直接继承自这个类。
gridPOATie.java 定义类public class gridPOATie
extends simpleDemo.gridPOA
{
}
当使用代理方式而不是继承方式创建Server Object时,使用这个类。
-jpoa 参数只生成上述两个文件,实际上Skeleton Code也包含gridOperations.java,因为class gridPOA就是实现gridOperations的。
上述接口(类)的继承关系如下:
grid.idl中只定义了一个interface grid,实际上如果定义了多个interface, 那么对每个interface都会生成和上面类似的一套代码。
第三步:编写Server方的Implementation代码(即编写Servant)
到现在为止,我们才开始真正编写代码。
引入了POA概念后,Server方的实现对象称为Servant, 编写Implementation代码实际上就是对IDL定义的每个interface,都编写一个Servant,其中要现实interface中定义的每个方法。.
这里我们将Servant类定义为class gridImpl,类名字可以自己随便取,通常都命名为xxxImpl的形式。Class gridImpl直接继承自gridPOA。
在D:/mywork/simpleDemo目录下创建一个gridImpl.java文件,内容如下:
package simpleDemo;
public class gridImpl extends gridPOA{
short m_height; // store the height
short m_width; // store the width
int m_array[][]; // a 2D array to hold the grid data
private int i=0;
private int j=0;
private int n=0;
public gridImpl(short width, short height)
{
m_array = new int[width][height]; // allocate the 2D array
m_height = height; // set up height
m_width = width; // set up width
}
/*
* Implementation of the method which reads the height attribute
*/
public short height()
{
return m_height;
}
/*
* Implementation of the method which reads the width attribute
*/
public short width()
{
return m_width;
}
/*
* Implementation of the set operation
*/
public void set (short x, short y, int value)
{
i++;
n++;
System.out.println ("-----µÚ "+n+" ´Îµ÷Óà Server / µÚ "+i+" ´Îµ÷Óà grid.set()----");
System.out.println ("In grid.set() x = " + x);
System.out.println ("In grid.set() y = " + y);
System.out.println ("In grid.set() value = " + value);
m_array[y][x] = value;
}
/*
* Implementation of the get operation
*/
public int get (short x, short y)
{
j++;
n++;
System.out.println ("-----µÚ "+n+" ´Îµ÷Óà Server / µÚ "+j+" ´Îµ÷ÓÃ: grid.get()----");
int value = m_array[y][x];
System.out.println ("In grid.get() x = " + x);
System.out.println ("In grid.get() y = " + y);
System.out.println ("In grid.get() value = " + value);
return value;
}
}
编写好Implementation代码后,实际上你就可以编译了。使用it_javac 命令进行编译。在编译gridImpl.java之前,请先编译idl生成的stub和skeleton代码。
1) 转到D:/mywork/simpleDemo,用mkdir classes建立一个目录d:/mywork/simpleDeom/classes, 我们编译后的class文件都放在这个目录下。
2) 编译桩和骨架代码。转到D:/mywork/simpleDemo/java_output/simpleDemo目录,键入命令:
it_javac –d d:/mywork/simpleDemo/classes *.java
(-d 参数指明编译后的class文件的输出目录,因为.java中指定了包名simpleDemo,会自动创建一个simpleDemo子目录,.class文件放在此子目录中。)
3) 编译gridImpl.java。转到D:/mywork/simpleDemo, 键入命令:
it_javac –classpath ./classes –d classes gridImpl.java。
(-classpath 指明gridImpl.java要用到的其他类文件,因为gridImpl.java中用到了骨架文件中的类,也即第2)步所编译的文件,所以需要用-classpath指明。)
键入it_javac可以查看此命令的帮助。it_javac实际上还是调用jdk的javac进行编译的,需要用参数JAVA_HOME指定,这是为什么我们要对JAVA_HOME进行配置的原因。因此改变JAVA_HOME就可改变使用的javac编译器,非常方便。
使用it_javac而不直接使用javac的另一好处是it_javac会自动在原有CLASSPATH环境变量后加上CORBA所需的类库又不改变原有CLASSPATH的值(所以我们不需在环境变量中对CLASSPATH做orbix2000的类库设置,而只需做其他设置),同时用户还可以用-classpath 指定额外的class路径而不会覆盖已有的CLASSPATH值。it_javac会将这些值全部传递给JAVA_HOME指定的javac编译器。
第四步:编写Server
Servant仅是实现代码,而Server是包含main()函数的可执行的代码。Server的主要任务就是创建所需的Servant,同时通知POA已准备就绪,可以接受客户方的请求。
新建一个文件gridServer.java,放在D:/mywork/simpleDemo下,代码如下:
package simpleDemo;
import java.io.*;
import org.omg.CORBA.*;
import org.omg.PortableServer.*;
public class gridServer {
public static ORB global_orb;
public static void main(String[] args) {
final short width = 100;
final short height = 100;
// 1. Initalise ORB for an application and resolve RootPOA reference
try{
System.out.println("Initializing the ORB");
global_orb = ORB.init (args,null);
org.omg.CORBA.Object poa_obj = global_orb.resolve_initial_references("RootPOA");
org.omg.PortableServer.POA root_poa = org.omg.PortableServer.POAHelper.narrow(poa_obj);
// 2. Create servants, activate them, write their IORs to files.
//
System.out.println("Creating objects");
gridImpl grid=new gridImpl(width,height);
byte[] grid_oid = root_poa.activate_object(grid);
export_object(root_poa, grid_oid, "../grid.ior", gridHelper.id());
// 3. Activate the POA Manager to allow new requests to arrive
//
System.out.println("Activating the POA Manager");
org.omg.PortableServer.POAManager poa_manager = root_poa.the_POAManager();
poa_manager.activate();
// 4. Give control to the ORB to let it process incoming requests
//
System.out.println("Giving control to the ORB to process requests");
global_orb.run();
System.out.println ("Done");
}
catch(org.omg.CORBA.ORBPackage.InvalidName ex)
{
System.out.println("Failed to obtain root poa " + ex );
}
catch(org.omg.PortableServer.POAPackage.WrongPolicy ex)
{
System.out.println("Invalid POA policy " + ex );
}
catch(org.omg.PortableServer.POAPackage.ServantAlreadyActive ex)
{
System.out.println("POA servant already active " + ex );
}
catch(org.omg.PortableServer.POAManagerPackage.AdapterInactive ex)
{
System.out.println("POAManager activate failed" + ex );
}
System.out.println("gridServer exiting...");
}
/**
* This function takes a poa and object id, builds an object
* reference representing that object, and exports the object
* reference to a file.
*/
public static void export_object(POA poa, byte[] oid, String filename, String type_id)
{
try
{
org.omg.CORBA.Object ref = poa.create_reference_with_id(oid, type_id);
String stringified_ref = global_orb.object_to_string(ref);
RandomAccessFile FileStream = null;
try
{
FileStream = new RandomAccessFile(filename,"rw");
FileStream.writeBytes(stringified_ref);
FileStream.close();
}
catch(Exception ex)
{
System.out.println("Failed to write to " + filename );
}
}
catch(org.omg.PortableServer.POAPackage.WrongPolicy ex)
{
System.out.println("Invalid POA policy " + ex );
}
}
}
通常,一个Server主程序执行以下几个步骤:
1) 初始化ORB;
2) 创建Servant对象;
3) 激活POA管理器;
4) 通知ORB准备接受请求。
只有第二步是需要用户编写的,其他步骤所有Server程序都大致一样。在创建Servant对象后还需注册其引用,这里我们使用了将其引用保留到文件中的方法(file based)。方法export_object()用来完成这一个工作,这是我们自己定义的,实际上可以作为一个通用方法使用在所有Server程序中使用。当客户方
Orbix 2000提供了两种获取对象引用的方式,一种叫file based,即读取文件的方式;一种叫Naming Service,即通常所说的命名服务。前一种方式的好处是不需要启动ORB守护进程,只需启动Server和Client两个程序就可以了。当Client需要使用获得Servant的引用时,需要从文件中读取,所以实际上当Client和Server在同一台机器上时才是可行的,在真正跨机器、跨平台的分布式应用中,通常使用Naming Servic,它的不足是需要启动Naming Service守护进程,这对其健壮性要求很高,可能成为系统瓶颈。
第五步:编写Client
Client程序就是客户方的可执行程序,它需要使用到Server方的服务。
新建一个文件gridClient.java,保存到D:/mywork/simpleDemo,源代码如下:
package simpleDemo;
import java.io.*;
import org.omg.CORBA.*;
/*
* The javaclient will connect to the Grid server and invoke
* operations enabling it to manipulate a 2 dimensional array.
*/
public class gridClient
{
public static void main (String args[])
{
System.out.println ("The Client begin.../n");
ORB orb=null;
grid gridProxy = null;
grid gridProxy2=null;
org.omg.CORBA.Object obj_ref=null;
/*
* Initalise the ORB for an Orbix application.
*/
try {
orb=ORB.init (args,null);
/*
* Establish a CORBA connection with the grid server and
* return a proxy for it.
*/
obj_ref=import_object(orb,"../grid.ior");
gridProxy = gridHelper.narrow (obj_ref);
gridProxy2 = gridHelper.narrow(obj_ref);
/*
* Get the width and height by calling the remote
* width and height operations on the grid server.
*/
short width = 0;
short height = 0;
/*
* Set some values in the remote grid.
*/
short x_coord = 2;
short y_coord = 4;
try {
gridProxy.set (x_coord, y_coord, 123);
gridProxy2.set (x_coord, y_coord, 111);
}
catch (SystemException ex) {
System.out.println ("FAIL/tException setting values in the grid.");
System.out.println (ex.toString());
System.exit (1);
}
/*
* Check the values placed in the grid
* were set correctly.
*/
int val = 0,val2=0;
x_coord = 2; y_coord = 4;
val = gridProxy.get (x_coord, y_coord);
val2 = gridProxy2.get (x_coord, y_coord);
System.out.println ("Value for grid[2,4] is " + val);
System.out.println ("Value2 for grid[2,4] is " + val2);
}
catch (org.omg.CORBA.SystemException ex)
{
System.err.println(ex);
System.exit(ex.minor);
}
catch (Exception ex)
{
System.err.println(ex);
System.exit(1);
}
finally
{
if (orb != null)
{
try { orb.shutdown(true); } catch (Exception ex) { /* do nothing */ }
}
}
System.out.println ("/nGrid demo finished.");
}
public static org.omg.CORBA.Object import_object(ORB orb, String filename)
{
String ior = null;
RandomAccessFile FileStream = null;
System.out.println(" Reading object reference from " + filename);
try
{
FileStream = new RandomAccessFile(filename,"r");
ior = FileStream.readLine();
return orb.string_to_object(ior);
}
catch(Exception e)
{
System.out.println("Error opening file " + filename);
return null;
}
}
}
通常,客户程序需要执行以下步骤:
1) 初始化ORB;
2) 获得Servant对象的引用;
3) 调用其上的操作
这里使用了读取文件的方法获得对象引用,自定义的方法import_object()用来完成这里任务。
这个程序生成了两个客户对象:gridProxy和gridProxy2, 只是为了说明当使用同一个引用的时候,尽管是多个客户对象,但操作的是同一个Servant对象的数据。
第六步:编译和运行程序
最后一步便是编译和执行程序了。
转到D:/mywork/simpleDemo目录下,键入命令:
it_javac –classpath classes –d classes gridServer.java
it_javac –classpath classes –d classes gridClient.java
编译服务器和客户程序。编译后的.class文件放在./classes/simpleDemo目录下。
然后便可执行它们了。转到classes目录下,键入:
it_java simpleDemo.gridServer 启动服务器。
重新开一个DOS窗口,
转到D:/mywork/simpleDemo/classes,键入
it_java simpleDemo.girdClient 运行客户程序。
图 2.4 程序运行结果
可以看到,尽管gridProxy将值设为123,但结果仍是111,这是因为已被gridProxy2修改的缘故。这个小问题告诉我们,在设计Servant对象的实现时,需要考虑可能多个客户修改同一个数据所带来的影响。
至此,一个简单的但五脏俱全的CORBA应用程序遍全部完成了。
2.2.2 使用集成开发环境JBuilder4
使用集成开发环境JBuilder 4可以带来不少便利,例如可以使用工程化(Project)管理,可以利用向导功能等。
2.2.2.1 为JBuilder4配置CORBA环境
在进行CORBA编程前需要对JBuilder4进行一些配置。
1) 配置Orbix2000 CORBA Library
需要将Orbix2000 提供的CORBA类库增加到JBuilder4环境中来。
选择“tools”-“Configure Libraries”菜单,弹出如下窗口:
图 2.5 Configure Libraries
单击“New”按钮添加Library。弹出如下窗口:
图 2.6 增加Orbix2000 CORBA类库
按图2.6 所示将”Name”设置为“orbix2000”, “Location”选择“User Home”,单击“Add”按钮,选择目录“C:/Program Files/IONA/orbix_art/1.2/classes”,所需的orbix 2000 CORBA类库便添加进来了,添加后如图2.7所示。
图2.7 配置后的orbix 2000 library
2) 为Project 指定缺省的CORBA环境
刚才仅仅是增加了类库。因为JBuilder4能支持多种ORB,所以我们需要指定一种ORB。
选择“Tools”-“Enterprise setup”菜单,弹出如图2.8所示窗口。
图 2.8 Enterprise setup for CORBA
缺省情况下,JBuilder4支持Visibroker和OrbixWeb两种ORB。Orbix2000是OrbixWeb的升级产品,JBuilder4目前不提供缺省支持,但我们可以自己添加。单击“New”按钮,添加Orbix 2000 CORBA环境,如图2.9所示。
图 2.9 配置Orbix2000 CORBA 环境
其中“Name for this configuration”设为“orbix2000”, “Path for ORB tools”设为C:/Progame Files/IONA/bin”,“Library for projects”设为“orbix2000”(选择前面第一步中设置的orbix2000 Library),“IDL compiler command”设为“idl –jbase –jpoa”,“Command option for output directory”设为“ ”(一个空格,如果不设置,JBuilder会报告出错)。
2.2.2.2 使用JBuilder4开发CORBA程序
好了,现在可以编程了。如果你还不熟悉使用JBuilder4,按照以下步骤进行:
1) 新建一个工程(Project)
和大多数集成开发环境一样,JBuilder4采用工程进行管理,建立工程是开发程序的第一步。
选择“File”-“New Project”菜单,打开“Project wizard”, 如图2.10所示。
图2.10 工程向导(Project wizard)
为了和2.2.1节中的例子相区别,我们给“Project name”取名为“simpleDemo2”,“Root path”设为“D:/mywork”,其他采用缺省设置。
JBuilder4会在“Root path”下建立一个与“Project name”同名的目录(这里为D:/mywork/simpleDemo2),并建立子目录src, bak,classes,分别放置源文件、备份文件和Class文件。
单击“Next”按钮,其中会有“Required libraries”,确保有“orbix2000”。
你可以按提示操作,之后单击“Finish”完成工程simpleDemo2的创建。
2) 创建IDL
实际上你可把已有的IDL文件加到工程中,这里为了说明用JBuilder4开发的全过程,我们新建一个IDL文件。
选择“File”-“New”按钮,切换到“Enterprise”页,可以看到如图2.11所示窗口。
图2.11 创建IDL向导
单击“Sample IDL”,弹出如图2.12所示窗口。
图2.12 Sample IDL Wizard
这里,“Package”设为空,因为我们自己在.idl中定义module, IDL编译器会自动生成相应的包名。如果你在这里设置Package, 则会将此Package作为moudle的前缀,即生成xx.xx形式的包名。“File name”设为“grid.idl”。单击“OK”按钮,然后编写IDL代码。我们把2.2.1 节中的D:/mywork/simpleDemo/grid.idl的代码拷贝到这里。如图2.13所示。
图2.13 编写grid.idl
但是很遗憾,Orbix 2000的idl 编译器命令无法在JBuilder4中使用,这是因为JBuilder4需要在“Enterprise setup”中显式的指定output directory的开关参数(如-O), JBuilder会重新组装idl 命令,并自动将缺省的out directory放在此开关参数后,但中间会有一个空格。如下所示:
C:/Program Files/IONA/bin/idl -jbase -jpoa:O "D:/mywork/simpleDemo2/classes/Temporary_Files" "D:/mywork/simpleDemo2/src/grid.idl"
而在orbix 2000的idl –jbase:-O 指定output directory时,必须让output directory紧跟在-O参数后,中间不能有任何空格。因此,这条命令会出错。
所以,我们只能在命令行方式下编译idl了。
在DOS窗口下转到D:/mywork/simpleDemo2/src目录下,输入命令:
idl –jbase –jpoa grid.idl
编译好的.java文件放在D:/mywork/simpleDemo2/src/output_java/simpleDemo下,在JBuilder4菜单中选择“Project”-“Add new files/package…”命令将它们加到工程中来。当需要在工程中增加已编好的java程序时,你可以采用这种方法。
按“Ctrl+F9”或选择菜单中的“Project”-“Make project simpleDemo2.jpx”命令,可以编译我们刚才增加的由IDL生成的java程序(即桩和骨架程序)。
在编译中会发现grid.idl编译不通过,我们前面已经讲述了原因,因为已经手工编译过了,我们不去管它。
另外,还会发现如下警告信息:
Warning#:908:check sourcepath;source :/mywork/simpleDemo2/src/java_output/simpleDemo/gridPOATie.java cannot be found on sourcepath by appending /simpleDemo/gridPOATie.java to each sourcepath entry
可以在“Project”-“Project properties”中的“Paths”的“Source”中增加“D:/mywork/simpleDemo2/src/java_output”一项即可。出现类似的问题时可以用这种方式解决。当然因为这是警告信息,你也可以忽略它。
图2.14 为工程增加Source path
3) 编写Servant、Server及客户程序
在JBuilder4中新建一个.java文件可以使用菜单中的“File”-“New class”,如图2.15所示。
图2.15 Class Wizard
我们用这种方式创建gridImpl.java,这里“Class name”设为“gridImpl”,在“Option”中设为“Public”。一个Java文件中只能有一个public class,它必须和.java文件名同名。
由于gridImpl是实现代码(即Servant),所以上面没有选择“Generate main method”。如果要创建Server或Client程序,应该选这一选项。
这里我们采用和2.2.1中相同的代码。
4)编译执行程序
创建好Servant、Server和Client后,我们就可以编译执行了。
在执行程序前,需要做以下工作:
1) 将grid.idl从工程中删去。(这是因为orbix2000 idl无法在JBuilder4下编译成功,而每次执行程序前JBuilder4都会去编译它)
2) 重新配置orbix2000 Library。在其classes中增加以下两个目录:
C:/Program Files/IONA/etc
C:/Program Files/IONA/orbix_art/1.2/localhost
前一个目录存放orbix 2000 license(license.txt);后一个目录存放localhost配置文件(localhost.cfg)。每一个Orbix2000程序都会检查license和配置文件。
增加后如图2.16所示。
图2.16 重新配置orbix2000 Library
3) 配置虚拟机(VM)参数
你可以用菜单中的“Project”-“Project properties…”-“Run”进行配置。如图2.17所示。
图2.17 配置VM环境
“Main class”选择你要运行的程序,这里为simpleDemo.gridServer。
“VM parameters”设置为以下值:
-Dorg.omg.CORBA.ORBClass=com.iona.corba.art.artimpl.ORBImpl -Dorg.omg.CORBA.ORBSingletonClass=com.iona.corba.art.artimpl.ORBSingleton
注意只能是一行,不能有回车。
“Application parameters”设置为:
-ORBdomain_name localhost
这样你就可以运行gridServer程序了。
运行gridClient时,保证设置和上述一样。右键单击gridClient.java,从弹出菜单中选择“Run”,便可运行。
为了设置能对所有工程生效,你可以在“Project”-“Default project properties…”中对VM环境进行设置,和上面设置的一样。
3 使用ORBacus开发CORBA程序
ORBacus是OOC(Object Oriented Company)公司的ORB产品。ORBacus小巧但功能齐全,尤其是性能很高,提供实时CORBA特性,非常适合于电信领域。另外,ORBacus提供源代码,这对我们进一步揭开CORBA ORB内幕、提高程序性能、解决棘手问题都有好处。同时它还是免费的,当然商业用途例外。
3.1 安装环境
这里我们使用ORBacus 4.0.5。
ORBacus不需要安装过程,只需将它拷贝到某个目录下就行。这里假设在C:/ORBacus目录下。
ORBacus有一个bin/目录,存放了一些可执行文件,如jidl.exe( ORBacus 的IDL编译器)等。
另外还有一个lib/目录,这里存放了ORBacus的库文件,以压缩包的形式存在。
ORBacus也不用做任何配置。但为了使用方便,我们可以做以下简单配置:
参照2.1.3节介绍的方法,
1)为 CLASSPATH增加:
C:/Orbacus/lib/ob.jar; ./; ./classes
ob.jar是最基本的ORB类库。如果你的程序使用了其他功能,需要增加相应的类库。例如使用了命名服务,需要增加OBNaming.jar等。
2) 为PATH增加:
C:/Orbacus/bin
实际上ORBacus/bin/下只提供了jidl.exe等程序。ORBacus没有自己的javac、java程序,而是直接使用JDK的相应程序。事实上这对我们更加方便。保证PATH变量设置了JDK路径。
如果你的ORBacus的目录不是这样,作相应改变。
3.2 在ORBacus下编写CORBA程序
下面我们以ORBacus附带的一个例子讲解使用ORBacus编写CORBA程序的过程。这个例子在src/ob/demo/hello目录下。为了不致改变它,我们把它(连同hello目录)拷贝到D:/mywork目录下。
我们要用到以下几个文件:
D:/mywork/hello/Hello.idl idl定义文件
D:/mywork/hello/Hello_Impl.java Servant实现
D:/mywork/hello/Client.java 客户程序
D:/mywork/hello/Server.java 服务器程序
3.2.1 命令行方式
我们先介绍命令行方式下的编程步骤。实际上不管使用何种ORB平台,编程步骤都是是类似的。2.2.1节已经做了详细说明,这里只是简单介绍。
1)IDL 定义
Hello.idl定义如下:
interface Hello
{
void say_hello();
};
注意Hello.idl没有定义Module。它只定义了一个接口Interface Hello,并且只有一个方法 void say_hello()。
2)编译IDL为Java Stub和Skeleton
ORBacus的IDL Complier命令为jidl。打开DOS窗口,转到D:/mywork/hello目录下,键入:
jidl --package hello Hello.idl
其中参数—package 指定编译后.java文件的包名,实际上一般不显式指定,直接用.idl中的module名作package名。这里是因为Hello.idl中没有定义module,所以需要我们自己指定一个package名。
编译后生成以下几个文件:
JAVA Client Stub:
D:/mywork/hello/hello/HelloOperations.java
D:/mywork/hello/hello/Hello.java
D:/mywork/hello/hello/ _HelloStub.java
D:/mywork/hello/hello/HelloHelper.java
Java Server Skeleton:
D:/mywork/hello/hello/HelloOperations.java
D:/mywork/hello/hello/HelloHolder.java
D:/mywork/hello/hello/HelloPOA.java
各文件的含义参见2.2.1的说明。
我们现在就可以编译这些文件。转到D:/mywork/hello目录下,用mkdir classes建立classes子目录,用以放置编译后的.class文件,键入:
javac –d classes hello/*.java
其中 –d 参数指明.class文件的输出目录。
3)编写Servant Implementation
从现在开始,用户才真正编写程序代码。
Hello_Impl.java用来实现Servant。和Orbix2000 一样,它使用了POA。代码如下:
package hello;
public class Hello_impl extends HelloPOA
{
public void
say_hello()
{
System.out.println("Hello World!");
}
}
4)编写Server
Server用来创建相应的Servant对象,准备接受来自Client的请求。
Server.java 代码如下:
package hello;
public class Server
{
static int
run(org.omg.CORBA.ORB orb, String[] args)
throws org.omg.CORBA.UserException
{
//
//1. Resolve Root POA and Get a reference to the POA manager
//
//
org.omg.PortableServer.POA rootPOA =
org.omg.PortableServer.POAHelper.narrow(
orb.resolve_initial_references("RootPOA"));
//
org.omg.PortableServer.POAManager manager = rootPOA.the_POAManager();
//
// 2. Create implementation object and Save reference
//
Hello_impl helloImpl = new Hello_impl();
Hello hello = helloImpl._this(orb);
//
//
try
{
String ref = orb.object_to_string(hello);
String refFile = "Hello.ref";
java.io.FileOutputStream file =
new java.io.FileOutputStream(refFile);
java.io.PrintWriter out = new java.io.PrintWriter(file);
out.println(ref);
out.flush();
file.close();
}
catch(java.io.IOException ex)
{
System.err.println("hello.Server: can't write to `" +
ex.getMessage() + "'");
return 1;
}
//
// 3. Activate POA Manager to allow new requests to arrive
//
manager.activate();
// 4. Give control to ORB and Run implementation
orb.run();
return 0;
}
public static void
main(String args[])
{
// Set properties for ORB environment
java.util.Properties props = System.getProperties();
props.put("org.omg.CORBA.ORBClass", "com.ooc.CORBA.ORB");
props.put("org.omg.CORBA.ORBSingletonClass",
"com.ooc.CORBA.ORBSingleton");
int status = 0;
org.omg.CORBA.ORB orb = null;
try
{ // Init ORB for an server application
orb = org.omg.CORBA.ORB.init(args, props);
// Run the server application
status = run(orb, args);
}
catch(Exception ex)
{
ex.printStackTrace();
status = 1;
}
if(orb != null)
{
//
// Since the standard ORB.destroy() method is not present in
// JDK 1.2.x, we must cast to com.ooc.CORBA.ORB so that this
// will compile with all JDK versions
//
try
{
((com.ooc.CORBA.ORB)orb).destroy();
}
catch(Exception ex)
{
ex.printStackTrace();
status = 1;
}
}
System.exit(status);
}
}
可以看到,和Orbix 2000一样,在ORBacus下,一个Server程序也需要执行以下几个步骤:
1) 初始化ORB,获得RootPOA引用;
2) 创建Servant对象,并保存对象引用;
3) 激活POA管理器;
4) 通知ORB准备接受请求。
所不同的是,在main()函数开头有一段设置ORB属性的语句。实际上Orbix 2000的it_java命令已在命令行中自动设置了这些属性,所以不需要在程序中设定,当然像ORBacus一样在程序中设定也是可以的。同样的道理,在ORBacus下,也可以在java.exe命令参数中指定属性而不需在程序中设置。
另外,ORBacus不仅能将对象引用存储为一般的文件,还能存储为HTML文件。这通常用在Client 是一个Java Applet的情况下。为了简明起见,我们把这段代码去掉了。
5)编写Client
Client.java代码如下(去掉了Applet部分):
package hello;
import java.awt.*;
import java.awt.event.*;
public class Client
{
static int
run(org.omg.CORBA.ORB orb, String[] args)
throws org.omg.CORBA.UserException
{
//
// Get "hello" object
//
org.omg.CORBA.Object obj =
orb.string_to_object("relfile:/Hello.ref");
if(obj == null)
{
System.err.println("hello.Client: cannot read IOR from Hello.ref");
return 1;
}
Hello hello = HelloHelper.narrow(obj);
//
// Main loop
//
System.out.println("Enter 'h' for hello or 'x' for exit:");
int c;
try
{
String input;
do
{
System.out.print("> ");
java.io.DataInputStream dataIn =
new java.io.DataInputStream(System.in);
java.io.BufferedReader in = new java.io.BufferedReader(
new java.io.InputStreamReader(dataIn));
input = in.readLine();
if(input.equals("h"))
hello.say_hello();
}
while(!input.equals("x"));
}
catch(java.io.IOException ex)
{
System.err.println("Can't read from `" + ex.getMessage() + "'");
return 1;
}
return 0;
}
public static void
main(String args[])
{
int status = 0;
org.omg.CORBA.ORB orb = null;
// Set properties for ORB
java.util.Properties props = System.getProperties();
props.put("org.omg.CORBA.ORBClass", "com.ooc.CORBA.ORB");
props.put("org.omg.CORBA.ORBSingletonClass",
"com.ooc.CORBA.ORBSingleton");
try
{ // Init ORB for client application
orb = org.omg.CORBA.ORB.init(args, props);
// Run the applciation
status = run(orb, args);
}
catch(Exception ex)
{
ex.printStackTrace();
status = 1;
}
if(orb != null)
{
//
// Since the standard ORB.destroy() method is not present in
// JDK 1.2.x, we must cast to com.ooc.CORBA.ORB so that this
// will compile with all JDK versions
//
try
{
((com.ooc.CORBA.ORB)orb).destroy();
}
catch(Exception ex)
{
ex.printStackTrace();
status = 1;
}
}
System.exit(status);
}
6)编译和运行程序
现在可以编译和运行程序了。
转到D:/mywork/hello目录下,键入:
javac –d classes Client.java
javac –d classes Server.java
编译好后,进入classes目录,先运行Server:
java hello.Server
重开一个DOS窗口,进入D:/mywork/hello/classes目录,运行Client:
java hello.Client
3.2.2 使用集成开发环境JBuilder4
3.2.2.1 为JBuilder4配置ORBacus CORBA环境
关于配置的详细过程请参考2.2.2.1节。这里进行简要说明。
1)配置ORBacus CORBA Library
需要将ORBacus 提供的CORBA类库增加到JBuilder4环境中来。
选择“tools”-“Configure Libraries”菜单,增加orbacus类库。
添加后如图3.1所示。
图3.1 配置后的orbacus library
2)为project 指定缺省的CORBA环境
选择“Tools”-“Enterprise setup”菜单,单击“New”按钮添加ORBacus环境。如图3.2所示。
图3.2 配置ORBacus CORBA 环境
其中“Name for this configuration”设为“ORBacus”, “Path for ORB tools”设为“C:/orbacus/bin”,“Library for projects”设为“orbacus”(选择前面第一步中设置的orbacus Library),“IDL compiler command”设为“jidl”,“Command option for output directory”设为“--output-dir”。
另外保证“Project”-“Properties”-“Build”-“IDL”中的“IDL complier”设置为“ORBacus”。若需要其它idl参数,例如指定include 文件,可以在“additional options”中指定,如图3.3所示。(在”Include Path”中指定无效。)
图3.3 在“Additional options”中指定Include path
3.2.2.2 使用JBuilder4开发CORBA程序
使用JBuilder4开发CORBA程序的详细步骤请参阅2.2.2.2节说明,这里以D:/mywork/hello程序进行简要讲解。
1) 新建一个工程,工程名为hello,根目录为D:/mywork/hello;
2) 将Hello.idl, Hello_Impl.java,Server.java和Client.java添加到工程中;
3) 为Hello.idl指定package。在“Project”-“Properties”-“Build”-“IDL”的“Additional options”中指定:--package hello。(在“package”中指定无效)
4) 编译所有文件;
5) 运行Server;
6) 运行Client。
程序运行结果如下:
图3.4 程序运行结果
注意,如果在main()函数中没有设置而ORB properties,则需要在JAVA虚拟机环境中指定。在“Project”-“Project properties…”-“Run”中将”VM parameter”设为
-Dorg.omg.CORBA.ORBClass=com.ooc.CORBA.ORB -Dorg.omg.CORBA.ORBSingletonClass=com.ooc.CORBA.ORBSingleton
注意,只能是一行,不能有回车。如图3.5所示:
图3.5 当没有在程序中设置ORB属性时设置VM参数
4.Orbix 2000和ORBacus环境共存的几个问题
4.1 程序相互移植
事实上,由于Orbix 2000和而ORBacus均使用POA的方式,而这是OMG的标准接口,所以,Orbix 2000的程序只需在main()开头加上设置属性的代码就可以在ORBacus下使用;而ORBacus的程序则可以去掉这段属性设置代码就可以在Orbix 2000下使用。当然,还有一个前提,ORBacus下不使用Orbix 2000的专有类库,Orbix 2000下不使用ORBacus的转有类库。
例如,要在ORBacus环境下使用我们在2.2.1节中编写的simpleDemo程序,可以如下进行:
1)在D:/mywork/simpleDemo/gridServer.java和gridClient.java的main()函数开头加以下语句:
java.util.Properties props = System.getProperties();
props.put("org.omg.CORBA.ORBClass", "com.ooc.CORBA.ORB");
props.put("org.omg.CORBA.ORBSingletonClass",
"com.ooc.CORBA.ORBSingleton");
同时将
global_orb = ORB.init (args,null);
替换为
global_orb = ORB.init (args,props);
2) 编译IDL
转到D:/mywork/simpleDemo目录下,键入:
jidl grid.idl;
2 编译Java
键入:
javac simpleDemo/*.java *.java
3 运行Server和Client
转到classes目录下,键入:
java simpleDemo.gridServer
重开一个DOS窗口,转到D:/mywork/simpleDemo/classes目录下:
键入:
java simpleDemo.gridClient
同样,要将ORBacus下的hello程序移植到Orbix 2000下,按以下步骤进行:
1)将C:/orbacus/lib/ob.jar从CLASSPATH环境变量中删除。因为it_javac和it_java程序会读取CLASSPATH变量,在命令行参数中自动设置-classpath参数时,总是将自己的类库放在CLASSPATH变量的最后,所以需要将ORBacus的类库设置去掉。
2)将D:/mywork/hello/Server.java和Client.java中main()函数开头一段设置属性的代码注释掉。同时将
global_orb = ORB.init (args,props);
替换为
global_orb = ORB.init (args,null);
另外,将Server.java和Client.java的main()函数中以下代码注释掉:
/* if(orb != null)
{
//
// Since the standard ORB.destroy() method is not present in
// JDK 1.2.x, we must cast to com.ooc.CORBA.ORB so that this
// will compile with all JDK versions
//
try
{
((com.ooc.CORBA.ORB)orb).destroy();
}
catch(Exception ex)
{
ex.printStackTrace();
status = 1;
}
}*/
在Client.java读取对象引用时,由于Orbix2000不支持以下方式:
org.omg.CORBA.Object obj =orb.string_to_object("relfile:/Hello.ref");
需要将这段代码改为:
org.omg.CORBA.Object obj = null;
try{
String refFile="hello.ref";
java.io.BufferedReader in = new java.io.BufferedReader(
new java.io.FileReader(refFile));
String ref=in.readLine();
obj=orb.string_to_object(ref);
}
catch(Exception e){
System.out.println("system io error is "+e.toString());
}
3) 编译IDL
转到D:/mywork/hello目录下,键入:
idl –jbase:-Phello –jpoa:-Phello Hello.idl
4) 编译JAVA
键入:
it_javac –d classes output_java/hello/*.java *.java
5) 运行程序
转到classses目录下,键入:
it_java hello.Server
重开一个DOS窗口,转到D:/mywork/hello/classes,键入:
it_java hello.Client
另外需要指出的是,有时候运行程序时会出现类似下面的错误:
10:21:45 3/13/2001 [dragon/192.168.2.178] (IT_IIOP:8) E - failed to create socke
t for client (while attempting to connect() to host 'dragon/192.168.2.178' on po
rt 1157) - exception: com.iona.corba.IT_ATLI.IOError: IDL:iona.com/IT_ATLI/IOErr
or:1.0
这是因为运行的目录不对,保证运行Server和Client时均在classes目录下。
4.2 同一台机器上同时使用Orbix 2000和 ORBacus
在同一台机器上同时使用Orbix 2000和 ORBacus有一个问题,那就是两者都要读取CLASSPATH变量。 所以,要想同时使用Orbix 2000和 ORBacus,需要将C:/orbacus/lib/ob.jar从CLASSPATH环境变量中删除。使用Orbix 2000时不需要做什么变化,因为它使用自己的it_javac和it_java程序;但使用ORBacus时, 在javac和java命令中需要显式的指定-classpath。
例如编译hello/Client.java时,键入:
javac –d classes –classpath c:/orbacus/lib/ob.jar;./classes Client.java
注意是在D:/mywork/hello目录下,./classes参数不能缺少。
运行classes/hello/Client时,键入:
java –classpath c:/orbacus/lib/ob.jar;./ hello.Client
注意是在D:/mywork/hello/classes目录下,当前目录 ./ 参数不能缺少。
但是这样对使用ORBacus很麻烦,我们可以为ORBacus编写类似于it_javac和it_java的程序,取名为ob_javac和ob_java,它们用脚本语言写成。代码如下:
ob.javac.bat :
@echo off
REM ob_javac.bat
REM Copyright (c) 2001 BUPT-NKL, LongXiangming.
REM
REM This batch file delegates to a version specific
REM executable (.exe) with javac for ORBacus so that
REM you can use ORBacus and Orbix 2000 at the same machine.
REM Remember removing the C:/orbacus/lib/ob.jar from CLASSPATH variable.
REM It's written using the Shell Command Extensions;
REM %* lists all arguments (1, 2, etc.)
REM
REM Unfortunately, Shell Command Extensions are not available
REM on Windows 95.
REM
if "%JAVA_HOME%"=="" goto ERROR_1
if "%OB_LIB%"=="" goto ERROR_2
echo %JAVA_HOME%/bin/javac -classpath %OB_LIB%;./classes %*
"%JAVA_HOME%/bin/javac" -classpath %OB_LIB%;./classes %*
goto END
:ERROR_1
echo Please set JAVA_HOME to the JDK path in the system variables. eg, JAVA_HOME=C:/JDK1.3
goto END
:ERROR_2
echo Please set OB_LIB to the ORBacus lib path in the system variables, eg. OB_LIB=C:/orbacus/lib/ob.jar.
goto END
:END
ob_java.bat:
@echo off
REM ob_java.bat
REM Copyright (c) 2001 BUPT-NKL, LongXiangming.
REM Version 1.0
REM This batch file delegates to a version specific
REM executable (.exe) with javac for ORBacus so that
REM you can use ORBacus and Orbix 2000 at the same machine.
REM Remember removing the C:/orbacus/lib/ob.jar from CLASSPATH variable.
REM It's written using the Shell Command Extensions;
REM %* lists all arguments (1, 2, etc.)
REM
REM Unfortunately, Shell Command Extensions are not available
REM on Windows 95.
REM
if "%JAVA_HOME%"=="" goto ERROR_1
if "%OB_LIB%"=="" goto ERROR_2
echo %JAVA_HOME%/bin/java -Dorg.omg.CORBA.ORBClass=com.ooc.CORBA.ORB -Dorg.omg.CORBA.ORBSingletonClass=com.ooc.CORBA.ORBSingleton -classpath %OB_LIB%;./;./classes %*
"%JAVA_HOME%/bin/java" -Dorg.omg.CORBA.ORBClass=com.ooc.CORBA.ORB -Dorg.omg.CORBA.ORBSingletonClass=com.ooc.CORBA.ORBSingleton -classpath %OB_LIB%;./;./classes %*
goto END
:ERROR_1
echo Please set JAVA_HOME to the JDK path in the system variables. eg, JAVA_HOME=C:/JDK1.3
goto END
:ERROR_2
echo Please set OB_LIB to the ORBacus lib path in the system variables, eg. OB_LIB=C:/orbacus/lib/ob.jar.
goto END
:END
你可以把这两个程序放到C:/orbacus/bin目录下,在使用它们之前请设置好OB_LIB环境变量(参照2.1.3节说明)。
这样,当编译hello/Client.java时,可以:
ob_javac Client.java
运行classes/hello/Client时,可以:
ob_java hello.Client
这样便可做到Orbix 2000和 ORBacus井水不犯河水了。
4.3 编写不需任何修改能同时在Orbix 2000和ORBacus下使用的程序
由于这两个产品均使用POA方式,编写这样的程序是可能的。当然有一个前提,就是它们均使用OMG CORBA标准类库接口(通常是org.omg.*),而不使用专有扩展(通常是com.iona.*或com.ooc.*等)。
这样的程序甚至不需要重新编译,我们可以把由Orbix 2000生成的.class文件直接拿到ORBacus下使用,反之亦然。这是由于使用CORBA标准接口编程,因此无论是Orbix 2000下还是在ORBacus下都能编译通过。而在JAVA中,各.class文件相互独立,只有当运行时使用到其他类库时才会去读取,而ob_java或it_java使用了-classpath 指定分别属于自己的类库,因此尽管编译后的.class文件相同,但运行时却使用了不同的ORB。
要做到这点,无疑在main()函数中不能显式的设置属性,而必须在外部设置。
例如,对于在Orbix 2000下编译好的simpleDemo程序(保证没有在程序中设置ORB属性),可以直接在而ORBacus下使用。
进入到D:/mywork/simpleDemo,用Orbix 2000编译:
it_javac –d classes java_output/simpleDemo/*.java *.java
然后,在ORBacus下运行:
ob_java simpleDemo.gridServer
ob_java simpleDemo.gridClient
程序能正常运行。
4.4 Orbix 2000 ORB和ORBacus ORB互联
由于所有的ORB产品都遵循IIOP协议,同时,Orbix 2000和 ORBacus均实现了标准POA,所以它们之间互联是没有问题的。
假设Server和Client程序位于两台机器上,Server为Orbix2000,Client 为ORBacus,或者反过来。当使用基于文件的方式获得对象引用时,需要将Server生成的.ior或.ref文件拷贝到Client机器上(注意目录位置)。实践证明程序能正常运行。
当然这种方式是没有实际意义的,没有谁会去将文件人工的拷来拷去。所以,当不在同一机器上时,通常使用命名服务的方式实现互联。
但在某种情况下基于文件的方式仍能方便的互联,那就是当客户方使用Java Applet,服务器方使用html文件的时候。
当然,我们可以在同一台机器上进行这一实验。
我们仍然使用simpleDemo程序。用Orbix 2000启动Server:
it_java simpleDemo.gridServer
然后用ORBacus启动Client:
程序仍能正常运行。但客户和服务器使用的是不同的ORB。
附录 A OMG IDL 语法
Symbol Meaning
::= Is defined to be
| Alternatively
<text> Nonterminal
“text” Literal
* The preceding syntactic unit can be repeated zero or more times
+ The preceding syntactic unit can be repeated one or more times
{ } The enclosed syntactic units are grouped as a single syntactic unit
[ ] The enclosed syntactic unit is optional—may occur zero or one time
OMG IDL Grammar
(1) <specification> ::= <definition>+
(2) <definition> ::= <type_dcl> “;”
| <const_dcl> “;”
| <except_dcl> “;”
| <interface> “;”
| <module> “;”
(3) <module> ::= “module” <identifier> “{“ <definition> + “}”
(4) <interface> ::= <interface_dcl>
| <forward_dcl>
(5) <interface_dcl> ::= <interface_header> “{” <interface_body> “}”
(6) <forward_dcl> ::= “interface” <identifier>
(7) <interface_header> ::= “interface” <identifier> [ <inheritance_spec> ]
(8) <interface_body> ::= <export> *
(9) <export> ::= <type_dcl> “;”
| <const_dcl> “;”
| <except_dcl> “;”
| <attr_dcl> “;”
| <op_dcl> “;”
(10) <inheritance_spec> ::= “:” <scoped_name> { “,” <scoped_name> } *
(11) <scoped_name> ::= <identifier>
| “::” <identifier>
| <scoped_name> “::” <identifier>
(12) <const_dcl> ::= “const” <const_type> <identifier> “=”
<const_exp>
(13) <const_type> ::= <integer_type>
| <char_type>
| <wide_char_type>
| <boolean_type>
| <floating_pt_type>
| <string_type>
| <wide_string_type>
| <fixed_pt_const_type>
| <scoped_name>
(14) <const_exp> ::= <or_expr>
(15) <or_expr> ::= <xor_expr>
| <or_expr> “|” <xor_expr>
(16) <xor_expr> ::= <and_expr>
| <xor_expr> “^” <and_expr>
(17) <and_expr> ::= <shift_expr>
| <and_expr> “&” <shift_expr>
(18) <shift_expr> ::= <add_expr>
| <shift_expr> “>>” <add_expr>
| <shift_expr> “<<” <add_expr>
(19) <add_expr> ::= <mult_expr>
| <add_expr> “+” <mult_expr>
| <add_expr> “-” <mult_expr>
(20) <mult_expr> ::= <unary_expr>
| <mult_expr> “*” <unary_expr>
| <mult_expr> “/” <unary_expr>
| <mult_expr> “%” <unary_expr>
(21) <unary_expr> ::= <unary_operator> <primary_expr>
| <primary_expr>
(22) <unary_operator> ::= “-”
| “+”
| “~”
(23) <primary_expr> ::= <scoped_name>
| <literal>
| “(” <const_exp> “)”
(24) <literal> ::= <integer_literal>
| <string_literal>
| <wide_string_literal>
| <character_literal>
| <wide_character_literal>
| <fixed_pt_literal>
| <floating_pt_literal>
| <boolean_literal>
(25) <boolean_literal> ::= “TRUE”
| “FALSE”
(26) <positive_int_const> ::= <const_exp>
(27) <type_dcl> ::= “typedef” <type_declarator>
| <struct_type>
| <union_type>
| <enum_type>
| “native” <simple_declarator>
(28) <type_declarator> ::= <type_spec> <declarators>
(29) <type_spec> ::= <simple_type_spec>
| <constr_type_spec>
(30) <simple_type_spec> ::= <base_type_spec>
| <template_type_spec>
| <scoped_name>
(31) <base_type_spec> ::= <floating_pt_type>
| <integer_type>
| <char_type>
| <wide_char_type>
| <boolean_type>
| <octet_type>
| <any_type>
| <object_type>
(32) <template_type_spec> ::= <sequence_type>
| <string_type>
| <wide_string_type>
| <fixed_pt_type>
(33) <constr_type_spec> ::= <struct_type>
| <union_type>
| <enum_type>
(34) <declarators> ::= <declarator> { “,” <declarator> } *
(35) <declarator> ::= <simple_declarator>
| <complex_declarator>
(36) <simple_declarator> ::= <identifier>
(37) <complex_declarator> ::= <array_declarator>
(38) <floating_pt_type> ::= “float”
| “double”
| “long” “double”
(39) <integer_type> ::= <signed_int>
| <unsigned_int>
(40) <signed_int> ::= <signed_short_int>
| <signed_long_int>
| <signed_longlong_int>
(41) <signed_short_int> ::= “short”
(42) <signed_long_int> ::= “long”
(43) <signed_longlong_int> ::= “long” “long”
(44) <unsigned_int> ::= <unsigned_short_int>
| <unsigned_long_int>
| <unsigned_longlong_int>
(45) <unsigned_short_int> ::= “unsigned” “short”
(46) <unsigned_long_int> ::= “unsigned” “long”
(47) <unsigned_longlong_int> ::= “unsigned” “long” “long”
(48) <char_type> ::= “char”
(49) <wide_char_type> ::= “wchar”
(50) <boolean_type> ::= “boolean”
(51) <octet_type> ::= “octet”
(52) <any_type> ::= “any”
(53) <object_type> ::= “Object”
(54) <struct_type> ::= “struct” <identifier> “{” <member_list> “}”
(55) <member_list> ::= <member>+
(56) <member> ::= <type_spec> <declarators> “;”
(57) <union_type> ::= “union” <identifier> “switch” “(”
<switch_type_spec> “)” “{” <switch_body>
“}”
(58) <switch_type_spec> ::= <integer_type>
| <char_type>
| <boolean_type>
| <enum_type>
| <scoped_name>
(59) <switch_body> ::= <case>+
(60) <case> ::= <case_label>+ <element_spec> “;”
(61) <case_label> ::= “case” <const_exp> “:”
| “default” “:”
(62) <element_spec> ::= <type_spec> <declarator>
(63) <enum_type> ::= “enum” <identifier> “{” <enumerator> { “,”
<enumerator> } * “}”
(64) <enumerator> ::= <identifier>
(65) <sequence_type> ::= “sequence” “<” <simple_type_spec> “,”
<positive_int_const> “>”
| “sequence” “<” <simple_type_spec> “>”
(66) <string_type> ::= “string” “<” <positive_int_const> “>”
| “string”
(67) <wide_string_type> ::= “wstring” “<” <positive_int_const> “>”
| “wstring”
(68) <array_declarator> ::= <identifier> <fixed_array_size>+
(69) <fixed_array_size> ::= “[” <positive_int_const> “]”
(70) <attr_dcl> ::= [ “readonly” ] “attribute”
<param_type_spec> <simple_declarator> {
“,” <simple_declarator> }*
(71) <except_dcl> ::= “exception” <identifier> “{“ <member>* “}”
(72) <op_dcl> ::= [ <op_attribute> ] <op_type_spec> <identi-fier>
<parameter_dcls> [ <raises_expr> ] [
<context_expr> ]
(73) <op_attribute> ::= “oneway”
(74) <op_type_spec> ::= <param_type_spec>
| “void”
(75) <parameter_dcls> ::= “(” <param_dcl> { “,” <param_dcl> } * “)”
| “(” “)”
(76) <param_dcl> ::= <param_attribute> <param_type_spec>
<simple_declarator>
(77) <param_attribute> ::= “in”
| “out”
| “inout”
(78) <raises_expr> ::= “raises” “(” <scoped_name> { “,”
<scoped_name> } * “)”
(79) <context_expr> ::= “context” “(” <string_literal> { “,”
<string_literal> } * “)”
(80) <param_type_spec> ::= <base_type_spec>
| <string_type>
| <wide_string_type>
| <fixed_pt_type>
| <scoped_name>
(81) <fixed_pt_type> ::= “fixed” “<“ <positive_int_const> “,”
<integer_literal> “>”
(82) <fixed_pt_const_type> ::= “fixed”
附录 B orbix 2000 idl 编译器命令用法
idl complier 用法如下:
idl [-flags] [-?] [-Dname[=value]] [-Idir] [-Uname] [-V] [-u] [-v] [-w]
[-jbase] [-R] [-base] [-poa] [-pss_r] [-moa] [-psdl] [-jpoa]
file [file]*
各开关参数含义如下:
Switch | Description |
-Dname[:value]
| Defines the preprocessor's name. |
-E | Runs preprocessor only, prints on |
-Idir
| Includes dir in search path for preprocessor. |
-R[-v]
| Populates the interface repository (IFR). The -v modifier specifies verbose mode. |
-Uname
| Undefines name for preprocessor. |
-V | Prints version information and exits. |
-u | Prints usage message and exits. |
-w | Suppresses warning messages. |
-plugin
[: | Specifies to load the IDL plug-in specified by plugin to generate code that is specific to a language or ART plug-in. You must specify at least one plugin to the idl compiler Use one of these values for plugin:
Each plugin switch can be qualified with one or more colon-delimited modifiers. |
下面是对plugin modifier参数的说明。
Modifiers for -jbase and -jpoa switches | |
Modifier | Description |
-Ppackage
| Use package as the root scope to package all unspecified modules. By default, all Java output is packaged in the IDL module names. |
-Pmodule=package
| Use package as the root scope for the specified module. |
-Odir
| Output all java code to dir. The default is the current directory. |
-Gdsi -Gstream | Output DSI or stream-based code. The default is |
-Mreflect -Mcascade | Specifies the POA dispatch model to use either reflection or cascading |
-J1.1 -J1.2 | Specifies the JDK version. The default is 1.2. |
-VTRUE -VFALSE | Generate native implementation for valuetypes. The default is |
-FTRUE -FFALSE | Generate factory implementation for valuetypes. The default is |
附录 C ORBacus idl 编译器命令用法
jidl [options] idl-files ...
Options:
-h, --help Show this message.
-v, --version Show ORBacus version.
-e, --cpp NAME Use NAME as preprocessor.
-d, --debug Print diagnostic messages.
-DNAME Define NAME as 1.
-DNAME=DEF Define NAME as DEF.
-UNAME Remove any definition for NAME.
-IDIR Put DIR in the include file search path.
-E Run preprocessor only.
--no-skeletons Don't generate skeletons.
--no-comments Don't add comments to generated code.
--locality-constrained Create locality-constrained objects.
--tie Generate tie classes.
--clone Generate clone methods for data types.
--all Generate code for included files.
--impl Generate example implementation classes.
--impl-tie Generate example implementation classes
using tie method.
--package PKG Generate everything relative to the
package PKG.
--prefix-package PRE PKG Generate code for the prefix PRE
relative to the package PKG.
--auto-package Derive package names from prefixes.
--output-dir DIR Write .java files to directory DIR.
--file-list FILE Write a list of generated files to FILE.
--with-interceptor-args Compile with support for argument, result
and exception list values for interceptors.
--no-local-copy Don't copy valuetypes for local invocations.
--case-sensitive Allow case-sensitive identifiers.
(Not CORBA compliant.)