消息驱动Bean与CORBA技术解析
1. 消息驱动Bean概述
消息驱动Bean是一种特殊的企业级JavaBean(EJB),它在容器的Java消息服务(JMS)系统中充当消息消费者。与标准的JMS消息消费者类似,消息驱动Bean从JMS队列或主题接收消息,并根据消息内容执行业务逻辑。
在EJB部署时,部署者会为队列或主题创建监听器,容器会根据需要自动创建和移除消息驱动Bean实例,以处理传入的消息。
以下是一个发送消息的示例代码:
message.setInt("ScheduleID",15);
message.setDouble("Price",1700);
message.setString("CreditCard","1234-1234-1234-1234");
message.setLong("CreditCardExp",System.currentTimeMillis());
message.setString("CreditCardType","Visa");
//Send Message
qs.send(message);
}
catch (NamingException ex) {
ex.printStackTrace();
}catch (JMSException ex) {
ex.printStackTrace();
}
}
}
操作步骤如下:
1. 保存客户端应用程序代码。
2. 编译客户端应用程序。
3. 运行客户端应用程序。
消息驱动Bean相较于标准JMS消费者具有以下优势:
-
实例管理
:消息驱动Bean实例由EJB容器完全管理。容器可以根据需要创建多个EJB实例,以并发处理大量消息。而在标准JMS系统中,开发者需要创建一个使用服务器范围会话池的消息监听器类。
-
服务支持
:容器为消息驱动Bean提供其他标准EJB服务,如安全服务和自动事务管理。
-
可移植性
:消息驱动Bean具有EJB的“一次编写,随处部署”的特性。JMS消息监听器与特定的会话池、队列或主题绑定,而消息驱动Bean可以独立于可用的服务器资源进行开发。消息驱动Bean的队列和主题仅在部署时分配,使用特定容器实例上的可用资源。
在使用消息驱动Bean时,我们可能会有以下思考:
- 何时使用JMS,何时使用消息驱动Bean?
- 应该使用队列还是主题?
- 是否需要消息持久性?
2. CORBA技术介绍
2.1 CORBA定义
通用对象请求代理体系结构(CORBA)是一种规范,它描述了对象如何通过网络与其他对象进行通信。该规范提供了与供应商和实现语言无关的特性。对象管理组织(OMG)创建并维护CORBA规范,其成员包括3M、IBM、花旗集团、惠普、太阳微系统、富士通、甲骨文、美国银行、雪佛龙、福特和波音等800多家公司。
许多供应商都实现了CORBA规范,例如Borland的Visibroker,它是Borland企业服务器包的一部分。
当一个程序与运行在另一台机器上的对象进行通信时,会遇到以下问题:
- 程序如何找到对象?
- 程序如何与对象进行通信?
- 程序如何将参数转换为对象所需的正确格式?
- 如果对象未运行,程序如何处理错误?
CORBA通过指定系统各部分必须遵守的接口来解决这些问题:
-
查找对象
:程序通过定位CORBA命名服务,并请求命名服务提供对象的位置来找到对象。这使得应用程序可以在对象位于任何位置的情况下运行。
-
通信方式
:CORBA指定对象请求代理(ORB)管理对象之间的通信。对象使用互联网ORB间协议(IIOP)进行通信。
-
参数转换
:CORBA指定了多种语言的数据类型映射。ORB将数据类型从一种格式转换为另一种格式,例如将一种语言中的字符串转换为另一种语言中的字符串。支持的语言包括COBOL、C、C++、Java、Delphi等。
-
错误处理
:CORBA指定了系统级和应用级错误的格式。错误的抛出由ORB库处理。例如,C++和Java有不同的错误抛出方法和标准,CORBA通过允许将C++错误抛出并传递到Java应用程序中,实现了这些语言之间的桥接。
除了CORBA,还有其他对象互操作性模型,如微软的COM、DCOM和COM+,以及SUN的远程方法调用(RMI)和企业JavaBean(EJB)。这些通信方法都是专有的,但都有与CORBA对象通信的适配器。EJB通过在其规范中包含CORBA要求,朝着与CORBA的完全互操作性发展。
尽管存在多种对象互操作性模型,但CORBA仍然是唯一具有真正供应商和语言独立性的开放标准。
2.2 CORBA组件
CORBA系统包含以下重要组件:
|组件|说明|
|----|----|
|ORB|一组通信库,使对象能够在分布式环境中透明地通信。它处理进程内、进程外和跨网络的对象通信。客户端应用程序通常进行标准的本地方法调用,ORB库将其转换为实际的对象调用,包括数据编组、对象定位和方法调用。不同机器上的ORB可以不同,但它们必须使用标准的IIOP协议进行通信。|
|客户端存根和服务器骨架|CORBA定义了接口定义语言(IDL),用于完全描述对象的公共接口。使用IDL,可生成客户端存根和服务器骨架代码,用于处理与ORB库的底层通信、控制网络连接以及数据的编组和解组。不同供应商的IDL编译器生成的代码实现可能不同,但类名和公共接口相同。例如,Visibroker根据服务端的位置优化通信方式,若服务端在同一进程中,使用本地方法调用,可大幅提高性能;若在同一机器的不同进程中,使用进程间通信方法,性能有一定提升;若在不同服务器上,则使用IIOP进行标准的CORBA调用。客户端存根用于创建客户端代理对象,透明地调用服务器对象的方法;服务器骨架为服务端提供CORBA功能框架,激活对象、监听网络连接并处理传入的调用。|
|对象适配器(OA)|管理服务端与ORB的交互。它生成对象引用并激活服务端,负责管理方法调用。早期的CORBA版本中,OA称为基本对象适配器(BOA),由于规范不完善,不同供应商的实现不同,导致服务端在不同供应商之间迁移困难。后来的规范将其重命名为可移植对象适配器(POA),POA提供了对服务端创建、生命周期、持久性和命名的控制。单个ORB可以创建多个POA,每个POA可以有不同的策略和服务端。|
|互操作性对象引用(IOR)和命名服务|每个服务端都可以生成一个IOR,它是一个标准引用,包含了与服务端进行通信所需的所有信息。IOR可以写成字符串形式,称为字符串化的IOR。客户端存根提供方法将字符串化的对象引用转换回对象引用。将IOR写入文件并与客户端应用程序共享不是一个好方法,因为每次运行对象时IOR可能会改变。CORBA定义了命名服务来解决这个问题,它是一个分层的命名图,使客户端应用程序能够找到并绑定到服务端。许多供应商提供了更易于使用的命名服务实现,例如Visibroker的OSAgent。OSAgent运行在一台机器上,提供扁平的命名空间。服务端使用UDP广播自动定位并注册到OSAgent,客户端应用程序通过调用绑定方法找到网络上的OSAgent并请求服务端,OSAgent将对象引用返回给客户端应用程序。|
以下是Visibroker客户端应用程序、服务端和OSAgent命名服务之间的交互流程:
graph LR
A[启动OSAgent服务] --> B[启动服务端]
B --> C[服务端广播本地子网查找OSAgent]
C --> D[选择第一个响应的OSAgent]
D --> E[服务端向OSAgent注册服务器名称]
E --> F[服务端等待激活]
G[客户端应用程序启动] --> H[客户端广播本地子网查找OSAgent]
H --> I[选择第一个响应的OSAgent]
I --> J[客户端调用服务端方法]
J --> K[客户端向OSAgent请求服务端对象引用]
K --> L[OSAgent返回对象引用]
L --> M[客户端绑定对象引用并调用方法]
M --> N[POA激活服务端并调用方法,返回结果给客户端]
N --> O[客户端可继续使用对象,无需再次使用OSAgent]
2.3 接口定义语言(IDL)
IDL为服务端和访问服务端的应用程序之间提供了公共接口契约,对于创建可从任何平台上的任何语言访问的CORBA对象至关重要。实际的IDL文件是一个文本文件,JBuilder根据其
.IDL
文件扩展名识别它。
IDL使用一组定义好的数据类型和结构来建模公共接口,其中没有编程语句或变量定义。创建公共接口后,IDL编译器将IDL文件转换为所选语言的客户端存根和服务器骨架代码。这里的“编译器”并不进行真正的编译,只是生成文件。IDL模仿C++的语法,使用花括号、转义标识符和注释。
以下是一个示例IDL文件:
module ChalkTalk {
typedef sequence<string> CourseName;
interface CourseData {
string getCourseList(inout CourseName names);
};
};
该IDL文件的说明如下:
-
module
语句指定了一个Java包名,包含在IDL中定义的接口。在上述示例中,
ChalkTalk
模块生成一个名为
ChalkTalk
的Java包,模块内定义的所有接口和数据类型都将放在该包中。
-
typedef
定义了一个新的数据类型
CourseName
,它是一个字符串序列,映射到Java数组。
-
interface
表示一个CORBA对象,在示例中,
CourseData
接口映射到一个名为
CourseData
的Java类,该类包含一个
getCourseList
方法,返回一个字符串,并将
CourseName
数据类型作为输入和输出参数。
IDL方法签名的格式为:
<return type> methodName( direction datatype name, direction datatype name, ...)
其中:
-
return type
是有效的数据类型,包括用户定义的数据类型。
-
direction
表示参数的流向,具体如下:
|方向|定义|
|----|----|
|in|数据从客户端传递到服务器|
|out|数据从服务器传递到客户端|
|inout|数据在客户端和服务器之间双向传递|
- 有效的数据类型包括用户定义的数据类型和基本数据类型,基本数据类型与Java数据类型的映射如下:
|IDL数据类型|Java数据类型|定义|
|----|----|----|
|boolean|boolean|标准的布尔值,真或假|
|char|char|IDL中char是8位值,Java中char是16位值。Java原生支持Unicode,即16位字符集。如果Java char超过8位值,会抛出CORBA数据转换异常|
|wchar|char|IDL扩展规范以包含Unicode和16位字符数据类型,IDL的wchar直接映射到Java char|
通过以上介绍,我们了解了消息驱动Bean和CORBA技术的基本概念、特点和使用方法。消息驱动Bean在JMS系统中提供了高效的消息处理能力,而CORBA则为分布式对象通信提供了跨平台和跨语言的解决方案。在实际应用中,我们可以根据具体需求选择合适的技术来构建分布式系统。
3. 构建CORBA服务器和客户端
3.1 构建CORBA服务器
构建CORBA服务器的关键步骤包括定义IDL接口、编译IDL文件、实现服务器端代码。
步骤1:定义IDL接口
我们可以使用之前提到的示例IDL文件
course.idl
:
module ChalkTalk {
typedef sequence<string> CourseName;
interface CourseData {
string getCourseList(inout CourseName names);
};
};
这个IDL文件定义了一个模块
ChalkTalk
,其中包含一个新的数据类型
CourseName
和一个接口
CourseData
,接口中有一个方法
getCourseList
。
步骤2:编译IDL文件
使用Visibroker的
idl2java
编译器(位于Borland Enterprise Server目录的
bin
子目录)来编译IDL文件。编译后会生成一系列Java文件,包括客户端存根和服务器骨架代码。
步骤3:实现服务器端代码
以下是一个简单的服务器端代码示例:
import ChalkTalk.*;
import org.omg.CORBA.*;
import org.omg.PortableServer.*;
public class CourseDataServer {
public static void main(String[] args) {
try {
// 初始化ORB
ORB orb = ORB.init(args, null);
// 获取根POA的引用
org.omg.CORBA.Object poaObj = orb.resolve_initial_references("RootPOA");
POA rootPOA = POAHelper.narrow(poaObj);
rootPOA.the_POAManager().activate();
// 创建服务端实例
CourseDataImpl courseDataImpl = new CourseDataImpl();
// 将服务端对象转换为CORBA对象引用
org.omg.CORBA.Object ref = rootPOA.servant_to_reference(courseDataImpl);
CourseData href = CourseDataHelper.narrow(ref);
// 将对象引用绑定到命名服务(这里假设使用OSAgent)
// 具体绑定代码根据命名服务实现不同而不同
// 例如,使用OSAgent时,服务端会自动注册
// 等待客户端请求
System.out.println("CourseDataServer ready and waiting ...");
orb.run();
} catch (Exception e) {
System.err.println("ERROR: " + e);
e.printStackTrace(System.out);
}
}
}
class CourseDataImpl extends CourseDataPOA {
@Override
public String getCourseList(org.omg.CORBA.StringSeqHolder names) {
// 实现具体的业务逻辑
names.value = new String[]{"Course1", "Course2", "Course3"};
return "Course list retrieved successfully";
}
}
在这个示例中,我们首先初始化了ORB,获取了根POA的引用并激活它。然后创建了服务端实例,并将其转换为CORBA对象引用。最后,服务端等待客户端的请求。
3.2 构建CORBA客户端
构建CORBA客户端的步骤包括编译IDL文件(与服务器端相同)、实现客户端代码。
步骤1:编译IDL文件
同样使用
idl2java
编译器编译之前的
course.idl
文件,生成客户端所需的存根代码。
步骤2:实现客户端代码
以下是一个简单的客户端代码示例:
import ChalkTalk.*;
import org.omg.CORBA.*;
public class CourseDataClient {
public static void main(String[] args) {
try {
// 初始化ORB
ORB orb = ORB.init(args, null);
// 获取命名服务对象引用(这里假设使用OSAgent)
// 具体获取代码根据命名服务实现不同而不同
// 例如,使用OSAgent时,客户端通过广播查找
org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
org.omg.CosNaming.NamingContextExt ncRef = org.omg.CosNaming.NamingContextExtHelper.narrow(objRef);
// 绑定到服务端对象
org.omg.CORBA.Object courseDataObj = ncRef.resolve_str("CourseData");
CourseData courseData = CourseDataHelper.narrow(courseDataObj);
// 调用服务端方法
org.omg.CORBA.StringSeqHolder names = new org.omg.CORBA.StringSeqHolder();
String result = courseData.getCourseList(names);
// 输出结果
System.out.println("Result: " + result);
System.out.println("Course list:");
for (String name : names.value) {
System.out.println(name);
}
} catch (Exception e) {
System.err.println("ERROR: " + e);
e.printStackTrace(System.out);
}
}
}
在这个示例中,我们首先初始化了ORB,然后获取命名服务对象引用,通过命名服务绑定到服务端对象。接着调用服务端的
getCourseList
方法,并输出结果。
4. 总结与思考
消息驱动Bean和CORBA技术在分布式系统开发中都有着重要的作用。消息驱动Bean为JMS系统提供了高效的消息处理机制,使得开发者可以专注于业务逻辑的实现,同时享受EJB容器提供的各种服务。而CORBA则为分布式对象通信提供了跨平台、跨语言的解决方案,通过IDL定义接口,使得不同语言编写的对象可以方便地进行交互。
在实际应用中,我们需要根据具体的需求来选择合适的技术。如果主要关注消息处理和异步通信,消息驱动Bean可能是一个不错的选择;如果需要构建分布式系统,实现不同平台和语言之间的对象通信,CORBA则更具优势。
同时,我们也应该思考一些问题,例如在使用CORBA时,如何选择合适的命名服务实现,如何优化ORB的性能等。在使用消息驱动Bean时,如何确保消息的顺序性和持久性等。通过不断地学习和实践,我们可以更好地掌握这些技术,构建出更加高效、稳定的分布式系统。
总之,消息驱动Bean和CORBA技术为我们提供了强大的工具,帮助我们应对分布式系统开发中的各种挑战。在未来的开发中,我们可以根据具体场景灵活运用这些技术,创造出更有价值的应用。
超级会员免费看
80

被折叠的 条评论
为什么被折叠?



