利用CORBA技术实现分布式应用程序


(来源:http://www.ccw.com.cn)

公共对象请求代理规范(CORBA)是在不同平台、不同语言之间实现对象通信的模型,它为分布式应用环境下对象资源共享、代码重用、可移植和对象间相互访问建立了通用标准,同样也为在大量硬件、软件之间实现互操作提供了良好的解决方案。
与传统的DCOM和RIM相比,CORBA体系结构独立于语言、独立于工作平台等方面的特点,使得基于CORBA开发的应用系统在网络实现中具有更大的优势。本文通过利用Java语言编制的客户/服务器程序,简要分析了CORBA的体系结构,希望能对读者理解CORBA有所帮助。
CORBA规范的体系结构
1.对象请求代理(ORB)
对象请求代理(ORB)是CORBA规范的基础,其主要功能是定位服务对象,分析客户对象的请求,获取服务对象的功能接口,在客户与服务对象间建立通信连接。
2.接口定义语言(IDL)
IDL是CORBA的另外一个重要组成部分,用于说明CORBA服务对象完成的功能,但不能够利用IDL实现该功能。IDL是独立于其他编程语言的功能描述性语言,这从另外一个侧面说明了CORBA的语言环境独立性。利用IDL,完成CORBA服务对象方法的说明,然后利用语言映射工具,将用IDL定义的CORBA对象方法说明翻译成高级编程语言的接口说明,最后根据接口说明,利用C++或Java编写对象实现程序。
3. Stub和Skeleton
客户对象的桩(Stub)和服务对象的骨架(Skeleton)是IDL与对象实现语言之间的桥梁。
客户对象的桩是IDL定义的方法接口经IDL编译器编译后,在编写客户对象时,高级编程语言可应用的用于识别服务对象方法的代码。客户对象的桩提供服务对象接口的简要说明。服务对象的骨架是IDL定义的方法接口经IDL编译器编译后,形成的说明服务对象提供方法的框架。对于服务对象接口中的每个方法,必须在服务对象程序中实现。
基于CORBA的分布式应用
分布式应用程序设计的主要问题是确定建立在对象级上的客户与服务对象的关系。从其最根本的功能来讲,服务对象提供远程接口,客户对象调用远程接口,客户对象不需要了解远程对象的位置以及实现细节,也不需要了解哪个ORB 用于对象之间的交互。按照实现过程,CORBA的实现分为两种方式:命名服务对象引用方式和字符串化对象引用方式。
下面介绍基于CORBA技术,用Java语言在网络中建立分布式应用的具体方法。
1.对象功能描述
在服务对象端,模拟股票系统的信息发布,随机生成10支股票的股票名称与价格。在客户端,访问服务器,获得股票名称与价格。其获取方式分为两种:取得全部股票的信息和取得某支股票的价格。
2.系统简要设计
根据对象功能的说明,用UML描述出服务对象需要实现的功能:
StockSymbol :String[]
getStockValue( StockSymbol :symbol ) :float
StockSymbolList getStockSymbols() :StockSymbol[]
3.服务对象接口定义
根据系统分析及设计结果,用IDL编写出服务对象方法描述程序。
/*StockMarket.idl 服务对象方法接口定义文件*/
//模块定义
module StockMarket
{
typedef string StockSymbol;
typedef sequence<StockSymbol> StockSymbolList;
//接口定义
interface StockServer
{
float getStockValue(in StockSymbol symbol); StockSymbolList getStockSymbols();
};
};
4.编译StockMarket.idl
在命令行中键入:
> idltojava -fno-cpp StockMarket.idl
其中idltojava为IDL程序翻译程序,该软件随JDK 1.3开发工具包提供。
如果使用的JDK版本为1.2以下,可以在Sun公司的Java分类主页中下载该软件,网址为:
Http://developer.javasoft.com/developer/earlyAccess/jdk12/idltojava.html
-fno-cpp为idltojava的程序参数,强制编译过程中不使用C语言预处理。
编译StockMarket.idl后,在该文件所在目录中生成名称为StockMarket的子目录。其中StockServer.java文件中包括在StockMarket.idl中定义的服务对象接口StockServer,服务对象必须实现该接口。_StockServerImplBase.java中包括实现服务对象程序时用到的其他类,服务对象在实现时必须继承该类。
5.编写服务对象实现程序
/*StockServerImpl.java 服务对象实现程序*/
// 包定义
package StockMarket;
// 引入应用类库
import java.util.Vector;
import org.omg.CORBA.ORB;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContext;
import org.omg.CosNaming.NamingContextHelper;
public class StockServerImpl extends
_StockServerImplBase implements StockServer
{
//定义股票名称与股票价格存储对象
private Vector myStockSymbols;
private Vector myStockValues;
// 随机生成股票名称需要用到的字母
private static char ourCharacters[] = { ‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’, ‘H’, ‘I’, ‘J’, ‘K’, ‘L’, ‘M’, ‘N’,‘O’, ‘P’, ‘Q’, ‘R’, ‘S’, ‘T’, ‘U’, ‘V’, ‘W’, ‘X’, ‘Y’, ‘Z’};
// 服务对象名称,用于生成ORB
private static String ourPathName = “StockServer”;
// StockServerImpl类的构造函数
public StockServerImpl()
{
myStockSymbols = new Vector();
myStockValues = new Vector();
// 随机生成股票名称及相应价格
for (int i = 0; i < 10; i++)
{
// 生成含有4个空格的字符数组
StringBuffer stockSymbol=new StringBuffer(“ ”);
// 将每个字符替换为随机生成的字符
for (int j = 0; j < 4; j++)
{
stockSymbol.setCharAt(j,ourCharacters[(int)(Math.random()* 26f)]);
}
// 将股票名称存储在myStockSymbols中
myStockSymbols.addElement(stockSymbol.
toString());
//将股票价格存储在myStockValues中
myStockValues.addElement(new Float
(Math.random() * 100f));
}
// 在服务对象的标准输出中打印出股票信息
System.out.println(“Generated stock symbols:”);
for (int i = 0; i < 10; i++)
{
System.out.println(myStockSymbols.elementAt
(i) + “---” +myStockValues.elementAt(i));
}
System.out.println();
}
//用于返回给定股票的价格
public float getStockValue(String symbol)
{
// 在服务对象的股票价格存储对象中匹配给定的股票名称
nt stockIndex = myStockSymbols.indexOf(symbol);
// 如果匹配成功,返回该股票的价格
if (stockIndex != -1)
{
return ((Float)myStockValues.elementAt(stockIndex)).floatValue();
}
else // 在服务对象中未发现给定的股票名称
{
return 0f;
}
}
// 返回服务对象中全部股票名称
public String[] getStockSymbols()
{
String[]symbols = new String
[myStockSymbols.size()];
myStockSymbols.copyInto(symbols);
return symbols;
}
// main方法定义
public static void main(String args[])
{
try
{
// 初始化ORB
ORB orb = ORB.init(args, null);
// 生成StockServerImpl实例
StockServerImpl stockServer = new StockServerImpl();
//将orb与StockServer实例进行连接
orb.connect(stockServer);
//在CORBA中获取命名服务
org.omg.CORBA.Object obj =
orb.resolve_initial_references(“NameService”);
//缩小命名服务对象寻找范围
NamingContext namingContext =
NamingContextHelper.narrow(obj);
// 将StockServer对象与命名服务进行绑定
NameComponent nameComponent = new
NameComponent(ourPathName,“”);
NameComponent path[] = {
nameComponent };
namingContext.rebind(path,stockServer);
// 生成任意对象,等待客户对象调用服务对象方法
java.lang.Object waitOnMe = new
java.lang.Object();
// 方法访问同步
synchronized (waitOnMe)
{
waitOnMe.wait();
}
}
catch (Exception ex)
// 捕获例外
{
System.err.println(“Couldn’t bind StockServer: ” + ex.getMessage());
}
}
}
6.编写客户对象实现程序
/*StockMarketClient.java客户对象实现程序*/
//包定义
package StockMarket;
//引入应用的类库
import org.omg.CORBA.ORB;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContext;
import org.omg.CosNaming.NamingContextHelper;
// 客户对象
public class StockMarketClient
{
// 定义客户ORB
public static ORB ourORB;
// 服务对象StockServer实例定义
private StockServer myStockServer;
// 客户对象构造方法
StockMarketClient() { }
// 运行客户对象
public void run()
{
connect();
if (myStockServer != null)
{
doSomething();
}
}
// 建立与服务对象StockServer连接的方法
protected void connect()
{
try
{
// 获得命名服务
org.omg.CORBA.Object obj =
ourORB.resolve_initial_references(“NameService”);
NamingContext namingContext =
NamingContextHelper.narrow(obj);
// 定位服务对象
NameComponent nameComponent = new NameComponent(“StockServer”,“”);
NameComponent path[] = { nameComponent };
myStockServer =StockServerHelper.narrow(namingContext.resolve(path));
}
// 捕获例外
catch (Exception ex)
{
System.err.println(“Couldn’t resolve
StockServer: ” + ex);
myStockServer = null;
return;
}
System.out.println(“Succesfully bound to a StockServer.”);
}
// 调用服务对象方法
protected void doSomething()
{
try
{
// 获取服务对象中全部股票名称和价格
String[]stockSymbols=myStockServer.
getStockSymbols();
// 在客户端打印出股票名称和价格
for (int i = 0; i < stockSymbols.
length; i++)
{
System.out.println(stockSymbols[i]
+ “” + myStockServer.getStockValue(stockSymbols
[i]));
}
}
// 捕获例外
catch (org.omg.CORBA.SystemException ex)
{
System.err.println(“Fatal error: ” + ex);
}
}
// 客户对象main方法定义
public static void main(String args[])
{
// 初始化ORB
ourORB = ORB.init(args, null);
// 生成客户对象实例
StockMarketClient stockClient = new StockMarketClient();
// 执行客户对象的run()方法
stockClient.run();
}
}
7.编译客户和服务对象实现程序
在命令行中键入如下命令编译服务对象实现程序,注意TockServerImpl.java和StockServerImpl.java存储在StockMarket目录中。在Unix平台中,用“/”代替“\”:
> javac StockMarket\StockServerImpl.java
在命令行中键入如下命令编译客户对象实现程序:
> java StockMarket\StockServerImpl.java
8.启动CORBA对象命名服务
在服务对象的计算机中键入如下命令启动对象命名服务:
> tnameserv -ORBInitialPort 3388
9.运行服务程序
如果上述程序编译没有问题,就可以在网络中的任意计算机上运行服务对象。程序运行时需要给定该计算机在网络中的标识和进行网络通信的端口号。本文中服务对象运行的计算机名称为XQ4,在给定端口号时,由于Unix平台小于1024的端口是系统保留的,因此给定的端口号必须大于1024。在命令行中键入如下命令:
> java StockMarket.StockServerImpl -ORBInitialHost XQ9 -ORBInitialPort 3388
由于是应用随机方法产生的股票名称和价格,因此程序每次运行的输出结果会不同。
10.运行客户程序
将编译好的客户程序发布到网络中安装了JDK的任意计算机中,在命令行中键入如下命令,注意在命令行中指定对象访问的计算机的名称和端口:
> java StockMarket.StockMarketClient -
ORBInitialHost XQ 9 -ORBInitialPort 3388
可以看到产生与服务对象程序相同的输出。
小 结
基于CORBA规范,用户可以透明地访问信息,不需要知道信息存在于什么软件中、使用什么硬件平台以及位于企业网络的什么地方。作为面向对象系统的通信核心,CORBA为网络计算带来了真正的互操作性。另外,软件开发人员开发基于CORBA的分布式应用程序不需要了解太多有关网络协议的技术细节,使得软件开发周期大大缩短,同时也加大了代码可重用性。
上述程序应用JDK 1.2开发工具,分别在Windows 98、Solaris 2.5应用环境中调试、编译和运行通过。