级别: 初级 Vernon Green (vernon_green@uk.ibm.com), 高级软件工程师, IBM
2004 年 2 月 01 日 本文向您展示了如何使用 WebSphere Application Server V5 的扩展消息传递服务(Extended Messaging Service,EMS)来连接现有的消息传递应用程序,在这些应用程序中,消息格式已经定义而内部扩展消息传递格式却不合适。这将使新的应用程序能够使用通过扩展消息传递提供的工具和运行时构件,并且与不能修改自己的消息格式的现有应用程序交换消息。
本文向您展示了如何使用WebSphere Application Server V5 的扩展消息传递服务(Extended Messaging Service,EMS)来连接现有的消息传递应用程序,在这些应用程序中,消息格式已经定义而内部扩展消息传递格式却不合适。这将使新的应用程序能够使用通过扩展消息传递提供的工具和运行时构件,并且与不能修改自己的消息格式的现有应用程序交换消息。
引言
扩展的消息传递服务(Extended Messaging Service,EMS)是WebSphere ApplicationServer Enterprise Edition Version 5(Application Server)的特征之一。EMS提供了基于 Enterprise Java Beans(EJB)编程模型的简化编程模型来传入消息及其应答。EMS还可以处理传出消息并提供响应的相关性。您可以通过使用通用的消息传递模式来启用这种功能。这些消息模式是通过使用基于Java 消息传递服务(Java MessageService,JMS)流消息的私有消息格式实现的。
如果在每一端都启用扩展消息传递来发送和接收它创建的消息,则这一切会很正常。然而,当您集成应用程序以致出现现有的消息传递应用程序在一端而扩展消息传递应用程序在另一端的情况时,您就需要改变 EMS消息格式以使它们能够交换消息。
本文描述了如何用自定义、用户编写的格式化器和解析器来代替扩展消息传递提供的格式化器和解析器。为了举例说明这些步骤,我们将构建一个简单的应用程序发送者Bean以及一个简单的格式化器和解析器来处理消息内容。另外,我们还将解释对发送者Bean的修改,目的是使用新的格式化器和解析器。在本文中描述的发送者Bean 是通过使用 WebSphere Studio Application Developer Integration Edition Version 5(ApplicationDeveloper)构建的。
本文假定您具有扩展消息的基本知识以及JMS 的基本应用知识。
样本应用程序
为了说明这一切如何工作,让我们看一个样本应用程序场景。考虑我们有一个现有的应用程序,它以顾客编号的形式接收请求,并且发送包含顾客姓名的应答。这是一个大大简化了的应用程序,并且为了保持简单,我们假定所有的数据都是文本的格式进行处理的。我们需要能够发送消息到现有的应用程序并接收响应,方式是通过扩展消息来建立客户端和现有消息传递应用程序的桥。
图 1. 简单的应用程序
图1 展示了组成了样本应用程序的部分。图中从左到右为:
-
客户端
- 调用以顾客编号的形式发送的发送者Bean。在我们的示例中,客户端将等待发送者 Bean从响应消息中返回顾客姓名。
-
发送者 Bean
- 以包含顾客编号的单个字符串的形式从客户端提取输入。发送者Bean 然后格式化 JMS 消息,它将 JMS 消息编写成消息队列 Q1。发送者 Bean 还处理从队列 Q2检索响应。对于响应,发送者 Bean从队列中读取消息,然后解析消息以提取响应字符串。通常如下调用发送者Bean:
customerName = sb.getCustomerName(custID);
|
-
现有的消息传递应用程序
- 从队列 Q1中检索消息请求,对于它接收的每个消息,都发送一个响应到队列Q2。在我们的简单示例中,它将发送顾客姓名来响应包含顾客编号的请求。
构建扩展消息传递服务组件
要构建简单的发送者Bean和更改扩展消息传递所用的标准格式化器和解析器,我们将执行以下步骤:
- 创建发送者Bean 来处理请求和响应。
- 创建格式化器来构造 JMS文本消息。
- 创建解析器来解释 JMS文本消息并返回响应。
- 通过修改发送者 Bean 来将所有的部分连接在一起,以便使用新的格式化器和解析器。
步骤 1. 创建发送者 Bean
在这一步中,我们将创建 EJB项目和 EMS 发送者 Bean。我们将使用 EMS 向导来创建发送者 Bean。
- 启动 ApplicationDeveloper。
- 选择
Window => Open perspective => J2EE进入 J2EE 透视图。如果 J2EE 不是所列选项之一,就选择
Other => J2EE。
- 选择
File => New => EJB Project。接着选择
Create EJB 2.0 project,然后选择
Next。
- 在对话框中输入下列值(图 2):
-
Project name:
Sender
-
Enterprise applicationproject
:
New
-
New project name
:
emsText
- 单击
Finish。
图 2. 创建 EJB 项目
- 选择
File => New => Other,然后选择
Extended Messaging => Create Sender Bean,单击
Next。
- 从 EJB 项目下拉列表中选择
Sender。
- 单击
Create sender启动 Create Sender 向导(图 3)。
- 在下一个对话框中输入下列值:
-
Bean name:
Sender
-
Default package:
emsText
图 3. 创建 Enterprise Bean
- 单击
Next,然后单击
Finish。您将返回到 Create Sender bean 向导。
- 现在输入下列值,如图 4 所示。
- 单击
Next。
图 4.创建发送者 Bean
- 发送者响应对话框会出现(图 5)。输入下列值:
-
Response timeout option:
No timeout
-
Method name:
getCustomerName
- 选择
Add to remote interface,以将新生成的方法提升为发送者 Bean 的远程接口。这使它能够被客户端调用。
- 单击
Next。
图 5. Send with response 方法
现在,我们需要指定数据映射选项。在本例中,我们将通过键入参数来显式地定义方法签名,并且返回发送方法的值(也就是要发送的对象类型以及您期望作为响应返回什么)。
- 选择
Define method signature,然后单击
Next。
- 在 Define the sending method signature 对话框中(图 6),输入下列值:
-
Return Type:
String
-
Parameter List
:
String custID
- 单击
Next。
图 6. 定义发送方法签名
- summary 屏幕(图 7)出现。检查是否所有的值都已经正确输入,然后单击
Finish。
图 7. Summary 屏幕
当向导完成时,会生成发送者 Bean,包括 getCustomerName() 方法,该方法也添加到远程接口中。还会创建输出端口的资源引用及其 WebSphere 绑定。清单 1 展示了在发送者 Bean 发送者(Sender)中为 getCustomerName() 方法生成的代码。在本文后面我们将回头参阅这段代码。
清单
1. 发送者 Bean 为方法 getCustomerName() 生成的代码
1. public String getCustomerName(String custID) throws CMMException
2. {
3. // Create sender
4. CMMSender sender = CMMFactory.createSender("ems/oPort");
5. try {
6. // Create message factory7. MessageFactory factory = sender.getMessageFactory();
8. // Create formatter
9. CMMFormatter formatter = CMMFactory.createCMMFormatter(factory);
10. // Add parameters to the message
11. formatter.addStringParameter(custID);
12. // Get the message
13. Object request = formatter.getMessage();
14. // Set message type
15. sender.setRequestMessageType("TextMessage");
16. // Send request receive response
17. Object response = sender.sendRequestReceiveResponse(request);
18. // Create parser
19. CMMParser parser = CMMFactory.createCMMParser(response);
20. // Process exception
21. if (parser.containsException()){
22. try{
23. throw parser.getException();
24. }
25. catch(CMMException exc){ throw exc;}
26. catch(Exception exc){ throw new CMMException("Unexpected Exception", exc);}
27. }
28. // Extract response and return
29. return parser.getStringParameter();
30. }
31. finally
32. {
33. sender.close();
34. }
35. }
|
步骤 2. 创建格式化器
现在,我们已经创建了发送者 Bean,下一步就是创建该 Bean 将使用的新格式化器。如果我们考察一下发送者 Bean 中为 getCustomerName() 方法生成的代码,我们就可以看出,发送者 Bean 执行三种与格式化器的交互:
- 创建格式化器,而它又创建 JMS 消息。请参见上面的发送者方法代码中的第 9 行。
- 通过调用 addStringParameter() 方法将传送给发送者 Bean 的参数添加到消息中。请参见第 11 行。
- 通过调用 getMessage() 方法检索已格式化的 JMS 消息。请参见第 13 行。
为了简单起见,我们假定所有的数据都是以文本格式处理的,并且 JMS 消息将使用 TextMessage 格式。
我们需要用 Application Developer 创建 Java 类(命名为 FormatText),为了简单起见,将其添加到包含发送者 Bean 的 emsText 包中。
在下面的步骤中,我们将创建该 Java 类。
- 在 Application Developer 中的
J2EE Navigator视图中,展开
Sender项目文件夹,然后选择包
emsText。
- 选择
New => Class。
- 在 Java Class 对话框中输入下列值:
Name:
FormatText 。
- 单击
Finish。
用清单 2 中所示的代码来代替 FormatText.java 类文件中的框架代码,这实现了处理 String 参数的格式化器,然后保存该文件。代码之后有对代码行的描述。
清单
2. 格式化器 FormatText
的代码
1. package emsText;
2. import javax.jms.TextMessage;
3. import javax.jms.JMSException;
4. import com.ibm.websphere.ems.CMMException;
5. import com.ibm.ws.spi.ems.transport.MessageFactory;
6. import com.ibm.ws.spi.ems.format.Formatter;
7. public class FormatText implements Formatter {
8. private String msgText = null;
9. private TextMessage message;
10. public FormatText(MessageFactory factory) throws CMMException
11. {
12. message = (TextMessage)factory.createMessage("javax.jms.TextMessage");
13. }
14. public void addStringParameter(String s)
15. {
16. if (msgText == null)
17. {
18. msgText = s;
19. }
20. else
21. {
22. msgText = msgText + s;
23. }
24. }
25. public Object getMessage() throws CMMException
26. {
27. try
28. {
29. message.setText(msgText);
30. }
31. catch (JMSException exc)
32. {
33. System.out.println("JMS Exception " + exc);
34. throw new CMMException("Format error, reason: ", exc);
35. }
36. msgText = null;
37. return message;
38. }
39. public void setException(Exception exc) throws CMMException
40. {
41. }
42. }
|
|
行号
|
注解
| | 1 | 定义您将使用的包名,emsText。 | | 2 - 6 | 定义格式化器中所需的 JMS 和扩展消息传递类。 | | 7 | 命名类 FormatText。格式化器接口要求实现 getMessage() 和 setException() 方法。 | | 8 - 9 | 定义组成 JMS 消息的变量。 | | 10 - 13 | 定义构造器方法。传送给构造器一个扩展消息传递工厂对象,格式化器然后使用该对象来创建 JMS 消息。在这种情况下,传送给 createMessage() 方法的参数
javax.jms.TextMessage 定义了创建的 JMS 消息的类型。其他的 JMS 消息可以通过传入适当的字符串来创建。
| | 14 - 24 | 定义 addStringParameter() 方法。每个传送给发送者 Bean 的 String 参数都依次传送给格式化器中的 addStringParameter() 方法。这种方法简单地将字符串连接在一起,形成一个字符串,在完成时将在JMS 消息中设置它。 | | 25 - 38 | 定义 getMessage() 方法。当传送给发送者 Bean 的所有参数都已经通过 addStringParameter() 方法传送给发送者 Bean 时,通过发送者 Bean 来调用这种方法。try 代码块用于给 JMS 消息添加字符串。应该会引起 JMS 错误,然后 catch 代码块将该错误报告给日志文件,并且抛出扩展消息传递异常。最后,保存的消息设置为空,而 JMS 消息返回到发送者 Bean。 | | 39 - 41 | 定义 setException() 方法。如果错误发生,就用这种方法来设置消息中的异常。在本例中,发送者 Bean 并不调用该方法。 |
在本例中,我们故意假定只有 String 对象将传送给格式化器。由于发送者 Bean 可以接受许多不同的参数类型,所以它们可以通过不同的 add
XxxxParameter 方法生成,其中
Xxxx取决于数据类型。举例来说,如果 int 数据类型定义为传送给发送者 Bean 的参数之一,则将在发送者 Bean 中生成 addIntParameter() 方法调用,格式化器将必须对其进行处理。当它们在发送者 Bean 中生成时,您只需在格式化器中定义 add
XxxxParameter 方法。
步骤 3. 创建解析器
由于我们期望接收对请求的响应,所以下一步就是创建一个解析器来处理响应。此外,如果我们考察一下生成用来处理发送者 Bean 的 getCustomerName() 方法中的响应,我们可以看出在发送者 Bean 和解析器之间有四个交互:
- 创建解析器并传送响应 JMS 消息。请参见上面的清单 1 中的第 19 行。
- 通过调用 containsException() 方法检查在响应消息中是否返回了任何异常。请参见第 21 行。
- 通过调用 getException() 方法来检索异常(如果返回了一个异常的话)。请参见第 23 行。
- 通过调用 getStringParameter() 方法从 JMS 消息中获取值。请参见第 29 行。
接下来,我们需要用 Application Developer 创建一个 Java 类(命名为 ParseText),为了简单起见,我们将其添加到包含发送者 Bean 和 FormatText 类的 emsText 包。
在这一部分中,我们将创建 Java 类 ParseText。
- 在 Application Developer 的
J2EE Navigator视图中,展开
Sender项目文件夹并选择包
emsText。
- 选择
New => Class。
- 在 Java Class 对话框中输入下列值:
Name:
ParseText 。
- 单击
Finish。
- 用清单 3 中所示的代码来代替 ParseText.java 类文件中的框架代码,这会实现处理 String 响应的简单解析器,然后保存该文件。代码之后有对代码行的描述。
清单
3. 解析器 ParseText 的代码
1. package emsText;
2. import javax.jms.TextMessage;
3. import javax.jms.JMSException;
4. import com.ibm.websphere.ems.CMMException;
5. import com.ibm.websphere.ems.FormatException;
6. import com.ibm.ws.spi.ems.format.Parser;
7. public class ParseText implements Parser {
8. private TextMessage message;
9. public ParseText(Object message) throws FormatException
10. {
11. if (message instanceof TextMessage)
12. {
13. this.message = (TextMessage)message;
14. }
15. else
16. {
17. throw new FormatException("Unsupported message type");
18. }
19. }
20. public String getStringParameter() throws CMMException
21. {
22. String parameter = null;
23. try
24. {
25. parameter = this.message.getText();
26. }
27. catch (JMSException exc)
28. {
29. System.out.println("JMS Exception " + exc);
30. throw new CMMException("Error parsing message: ", exc);
31. }
32. return (parameter);
33. }
34. public boolean containsException()
35. {
36. return (false);
37. }
38. public Exception getException()
39. {
40. return null;
41. }
42.
|
|
行号
|
注解
| | 1 | 定义您将使用的包名,emsText。 | | 2 - 6 | 定义解析器中所需的 JMS 和扩展消息传递类。 | | 7 | 将该类命名为 ParseText。该解析器接口需要实现 containsException() 和 getException() 方法。 | | 8 | 定义用于保存在构造器方法中传入的 JMS 消息的变量。 | | 9 - 19 | 定义构造器方法。将响应 JMS 消息传送给构造器。检查该消息以确定其类型是否是 TextMessage。如果是,就将其保存在已定义的变量消息中。如果不支持该类型,则抛出 FormatException。 | | 20 - 33 | 定义 getStringParameter() 方法。当调用这个方法时,解析器从 JMS 消息返回整个文本消息。如果 JMS 异常出现,就报告它,然后抛出新的扩展消息传递异常。 | | 34 - 37 | 定义 containsException() 方法。当构造应答时,通过扩展消息传递,检索的响应消息可以包含出现在远程端的异常的细节。在我们的例子中,远程端不是利用扩展消息传递的应用程序,因此,我们不能获取任何返回的异常。这种方法总会返回错误。 | | 38 - 41 | 定义 getException() 方法。这种方法返回在响应中接收的异常的细节。由于 containsException() 方法总会返回错误,所以将从不调用这种方法。它返回空的异常。 |
为了简单起见,我们将假定在解析器代码中响应将作为单个 String 返回。当发送者 Bean 生成时,它可以生成不同于 getStringParameter 的方法调用。举例来说,如果返回类型指定为 int 数据类型,它可以生成 getIntParameter。如果情况确实如此,解析器就必须提供附加的方法并且处理该数据。
步骤 4. 将各部分连接在一起
现在,我们已经定义了发送者 Bean、格式化器和解析器,剩下的事情就是修改发送者 Bean 来使用新的格式化器和解析器。
替换发送者
Bean
中的标准格式化器
在发送者 Bean 的 getCustomerName() 方法中(清单 1),我们需要更改第 9 行,该行创建了格式化器。找到下列行:
8. // Create formatter
9. CMMFormatter formatter = CMMFactory.createCMMFormatter(factory);
|
将第 9 行更改为:
9. FormatText formatter = new FormatText(factory);
|
替换发送者
Bean
中的标准格式化器
在发送者 Bean 的 getCustomerName() 方法中(清单 1),我们需要更改第 19 行,该行创建了格式化器。找到下列行:
18. // Create parser
19. CMMParser parser = CMMFactory.createCMMParser(response);
|
将第 19 行更改为:
19. ParseText parser = new ParseText(response);
|
接下来,保存所有的文件,并且通过选择
Project => Rebuild All来构建项目。
既然我们已经使用新的格式化器和解析器开发了我们的发送者 Bean,剩下的就是根据现有的消息传递系统来测试它了。
结束语
本文演示了您可以如何利用扩展消息传递来连接现有的消息传递应用程序,在这些应用程序中,消息格式已经定义而内部扩展消息传递格式却不合适。这将使新的应用程序能够使用通过扩展消息传递提供的工具和运行时构件,并且与不能修改自己的消息格式的现有应用程序交换消息。
参考资料
-
Simplify Applications by Using WebSphere Extended Messaging.
- 这篇文章概述了扩展消息传递以及如何利用它来简化应用程序。
-
Creating Extended Messaging Applications for WebSphere Application Server Enterprise
- 这篇文章提供了描述如何构建和测试发送者-接收者 EMS 应用程序的教程。
关于作者  | |  |
Vernon Green
IBM WebSphere Application Server
开发方面的高级软件工程师,居住在英国的 Hursley。Vernon
是一名软件构架师兼开发人员,复杂开发 Application
Server
中的功能,他在为分布式环境开发软件方面有大量的经验。
|
对本文的评价
|