CORBA了解
CORBA(Common Object Request Broker Architecture, 公共对象请求代理体系结构)是由OMG(对象管理组织,Object Management Group)提出的应用软件体系结构和对象技术规范,其核心是一套标准的语言、接口和协议,以支持异构分布应用程序间的互操作性及独立于平台和编程语言的对象重用。
CORBA经过近十年的发展,已逐步走向成熟,并成功地应用在我国许多大型的软件系统中,由此产生了对掌握CORBA技术的软件开发人员的大量需求。在此,我们应广大读者的要求组织了本次讲座。 本系列讲座分别介绍了CORBA的基本思想、体系结构以及CORBA应用程序的设计与开发,希望借此能帮助广大软件开发、设计人员,开阔思路,加深对CORBA的理解,进而真正掌握这门技术,并能在实际工作中加以灵活运用,更高效、迅速地开发出更强壮的软件系统,最终促进我国软件事业的蓬勃发展。
CORBA产生的背景
近年来,随着互联网技术的日益成熟,公众及商业企业正享受着高速、低价网络信息传输所带来的高品质数字生活。但是,由于网络规模的不断扩大以及计算机软硬件技术水平的飞速提高,给传统的应用软件系统的实现方式带来了巨大挑战。
首先,在企业级应用中,硬件系统集成商基于性能、价格、服务等方面的考虑,通常在同一系统中集成来自不同厂商的硬件设备、操作系统、数据库平台和网络协议等,由此带来的异构性给应用软件的互操作性、兼容性以及平滑升级能力带来了严重问题。
另外,随着基于网络的业务不断增多,传统的客户/服务器(C/S)模式的分布式应用方式越来越显示出在运行效率、系统网络安全性和系统升级能力等方面的局限性。
为了解决分布式计算环境(DCE,Distributed Computing Environment)中不同硬件设备和软件系统的互联,增强网络间软件的互操作性,解决传统分布式计算模式中的不足等问题,对象管理组织(OMG)提出了公共对象请求代理体系结构(CORBA),以增强软件系统间的互操作能力,使构造灵活的分布式应用系统成为可能。
正是基于面向对象技术的发展和成熟、客户/服务器软件系统模式的普遍应用以及集成已有系统等方面的需求,推动了CORBA技术的成熟与发展。作为面向对象系统的对象通信的核心,CORBA为当今网络计算环境带来了真正意义上的互联。
CORBA的发展历程
1. 对象管理组织(OMG)简介
OMG成立于1989年,作为一个非营利性组织,集中致力于开发在技术上具有先进性、在商业上具有可行性并且独立于厂商的软件互联规范,推广面向对象模型技术,增强软件的可移植性(Portability)、可重用性(Reusability)和互操作性(Interoperability)。
该组织成立之初,成员包括Unisys、Sun、Cannon、Hewlett-Packard、Philips等在业界享有声誉的软硬件厂商,目前该组织拥有800多家成员。
2. CORBA主要版本的发展历程
● 1990年11月,OMG发表《对象管理体系指南》,初步阐明了CORBA的思想;
● 1991年10月,OMG推出1.0版,其中定义了接口定义语言(IDL)、对象管理模型以及基于动态请求的API和接口仓库等内容;
● 1991年12月,OMG推出了CORBA 1.1版,在澄清了1.0版中存在的二义性的基础上,引入了对象适配器的概念;
● 1996年8月,OMG基于以前的升级版本,完成了2.0版的开发,该版本中重要的内容是对象请求代理间协议(IIOP,Internet Inter-ORB Protocol)的引入,用以实现不同厂商的ORB真正意义上的互通;
● 1998年9月,OMG发表了CORBA 2.3版,增加了支持CORBA对象的异步实时传输、服务质量规范等内容。目前,宣布支持CORBA 2.3规范的中间件厂商包括Inprise(Borland)、Iona、BEA System等著名的CORBA产品生产商。
CORBA体系结构概述
CORBA规范充分利用了现今软件技术发展的最新成果,在基于网络的分布式应用环境下实现应用软件的集成,使得面向对象的软件在分布、异构环境下实现可重用、可移植和互操作。其特点可以总结为如下几个方面:
1. 引入中间件(MiddleWare)作为事务代理,完成客户机(Client)向服务对象方(Server)提出的业务请求(引入中间件概念后分布计算模式如图1所示);
图1 引入中间件后客户机与服务器之间的关系
2. 实现客户与服务对象的完全分开,客户不需要了解服务对象的实现过程以及具体位置(参见图2所示的CORBA系统体系结构图);
3. 提供软总线机制,使得在任何环境下、采用任何语言开发的软件只要符合接口规范的定义,均能够集成到分布式系统中;
4. CORBA规范软件系统采用面向对象的软件实现方法开发应用系统,实现对象内部细节的完整封装,保留对象方法的对外接口定义。
在以上特点中,最突出的是中间件的引入, 在CORBA系统中称为对象请求代理(ORB,Object Request Broker)和采用面向对象的开发模式。
对象模型是应用开发人员对客观事物属性和功能的具体抽象。由于CORBA使用了对象模型,将CORBA系统中所有的应用看成是对象及相关操作的集合,因此通过对象请求代理(ORB),使CORBA系统中分布在网络中应用对象的获取只取决于网络的畅通性和服务对象特征获取的准确程度,而与对象的位置以及对象所处的设备环境无关。
图2 CORBA系统体系结构图
CORBA的主要应用方向及中间件产品介绍
CORBA规范的推出,重新调整了客户机与服务器之间的关系。客户机可以向服务器提出事务请求,同时也可以为下一个请求充当服务器角色。
由于CORBA系统引入了中间件的概念,即事务代理,由中间件完成客户机与服务器之间的通信,使得服务器对于客户机的位置相对透明,取消了原有分布式计算模型中客户机、服务器之间的一一对应关系。CORBA客户机可以在运行时动态获得服务对象的位置,并且可以对多个服务对象提交事务请求,因此,极大推动了分布计算的发展。
分布计算是指网络中两个或两个以上的软件相互共享信息资源。这些软件可以位于同一台计算机中,也可以部署在网络节点的任意位置。基于分布式模型的软件系统具有均衡运行系统负载、共享网络资源的技术优势。
另外,CORBA规范约束采用面向对象的分布式软件的构造方法,以接口定义语言的形式实现对象内部细节的完整封装,从而降低了软件系统的复杂程度,增加了软件功能的可重用性。CORBA提供到C/C++、Java、SmallTalk等高级语言的映射,很大程度地减小了对程序设计语言的依赖性,使软件开发人员可以在较大范围内共享已有成果。
正是以上特点推动了分布式多层软件体系结构的发展。目前,CORBA技术在银行、电信、保险、电力和电子商务领域都有广泛的应用。
软件市场中能够见到的CORBA中间件产品很多,但基于不同公司的产品战略以及研发方向,各个产品在服务性能、对高级语言的支持和所依赖的系统平台方面有很大区别。根据整理的资料,笔者对主要产品在上述几方面进行了分析(参见表1),供读者在选择时参考。 用JAVA开发CORBA应用实例
通用对象代理体系结构CORBA(Common Object Request Broker Architecture)是对象管理组织所定义的用来实现现今大量硬件、软件之间互操作的解决方案,CORBA也是迈向面向对象标准化和互操作的重要一步。
■ CORBA技术简介
简单地说,CORBA允许应用之间相互通信,而不管它们存在于哪里以及是谁设计的。CORBA1.1于1991年由OMG发布,其中定义了接口定义语言(IDL)以及在对象请求代理(ORB)中实现客户对象与服务器对象之间交互的应用编程接口(API)。CORBA2.0于1994年发布,规定了各个供应商之间的ORB的通信规则。
CORBA标准主要分为三个部分:接口定义语言(IDL)、对象请求代理(ORB)以及ORB之间的互操作协议IIOP。
ORB是对象之间建立Client/Server关系的中间件。使用ORB,客户可以透明地调用一个服务对象上的方法,这个服务对象可以在本地,也可以在通过网络连接的其他机器上。ORB截获这一调用同时负责查找实现服务的对象并向其传递参数、调用方法返回最终结果。客户并不知道服务对象位于什么地方,它的编程语言和操作系统是什么,也不知道不属于对象接口的其他系统部分。这样,ORB在异构分布环境下为不同机器上的应用提供了互操作性,并无缝地集成了多种对象系统。
在开发传统的Client/Server应用时,开发者使用他们自己设计的或一个公认的标准来定义用于设备之间通信的协议。协议的定义依赖于实现语言、网络传输和许多其他因素,而ORB的出现简化了这一过程。使用ORB时,协议是使用接口定义语言(IDL)定义的,而IDL是独立于语言的。并且ORB提供很强的灵活性,它使程序员选择最适合的操作系统、执行环境,甚至系统各个组件也可以采用不同的编程语言实现。更重要的是,它允许现有组件的集成。在一个基于ORB的解决方案中,开发者可以使用与创建新对象一样的IDL对遗留系统进行建模,他们创建"包装"代码以在标准化的软件总线与遗留系统接口之间传递信息。
使用CORBA,用户可以透明地访问信息,并不需要知道信息存在于什么软件中、使用什么硬件平台,以及位于企业网络的什么地方。作为面向对象系统的通信核心,CORBA为今天的计算环境带来了真正的互操作性。
■ CORBA与JAVA的相互关系
CORBA不只意味着对象请求代理(ORB),它还是非常全面的分布式对象平台。CORBA使JAVA应用可以跨越网络、语言以及操作系统,并为JAVA提供了一组分布服务,如分布式自我观察、动态发现、事务、关系、安全和命名等。
JAVA不仅是一种语言,它还是一个动态代码系统,它对运行对象来说是一个可移植的虚拟机(JVM)。JAVA为开发、管理、发布Client/Server应用提供了更简单的方式。人们可以通过将应用放在一个Web服务器上将这一应用发布给成千上万个用户,而不必关心它的安装和升级。JAVA还非常适合服务器的开发,它可以动态地将服务代码移向最需要它们的地方。
JAVA将会使CORBA对象能够运行在从主机、网络计算机到蜂窝电话等可编程的各种机器上,并简化了大型CORBA系统的代码发布。对客户和服务对象来说JAVA是很理想的编程语言,JAVA内置的多线程、垃圾收集和错误处理使编写健壮的网络对象变得很容易。
这两种对象模型可以很好地相互补充,CORBA处理网络的透明性,JAVA处理实现的透明性,CORBA为JAVA可移植应用环境提供了一个分布式的结构。
■ 使用JAVA开发CORBA应用
下面让我简要介绍一下开发CORBA的步骤。
使用JAVA开发CORBA应用需要如下五个步骤:
1. 使用IDL创建接口 (About.idl)
2. 下面的OMG IDL描述一个CORBA对象。
module About
{
interface Show
{
string ShowName();
};
};
将其存为Show.idl。
3. 编译接口并生成CORBA支持文件
我们用以下命令编译这个 IDL 接口:
idltojava Show.idl
idltojava是SUN公司的IDL编译器,可以免费从SUN公司站点上下载。
因为idltojava在编译IDL文件之前,需要进行预编译,而如果你的机器上没有预编译器,可以使用以下命令:
idltojava -fno-cpp Show.idl
编译后将在当前目录下生成About子目录,其中会包括一些支持文件,如有兴趣可以看一下,但一定不要修改。
4. 实现服务器 (ShowServer.java)
ShowServer的main() 方法,可完成以下任务:
(1) 创建一个 ORB 实例
(2) 创建一个服务对象实例(CORBA About 对象的实现)并通知 ORB
(3)获取一个命名上下文的CORBA对象引用,在该命名上下文中注册新的CORBA对象
(4)在命名上下文中将新对象注册在"About"名下
(5)等待对新对象的调用
实现服务器源程序如下:
import About.;
import org.omg.CosNaming.;
import org.omg.CosNaming.NamingContextPackage.;
import org.omg.CORBA.;
class ShowObject extends _ShowImplBase
{
public String ShowName()
{
return "/nMy name is Seymour!!/n";
}
}
public class ShowServer {
public static void main(String args[])
{
try{
// 创建和初始化 ORB
ORB orb = ORB.init(args, null);
// 创建服务对象并将其向 ORB 注册
ShowObject ShowRef = new ShowObject();
orb.connect(ShowRef);
// 获取根命名上下文
org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
NamingContext ncRef = NamingContextHelper.narrow(objRef);
// 绑定命名中的对象引用
NameComponent nc = new NameComponent("About", "");
NameComponent path[] = {nc};
ncRef.rebind(path, ShowRef);
// 等待来自客户机的调用
java.lang.Object sync = new java.lang.Object();
synchronized (sync) {
sync.wait();
}
} catch (Exception e) {
System.err.println("ERROR: " + e);
e.printStackTrace(System.out);
}
}
}
4.实现客户机 (ShowClient.java)
下面的应用程序客户机将完成以下任务:
(1)创建一个ORB;
(2)获取一个指向命名上下文的引用;
(3)在命名上下文中查找"Show"并获得指向该 CORBA 对象的引用;
(4)调用对象的 ShowName() 操作并打印结果。
import About.;
import org.omg.CosNaming.;
import org.omg.CORBA.;
public class ShowClient
{
public static void main(String args[])
{
try{
// 创建和初始化 ORB
ORB orb = ORB.init(args, null);
// 获取根命名上下文
org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
NamingContext ncRef = NamingContextHelper.narrow(objRef);
//解析命名中的对象引用
NameComponent nc = new NameComponent("About", "");
NameComponent path[] = {nc};
About.Show ShowRef = ShowHelper.narrow(ncRef.resolve(path));
// 调用 Show 服务对象并打印结果
String show = ShowRef.ShowName();
System.out.println(show);
} catch (Exception e) {
System.out.println("ERROR : " + e) ;
e.printStackTrace(System.out);
}
}
}
5.构建和运行ShowName程序
(1)编译 .java 文件,包括 stub 和 skeleton(在About目录中):
javac .java About/.java
(2)启动一个MS-DOS命令解释器,输入以下命令,确保名字服务器处于运行状态:
tnameserv -ORBInitialPort 1050
(3)启动另一个MS-Dos命令解释器,输入以下命令,启动Show服务器:
java ShowServer -ORBInitialPort 1050
(4)再启动一个MS-Dos命令解释器Show应用程序客户机:
java ShowClient -ORBInitialPort 1050
这时屏幕上会出现"My name is Seymour!"的字样,说明实验已经成功了。
EAI技术分析
作者:沙晋(savorjava@yahoo.com.cn)
关键词:EAI,EIS,CORBA,J2EE,JCA
摘要:随着社会信息化进程的进一步加快,以及信息化技术的不断进步,很多公司开始发现在引进新的应用和系统的同时,如何保证公司旧有的应用和系统投资不至于被全部抛弃或替换是节省公司运作成本并有效利用公司资源的重要手段。但由于旧有应用和系统所采用的体系结构与新的系统存在及大的差异,往往使这些应用集成到新的系统中并不容易,本文就是针对在这种企业应用集成需求下的各种可用技术进行分析及建议。
阻碍企业将新旧应用系统集成在一起的问题似乎显而易见,不外乎由以下两点所组成:
•所采用的体系结构不同;
•所使用的技术不同;
但要完全跨越这两条企业应用集成的鸿沟却是存在巨大困难的。为了更好地解决这些问题,业界已经出现了许多相关技术及方案,如CORBA,J2EE,XML,DCOM等等。本文不是为了向读者具体介绍这些技术及方案的实施,本文的目的根据在企业应用集成中可能出现的各种情况,分析不同技术的优缺点,并给出相应可行的建议。
1.介绍EAI
EAI,也就是企业应用集成并不是一个新的概念。但步入九十年代后,EAI的重要性开始得以体现并倍受关注。原因很简单,企业需要不断改进他们应用系统的功能,作为企业利益最大化的工具,企业的管理者希望他们对其所作的投资能够得到回报。但显然的,企业的管理者们渐渐开始意识到,如引进新的应用系统不能与旧有应用系统很好的集成在一起工作,将导致过去投资被浪费,旧有的应用系统功能部分或全部被抛弃。这显然是企业的管理者们所不愿看到的,于是在纷纷采用新的体系结构进行应用系统开发同时,如何将旧有系统有效的集成进来开始正式走上各个公司的研究桌面。
在本文中,我们将企业的应用系统称为企业信息系统EIS。EAI的最终目的就是要将企业的各种EIS集成到一起,这一过程应尽可能不对已有的应用程序做出(过多)的修改,并实现数据共享和业务流程的集成。
当然,企业需要在EAI之前进行策划,以确定实施EAI在时间及成本方面的确优于完全引进新的应用系统。因为失败的EAI过程将会为企业带来更大的损失,集成风险的比重应该受到足够的关注。
后面,文中将给出几种不同集成技术的分析,指出应当采用的适当技术。但应该注意的是,集成技术还在不断的发展,所给出的建议未必是最优或在将来仍为最优,这也与笔者的经验有关,我们必须承认集成工作需要太多的知识,也相当复杂,特别是在所需集成的EIS数量较大且体系结构互异时,集成难度更是直线上升。因此,如何运用和组合文中所给出的集成技术及建议是需要读者好好考虑的,不要把它们当成模式,它们只是一些可选且未必最优的方案,也许EAI永远没有固定的模式。
有一点需要说明的是,文中对于使用不同语言的异构系统以Java和C++为例,相信它们能够代表目前的流行案例,便于读者理解及运用。
还有一点需要强调的是,本文中所涉及的集成案例都是以应用与业务集成为主,关于数据集成以太表示层集成不在本文的讨论范围之列。
2.技术解析
问题会使人思考,EAI的需求则驱使相关技术飞速发展,尽管这些技术还没有使EAI易如反掌,但它们的确使EAI的成功有了更大的保障。在本文中,我们将多多少少的涉及以下的一些技术规范。这里我们不是要详细描述每一种技术规范,因为其中的每一项都足以用几本书来讲解,而且这些书都已经存在了,这应该算是一个好消息。我们所需要做的,只是了解并分析它们的优缺点及适用性。
2.1 通用对象请求代理结构CORBA
说到EAI就很难让人不联想到CORBA。毕竟,让不同编程语言协同工作的主要方法之一就是利用CORBA。作为一个分布式对象的体系结构,CORBA的最初目的就是能够使不同的编程语言、操作系统和软件平台之间实现协同工作。而且,发展到今天,CORBA2已经完全基于面向对象技术,CORBA3则是朝着基于组件的方向发展,其开放性使在不同的CORBA实现商之间进行沟通成为可能,部分甚至可以达到100%的源代码兼容。
•优点:
•以一种中间件的方式为不同编程语言提供协同工作的可能;
•对操作系统没有特殊的要求和依赖,仅取决于实现商,但实现商可以选择;
•有效长且成熟的发展历史,与许多流行的应用系统(如J2EE)在体系结构上关系密切。
•缺点:
•具体的性能与所选实现商的实现有关,且性能再好,中间件的一些服务始终都是瓶颈;
•一般情况下需要修改源代码来实现对旧有应用软件的包装;
•适用:
当需要集成的两个企业应用软件互为异构,由不同的编程语言实现时,Java与C++就是一个很好的例子。要这两种语言进行协同工作的几乎惟一的方法就是利用CORBA。当然,使用JDK所提供的功能特性JNI也是可能的,但其复杂性以及对Java可移植性的破坏使其不能胜任该集成工作。且JNI不具备分布实施的能力,它的目标也不在于此。
CORBA很适合于通过修改源代码来包装现有应用软件,为其他异构系统提供新的CORBA分布式对象。对于远程方式的请求,IIOP协议会是一个好的选择,例如通过J2EE的RMI-IIOP来调用CORBA的分布式对象。
2.2 Java2平台企业版J2EE
在近几年的企业应用系统开发中,J2EE无疑扮演了一个重要的脚色。开发业务逻辑或中间层组件的最重要的技术就是EJB,它提供了对主要的企业技术如事务、安全性以及持续性的支持,便利了业务组件的开发。尽管EJB受限于Java编程语言,但这种技术本身并不存在问题。同时,J2EE与CORBA技术所达成的一致性为低层组件的请求提供了可行之路,RMI-IIOP和JMS等技术无疑为J2EE提供了强有力的功能核心。
•优点:
•基于规范的平台,不受限于特定的操作系统或硬件平台,有大量实现商可以选择;
•提供现代的组件体系结构,这种结构简化了复杂组件的开发;
•提供主要的企业技术如事务、安全性以及持续性的支持,并以声明和编辑方式对这些服务提供支持。
•相对成熟,支持大量中间件技术,能够为EAI提供满意的性能及可升级性。
•缺点:
•受限于Java编程语言,尽管可通过其他中间件技术(如CORBA)支持;
•实现商之间的可移值性还达不到100%;
•与特定于某个操作系统或平台的实现技术相比,性能还有待进一步提高,且资源占用量较大。
•适用:
J2EE规范本身就提供了一个巨大的企业应用集成平台,基于Java使其不依赖于运行的硬件平台和操作系统,然而也使其受限于单一语言开发。但这一开发平台,目前已经有不同的厂商提供了符合规范说明的各种实现方法。J2EE支持大量中间件技术,和现有的系统能够协同工作。HTTP,RMI-IIOP,JMS,JDBC,JCA以及对XML,企业事务,企业安全方面的支持使其成为目前几种企业应用集成平台中的首选。
2.3 XML和XSLT
XML除了大量应用在因特网技术及文档描述中外,在数据交换中也承担了一个重要的角色。作为一个独立的平台,只需用标准文本,XML能够被所有程序语言读写,一旦使用了DTD或Schema,XML的解释程序就能对文件内容进行验证并处理。XSLT同样是基于XML技术的,但它的作用是重新格式化并传输XML数据文件,从而得到一个全新指定格式的XML文档,相信读者已经可以想象这些技术在不同应用系统中进行数据交换时发生的巨大作用了。
•优点:
•内容由标准文本组成,任何平台和程序语言都可以使用;
•各种程序语言的解释程序可以根据DTD或Schema对文件内容进行验证并处理;
•格式的转换基本不受限制,可以满足不同应用系统的需求。
•缺点:
•DTD在过去被大量的使用,但DTD本身不是XML,而是基于正则表达式的;
•当XML内容较大时,解释程序的执行效率会是一个问题;
•适用:
当不同的应用系统使用着各自的数据格式,或符合复杂的行业标准,而现在需要在各个应用系统之间交换数据,那么XML和XSLT提供了一个可行的手段。当然,XML并不能解决所有的数据交换问题,如何将各种不同的原始数据格式以XML文档来记录就是一件棘手的问题。但好的一面是各种平台及编程语言目前都已经很好的支持了XML及XSLT,一旦XML准备就绪,XSLT就准备将其转换成其他应用系统需要的数据格式。
2.4 分布式组件对象模型DCOM
DCOM扩充了在网络中通过COM支持的对象,并允许COM应用软件分布在局域网中的多个计算机上。DCOM通过网络协议定义过程中的通信。在运行时,COM为客户程序和使用RPC的组件提供服务,而且遵循DCOM协议标准。
•优点:
•在Windows平台上提供基于COM体系结构的分布式处理;
•在Windows平台上使用能够达到较为满意的性能要求。
•缺点:
•在跨平台使用中存在困难,且性能无法得到保障。
•适用:
在Windows平台上进行集成实施的首选,但与其他平台及编程语言的协同工作需要借助于第三方厂商的支持。
2.5 消息中间件MOM
企业消息传递使得应用程序能够跨多平台进行可靠的传输。通过使用可靠的消息队列,提供支持消息传递所需的目录、安全和管理服务,MOM确保验证过的应用之间消息传送的安全,它通常提供同步和异步的传输模式。在企业内部保证可靠的传输最通用的方法就是使用消息传递系统。CORBA和J2EE目前就支持MOM的工业标准接口。
•优点:
•为不同的企业应用系统提供了跨多平台的消息传输;
•除支持同步传输模式外,还支持异步传输,有助于在应用间可靠地进行消息传输。
•缺点:
•与其他中间件技术一样,高流量的性能瓶颈问题正在改善;
•适用:
如果要在多个平台上的应用程序之间保证可靠的传输,且这些应用程序并不在同一时间运行时,应用之间的RPC直接通信或传输数据将不能胜任,而消息中间件MOM会是一个好的选择。即使当请求建立时,接收方应用程序没有运行,这个请求也不会丢失,这就是异步传输的优势。
2.6 J2EE连接器体系结构JCA
JCA是在J2EE1.3的版本规范中提出的,由EIS厂家来执行和提供。JCA的资源适配器是规范化的EIS代理,可插入到任务符合J2EE规范的应用服务器中,并通过应用服务器提供的标准EIS访问接口CCI来对EIS执行操作。JCA向基于EAI的应用程序开发者提供了通过一个将EIS整合进入J2EE的标准方法。此方法定义了一套开发者能在J2EE环境中使用的通用API和服务。
•优点:
•JCA不仅能在数据上将EIS系统集成到J2EE应用中,它还能够将安全与事务等管理涉入到符合条件的EIS系统中。
•JCA的出现使得将遗留的EIS系统集成到J2EE应用中的操作复杂度由NxM减小为N+M。
•JCA由于基于Java技术,在多平台的移植过程中所遇到的阻力较小。
•缺点:
•JCA是一种紧耦合的问题解决方案,它的实现需要涉及所希望集成的遗留EIS系统的API,并且对这些操作进行封装。
•JCA是基于Java技术的,尽管不需要所被集成的EIS系统也是Java实现,但通过JCA去使用该EIS的客户应用却必须是Java实现。
•JCA的实现并不容易,如果实现联接管理部分是JCA所必须的,则一旦加入了事务及安全管理则复杂度将急剧上升,如果是以CCI来实现则遇到的问题可能会更多。
•适用:
JCA所提供的好处基本上是面向J2EE应用服务器及EIS系统供应商的,因为他们的产品往往是符合各种标准与规范的,JCA所给出的统一规范对于他们来说无疑是降低风险并减少开发成本的好武器。但对于一般的企业自产的应用系统而言,JCA就未必能够发挥太大的作用,相反,它有可能成为开发过程中的瓶颈。原因有几点,第一,JCA即资源适配器的开发并不是所有的开发者都能够胜任,它的开发模式与编写普通代码不同,JCA将设计模式中的Factory等模式发挥得淋漓尽致。第二,JCA作为一个统一的规范,它的实现也需要很多的标准与规范来支持,如XA分布事务等。而一个企业的自产应用系统往往并不具有这些标准与规范,所实现的资源适配器并不能享受JCA所提供的众多优点。第三,企业自行开发的资源适配器最终还是要插入到各种J2EE应用服务器中去,但作为第三方的开发者,不了解所使用的J2EE应用服务器的相关特征,甚至应用服务器中存在的缺陷,尽管双方都遵循JCA规范,但实现的不同使得第三方所开发的资源适配器未必能正常发布或应用。
EAI技术分析
作者:沙晋(savorjava@yahoo.com.cn)
关键词:EAI,EIS,CORBA,J2EE,JCA
摘要:随着社会信息化进程的进一步加快,以及信息化技术的不断进步,很多公司开始发现在引进新的应用和系统的同时,如何保证公司旧有的应用和系统投资不至于被全部抛弃或替换是节省公司运作成本并有效利用公司资源的重要手段。但由于旧有应用和系统所采用的体系结构与新的系统存在及大的差异,往往使这些应用集成到新的系统中并不容易,本文就是针对在这种企业应用集成需求下的各种可用技术进行分析及建议。
阻碍企业将新旧应用系统集成在一起的问题似乎显而易见,不外乎由以下两点所组成:
•所采用的体系结构不同;
•所使用的技术不同;
但要完全跨越这两条企业应用集成的鸿沟却是存在巨大困难的。为了更好地解决这些问题,业界已经出现了许多相关技术及方案,如CORBA,J2EE,XML,DCOM等等。本文不是为了向读者具体介绍这些技术及方案的实施,本文的目的根据在企业应用集成中可能出现的各种情况,分析不同技术的优缺点,并给出相应可行的建议。
1.介绍EAI
EAI,也就是企业应用集成并不是一个新的概念。但步入九十年代后,EAI的重要性开始得以体现并倍受关注。原因很简单,企业需要不断改进他们应用系统的功能,作为企业利益最大化的工具,企业的管理者希望他们对其所作的投资能够得到回报。但显然的,企业的管理者们渐渐开始意识到,如引进新的应用系统不能与旧有应用系统很好的集成在一起工作,将导致过去投资被浪费,旧有的应用系统功能部分或全部被抛弃。这显然是企业的管理者们所不愿看到的,于是在纷纷采用新的体系结构进行应用系统开发同时,如何将旧有系统有效的集成进来开始正式走上各个公司的研究桌面。
在本文中,我们将企业的应用系统称为企业信息系统EIS。EAI的最终目的就是要将企业的各种EIS集成到一起,这一过程应尽可能不对已有的应用程序做出(过多)的修改,并实现数据共享和业务流程的集成。
当然,企业需要在EAI之前进行策划,以确定实施EAI在时间及成本方面的确优于完全引进新的应用系统。因为失败的EAI过程将会为企业带来更大的损失,集成风险的比重应该受到足够的关注。
后面,文中将给出几种不同集成技术的分析,指出应当采用的适当技术。但应该注意的是,集成技术还在不断的发展,所给出的建议未必是最优或在将来仍为最优,这也与笔者的经验有关,我们必须承认集成工作需要太多的知识,也相当复杂,特别是在所需集成的EIS数量较大且体系结构互异时,集成难度更是直线上升。因此,如何运用和组合文中所给出的集成技术及建议是需要读者好好考虑的,不要把它们当成模式,它们只是一些可选且未必最优的方案,也许EAI永远没有固定的模式。
有一点需要说明的是,文中对于使用不同语言的异构系统以Java和C++为例,相信它们能够代表目前的流行案例,便于读者理解及运用。
还有一点需要强调的是,本文中所涉及的集成案例都是以应用与业务集成为主,关于数据集成以太表示层集成不在本文的讨论范围之列。
2.技术解析
问题会使人思考,EAI的需求则驱使相关技术飞速发展,尽管这些技术还没有使EAI易如反掌,但它们的确使EAI的成功有了更大的保障。在本文中,我们将多多少少的涉及以下的一些技术规范。这里我们不是要详细描述每一种技术规范,因为其中的每一项都足以用几本书来讲解,而且这些书都已经存在了,这应该算是一个好消息。我们所需要做的,只是了解并分析它们的优缺点及适用性。
2.1 通用对象请求代理结构CORBA
说到EAI就很难让人不联想到CORBA。毕竟,让不同编程语言协同工作的主要方法之一就是利用CORBA。作为一个分布式对象的体系结构,CORBA的最初目的就是能够使不同的编程语言、操作系统和软件平台之间实现协同工作。而且,发展到今天,CORBA2已经完全基于面向对象技术,CORBA3则是朝着基于组件的方向发展,其开放性使在不同的CORBA实现商之间进行沟通成为可能,部分甚至可以达到100%的源代码兼容。
•优点:
•以一种中间件的方式为不同编程语言提供协同工作的可能;
•对操作系统没有特殊的要求和依赖,仅取决于实现商,但实现商可以选择;
•有效长且成熟的发展历史,与许多流行的应用系统(如J2EE)在体系结构上关系密切。
•缺点:
•具体的性能与所选实现商的实现有关,且性能再好,中间件的一些服务始终都是瓶颈;
•一般情况下需要修改源代码来实现对旧有应用软件的包装;
•适用:
当需要集成的两个企业应用软件互为异构,由不同的编程语言实现时,Java与C++就是一个很好的例子。要这两种语言进行协同工作的几乎惟一的方法就是利用CORBA。当然,使用JDK所提供的功能特性JNI也是可能的,但其复杂性以及对Java可移植性的破坏使其不能胜任该集成工作。且JNI不具备分布实施的能力,它的目标也不在于此。
CORBA很适合于通过修改源代码来包装现有应用软件,为其他异构系统提供新的CORBA分布式对象。对于远程方式的请求,IIOP协议会是一个好的选择,例如通过J2EE的RMI-IIOP来调用CORBA的分布式对象。
2.2 Java2平台企业版J2EE
在近几年的企业应用系统开发中,J2EE无疑扮演了一个重要的脚色。开发业务逻辑或中间层组件的最重要的技术就是EJB,它提供了对主要的企业技术如事务、安全性以及持续性的支持,便利了业务组件的开发。尽管EJB受限于Java编程语言,但这种技术本身并不存在问题。同时,J2EE与CORBA技术所达成的一致性为低层组件的请求提供了可行之路,RMI-IIOP和JMS等技术无疑为J2EE提供了强有力的功能核心。
•优点:
•基于规范的平台,不受限于特定的操作系统或硬件平台,有大量实现商可以选择;
•提供现代的组件体系结构,这种结构简化了复杂组件的开发;
•提供主要的企业技术如事务、安全性以及持续性的支持,并以声明和编辑方式对这些服务提供支持。
•相对成熟,支持大量中间件技术,能够为EAI提供满意的性能及可升级性。
•缺点:
•受限于Java编程语言,尽管可通过其他中间件技术(如CORBA)支持;
•实现商之间的可移值性还达不到100%;
•与特定于某个操作系统或平台的实现技术相比,性能还有待进一步提高,且资源占用量较大。
•适用:
J2EE规范本身就提供了一个巨大的企业应用集成平台,基于Java使其不依赖于运行的硬件平台和操作系统,然而也使其受限于单一语言开发。但这一开发平台,目前已经有不同的厂商提供了符合规范说明的各种实现方法。J2EE支持大量中间件技术,和现有的系统能够协同工作。HTTP,RMI-IIOP,JMS,JDBC,JCA以及对XML,企业事务,企业安全方面的支持使其成为目前几种企业应用集成平台中的首选。
2.3 XML和XSLT
XML除了大量应用在因特网技术及文档描述中外,在数据交换中也承担了一个重要的角色。作为一个独立的平台,只需用标准文本,XML能够被所有程序语言读写,一旦使用了DTD或Schema,XML的解释程序就能对文件内容进行验证并处理。XSLT同样是基于XML技术的,但它的作用是重新格式化并传输XML数据文件,从而得到一个全新指定格式的XML文档,相信读者已经可以想象这些技术在不同应用系统中进行数据交换时发生的巨大作用了。
•优点:
•内容由标准文本组成,任何平台和程序语言都可以使用;
•各种程序语言的解释程序可以根据DTD或Schema对文件内容进行验证并处理;
•格式的转换基本不受限制,可以满足不同应用系统的需求。
•缺点:
•DTD在过去被大量的使用,但DTD本身不是XML,而是基于正则表达式的;
•当XML内容较大时,解释程序的执行效率会是一个问题;
•适用:
当不同的应用系统使用着各自的数据格式,或符合复杂的行业标准,而现在需要在各个应用系统之间交换数据,那么XML和XSLT提供了一个可行的手段。当然,XML并不能解决所有的数据交换问题,如何将各种不同的原始数据格式以XML文档来记录就是一件棘手的问题。但好的一面是各种平台及编程语言目前都已经很好的支持了XML及XSLT,一旦XML准备就绪,XSLT就准备将其转换成其他应用系统需要的数据格式。
2.4 分布式组件对象模型DCOM
DCOM扩充了在网络中通过COM支持的对象,并允许COM应用软件分布在局域网中的多个计算机上。DCOM通过网络协议定义过程中的通信。在运行时,COM为客户程序和使用RPC的组件提供服务,而且遵循DCOM协议标准。
•优点:
•在Windows平台上提供基于COM体系结构的分布式处理;
•在Windows平台上使用能够达到较为满意的性能要求。
•缺点:
•在跨平台使用中存在困难,且性能无法得到保障。
•适用:
在Windows平台上进行集成实施的首选,但与其他平台及编程语言的协同工作需要借助于第三方厂商的支持。
2.5 消息中间件MOM
企业消息传递使得应用程序能够跨多平台进行可靠的传输。通过使用可靠的消息队列,提供支持消息传递所需的目录、安全和管理服务,MOM确保验证过的应用之间消息传送的安全,它通常提供同步和异步的传输模式。在企业内部保证可靠的传输最通用的方法就是使用消息传递系统。CORBA和J2EE目前就支持MOM的工业标准接口。
•优点:
•为不同的企业应用系统提供了跨多平台的消息传输;
•除支持同步传输模式外,还支持异步传输,有助于在应用间可靠地进行消息传输。
•缺点:
•与其他中间件技术一样,高流量的性能瓶颈问题正在改善;
•适用:
如果要在多个平台上的应用程序之间保证可靠的传输,且这些应用程序并不在同一时间运行时,应用之间的RPC直接通信或传输数据将不能胜任,而消息中间件MOM会是一个好的选择。即使当请求建立时,接收方应用程序没有运行,这个请求也不会丢失,这就是异步传输的优势。
2.6 J2EE连接器体系结构JCA
JCA是在J2EE1.3的版本规范中提出的,由EIS厂家来执行和提供。JCA的资源适配器是规范化的EIS代理,可插入到任务符合J2EE规范的应用服务器中,并通过应用服务器提供的标准EIS访问接口CCI来对EIS执行操作。JCA向基于EAI的应用程序开发者提供了通过一个将EIS整合进入J2EE的标准方法。此方法定义了一套开发者能在J2EE环境中使用的通用API和服务。
•优点:
•JCA不仅能在数据上将EIS系统集成到J2EE应用中,它还能够将安全与事务等管理涉入到符合条件的EIS系统中。
•JCA的出现使得将遗留的EIS系统集成到J2EE应用中的操作复杂度由NxM减小为N+M。
•JCA由于基于Java技术,在多平台的移植过程中所遇到的阻力较小。
•缺点:
•JCA是一种紧耦合的问题解决方案,它的实现需要涉及所希望集成的遗留EIS系统的API,并且对这些操作进行封装。
•JCA是基于Java技术的,尽管不需要所被集成的EIS系统也是Java实现,但通过JCA去使用该EIS的客户应用却必须是Java实现。
•JCA的实现并不容易,如果实现联接管理部分是JCA所必须的,则一旦加入了事务及安全管理则复杂度将急剧上升,如果是以CCI来实现则遇到的问题可能会更多。
•适用:
JCA所提供的好处基本上是面向J2EE应用服务器及EIS系统供应商的,因为他们的产品往往是符合各种标准与规范的,JCA所给出的统一规范对于他们来说无疑是降低风险并减少开发成本的好武器。但对于一般的企业自产的应用系统而言,JCA就未必能够发挥太大的作用,相反,它有可能成为开发过程中的瓶颈。原因有几点,第一,JCA即资源适配器的开发并不是所有的开发者都能够胜任,它的开发模式与编写普通代码不同,JCA将设计模式中的Factory等模式发挥得淋漓尽致。第二,JCA作为一个统一的规范,它的实现也需要很多的标准与规范来支持,如XA分布事务等。而一个企业的自产应用系统往往并不具有这些标准与规范,所实现的资源适配器并不能享受JCA所提供的众多优点。第三,企业自行开发的资源适配器最终还是要插入到各种J2EE应用服务器中去,但作为第三方的开发者,不了解所使用的J2EE应用服务器的相关特征,甚至应用服务器中存在的缺陷,尽管双方都遵循JCA规范,但实现的不同使得第三方所开发的资源适配器未必能正常发布或应用。
CORBA基本介绍
CORBA(公用对象请求代理体系)是OMG(对象管理组织)于1991年提出的基于对象技术的分布计算应用软件体系结构。CORBA标准主要分为三个部分:接口定义语言(IDL)、对象请求代理(ORB),以及ORB之间的互操作协议IIOP,核心是对象请求代理。CORBA可以抽象系统平台、网络通讯及编程语言的差异。通过在CORBA技术规范中定义多种类型的服务,如名字服务(Naming Service)、事务服务(Transaction Service)、对象生命期服务(LifeCycle Service)、并发控制服务(Concurrency Control Service)、时间服务(Time Service)等功能,为应用软件开发者提供一个全面、健壮、可扩展的分布对象计算平台,使面向对象的应用软件在分布异构环境下方便地实现可重用、易移植和互操作。
与RMI 相比,CORBA 是为更大、可伸缩更强的系统准备的,在这些系统中可能有数千个对象;CORBA 的编程和部署比RMI 更复杂,但允程序员开发需要事务、安全性等支持的企业级系统;CORBA 的命名服务也比RMI 命名注册功能更强大和灵活。
CORBA 的实现称为ORB(Object Request Broker,对象请求代理)。Java IDL 即是CORBA 的一个实现,它是JDK1.3 或更高版本的核心软件包之一,定义在org.omg.CORBA及其子包中。在Java IDL 的支持下,开发人员可以使用如下两种方法将Java 和CORBA 集成在一起:
·??创建Java 对象并使之可在CORBA ORB 中展开,
·??创建Java 类并作为和其它ORB 一起展开的CORBA 对象的客户。这种方法提供了另外一种途径,通过它Java 可以被用于将你的新的应用和以前遗留的系统相集。
CORBA对象服务的实现方式分为两种:对象的命名引用方式和字符串化对象引用方式。不论采用何种高级语言,创建CORBA应用程序的过程大体如下:
● 编写IDL接口定义文件;
● 将接口定义文件编译为相应高级语言源代码,产生服务器框架与客户端存根;
● 基于服务器框架,编写服务对象实现程序;
● 基于客户端存根,编写客户对象调用程序;
● 分别编译客户对象和服务对象程序;
● 运行服务对象和客户对象程序;
下面通过一个实例,描述如何通过Java创建CORBA应用程序。
module HelloApp
{
interface Hello
{
string sayHello(in string message);
};
};
该抽象类是一个服务器 skeleton,它可为服务器提供基本的 CORBA 功能。它实现 Hello.java 接口。服务器类 HelloServant 扩展 _HelloImplBase。
该类是客户机 stub,可为客户机提供 CORBA 功能。它实现 Hello.java 接口。
n Hello.java
该接口含有 IDL 接口的 Java 版本。Hello.java 接口扩展 org.omg.CORBA.Object 并提供标准的 CORBA 对象功能。
n HelloHelper.java
这是一个终态类,可以提供辅助功能,特别是提供将 CORBA 对象引用转换为适当类型所需的 narrow() 方法。
n HelloHolder.java
这是一个终态类,其中含有 Hello 类型的公有实例成员。它可为“out” 和 “inout” 变量提供操作。CORBA 有这些变量,但不容易映射为 Java 的语义。
要完成该应用程序,只需在文件 HelloServer.java 和 HelloClient.java 中提供服务器和客户机的实现即可。
/*
* @author javamxj (CSDN Blog) 创建日期 2004-12-27
*/
import HelloApp.*;
public class HelloImpl extends _HelloImplBase {
/* 构造函数 */
public HelloImpl() {
super();
}
/* 实现接口声明方法sayHello */
public String sayHello(String message) {
System.out.println("我在CORBA的服务器端,客户端正在调用'sayHello'方法。 ");
System.out.println("Hello " + message);
return message;
}
}
3. 服务器
/*
* @author javamxj (CSDN Blog) 创建日期 2004-12-27
*/
import org.omg.CosNaming.*;
import org.omg.CORBA.*;
public class HelloServer {
public static void main(String args[]) {
try {
/* 创建和初始化 ORB */
ORB orb = ORB.init(args, null);
System.out.println("开始 ORB Server ...");
/* 创建一个实例并将其向 ORB 注册 */
HelloImpl helloImpl = new HelloImpl();
orb.connect(helloImpl);
System.out.println("将实例注册到ORB ");
/* 获取根命名上下文 */
org.omg.CORBA.Object objRef =orb.resolve_initial_references("NameService");
NamingContext ncRef = NamingContextHelper.narrow(objRef);
/* 绑定命名中的对象引用 */
NameComponent nc = new NameComponent("Hello", "");
NameComponent path[] = { nc };
ncRef.rebind(path, helloImpl);
/* 等待来自客户机的调用 */
java.lang.Object sync = new java.lang.Object();
synchronized (sync) {
sync.wait();
}
System.out.println("等待CORBA客户端调用...");
} catch (Exception e) {
System.err.println("错误: " + e);
e.printStackTrace(System.out);
}
}
}
HelloClient.java
/*
* @author javamxj (CSDN Blog) 创建日期 2004-12-27
*/
import HelloApp.*;
import org.omg.CosNaming.*;
import org.omg.CORBA.*;
public class HelloClient {
public static void main(String args[]) {
try {
/* 创建和初始化 ORB */
ORB orb = ORB.init(args, null);
/* 获取根命名上下文 */
org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
NamingContext ncRef = NamingContextHelper.narrow(objRef);
/* 解析命名中的对象引用 */
NameComponent nc = new NameComponent("Hello", "");
NameComponent path[] = { nc };
Hello h = HelloHelper.narrow(ncRef.resolve(path));
/* 调用 Hello 服务器对象并打印结果 */
System.out.println("我在客户端,开始调用CORBA服务器端的'sayHello'方法");
System.out.println("欢迎, " + h.sayHello("javamxj blog"));
} catch (Exception e) {
System.out.println("错误 : " + e);
e.printStackTrace(System.out);
}
}
}
Hello.idl
module HelloApp
{
interface Hello
{
string sayHello(in string message);
};
};
HelloImpl.java
/*
* @author javamxj (CSDN Blog) 创建日期 2004-12-27
*/
import org.omg.CORBA.*;
import HelloApp.HelloPOA;
public class HelloImpl extends HelloPOA {
private ORB orb;
public void setORB(ORB orb_val) {
orb = orb_val;
}
/* 实现接口声明方法sayHello */
public String sayHello(String message) {
System.out.println("我在CORBA的服务器端,客户端正在调用'sayHello'方法。 ");
System.out.println("Hello " + message);
return message;
}
}
HelloServer.java
/*
* @author javamxj (CSDN Blog) 创建日期 2004-12-27
*/
import HelloApp.*;
import org.omg.CosNaming.*;
import org.omg.CORBA.*;
import org.omg.PortableServer.*;
import org.omg.PortableServer.POA;
public class HelloServer {
public static void main(String args[]) {
try {
/* 创建和初始化 ORB */
ORB orb = ORB.init(args, null);
/* 获取对RootPOA的引用,启动POAManager */
POA rootpoa = POAHelper.narrow(orb
.resolve_initial_references("RootPOA"));
rootpoa.the_POAManager().activate();
/* 创建一个实例并将其向 ORB 注册 */
HelloImpl h = new HelloImpl();
h.setORB(orb);
System.out.println("将实例注册到ORB ");
/* 获取对服务端的对象引用 */
org.omg.CORBA.Object ref = rootpoa.servant_to_reference(h);
Hello href = HelloHelper.narrow(ref);
/* 从名称服务中获取根元素名称上下文 */
org.omg.CORBA.Object objRef = orb
.resolve_initial_references("NameService");
NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
/* 在Hello名称下注册新对象 */
String name = "Hello";
NameComponent path[] = ncRef.to_name(name);
ncRef.rebind(path, href);
/* 等待客户端的调用。 */
orb.run();
System.out.println("等待CORBA客户端调用...");
}
catch (Exception e) {
System.err.println("ERROR: " + e);
e.printStackTrace(System.out);
}
System.out.println("HelloServer Exiting ...");
}
}
HelloClient.java
/*
* @author javamxj (CSDN Blog) 创建日期 2004-12-27
*/
import HelloApp.*;
import org.omg.CosNaming.*;
import org.omg.CORBA.*;
public class HelloClient {
public static void main(String args[]) {
try {
/* 创建和初始化 ORB */
ORB orb = ORB.init(args, null);
/* 获取根命名上下文 */
org.omg.CORBA.Object objRef = orb
.resolve_initial_references("NameService");
NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
/* 在名称上下文中查找Hello对象,获取对它的引用。 */
String name = "Hello";
Hello h = HelloHelper.narrow(ncRef.resolve_str(name));
System.out.println("我在客户端,开始调用CORBA服务器端的'sayHello'方法");
System.out.println("欢迎, " + h.sayHello("javamxj blog"));
} catch (Exception e) {
System.out.println("错误 : " + e);
e.printStackTrace(System.out);
}
}
}
CORBA技术系列一:JAVA客户程序与C++服务程序互操作
CORBA技术诞生至今,已有十余年时间。十余年,对于IT界显得过于漫长。许多技术在十余年后看来可能都显得有些过时、落后。CORBA曾经在2000年左右辉煌过,现在却被网格、SOA、Web2.0等技术压得抬不起头。然而在学术界,CORBA还是有研究人员在学习使用,譬如某团队通过AOP技术对目标软件系统注入CORBA服务程序实现软件分布式监控。CORBA本身的技术在中间件中是比较领先的。然而,其被人所诟病之处在于其的复杂性。人们常说,CORBA复杂,不好用。其实CORBA本身的设计理念是很简单的,说它复杂的人是没有领会这简单设计理念背后的深邃思想。在这个系列中,我将尽可能以简单的例子来阐述CORBA的设计思想。
系列一:互操作
假设某企业有两套软件系统,一套使用JAVA开发,另一套系统使用C++开发。现在JAVA软件子系统想访问C++软件子系统的一个高密度科学计算服务。如何最快的实现这两个语言异构系统的互联。这就是一个最简单的互操作例子。在传统的企业计算环境中,常常会面对异构操作系统、异构编程语言、异构网络等情况。如何实现在异构系统中的软件模块无缝、平滑的互操作是软件系统集成面临的关键问题。CORBA第一个准确提出了什么是互操作,如何实现互操作。对于CORBA互操作的概念我在这里不阐述过多。这里,只以异构编程语言为例说明互操作。CORBA提出语言中立的接口描述语言IDL来解决互操作。开发人员在软件设计阶段定义好IDL接口,然后通过不同的IDL映射工具翻译成目标语言,在编码实现。下面看一个例子。
JAVA和C++互操作的例子:HelloWorld。服务器端有个Hello服务程序,对外提供方法hello_world。客户程序调用这个方法在服务端输出信息。
先看IDL描述
Hello.idl
interface Hello
{
void hello_world();
};
一)、C++服务程序
使用ORB厂商自带的IDL编译器生成对象存根、服务程序框架和实现框架,在控制台输入命令
idl --impl Hello.idl,--impl选项告诉编译器我想得到实现代码框架。编译完毕后生成六个文件
对象存根Hello.h,Hello.cpp
服务对象框架Hello_skel.h,Hello_skel.cpp
服务实现类Hello_impl.h,Hello_impl.cpp
我们不用关心对象存根和服务对象框架是如何实现的,我们只要按照传统的软件开发方法实现Hello_impl.cpp即可。
让我们看一下IDL编译器生成的Hello_impl头文件
#ifndef ___Hello_impl_h__
#define ___Hello_impl_h__
#include <Hello_skel.h>
//
// Interface declare ::Hello
//
class Hello_impl : virtual public POA_Hello,
virtual public PortableServer::RefCountServantBase
{
Hello_impl(const Hello_impl&);
void operator=(const Hello_impl&);
PortableServer::POA_var poa_;
public:
Hello_impl(PortableServer::POA_ptr);
~Hello_impl();
virtual PortableServer::POA_ptr _default_POA();
//
// Operation declare hello_world
//
virtual void hello_world()
throw(CORBA::SystemException);
};
#endif
为了说明的简单起见,我们只看公有方法
1)构造函数Hello_impl(PortableServer::POA_ptr);
参数PortableServer是CORBA规范中的可移植对象适配器,CORBA规范中指出,一个服务对象必须被一个可移植对象适配器管理。其它更加详细的说明留在后续专题。
2)virtual void hello_world() throw(CORBA::SystemException);
这就是我们关心的实现方法。因为分布式程序的脆弱性,必须在声明时指出可能出现的异常。这里使用CORBA规范中的SystemException。
实现这个方法,我们只是在服务器端输出一行信息。
void
Hello_impl::hello_world()
throw(CORBA::SystemException)
{
std::cout<<"Hello world"<<std::endl;
}
服务类实现完毕后,接下来实现服务主程序Server.cpp,代码如下:
#include <STAR/CORBA.h>
#include <Hello_impl.h>
#include <stdlib.h>
#include <errno.h>
#ifdef HAVE_FSTREAM
# include <fstream>
#else
# include <fstream.h>
#endif
#ifdef HAVE_STD_IOSTREAM
using namespace std;
#endif
int commonproc(CORBA::ORB_ptr orb, int argc, char* argv[])
{
//
// 获取RootPOA的引用
//
CORBA::Object_var poaObj = orb -> resolve_initial_references("RootPOA");
PortableServer::POA_var rootPOA = PortableServer::POA::_narrow(poaObj);
//
// 获取RootPOA的Manager的引用
//
PortableServer::POAManager_var manager = rootPOA -> the_POAManager();
//
// 创建Hello实现对象
//
Hello_impl* helloImpl = new Hello_impl(rootPOA);
PortableServer::ServantBase_var servant = helloImpl;
Hello_var hello = helloImpl -> _this();
//
// 将Hello对象的引用转换成字符串
//
CORBA::String_var s = orb -> object_to_string(hello);
//
// 将Hello对象的引用保存到文件helloobj.ref中
//
const char* refFile = "hello.ref";
ofstream out(refFile);
if(out.fail())
{
cerr << argv[0] << "Can't open " << refFile << ": "
<< strerror(errno) << endl;
return -1;
}
out << s << endl;
out.close();
//
// 激活POAManager,并启动ORB
//
manager -> activate();
orb -> run();
return 0;
}
int
main(int argc, char* argv[], char*[])
{
int state = 0;
CORBA::ORB_var orb;
try
{
//
// 初始化ORB
//
orb = CORBA::ORB_init(argc, argv);
//
// 调用服务器方的通用过程
//
state = commonproc(orb, argc, argv);
}
catch(const CORBA::Exception& ex)
{
cerr << ex << endl;
state = -1;
}
if(!CORBA::is_nil(orb))
{
try
{
orb -> destroy();
}
catch(const CORBA::Exception& ex)
{
cerr << ex << endl;
state = -1;
}
}
return state;
}这段代码初看,可能觉得比较复杂,其实它干的事基本上和业务逻辑无关,只是在初始化ORB、激活对象实现、创建对象引用并将其写入文件。值得说明的是,为了体现CORBA屏蔽异构性,最好将对象注册到名字服务中,然而为了代码实现的简洁,我只是简单的将对象引用写入文件。需要客户程序拿到这个文件还原出对象引用来。对于对象引用的概念在后续的章节在说。
二)实现Java客户端
在控制台使用java自带的idl编译器将idl描述映射为java语言
idlj -fall Hello.idl
编译完毕后,生成几个java文件,我们不用管。接着写java客户端主程序
import org.omg.CORBA.*;
import java.io.*;
public class HelloClient
{
static Hello helloImpl;
public static void main(String args[])
{
try{
// create and initialize the ORB
ORB orb = ORB.init(args, null);
try
{
BufferedReader bf = new BufferedReader(new InputStreamReader(new FileInputStream("hello.ref")) );//
String content = bf.readLine();//读出二进制形式的对象引用
helloImpl = HelloHelper.narrow(orb.string_to_object(content));//将其还原成对象引用
if(helloImpl == null)
System.out.println("error");
System.out.println("Obtained a handle on server object: " + helloImpl);
helloImpl.hello_world();//调用服务程序的方法
}
catch (Exception e)
{
System.out.println(e);
}
} catch (Exception e) ...{
System.out.println("ERROR : " + e) ;
e.printStackTrace(System.out);
}
}
}
三)运行
首先运行C++服务程序Server.exe,服务程序会在程序目录下生成对象引用并写入文件。将对象引用文件复制到java程序所在目录,输入java HelloClient 运行客户端。java客户端读取对象引用文件获取Hello对象引用,然后调用Hello对象引用的hello_world方法,在服务器端输出Hello world,调用成功!
四)总结
通过这个例子,我们可以看到用CORBA写互操作程序是一件非常简单的事情,服务程序和客户程序根本感觉不到网络的存在,在代码中没有繁琐的网络socket通信,调用编码,异常处理。除了程序员必须初始化corba的运行库外,剩下的全是传统的代码编写方式。
CORBA应用--服务器用JAVA编写,客户机分别用JAVA和C++(VC6+omniORB)编写的试验
1. 前言
现在很多人在对CORBA进行学习,大家都已经了解到CORBA是一个完全中间性的语言,可以使用接口定义语言(IDL)定义开发时使用接口的 Client 和实现接口的 Server 所需要的信息。Client 和 Server 的具体实现代码并不在IDL定义中编写,而是使用某种目标语言的IDL 编译器生成所需的代码存根及helper类,Client 和 Server再使用真正的编程语言来进行具体实现。
为了保证在不同的 CORBA 产品基础之上构建的分布式对象可以相互通信,Client和Server通过ORB(对象请求代理)进行通信。一般的运行流程是Client把请求发送给ORB,ORB再把请求发送给Server,Server把返回结果发送ORB,ORB再把返回结果发送给Client。ORB可以说Client和Server之间的翻译者。即使Client和Server使用不同的编程语言编写,只要是符合相同的IDL定义,ORB也可以完成相互的通信。
所有的文档在强调服务器及客户机可以是Java也可以是C++或其他语言(如:Delphi)进行编写,但在网站或书本是没有详细说如何应对多语言客户机的例子。《JAVA2核心技术》上面有些说明,但也只是介绍性的文章,故自己下载了omniORB304,进行了一次使用SUN的 tnameserv命名服务程序,服务器用JAVA编写,客户机分别用JAVA和C++(VC6+omniORB)编写的试验,希望通过一次编程的具体操作实例来体验或明了CORBA思想。
总体的编写过程如下:
用IDL定义一个接口文件,描绘要实现的功能,也可以说是定义一个要实现功能的一个模版(SysProp.idl)
使用"IDL to Java"编译器(这里是IDLJ)将IDL文件转化为Java编程语言中编写的接口定义,生成所需的代码存根及helper类
使用Java语言编写客户机和服务器的实现程序。
使用"IDL to C++"编译器(这里是omniidl)将IDL文件转化为C++编程语言中编写的接口定义,生成所需的代码存根及helper类
使用C++语言编写客户机实现程序(当然也可编写服务器程序,但本次试验没有进行)
起动命名服务tnameserv
起动Java编写的服务程序
用Java和C++编写的客户机分别调用相应的服务
2. 运行环境的设定:
总体环境由jdk1.3+omniORB3.0(www.uk.research.att.com/omniORB/doc/3.0) +vc6 组成,下面说明具体的安装。
2.1. 安装JDK1.3
从SUN公司DOWN JDK1.3或者通过其他方式得到jdk1.3进行安装,再设定相应的环境变量,在本文测试用的电脑上是如下所示:
CLASSPATH=.;
JAVA_HOME=D:/jdk130
修改原来的PATH变量,添加"%JAVA_HOME%/bin;",如下
PTAH=%JAVA_HOME%/bin;原变量
注意:我在第一次使用jbuilder的jdk1.3时,服务器不能正常工作,我只是发觉这么一回事,具体原因与本文无关而没有进行了解,请谅。
2.2. 安装VC6
VC6按常规方式安装,注意的是:在本文测试用的电脑上安装在如下位置
C:/Program Files/Microsoft Visual Studio
2.3.安装omniORB
从 www.uk.research.att.com/omniORB/doc/3.0 下载omniORB3.0 ( 本文测试所下载的文件是omniORB_304_x86_win32.zip )。
用WINZIP进行解压omniORB_304_x86_win32.zip到omniORB_304_x86_win32目录,目录内存在omni目录,复制omni目录内的文件到你把想存放的位置。
测试电脑安装在C:/omni
根据C:/omni/README.win32 文档进行设定,由于运行程序及命令行在控制台进行,所以本次测试并不根据文档要求去设定环境变量,而是编写了一个omni.bat,在使用控制台时,首先运行。
本测试电脑omni.bat内容如下
set TOP=c:/omni
set path=%TOP%/bin/x86_win32;%path%
set LIB=%TOP%/bin/x86_win32;%LIB%
set INCLUDE=%TOP%/include;%INCLUDE%
set VcOsDir=
set VSCommonDir=
如果你的电脑VC的环境变量已经设定在你的环境变量中,那么C:/Program Files/Microsoft Visual Studio/VC98/Bin/VCVARS32.BAT 就可以不运行。否则运行omni.bat前要首先运行VCVARS32.BAT。
3. 实践过程
约定所有编写的文件保存在D:/mywork/t1中,omni.bat也在这个目录内
3.1.编写SysProp.idl,功能是返回系统属性
interface SysProp
{ string getProperty(in string name);
};
3.2. 编写JAVA的服务器
3.2.1. 把IDL文件转化为JAVA编程语言代码存根类及helper类。
执行如下命令
idlj -fall SysProp.idl
在正常的情况下D:/mywork/t1 目录内将生成以下文件,否则请检查你的执行程序及文件
SysProp.java
SysPropHelper.java
SysPropHolder.java
SysPropOperations.java
_SysPropImplBase.java
_SysPropStub.java
3.2.2. 编写 SysPropServer.java
import org.omg.CosNaming.*;
import org.omg.CORBA.*;
//编写相对应的服务,一定要从 _类名ImplBase继承,并实现相应的方法
class SysPropS extends _SysPropImplBase //具体的服务实现
{ public String getProperty(String key)
{
System.out.println("调用"+key);
String S;
S=System.getProperty(key);
if (S==null) { S="null"; }
System.out.println(key+"="+S);
return S;
}
}
public class SysPropServer //起动服务的程序
{ public static void main(String args[])
{ try
{ System.out.println("创建和初始化 ORB ");
ORB orb = ORB.init(args, null);
System.out.println("创建服务对象并将其向 ORB 注册 ");
SysPropS impl = new SysPropS();
orb.connect(impl);
//打印IOR字符串
System.out.println(orb.object_to_string(impl));
org.omg.CORBA.Object namingContextObj =orb.resolve_initial_references("NameService");
NamingContext namingContext= NamingContextHelper.narrow(namingContextObj);
NameComponent[] path = {new NameComponent("SysProp", "")};
System.out.println("绑定服务...SysPropS");
namingContext.rebind(path, impl);
System.out.println("等待调用...SysPropS");
java.lang.Object sync = new java.lang.Object();
synchronized (sync)
{ sync.wait();
}
}
catch (Exception e)
{ System.err.println("Error: " + e);
e.printStackTrace(System.out);
}
}
}
3.3. 编写JAVA的客户机
3.3.1. 编写 SysPropClient.java 使用IOR字符串的方式
注意在代码内有一段注解掉的代码,用"//使用ORB的方法的开始"开始,用"//使用ORB的方法的结束"结束。这段代码是使用ORB方法的代码,如果在代码中"//使用IOR的方法开始"前一行添加"/*",在"//使用IOR的方法结束"后一行添加"*/",而把"//使用ORB的方法的开始"前面的"/*"去掉,把"//使用ORB的方法的结束"后面的"*/"去掉,就是使用ORB方法的代码,程序运行时就是" SysPropClient [环境变量] "的方式。以下是具体代码:
import org.omg.CosNaming.*;
import org.omg.CORBA.*;
public class SysPropClient
{
public static void main(String args[])
{
try{
String SetInfo,ReturnInfo,ref;
org.omg.CORBA.Object objRef;
SysProp syspropRef;
ORB orb = ORB.init(args, null);
//使用IOR的方法开始
if (args.length>=1)
{
ref=args[0];
}
else
{
System.out.println("SysPropClient [环境变量]");
return;
}
objRef = orb.string_to_object(ref);
syspropRef = SysPropHelper.narrow(objRef);
//使用IOR的方法结束
/*
//使用ORB的方法的开始
objRef = orb.resolve_initial_references("NameService");
NamingContext ncRef = NamingContextHelper.narrow(objRef);
// 进行服务定位
NameComponent nc = new NameComponent("SysProp", "");
NameComponent path[] = {nc};
syspropRef = SysPropHelper.narrow(ncRef.resolve(path));
//使用ORB的方法的开始结束
*/
if (args.length>1)
{
SetInfo=args[1];
}
else
{
SetInfo="java.home";
}
System.out.println("开始调用");
ReturnInfo = syspropRef.getProperty(SetInfo);
System.out.println(SetInfo+"="+ReturnInfo);
} catch (Exception e) {
System.out.println("ERROR : " + e) ;
}
}
}
3.3.2. 编译程序,在文件目录内执行如下命令
jAVAC *.JAVA
3.4. 进行测试
第1控制台,执行
tnameserv
测试时如下所示
D:/mywork/t1>java tnameserv
Initial Naming Context:
IOR:000000000000002849444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67436f
6e746578743a312e3000000000010000000000000054000101000000000c3139322e3136382e302e
31000ca6000000000018afabcafe00000002a999c474000000080000000000000000000000010000
0001000000140000000000010020000000000001010000000000
TransientNameServer: setting port for initial object references to: 900
Ready.
第2控制台,执行
java SysPropServer
测试时如下所示
D:/mywork/t1>java SysPropServer
创建和初始化 ORB
创建服务对象并将其向 ORB 注册
IOR:000000000000001049444c3a53797350726f703a312e30000000000100000000000000540001
01000000000c3139322e3136382e302e31000ca7000000000018afabcafe00000002a999dbeb0000
00080000000000000000000000010000000100000014000000000001002000000000000101000000
0000
绑定服务...SysPropS
等待调用...SysPropS
第3控制台,执行
java SysPropClient IOR:XXX JAVA.HOME
测试时如下所示
D:/mywork/t1>java SysPropClient IOR:000000000000001049444c3a53797350726f703a312e
3000000000010000000000000054000101000000000c3139322e3136382e302e31000ca700000000
0018afabcafe00000002a999dbeb0000000800000000000000000000000100000001000000140000
000000010020000000000001010000000000 java.home
开始调用
java.home=D:/bea/jdk130/jre
3.5. 编写C++的IOR客户机
从实践来讲编写C++的客户机程序同JAVA没有多大的区别,只不过JAVA是用idlj生成代码存根类及helper类,而omni是用omniidl来生成代码存根类及helper类,而编程思想及编码过程非常相似。
由于C++的程序要调用omni及VC6的相关文件,所以进入控制台后,如果VC没有进行环境变量设定,那么要先运行C:/Program Files/Microsoft Visual Studio/VC98/Bin/VCVARS32.BAT,再运行omni.bat,否则直接运行omni.bat后再编译程序及运行程序。
3.5.1. 把IDL文件转化为C++编程语言代码存根类及helper类。
执行如下命令
omniidl -bcxx SysProp.idl
在正常的情况下D:/mywork/t1 目录内将生成C++编程语言的代码存根类及helper类SysProp.hh和SysPropSK.cc。否则请检查你的执行程序及文件。
3.5.2. 编写SysPropC.cc
#include
#include
int main(int argc, char** argv)
{
try {
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv, "omniORB3");
if( argc < 2 || argc > 3 ) {
cout << "usage: SysPropC [环境变量名]" << endl;
return 1;
}
CORBA::Object_var obj = orb->string_to_object(argv[1]);
SysProp_ptr echoref = SysProp::_narrow(obj);
if( CORBA::is_nil(echoref) ) {
cerr << "没有对象" << endl;
return 1;
}
const char* message;
if (argc==3)
{
message=argv[2];
}
else
{
message="java.home";
}
CORBA::String_var dest = echoref->getProperty(message);
cout << (char*)message << "=" <<(char*)dest << endl;
orb->destroy();
}
catch(...) {
cerr << "Caught unknown exception." << endl;
}
return 0;
}
3.5.3. 编写dirc.mak,如下所示
TOP = C:/omni
OMNI_DYNAMIC_LIB = msvcstub.lib -NODEFAULTLIB:libcmt.lib -NODEFAULTLIB:libcmtd.lib
CORBA_CPPFLAGS = -D__WIN32__ -D__x86__ -D__NT__ -D__OSVERSION__=4
CORBA_LIB = omniORB304_rt.lib omnithread2_rt.lib /
$(OMNI_DYNAMIC_LIB) /
wsock32.lib advapi32.lib /
-libpath:$(TOP)/lib/x86_win32
CXXFLAGS = -O2 -MD -GX $(CORBA_CPPFLAGS) $(DIR_CPPFLAGS)
CXXLINKOPTIONS =
.SUFFIXES: .cc
.cc.obj:
cl /nologo /c $(CXXFLAGS) /Tp$<<all:: SysPropC.exe
SysPropC.exe: SysPropSK.obj SysPropC.obj
link -nologo $(CXXLINKOPTIONS) -out:$@ $** $(CORBA_LIB)
clean::
-del *.obj
-del *.exe
veryclean::
-del *.obj
-del echoSK.* echo.hh
-del *.exe
SysProp.hh SysPropSK.cc: SysProp.idl
$(TOP)/bin/x86_win32/omniidl -T -bcxx -Wbh=.hh -Wbs=SK.cc -Wbtp SysProp.idl
3.5.4. 编译程序,执行如下命令
nmake -f dirc.mak
3.5.5. 测试
在第4控制台
SysPropC IOR:XXX JAVA.HOME
本测试如下所示
D:/mywork/t1>syspropc IOR:000000000000001049444c3a53797350726f703a312e3000000000
010000000000000054000101000000000c3139322e3136382e302e31000ca7000000000018afabca
fe00000002a999dbeb00000008000000000000000000000001000000010000001400000000000100
20000000000001010000000000 os.name
os.name=Windows 2000
3.6. 编写C++的NAME方式客户机
为了使用NANE方式,必须为OMNI软件设置注册表信息,要在注册表中建立如下数据项(regedit)HKEY_LOCAL_MACHINE/SOFTWARE/ORL/omniORB/2.0/NAMESERVICE(字符串)。
NAMESERVICE的值为tnameserv(jdk1.3/bin内的程序)启动的IOR值(第一次设置时自行添加)。
注意为了使用这种方式每次起动tnameserv后要用新IOR值换去旧的IOR值,我测试过用omini的omniNames.exe程序做服务器,IOR值是不变的,但服务器用JVAV编写就会出错。如果起动tnameserv,用 c编写的服务器及客户机就可以在上面运行。本例子如下所示
Initial Naming Context:
IOR:000000000000002849444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67436f
6e746578743a312e3000000000010000000000000054000101000000000c3139322e3136382e302e
31000ca6000000000018afabcafe00000002a999c474000000080000000000000000000000010000
0001000000140000000000010020000000000001010000000000
TransientNameServer: setting port for initial object references to: 900
Ready.
那么就要把从 IOR:开始(含IOR:)后面的字符串放进注册表。
3.6.1.编写SysPropCC.cc
//使用NAME方式的客户机
#include
#include "SysProp.hh"
static CORBA::Object_ptr getObjectReference(CORBA::ORB_ptr orb);
int main (int argc, char **argv)
{
if( argc != 2 ) {
cout << "使用方法: SysPropCC <环境变量名>" << endl;
return 1;
}
try {
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv, "omniORB3");
CORBA::Object_var obj = getObjectReference(orb);
SysProp_ptr echoref = SysProp::_narrow(obj);
const char* message;
if (argc==2){message=argv[1];}
else {message="java.home"; }
CORBA::String_var dest = echoref->getProperty(message);
cout << (char*)message << "=" <<(char*)dest << endl;
orb->destroy();
}
catch(...) { cerr << "Caught unknown exception." << endl; }
return 0;
}
//
static CORBA::Object_ptr getObjectReference(CORBA::ORB_ptr orb)
{
CosNaming::NamingContext_var rootContext;
try {
// 定位服务器:
CORBA::Object_var obj;
obj = orb->resolve_initial_references("NameService");
// Narrow the reference returned.
rootContext = CosNaming::NamingContext::_narrow(obj);
if( CORBA::is_nil(rootContext) ) {
cerr << " 初始化不成功." << endl;
return CORBA::Object::_nil();
}
}
catch(CORBA::ORB::InvalidName& ex) {
cerr << " 没有找到服务" << endl;
return CORBA::Object::_nil();
}
CosNaming::Name name;
name.length(1);
name[0].id = (const char*) "SysProp";
name[0].kind = (const char*) "";
try {
// Resolve the name to an object reference.
return rootContext->resolve(name);
}
catch(...) { cerr << "定位不成功." << endl;}
return CORBA::Object::_nil();
}
3.6.2. 编写dircc.mak
TOP = C:/omni
DIR_CPPFLAGS = -I. -I$(TOP)/include
OMNI_DYNAMIC_LIB = msvcstub.lib -NODEFAULTLIB:libcmt.lib -NODEFAULTLIB:libcmtd.lib
CORBA_CPPFLAGS = -D__WIN32__ -D__x86__ -D__NT__ -D__OSVERSION__=4
CORBA_LIB = omniORB304_rt.lib omnithread2_rt.lib /
$(OMNI_DYNAMIC_LIB) /
wsock32.lib advapi32.lib /
-libpath:$(TOP)/lib/x86_win32
CXXFLAGS = -O2 -MD -GX $(CORBA_CPPFLAGS) $(DIR_CPPFLAGS)
CXXLINKOPTIONS =
.SUFFIXES: .cc
.cc.obj:
cl /nologo /c $(CXXFLAGS) /Tp$<
all:: SysPropCc.exe
SysPropCc.exe: SysPropSK.obj SysPropCc.obj
link -nologo $(CXXLINKOPTIONS) -out:$@ $** $(CORBA_LIB)
clean::
-del *.obj
-del *.exe
veryclean::
-del *.obj
-del echoSK.* echo.hh
-del *.exe
SysProp.hh SysPropSK.cc: SysProp.idl
$(TOP)/bin/x86_win32/omniidl -T -bcxx -Wbh=.hh -Wbs=SK.cc -Wbtp SysProp.idl
3.6.3. 编译文件
nmake -f dircc.mak
3.6.4. 测试
在第5控制台
SysPropCC JAVA.HOME,测试结果如下所示
D:/mywork/t1>syspropcc java.home
java.home=D:/bea/jdk130/jre
4. 小结
另还使用了j2sdkee1.2.1进行测试,由于j2sdkee1.2.1起动时的nameserver的PORT是1050所以启动服务要加参数,对于本文中的程序运行时要如下所示:
java SysPropServer -ORBInitialPort 1050
如果用IOR的方式,也可以用omni的omniNames.exe程序做命名服务器,用C++编写服务器,客户机使用Java和C++编写,c++编写服务器的例子可见omni的文档。
本次只是简单进行了测试,可以使大家了解一下,CORBA的这种特性,多种不同程序语言进行协作编程的具体运作过程,希望可以抛砖引玉。
C++调用Java web service帮助文档
一、简介在做java项目的时候,有时候我们需要提供一些方法给外部调用,调用者可以处在网络上的任何位置,只要通过特定的设置就可以调用java提供的方法。在java中,我们可以通过把这些方法做成web service。本文档主要对用Visual C++调用java做的webservice进行一系列的说明
二、使用说明C++调用web service的实质是:发送soap 请求并得到返回的结果。用 C++调用java的webservice,发现与调用微软的webservice还是不同的,用其自带的例子无法实现与java做的webservice通讯,下面和大家分享一下实现的方法。
1.首先,需要安装微软的mssoap tookit 3.0工具包,可以从微软的官方网站上下载;
2.使用java建立一个web服务 ,本例子的web service 使用开源项目cxf建立起来的,想了解cxf请登陆:
http://incubator.apache.org/cxf/
2.1 安装使用Soap Toolk 首先C++要调用web service,首先要下载mssoap tookit 3.0工具包,并安装好。mssoap tookit必须是3.0,否则会出现错误。默认安装好mssoup tookit 3.0後,就可以使用C++来调用webservice了。这时候可以使用mssoap tookit 3.0来监控C++的soap请求。现在假设网络上有一个web service:http://192.168.0.10:8082/ServiceGateway/ServiceServer,下面就这个web service来详细描述使用mssoap tookit 3.0来监控C++调用web service 的情形和不监控的情形,
2.2 使用mssoap tookit 3.0监视C++调用web service要想使用mssoap tookit 3.0来监控,首先请打开Trace Utility。
打开时候请新建一个 Formatted Trace,如下图
这时候会弹出一个如下图的窗口:
Local port,可以自己定义,使用本机上的一个空闲的端口即可,本例设置为:8080,该端口的意义:以后使用该端口来发送soap请求,Destination:localhos中的localhost请设置为web service的IP,本例应该设置为:192.168.0.10,Destination:80请设置为web service 的端口,本例设置为:8082,设置好之后就可以通过localport来监控soap请求了。
先查看wsdl文件:http://192.168.0.10:8082/ServiceGateway/ServiceServer?wsdl
<?xml version="1.0" encoding="utf-8" ?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://gateway/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="IndexServiceService" targetNamespace="http://gateway/">
<wsdl:types>
<xsd:schema attributeFormDefault="unqualified" elementFormDefault="unqualified" targetNamespace="http://gateway/" xmlns:tns="http://gateway/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="numList" type="tns:numList" />
<xsd:complexType name="numList">
<xsd:sequence>
<xsd:element name="arg0" type="xsd:int" />
<xsd:element maxOccurs="unbounded" minOccurs="0" name="arg1" type="xsd:int" />
</xsd:sequence>
</xsd:complexType>
<xsd:element name="numListResponse" type="tns:numListResponse" />
<xsd:complexType name="numListResponse">
<xsd:sequence>
<xsd:element maxOccurs="unbounded" minOccurs="0" name="return" type="xsd:int" />
</xsd:sequence>
</xsd:complexType>
<xsd:element name="login" type="tns:login" />
<xsd:complexType name="login">
<xsd:sequence>
<xsd:element minOccurs="0" name="arg0" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
<xsd:element name="loginResponse" type="tns:loginResponse" />
<xsd:complexType name="loginResponse">
<xsd:sequence>
<xsd:element minOccurs="0" name="return" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
</wsdl:types>
<wsdl:message name="loginResponse">
<wsdl:part element="tns:loginResponse" name="parameters" />
</wsdl:message>
<wsdl:message name="login">
<wsdl:part element="tns:login" name="parameters" />
</wsdl:message>
<wsdl:message name="numListResponse">
<wsdl:part element="tns:numListResponse" name="parameters" />
</wsdl:message>
<wsdl:message name="numList">
<wsdl:part element="tns:numList" name="parameters" />
</wsdl:message>
<wsdl:portType name="IndexService">
<wsdl:operation name="numList">
<wsdl:input message="tns:numList" name="numList" />
<wsdl:output message="tns:numListResponse" name="numListResponse" />
</wsdl:operation>
<wsdl:operation name="login">
<wsdl:input message="tns:login" name="login" />
<wsdl:output message="tns:loginResponse" name="loginResponse" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="IndexServiceServiceSoapBinding" type="tns:IndexService">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="numList">
<soap:operation soapAction="" style="document" />
<wsdl:input name="numList">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="numListResponse">
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="login">
<soap:operation soapAction="" style="document" />
<wsdl:input name="login">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="loginResponse">
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="IndexServiceService">
<wsdl:port binding="tns:IndexServiceServiceSoapBinding" name="IndexServicePort">
<soap:address location="http://192.168.0.10:8082/ServiceGateway/ServiceServer" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
可见有两个远程方法:
int[] numList(int,int[])
String login(String)
用vc建立一个console的程序,其全部代码如下:
// testwebservice.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <stdio.h>
#import "msxml4.dll"
using namespace MSXML2;
#import "C:/Program Files/Common Files/MSSoap/Binaries/mssoap30.dll" /
exclude("IStream", "IErrorInfo", "ISequentialStream", "_LARGE_INTEGER", /
"_ULARGE_INTEGER", "tagSTATSTG", "_FILETIME")
using namespace MSSOAPLib30;
void Add()
{
ISoapSerializerPtr Serializer;
ISoapReaderPtr Reader;
ISoapConnectorPtr Connector;
// Connect to the service.
Connector.CreateInstance(__uuidof(HttpConnector30));
//使用EndPointURL属性指定Web服务,因为使用soap tookit3.0来监控,所以使用本地的8080端口来发送soap请求
Connector->Property["EndPointURL"] = "http://localhost:8080/ServiceGateway/ServiceServer";
//这是不使用mssoap tookit 3.0来监控的情形,需要直接发送请求到远端的端口。
// Connector->Property["EndPointURL"] = "http://192.168.0.10:8082/ ServiceGateway/ServiceServer ";
Connector->Connect();
// 开始消息
// //在与服务器连接后,我们需要指定Web服务完成的操作。
//为了指定该操作,我们需要再次使用SoapConnector的Property属性
Connector->Property["SoapAction"] = "http://localhost:8080/ServiceGateway/ServiceServer";
//这是不使用mssoap tookit 3.0来监控的情形
//Connector->Property["SoapAction"] = " http://192.168.0.10:8082/ ServiceGateway/ServiceServer ";
Connector->BeginMessage();
// Create the SoapSerializer object.
Serializer.CreateInstance(__uuidof(SoapSerializer30));
// Connect the serializer object to the input stream of the connector object.
Serializer->Init(_variant_t((IUnknown*)Connector->InputStream));
// 创建SOAP消息
Serializer->StartEnvelope("","","");
Serializer->StartBody
// 开始SOAP消息中的一个元素,第一个参数描述了名字空间,是wsdl文件中的://targetNamespace="http://gateway/"
// 如果它是空值,就会缺省地使用SOAP-ENV。第二、第三个参数
//调用login方法,参数名由wsdl文件决定:
//<xsd:complexType name="login">
// <xsd:sequence>
// <xsd:element minOccurs="0" name="arg0" type="xsd:string" />
// </xsd:sequence>
// </xsd:complexType>
Serializer->StartElement("login","http://server.hw/","STANDARD","");
Serializer->StartElement("arg0","","STANDARD","");
Serializer->WriteString("cellcom");
Serializer->EndElement();
Serializer->EndElement();
Serializer->EndBody();
Serializer->EndEnvelope();
// 将该消息发送给web服务
Connector->EndMessage();
// // 读取响应
Reader.CreateInstance(__uuidof(SoapReader30));
// 将reader联接到connector的输出字符串
Reader->Load(_variant_t((IUnknown*)Connector->OutputStream), "");
// 显示结果
printf("Answer: %s/n", (const char*)Reader->RpcResult->text);
return;
}
int main(int argc, char* argv[])
{
CoInitialize(NULL);
Add();
CoUninitialize();
return 0;
}
上面的代码是一个完整的C++调用web service的情形,运行之后会发现Trace Utility里有些信息,如下图:
第一个框里是一个soap请求,第二个框是soap请求返回的结果。
2.3不使用mssoap tookit 3.0监视C++调用web service 不使用mssoap tookit 3.0监控的时候,C++的代码要做如下修改:
//使用EndPointURL属性指定Web服务,因为使用soap tookit3.0来监控,所以使用本地的8080端口来发送soap请求
//Connector->Property["EndPointURL"] = "http://localhost:8080/ServiceGateway/ServiceServer";
//这是不使用mssoap tookit 3.0来监控的情形,需要直接发送请求到远端的端口。
Connector->Property["EndPointURL"] = "http://192.168.0.10:8082/ ServiceGateway/ServiceServer ";
Connector->Connect();
// 开始消息
// //在与服务器连接后,我们需要指定Web服务完成的操作。
//为了指定该操作,我们需要再次使用SoapConnector的Property属性
// Connector->Property["SoapAction"] = "http://localhost:8080/ServiceGateway/ServiceServer";
//这是不使用mssoap tookit 3.0来监控的情形
Connector->Property["SoapAction"] = " http://192.168.0.10:8082/ ServiceGateway/ServiceServer ";
//要使用http://192.168.0.10:8082/ ServiceGateway/ServiceServer来指定服务。
//其他的基本上没改变。
2.4 被调用的方法参数是数组的情形 下面是调用numList的情形,通过查看wsdl知道,numList有两个参数:
<xsd:complexType name="numList">
<xsd:sequence>
<xsd:element name="arg0" type="xsd:int" />
<xsd:element maxOccurs="unbounded" minOccurs="0" name="arg1" type="xsd:int" />
</xsd:sequence>
</xsd:complexType>
通过询问web service 的开发人员知道,第一个参数是int,第二个参数是int[],该方法是把int[]里所有在第一参数之前的数加一,
Serializer->StartEnvelope("","","");
Serializer->StartBody("");
// 开始SOAP消息中的一个元素,第一个参数描述了名字空间,
// 如果它是空值,就会缺省地使用SOAP-ENV。第二、第三个参数
Serializer->StartElement("numList","http://gateway/","STANDARD","");
Serializer->StartElement("arg0","","STANDARD","");
Serializer->WriteString("2");
Serializer->EndElement();
Serializer->StartElement("arg1","","STANDARD","");
Serializer->WriteString("53");
Serializer->EndElement();
Serializer->StartElement("arg1","","STANDARD","");
Serializer->WriteString("58");
Serializer->EndElement();
查看soap监控里的信息: