使用 GlassFish 实现 JMS 消息传递 GlassFishJMS
程序员文章站
2024-03-21 08:58:34
...
对于当今的电子商务来说,在分布式异构系统之间进行通信已经成为不可避免的需求。现在,随着 Java 消息服务 (JMS) 等消息标准的出现,开发松散耦合的同步或异步通信分布式系统(用于交换商务数据和事件)已经非常简单。
本文主要介绍如何使用 JMS 和消息驱动 Beans( Message-Driven Beans ,MDB )进行消息传递,特别是企业消息传递的详细信息。本文还将介绍 GlassFish 应用程序服务器的消息传递功能及其配置设置。为了更好地说明这些技术,在此还将讨论一个简单的实时应用案例及其实施细节。
消息传递
简单地说,消息传递是在双方间发生的通信。企业消息传递可以定义为两个软件组件或两个应用程序之间的通信。消息传递就像发送物理信件一样简单,信件发送者准备好消息、给出正确的邮寄地址,然后选择正确的邮寄服务。在企业消息传递中也要有一个以特定格式向目的地发送消息的消息发送者。目的地是面向消息的中间件或者是帮助交换消息的消息队列。而且还要有从目的地同步或异步接收消息的消息用户。并不强制规定发送者和接受者在同样的时间在线,也不强制规定他们必须相互认识才能交换信息。
消息传递系统的关键好处是保持分布式系统松懈地耦合。 松懈耦合使消息传递解决方案不同于其他的紧密耦合通信解决方案(例如,远程方法调用 (Remote Method Invocation,RMI)、公共对象请求代理体系结构(Common Object Request Broker Architecture,CORBA)等等)。
JMS 消息传递
Java 消息服务( Java Messaging Service )是一个建立在 Java 2 平台企业版本 ( Java 2 Platform, Enterprise Edition,Java EE )之上的消息传递 API。它定义了一组创建、发送和接收消息常用接口。
JMS 支持两种消息传递模式:
点到点( PTP )模式
发布-订阅模式
点到点消息传递模式依赖于消息队列概念,也就是,将其中的消息地址写到称作队列 的特定目的地。接受者从队列中使用消息,对其进行处理,然后确认收到消息。点到点消息传递模式的主要特征有:
每条消息有一个使用者。
在发送者和接收者之间没有时间依赖性。
在发布-订阅模式中,消息地址被写到一个称作主题 的目的地。消息生成器发布消息,使用者订阅消息。这种消息传递模式的主要特征有:
每条消息有多个使用者。
在发送者和接受者之间存在时间依赖性,也就是,使用者必须积极接受消息。即使订阅者暂停使用,JMS API 也允许订阅者创建持久订阅方式 来接收消息。使用持久订阅方式,JMS 提供器一直保留订阅的消息直到收到取消订阅的请求或订阅过期。
消息类型
JMS 支持 5 种消息类型:
文本: 一条简单的文本消息或一个
java.lang.String
对象。
对象:
Serializable(可序列化)
Java 对象。
字节: 简单的字节流。
映射表: 一组名称 —— 值对集合。
数据流: 原始值数据流。
消息驱动 Beans(Message-Driven Beans,MDB)
消息驱动 bean 的工作是帮助异步处理消息。MDB 担任 JMS 消息的侦听器。JMS 客户机不能找到 MDB 并直接调用方法;相反,客户机将消息发送到 MDB 正在侦听的目的地。当接收到消息时,EJB 容器调用 MDB 的
onMessage
方法。这种方法通常将消息转换为 5 种 JMS 消息类型中的 1 种,并作为应用程序的业务逻辑进行处理。MDB 以异步模式运行,并且是无状态、事务感知的。这些功能使 MDB 更具较高的伸缩性,为企业发送消息提供了健壮的解决方案。
GlassFish 中的 JMS 支持
GlassFish 是开发和部署 Java EE 应用程序和 web 服务的开源应用程序服务器。该服务器与 Java Enterprise Edition 5 ( Java EE 5 ) 兼容,实际上是 Java EE 5 的参考实现。
GlassFish 通过提供一个完全集成的 JMS 提供器为 JMS 消息传递提供巨大支持。Java Message Service API 通过将 Sun Java System Message Queue 软件集成到 GlassFish,并提供透明的 JMS 消息传递支持得以实现。
GlassFish 支持两种 JMS 资源: 连接工厂和目的地资源。
连接工厂是一种由 JMS 客户机使用用于创建 JMS 提供器连接的对象。连接工厂有 3 种类型:
连接工厂: 由点到点以及发布 —— 订阅消息传递模式使用。
队列连接工厂: 由点到点消息传递模式使用。
主题连接工厂: 由发布 —— 订阅消息传递模式使用。
目的地是一个对象,JMS 消息生成器使用这个对象以便将消息邮寄到该对象;它也是一种资源,JMS 消息使用者使用来自该资源的消息。支持目的地的类型有:
队列: 队列是点到点通信的目的地。
主题: 主题是发布 —— 订阅通信的目的地。
下列内容是 GlassFish 支持的一些 JMS 连接功能。
连接池
GlassFish 服务器自动汇集 JMS 连接。用户可以使用 GlassFish 管理控制台或
asadmin
命令设置连接池属性。在创建连接工厂时配置连接池细节。由 GlassFish 支持的一些连接池参数有:
连接池的初始值和最小值: 在连接池中指示初始连接的数量。这也是给连接池设置的最小连接数量。
连接池最大值: 在连接池中指示可用连接的最大数量。
连接池重新调整的数量: 当连接池达到空闲超时时,应该移除的连接数量。
空闲超时: 在连接池中连接可以保持空闲的最长时间。
最大等待时间: 在发送连接超时以前系统等待的最长时间。
运行失败行为: 在发生运行失败的情况下,连接可能被中断然后重新连接。
事务支持: 事务支持的级别。支持事务的类型有“本地事务”、“XA 事务”和“没有事务”。
连接验证: 如果选择了该属性,将连接传递到应用程序之前要对其进行验证。
连接故障转移
如果连接丢失,该功能启用应用程序服务器重新连接到消息代理。如果启用重新连接并且无法连接初始消息代理,那么应用程序服务器将尝试重新连接另一个可用代理。用户可以配置重试数量和重试之间的时间间隔。
访问应用程序中的 JMS 资源
在 GlassFish 中, 可以以两种方式访问连接工厂和目的地: 使用 Java 命名和目录接口(Java Naming and Directory InterfaceTM,JNDI)查找或使用注释。
JNDI 查找
JMS 客户机使用 JNDI API 查找连接工厂和消息目的地。
InitialContext jndi = new InitialContext();
// Lookup queue connection factory
QueueConnectionFactory qFactory = (QueueConnectionFactory)jndi.
lookup("webTrackerConnFactory");
// Lookup queue
Queue queue = (Queue)jndi.lookup("webTrackerQueue");
注释
在 Java SE 5.0 中介绍过注释,它是编程的声明性样式。注释与元标记一样,可以应用于类、构造函数、方法、变量等等。
注释
@Resource
用于查找连接工厂和目的地。在 Web 应用程序中,如果注释放在变量上,那么 servlet 容器将注入请求的资源;也就是,注释变量将在为请求提供服务之前使用适当的值进行预先填充。
@Resource(name="connFactory", mappedName="webTrackerConnFactory")
private QueueConnectionFactory qFactory;
@Resource(name="jmsQueue", mappedName="webTrackerQueue")
private Queue queue;
使消息传递正常运行
迄今为止,我们已经讨论了如何将 JMS 和 MDB 一起运行来实施异步消息传递。我们也了解了 GlassFish 的功能和它提供的 JMS 支持。现在,我们将了解如何在称作 “Web Access Tracker”的实时应用案例的帮助下发挥这些技术的作用。
Web Access Tracker 帮助站点管理员或商务使用者监测用户请求统计,例如一天的访问数量、经常访问的页面、页面/服务请求时间、要求服务的时间等等。在下一部分我将解释一个解决方案,在不破坏实际应用程序性能的情况下收集被用户访问的每个网页的访问信息。您可以在参考资料部分的链接中下载该样例的完整资源代码,以及它的可部署二进制文件。
解决方案
在其他技术(例如 Java XML 绑定架构 、 Java Server Faces (JSF)、 servlet 筛选器等等)的帮助下,选择基于 JMS 的消息传递解决方案捕获和处理 web 访问数据。GlassFish 为 JMS 消息传递以及部署应用程序提供了支持。
演示应用程序使用 servlet 筛选器截取用户请求并从请求消息标题中提取所需信息。然后,该信息将作为 JMS 消息邮寄给消息队列。消息通过消息驱动 bean 这一 JMS 消息使用者异步传递。从消息中收集的数据将被永久保留到 XML 数据存储区中。JAXB 是用于访问和将数据存储到 XML 文件的首选技术。使用 JSF 开发用户界面。
系统需求
Java EE 5
GlassFish 应用程序服务器
其他库文件: Apache 服务器的 commons-beanutils.jar、 commons-collections.jar 和 commons-logging.jar 文件
Servlet 筛选器
servlet 筛选器或者筛选向资源发出的请求,或者筛选从资源中发出的响应,或者对两者同时执行筛选任务。您可以通过实施
Filter
界面书写自己的筛选器。该界面中的主要方法是执行核心筛选行为的
doFilter
方法。您还可以在筛选类中实施
初始化
和
销毁
方法。这些方法由 servlet 容器调用用于初始化和销毁筛选器。
样例应用程序使用称作
WebTrackerFilter
的 servlet 筛选器捕获属于请求的信息;也就是,被请求页面的 URL 、主机 IP、请求时间等等。从请求收集的信息将作为数据传输对象( Data Transfer Object,DTO )发送给消息分发器做进一步处理。
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)
throws java.io.IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest)
request;
HttpServletResponse httpServletResponse = (HttpServletResponse)
response;
// Extract required header parameters and set it in DTO
RequestData data = new RequestData();
data.setQueryString(request.getQueryString());
data.setRemoteAddress(request.getRemoteAddr());
data.setRequestURI(request.getRequestURI());
data.setResponseTime(elapsedTime);
data.setUserAgent(request.getHeader("User-Agent"));
// send the data to message dispatcher
messagedispatcher.sendRequestData(data);
chain.doFilter(httpServletRequest, httpServletResponse);
}
配置 Servlet 筛选器
我们已经了解如何编写自定义筛选器程序,那么现在我们需要在 web 应用程序部署的描述符中配置筛选器类;也就是 web.xml 文件。筛选器类可以在部署描述符文件中通过添加
<filter>
元素进行配置。该元素创建筛选器的名称以及筛选器实现类的名称。使用
<filter-mapping>
元素可以将 servlet/URL 模式映射到筛选器中。这将导致
WebTrackerFilter
被 URL 模式为 *.html 的所有请求调用。下列元素显示如何配置
WebTrackerFilter
。
<filter>
<filter-name>WebTrackerFilter</filter-name>
<filter-class>demo.webtracker.filter.WebTrackerFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>WebTrackerFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
异步消息传递
使用
WebTrackerFilter
可获得请求信息;下一步是开发 JMS 客户机以 JMS 消息的形式将数据邮寄到消息队列。选择点到点模式进行消息传递。
为典型 JMS 客户机开发的代码片断如下所示。您需要使用从 JNDI 树中检索到的 JMS 管理对象
InitialContext
,执行连接工厂和目的地的 JNDI 查找操作。另一种选择是,在运行时使用注释来注入 JMS 资源。在代码中使用的
webTrackerConnFactory
和
webTrackerQueue
分别是连接工厂和目的地的 JNDI 名称。客户机假设这些资源已经在 GlassFish 中进行了配置,那么配置需要在执行这些代码之前完成。参考 “在 GlassFish 中配置 JMS 资源” 部分来了解如何在 GlassFish 中配置 JMS 资源。
在连接工厂上使用
createQueueConnection
方法,可以创建一个到 JMS 服务提供器的
Connection(连接)
。连接提供对基础消息传输的访问。
接下来的任务是使用
createSession
方法在
Connection
对象上创建 JMS
Session(会话)
。会话是生成和使用消息的单线程上下文。
createSession
的第一个参数决定是否执行 JMS 会话,第二个参数指示消息确认模式。在本样例中,不执行会话,而是在接受消息时自动确认会话。
您还有必要创建一个用于将消息发送到目的地的
MessageProducer
。最后一步是创建 JMS
ObjectMessage
并设置请求数据为消息。然后将消息发送到消息队列。
// Get queue connection factory
QueueConnectionFactory qFactory = (QueueConnectionFactory)jndi.
lookup("webTrackerConnFactory");
// Get queue
Queue queue = (Queue)jndi.lookup("webTrackerQueue");
// Get queue connection
QueueConnection qConn = (QueueConnection)qFactory.
createConnection();
// Get session
Session session = qConn.createSession(false,
Session.AUTO_ACKNOWLEDGE);
// Set the JMS message
ObjectMessage msg = session.createObjectMessage();
msg.setObject(data);
// Send JMS message
session.createProducer(jmsLoc.getMessageQueue()).send(msg);
消息驱动 Beans
消息驱动 bean 将作为 JMS 消息的侦听器。当消息到达队列时,容器调用 MDB 的
onMessage()
方法。该方法将消息转换为数据原始消息类型(也就是,
ObjectMessage
),然后进行格式化并永久保存到数据存储区。MDB 实现
MessageDrivenBean
界面和消息类型支持的
MessageListener
界面(可选);也就是,如果 bean 支持 JMS,
javax.jms.MessageListener
将是要实现的界面。
下面给出了接收 JMS 消息的代码:
public void onMessage(Message message) {
if (message instanceof ObjectMessage) {
RequestData data = (RequestData)((ObjectMessage)message).
getObject();
// persist data
}
}
如果您正在使用 Java EE 5,MDB 可以使用
@MessageDriven
注释进行注释。该注释包含一个
mappedName
元素,该元素指定 bean 将要侦听的消息队列的 JNDI 名称 。MDB 可以使用
@Resource
注释注入
MessageDrivenContext
资源。在这种情况下,您不必声明 EJB 部署描述符的 bean 细节。MDB 的最前面几行如下所示:
@MessageDriven(mappedName = "webTrackerQueue")
public class WebTrackerEJB implements MessageListener {
/** Context for the MDB. */
@Resource
private MessageDrivenContext mdbContext;
.........
Java XML 绑定架构(JAXB)
从用户请求中捕获的数据可以永久存储到数据存储区。出于简单,在本样例中 XML 数据存储区已经选定。JAXB 是解编和编组 XML 文档的首选技术。JAXB 提供了一种方便途径,通过将 XML 模式绑定到 Java 表示并使用 Java 对象访问和处理 XML 内容。XML 的 Java 对象表示可以按照下列步骤创建:
准备数据存储区的 XML 结构。
创建 XML 模式定义(XML Schema Definition,XSD)。参考在 webtracker-src/config 目录中提供的 webAccess-sample.xml 和 webAccess.xsd 文件,寻找在本样例中使用的关于 XML 数据存储区和模式定义的更多细节。
使用 JAXB 的
XJC
命令来生成提供 XSD 所需的 Java 文件。在 <GlassFish_Root>/bin 目录中包含 XJC.bat 文件。
XML 文档解编
数据解编是从 XML 文档中创建对象树的过程。要解编 XML 文档,您需要创建
JAXBContext
。
JAXBContext
提供管理 XML/Java 绑定信息的抽象。含有模式派生类的 Java 包应作为一个参数传递,以便创建
JAXBContext
。使用
JAXBContext
创建的 XML 解组器可用于检索 XML 文档的根元素。
解编 XML 数据文件的代码如下所示:
// create a JAXBContext
JAXBContext jc = JAXBContext.newInstance
("demo.webtracker.xmlgen");
// create UnMarshaller
Unmarshaller unmarshaller = jc.createUnmarshaller();
JAXBElement rootElement = (JAXBElement)unmarshaller.unmarshal
(new FileInputStream(xmlDataStorePath));
// Get the root element
WebAccess access = (WebAccess)rootElement.getValue();
XML 文档编组
编组过程是从内容树中创建 XML 文档。要编组 XML 文档,您需要创建
JAXBContext
。
编组 XML 数据文件的代码如下所示:
// create a JAXBContext
JAXBContext jc = JAXBContext.newInstance
("demo.webtracker.xmlgen");
// create a Marshaller and marshal to webAccessLog.xml
Marshaller marshaller = jc.createMarshaller();
// Converts java object to XML data
marshaller.marshal(accessData, new File(xmlDataStorePath));
webTracker.ear 文件并上传它。
单击 OK 按钮。
将 webtracker-src/config 中提供的 webAccess.xml 文件复制到任意本地目录下。添加称作
xmlstore.path
的系统属性,并将它指向该目录。您可以从 GlassFish 管理控制台中选择 Application Server -> JVM Settings -> JVM Options -> Add JVM Option 设置系统属性。样例条目为 -Dxmlstore.path=C:/temp/webAccessLog.xml。
部署描述符
ejb-jar.xml
用于样例应用程序的 ejb-jar.xml 部署描述符如下所示: 您必须将
WebTrackerEJB
声明为消息驱动 bean 并将
javax.jms.Queue
作为消息目的地的类型。如果您已经在 Java EE 5 中注释了 MDB,那么可以忽略该配置。
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd">
<display-name>Web Tracker App</display-name>
<enterprise-beans>
<message-driven>
<display-name>WebTrackerEJB</display-name>
<ejb-name>WebTrackerEJB</ejb-name>
<ejb-class>demo.webtracker.ejb.WebTrackerEJB</ejb-class>
<transaction-type>Container</transaction-type>
<message-destination-type>
javax.jms.Queue
</message-destination-type>
</message-driven>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>WebTrackerEJB</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
sun-ejb-jar.xml
用于样例应用程序的 sun-ejb-jar.xml 部署描述符如下所示: 连接工厂和队列的 JNDI 名称将在描述符中声明。如果您已经在 Java EE 5 中注释了 MDB,那么可以忽略该配置。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-ejb-jar PUBLIC "-//Sun Microsystems, Inc.
//DTD Application Server 9.0 EJB 3.0//EN"
"http://www.sun.com/software/appserver/dtds/sun-ejb-jar_3_0-0.dtd">
<sun-ejb-jar>
<enterprise-beans>
<ejb>
<ejb-name>WebTrackerEJB</ejb-name>
<jndi-name>webTrackerQueue</jndi-name>
<mdb-connection-factory>
<jndi-name>webTrackerConnFactory</jndi-name>
</mdb-connection-factory>
</ejb>
</enterprise-beans>
</sun-ejb-jar>
测试应用程序
现在已经成功部署了应用程序,准备进行测试。可以使用下列 URL 访问应用程序的主页:
http://<host name>:<port number>/webTracker
图 1 显示了主页的快照。
图 1. Web Access Tracker 的主页
web 访问报告可以通过单击在左侧导航条提供的 Web Access Info 链接查看。在图 2 中显示该报告。
图 2. Web 访问报告
结束语
在本文中我们探索了 JMS 消息传递和 GlassFish 的消息传递功能。毫无疑问,JMS 已经赢得了广泛的行业支持并成为功能强大的企业消息传递解决方案。在本文中解释的应用案例,真正地演示了 JMS 使用消息传递解决方案解决实时问题的美妙之处。
本文主要介绍如何使用 JMS 和消息驱动 Beans( Message-Driven Beans ,MDB )进行消息传递,特别是企业消息传递的详细信息。本文还将介绍 GlassFish 应用程序服务器的消息传递功能及其配置设置。为了更好地说明这些技术,在此还将讨论一个简单的实时应用案例及其实施细节。
消息传递
简单地说,消息传递是在双方间发生的通信。企业消息传递可以定义为两个软件组件或两个应用程序之间的通信。消息传递就像发送物理信件一样简单,信件发送者准备好消息、给出正确的邮寄地址,然后选择正确的邮寄服务。在企业消息传递中也要有一个以特定格式向目的地发送消息的消息发送者。目的地是面向消息的中间件或者是帮助交换消息的消息队列。而且还要有从目的地同步或异步接收消息的消息用户。并不强制规定发送者和接受者在同样的时间在线,也不强制规定他们必须相互认识才能交换信息。
消息传递系统的关键好处是保持分布式系统松懈地耦合。 松懈耦合使消息传递解决方案不同于其他的紧密耦合通信解决方案(例如,远程方法调用 (Remote Method Invocation,RMI)、公共对象请求代理体系结构(Common Object Request Broker Architecture,CORBA)等等)。
JMS 消息传递
Java 消息服务( Java Messaging Service )是一个建立在 Java 2 平台企业版本 ( Java 2 Platform, Enterprise Edition,Java EE )之上的消息传递 API。它定义了一组创建、发送和接收消息常用接口。
JMS 支持两种消息传递模式:
点到点( PTP )模式
发布-订阅模式
点到点消息传递模式依赖于消息队列概念,也就是,将其中的消息地址写到称作队列 的特定目的地。接受者从队列中使用消息,对其进行处理,然后确认收到消息。点到点消息传递模式的主要特征有:
每条消息有一个使用者。
在发送者和接收者之间没有时间依赖性。
在发布-订阅模式中,消息地址被写到一个称作主题 的目的地。消息生成器发布消息,使用者订阅消息。这种消息传递模式的主要特征有:
每条消息有多个使用者。
在发送者和接受者之间存在时间依赖性,也就是,使用者必须积极接受消息。即使订阅者暂停使用,JMS API 也允许订阅者创建持久订阅方式 来接收消息。使用持久订阅方式,JMS 提供器一直保留订阅的消息直到收到取消订阅的请求或订阅过期。
消息类型
JMS 支持 5 种消息类型:
文本: 一条简单的文本消息或一个
java.lang.String
对象。
对象:
Serializable(可序列化)
Java 对象。
字节: 简单的字节流。
映射表: 一组名称 —— 值对集合。
数据流: 原始值数据流。
消息驱动 Beans(Message-Driven Beans,MDB)
消息驱动 bean 的工作是帮助异步处理消息。MDB 担任 JMS 消息的侦听器。JMS 客户机不能找到 MDB 并直接调用方法;相反,客户机将消息发送到 MDB 正在侦听的目的地。当接收到消息时,EJB 容器调用 MDB 的
onMessage
方法。这种方法通常将消息转换为 5 种 JMS 消息类型中的 1 种,并作为应用程序的业务逻辑进行处理。MDB 以异步模式运行,并且是无状态、事务感知的。这些功能使 MDB 更具较高的伸缩性,为企业发送消息提供了健壮的解决方案。
GlassFish 中的 JMS 支持
GlassFish 是开发和部署 Java EE 应用程序和 web 服务的开源应用程序服务器。该服务器与 Java Enterprise Edition 5 ( Java EE 5 ) 兼容,实际上是 Java EE 5 的参考实现。
GlassFish 通过提供一个完全集成的 JMS 提供器为 JMS 消息传递提供巨大支持。Java Message Service API 通过将 Sun Java System Message Queue 软件集成到 GlassFish,并提供透明的 JMS 消息传递支持得以实现。
GlassFish 支持两种 JMS 资源: 连接工厂和目的地资源。
连接工厂是一种由 JMS 客户机使用用于创建 JMS 提供器连接的对象。连接工厂有 3 种类型:
连接工厂: 由点到点以及发布 —— 订阅消息传递模式使用。
队列连接工厂: 由点到点消息传递模式使用。
主题连接工厂: 由发布 —— 订阅消息传递模式使用。
目的地是一个对象,JMS 消息生成器使用这个对象以便将消息邮寄到该对象;它也是一种资源,JMS 消息使用者使用来自该资源的消息。支持目的地的类型有:
队列: 队列是点到点通信的目的地。
主题: 主题是发布 —— 订阅通信的目的地。
下列内容是 GlassFish 支持的一些 JMS 连接功能。
连接池
GlassFish 服务器自动汇集 JMS 连接。用户可以使用 GlassFish 管理控制台或
asadmin
命令设置连接池属性。在创建连接工厂时配置连接池细节。由 GlassFish 支持的一些连接池参数有:
连接池的初始值和最小值: 在连接池中指示初始连接的数量。这也是给连接池设置的最小连接数量。
连接池最大值: 在连接池中指示可用连接的最大数量。
连接池重新调整的数量: 当连接池达到空闲超时时,应该移除的连接数量。
空闲超时: 在连接池中连接可以保持空闲的最长时间。
最大等待时间: 在发送连接超时以前系统等待的最长时间。
运行失败行为: 在发生运行失败的情况下,连接可能被中断然后重新连接。
事务支持: 事务支持的级别。支持事务的类型有“本地事务”、“XA 事务”和“没有事务”。
连接验证: 如果选择了该属性,将连接传递到应用程序之前要对其进行验证。
连接故障转移
如果连接丢失,该功能启用应用程序服务器重新连接到消息代理。如果启用重新连接并且无法连接初始消息代理,那么应用程序服务器将尝试重新连接另一个可用代理。用户可以配置重试数量和重试之间的时间间隔。
访问应用程序中的 JMS 资源
在 GlassFish 中, 可以以两种方式访问连接工厂和目的地: 使用 Java 命名和目录接口(Java Naming and Directory InterfaceTM,JNDI)查找或使用注释。
JNDI 查找
JMS 客户机使用 JNDI API 查找连接工厂和消息目的地。
InitialContext jndi = new InitialContext();
// Lookup queue connection factory
QueueConnectionFactory qFactory = (QueueConnectionFactory)jndi.
lookup("webTrackerConnFactory");
// Lookup queue
Queue queue = (Queue)jndi.lookup("webTrackerQueue");
注释
在 Java SE 5.0 中介绍过注释,它是编程的声明性样式。注释与元标记一样,可以应用于类、构造函数、方法、变量等等。
注释
@Resource
用于查找连接工厂和目的地。在 Web 应用程序中,如果注释放在变量上,那么 servlet 容器将注入请求的资源;也就是,注释变量将在为请求提供服务之前使用适当的值进行预先填充。
@Resource(name="connFactory", mappedName="webTrackerConnFactory")
private QueueConnectionFactory qFactory;
@Resource(name="jmsQueue", mappedName="webTrackerQueue")
private Queue queue;
使消息传递正常运行
迄今为止,我们已经讨论了如何将 JMS 和 MDB 一起运行来实施异步消息传递。我们也了解了 GlassFish 的功能和它提供的 JMS 支持。现在,我们将了解如何在称作 “Web Access Tracker”的实时应用案例的帮助下发挥这些技术的作用。
Web Access Tracker 帮助站点管理员或商务使用者监测用户请求统计,例如一天的访问数量、经常访问的页面、页面/服务请求时间、要求服务的时间等等。在下一部分我将解释一个解决方案,在不破坏实际应用程序性能的情况下收集被用户访问的每个网页的访问信息。您可以在参考资料部分的链接中下载该样例的完整资源代码,以及它的可部署二进制文件。
解决方案
在其他技术(例如 Java XML 绑定架构 、 Java Server Faces (JSF)、 servlet 筛选器等等)的帮助下,选择基于 JMS 的消息传递解决方案捕获和处理 web 访问数据。GlassFish 为 JMS 消息传递以及部署应用程序提供了支持。
演示应用程序使用 servlet 筛选器截取用户请求并从请求消息标题中提取所需信息。然后,该信息将作为 JMS 消息邮寄给消息队列。消息通过消息驱动 bean 这一 JMS 消息使用者异步传递。从消息中收集的数据将被永久保留到 XML 数据存储区中。JAXB 是用于访问和将数据存储到 XML 文件的首选技术。使用 JSF 开发用户界面。
系统需求
Java EE 5
GlassFish 应用程序服务器
其他库文件: Apache 服务器的 commons-beanutils.jar、 commons-collections.jar 和 commons-logging.jar 文件
Servlet 筛选器
servlet 筛选器或者筛选向资源发出的请求,或者筛选从资源中发出的响应,或者对两者同时执行筛选任务。您可以通过实施
Filter
界面书写自己的筛选器。该界面中的主要方法是执行核心筛选行为的
doFilter
方法。您还可以在筛选类中实施
初始化
和
销毁
方法。这些方法由 servlet 容器调用用于初始化和销毁筛选器。
样例应用程序使用称作
WebTrackerFilter
的 servlet 筛选器捕获属于请求的信息;也就是,被请求页面的 URL 、主机 IP、请求时间等等。从请求收集的信息将作为数据传输对象( Data Transfer Object,DTO )发送给消息分发器做进一步处理。
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)
throws java.io.IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest)
request;
HttpServletResponse httpServletResponse = (HttpServletResponse)
response;
// Extract required header parameters and set it in DTO
RequestData data = new RequestData();
data.setQueryString(request.getQueryString());
data.setRemoteAddress(request.getRemoteAddr());
data.setRequestURI(request.getRequestURI());
data.setResponseTime(elapsedTime);
data.setUserAgent(request.getHeader("User-Agent"));
// send the data to message dispatcher
messagedispatcher.sendRequestData(data);
chain.doFilter(httpServletRequest, httpServletResponse);
}
配置 Servlet 筛选器
我们已经了解如何编写自定义筛选器程序,那么现在我们需要在 web 应用程序部署的描述符中配置筛选器类;也就是 web.xml 文件。筛选器类可以在部署描述符文件中通过添加
<filter>
元素进行配置。该元素创建筛选器的名称以及筛选器实现类的名称。使用
<filter-mapping>
元素可以将 servlet/URL 模式映射到筛选器中。这将导致
WebTrackerFilter
被 URL 模式为 *.html 的所有请求调用。下列元素显示如何配置
WebTrackerFilter
。
<filter>
<filter-name>WebTrackerFilter</filter-name>
<filter-class>demo.webtracker.filter.WebTrackerFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>WebTrackerFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
异步消息传递
使用
WebTrackerFilter
可获得请求信息;下一步是开发 JMS 客户机以 JMS 消息的形式将数据邮寄到消息队列。选择点到点模式进行消息传递。
为典型 JMS 客户机开发的代码片断如下所示。您需要使用从 JNDI 树中检索到的 JMS 管理对象
InitialContext
,执行连接工厂和目的地的 JNDI 查找操作。另一种选择是,在运行时使用注释来注入 JMS 资源。在代码中使用的
webTrackerConnFactory
和
webTrackerQueue
分别是连接工厂和目的地的 JNDI 名称。客户机假设这些资源已经在 GlassFish 中进行了配置,那么配置需要在执行这些代码之前完成。参考 “在 GlassFish 中配置 JMS 资源” 部分来了解如何在 GlassFish 中配置 JMS 资源。
在连接工厂上使用
createQueueConnection
方法,可以创建一个到 JMS 服务提供器的
Connection(连接)
。连接提供对基础消息传输的访问。
接下来的任务是使用
createSession
方法在
Connection
对象上创建 JMS
Session(会话)
。会话是生成和使用消息的单线程上下文。
createSession
的第一个参数决定是否执行 JMS 会话,第二个参数指示消息确认模式。在本样例中,不执行会话,而是在接受消息时自动确认会话。
您还有必要创建一个用于将消息发送到目的地的
MessageProducer
。最后一步是创建 JMS
ObjectMessage
并设置请求数据为消息。然后将消息发送到消息队列。
// Get queue connection factory
QueueConnectionFactory qFactory = (QueueConnectionFactory)jndi.
lookup("webTrackerConnFactory");
// Get queue
Queue queue = (Queue)jndi.lookup("webTrackerQueue");
// Get queue connection
QueueConnection qConn = (QueueConnection)qFactory.
createConnection();
// Get session
Session session = qConn.createSession(false,
Session.AUTO_ACKNOWLEDGE);
// Set the JMS message
ObjectMessage msg = session.createObjectMessage();
msg.setObject(data);
// Send JMS message
session.createProducer(jmsLoc.getMessageQueue()).send(msg);
消息驱动 Beans
消息驱动 bean 将作为 JMS 消息的侦听器。当消息到达队列时,容器调用 MDB 的
onMessage()
方法。该方法将消息转换为数据原始消息类型(也就是,
ObjectMessage
),然后进行格式化并永久保存到数据存储区。MDB 实现
MessageDrivenBean
界面和消息类型支持的
MessageListener
界面(可选);也就是,如果 bean 支持 JMS,
javax.jms.MessageListener
将是要实现的界面。
下面给出了接收 JMS 消息的代码:
public void onMessage(Message message) {
if (message instanceof ObjectMessage) {
RequestData data = (RequestData)((ObjectMessage)message).
getObject();
// persist data
}
}
如果您正在使用 Java EE 5,MDB 可以使用
@MessageDriven
注释进行注释。该注释包含一个
mappedName
元素,该元素指定 bean 将要侦听的消息队列的 JNDI 名称 。MDB 可以使用
@Resource
注释注入
MessageDrivenContext
资源。在这种情况下,您不必声明 EJB 部署描述符的 bean 细节。MDB 的最前面几行如下所示:
@MessageDriven(mappedName = "webTrackerQueue")
public class WebTrackerEJB implements MessageListener {
/** Context for the MDB. */
@Resource
private MessageDrivenContext mdbContext;
.........
Java XML 绑定架构(JAXB)
从用户请求中捕获的数据可以永久存储到数据存储区。出于简单,在本样例中 XML 数据存储区已经选定。JAXB 是解编和编组 XML 文档的首选技术。JAXB 提供了一种方便途径,通过将 XML 模式绑定到 Java 表示并使用 Java 对象访问和处理 XML 内容。XML 的 Java 对象表示可以按照下列步骤创建:
准备数据存储区的 XML 结构。
创建 XML 模式定义(XML Schema Definition,XSD)。参考在 webtracker-src/config 目录中提供的 webAccess-sample.xml 和 webAccess.xsd 文件,寻找在本样例中使用的关于 XML 数据存储区和模式定义的更多细节。
使用 JAXB 的
XJC
命令来生成提供 XSD 所需的 Java 文件。在 <GlassFish_Root>/bin 目录中包含 XJC.bat 文件。
XML 文档解编
数据解编是从 XML 文档中创建对象树的过程。要解编 XML 文档,您需要创建
JAXBContext
。
JAXBContext
提供管理 XML/Java 绑定信息的抽象。含有模式派生类的 Java 包应作为一个参数传递,以便创建
JAXBContext
。使用
JAXBContext
创建的 XML 解组器可用于检索 XML 文档的根元素。
解编 XML 数据文件的代码如下所示:
// create a JAXBContext
JAXBContext jc = JAXBContext.newInstance
("demo.webtracker.xmlgen");
// create UnMarshaller
Unmarshaller unmarshaller = jc.createUnmarshaller();
JAXBElement rootElement = (JAXBElement)unmarshaller.unmarshal
(new FileInputStream(xmlDataStorePath));
// Get the root element
WebAccess access = (WebAccess)rootElement.getValue();
XML 文档编组
编组过程是从内容树中创建 XML 文档。要编组 XML 文档,您需要创建
JAXBContext
。
编组 XML 数据文件的代码如下所示:
// create a JAXBContext
JAXBContext jc = JAXBContext.newInstance
("demo.webtracker.xmlgen");
// create a Marshaller and marshal to webAccessLog.xml
Marshaller marshaller = jc.createMarshaller();
// Converts java object to XML data
marshaller.marshal(accessData, new File(xmlDataStorePath));
webTracker.ear 文件并上传它。
单击 OK 按钮。
将 webtracker-src/config 中提供的 webAccess.xml 文件复制到任意本地目录下。添加称作
xmlstore.path
的系统属性,并将它指向该目录。您可以从 GlassFish 管理控制台中选择 Application Server -> JVM Settings -> JVM Options -> Add JVM Option 设置系统属性。样例条目为 -Dxmlstore.path=C:/temp/webAccessLog.xml。
部署描述符
ejb-jar.xml
用于样例应用程序的 ejb-jar.xml 部署描述符如下所示: 您必须将
WebTrackerEJB
声明为消息驱动 bean 并将
javax.jms.Queue
作为消息目的地的类型。如果您已经在 Java EE 5 中注释了 MDB,那么可以忽略该配置。
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd">
<display-name>Web Tracker App</display-name>
<enterprise-beans>
<message-driven>
<display-name>WebTrackerEJB</display-name>
<ejb-name>WebTrackerEJB</ejb-name>
<ejb-class>demo.webtracker.ejb.WebTrackerEJB</ejb-class>
<transaction-type>Container</transaction-type>
<message-destination-type>
javax.jms.Queue
</message-destination-type>
</message-driven>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>WebTrackerEJB</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
sun-ejb-jar.xml
用于样例应用程序的 sun-ejb-jar.xml 部署描述符如下所示: 连接工厂和队列的 JNDI 名称将在描述符中声明。如果您已经在 Java EE 5 中注释了 MDB,那么可以忽略该配置。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-ejb-jar PUBLIC "-//Sun Microsystems, Inc.
//DTD Application Server 9.0 EJB 3.0//EN"
"http://www.sun.com/software/appserver/dtds/sun-ejb-jar_3_0-0.dtd">
<sun-ejb-jar>
<enterprise-beans>
<ejb>
<ejb-name>WebTrackerEJB</ejb-name>
<jndi-name>webTrackerQueue</jndi-name>
<mdb-connection-factory>
<jndi-name>webTrackerConnFactory</jndi-name>
</mdb-connection-factory>
</ejb>
</enterprise-beans>
</sun-ejb-jar>
测试应用程序
现在已经成功部署了应用程序,准备进行测试。可以使用下列 URL 访问应用程序的主页:
http://<host name>:<port number>/webTracker
图 1 显示了主页的快照。
图 1. Web Access Tracker 的主页
web 访问报告可以通过单击在左侧导航条提供的 Web Access Info 链接查看。在图 2 中显示该报告。
图 2. Web 访问报告
结束语
在本文中我们探索了 JMS 消息传递和 GlassFish 的消息传递功能。毫无疑问,JMS 已经赢得了广泛的行业支持并成为功能强大的企业消息传递解决方案。在本文中解释的应用案例,真正地演示了 JMS 使用消息传递解决方案解决实时问题的美妙之处。