副标题#e#
概要:我们可以利用MessageDrivenBean(动静驱动组件),在企业级的应用措施中举办异步的动静传送。
引言:Enterprise JavaBeans(EJB)1.1版本中界说了两种组件范例—session组件和entity组件。客户端工具可以同法式用EJB1.1的这两种组件的要领,然而,为了担任Message Oriented Middleware(MOM,面向工具的中间件)和Java Message Service(JMS,Java动静处事)的利益的需要,EJB框架中也相应的该当插手异步的动静通讯机制,所以,在EJB2.0中就界说了第三种组件范例—-MessageDrivenBean(动静驱动组件)
MessageDrivenBean兼备EJB和JMS的成果,虽然,假如您想要动静操纵技能,那么您大可只利用JMS就行了,可是新的动静驱动组件MessageDrivenBean提供了动静通讯的新的大概性。那么,这些组件如何整合到一个应用措施处事器框架中?他们的成果又如何扩大了已往JMS处事器的利用范畴?让我们看下文吧!
EJB和JMS
前面我们已经说过了,EJB1.1种为开拓者界说了两个企业级组件范例—–session和entity组件。session组件凡是实现一些贸易逻辑而且不能在多客户端共用。Entity组件则描写一个实体的面向工具的观念,而这个实体往往存在于像数据库那样牢靠的存储容器中。在这两种组件模子中,利用当地的或长途的接口来简化客户端的交互浸染。凭据界说,这种交互浸染是严格的同步的。举例来说,通过一个要领挪用把一个请求发送给组件,然后处事器工具返回一个响应。(如图1),
然而,在企业版应用措施的范畴中,也常常需要异步的动静通报,例如说,一个客户大概想发给处事器一条信息,可是并不需要可能不想要处事器做出应答,这时,客户端就没有须要期待处事器工具处理惩罚请求。对付客户端应用措施来说,在确保动静最终可以或许达随处事器并被正常处理惩罚的前提下,提交一条动静然后继承处理惩罚自己的事务,将会在很大的水平上提高效率。
可以或许处理惩罚异步动静的本领的Java技能可以在Java Message Service(JMS)中找到,JMS原本就是被开拓来提供传统的Message Oriented Middleware(MOM)产物的一个尺度Java接口。
此刻,一些公司开拓出了一整套新一代轻量级高效的纯Java的JMS产物,这些产物是开拓者可以或许成立JMS毗连来宣布或从其它应用措施组件中接管动静。下面的例程给出了与一个JMS提供者接口的须要步调:
代码段一:筹备客户端
客户端应用措施利用了带有JMS 主题的MessageListener来接管和处理惩罚动静。
import javax.jms.*;
/**
*一个例程,演示如何取得一个JMS
*毗连并取得一个动静监听者。在本例中
*我们将获取一个与一个JMS主题的毗连
*/
public class JMSSample {
public static void main (String args[])
{
InitialContext context = new InitialContext();
// 查找主题
Topic topic (Topic)context.lookup("MyTopic");
file://取得我们建设JMS毗连时所要用到的毗连建设器
TopicConnectionFactory tcf =
(TopicConnectionFactory)context.lookup(
"TopicConnectionFactory");
// 建设JMS毗连
TopicConnection conn = tcf.createTopicConnection();
// 从毗连中建设JMS session。
// 这样我们就可以建设一个非事务处理惩罚、AUTO_ACKNOWLEDGE的毗连
TopicSession session = conn.createTopicSession(
false, Session.AUTO_ACKNOWLEDGE);
// 建设主题订阅者
TopicSubscriber subscriber =
session.createSubscriber(topic);
// 监听者
subscriber.setMessageListener(myListener);
// 指出我们将要接管信息的毗连
conn.start();
}
}
#p#副标题#e#
除了回收上面的步调取得JMS MessageConsumer(动静消费者)的毗连之外,开拓者还可建设并注册一个或多个利用Message Consumer的JMS Message Listener(动静监听者)接口。Message Listener老是在一个单独的节制线程中执行,这就意味着在编写动静监听者时,开拓者不需要担忧并发性问题的呈现,见图2。
下面我给出了一个典范的JMS 动静监听者实现的代码。
代码段2:
/**
* 这个类是JMS MessageListener的一个实现
* 用来处理惩罚包括股票报价的动静
*/
class MyListener implements MessageListener {
/**
* 从收到的信息中取出股票报价
* 而且把它放入尺度输出流中并显示。
*/
public void onMessage(Message message) {
// 从动静工具中取出报价
// 我们知道动静发生者发送TextMessages
try
{
String quote = ((TextMessage)message).getText();
System.out.println("股票报价: " + quote);
}
catch(JMSException e)
{
System.out.println(
"错误处理惩罚动静: "+message);
}
}
}
在这个实现中,MessageListener吸收到的动静中包括了股票报价,动静监听者只是简朴的从动静体中取得股票报价并把它输出到尺度输出流中。
#p#分页标题#e#
开拓一个结实的JMS客户端措施大概长短常坚苦的,措施员必需要思量大概会同时接管多个动静,另外尚有生意业务安详性、并发性动静处理惩罚、工具生命周期、容错性和可扩展性,这些都是开拓者火急地想从EJB处事器中找到的成果。不外直到此刻,措施员们还不得不本身动手把这些技能团结在一起应用。
为了整合EJB1.1和JMS,JMS监听者必需要利用我们在代码段2中描写的要领来成立。JMS客户端措施必需参考一个stateless(无状态)的用于响应处理惩罚JMS动静的session组件,然后,JMS动静要通报给EJB。然而,JMS动静并不要求被序列化,这就意味着这条动静在通报到长途的EJB实例之前必需被转换成为有序的动静类或在动静监听者中部门地解构。并且,应用措施开拓者尚有责任打点JMS处事器之间的事务接洽,以及处理惩罚EJB、动静和并发性,这些都长短常巨大的工作。
纵然一个应用措施开拓者能把上面的这些都完成,而且也有本领会见JMS提供者并取得动静,但显然,他需要编写一大堆的代码,这对付我们这些凡人往往是不大大概实现的。EJB2.0办理了这个问题,它通过扩展EJB组件范例,为需要异步动静支持的组件开拓者提供简化的办理方案——新的MessageDriven组件范例。
MessageDrivenBean组件
MessageDrivenBean被陈设成为老是饰演信息消费者脚色的客户端。MessageDrivenBean没有客户端视图,这就意味着其当地和长途挪用接口都是不行用的。一个动静发生者发信息给一个主题或行列而且没有认识到一个事实–MessageDrivenBean正饰演着动静消费者的身份。这就导致了在基于系统的 JMS之间的宽松联络,并更多的思量到了在荟萃一个漫衍式计较情况时应有的机动性。
MessageDriven 组件没有对话状态,其实所有的组件实例当它们在没有处理惩罚动静时都是等价的。这有点和无状态的session组件的状态特征有些雷同。把组件实例会合起来是打点MessageDriven组件实例的普遍而又有效的步伐。
MessageDriven 组件必需以直接或间接的要领从接口javax.ejb.MessageDrivenBean中取得,而这个接口类则是从javax.jms.MessageListener接口得来并添加了两个要领。onMessage()要领是从javax.jms.MessageListener接口中担任来的,这个要领有独一的参数,就是javax.jms.Message,可以是任何有效的JMS动静范例。这个要领显然不包罗throw(抛出)子句,所以在处理惩罚动静时不会抛出任何应用措施异常。
当这个容器吸收到动静,它首先从一个可用实例池中取得一个MessageDriven组件(见图4)然后把陈设描写器中拟定的任务与执行线程接洽起来,使其可以或许流传安详上下文。另外,假如陈设描写器需要事务上下文的话,容器也会配置与之的关联。
一旦完成了打点任务,吸收到的动静羽觞传送到MessageDrivenBean实例的onMessage()要领中,而一旦这个要领完成后,动静所载的事务就会被执行或返回,然后组件从头返回可用实例池中。
当MessageDrivenBean实例被从容器中(凡是从实例池中)的任何强的参考中逐出,城市挪用ejbRemove()要领。ejbRemove()要领将释放任何被组件实例占用的资源。setMessageDrivenContext()要领有一个参数–javax.ejb.MessageDrivenContext类的一个实例。MessageDrivenContext类与界说在EJB1.1中的entity和session类有点雷同,当一个组件实例被建设,容器就把它通报进一个实例占用的上下文中,这个类有取得情况信息的要领也有相应的要领取得JTA UserTransaction类(用于打点事务定界的组件)。
另外,组件提供者还该当在EJB2.0处事器中可摄制的组件提供一个没有参数的ejb.Create()要领。这个组件实例可以得到任安在ejb.Create()用于举办处理惩罚的所需要的资源,好比说,在这一点上,MessageDrivenBean实例可以取得一个数据库毗连,假如ejb.Remove()要领被挪用的话,它将封锁或释放。
值得留意的是,MessageDrivenBean此刻已经大大的简化了建设JMS动静消费者的进程,下面的代码段3就建设并设置了一个EJB容器所委托建设的JMS动静消费者。开拓者此刻可以很容易的实现MessageDrivenBean接口,并可以把它设置在EJB处事器中且可以用来建设一个可收集动静的贸易组件。
代码段3:
#p#分页标题#e#
/**
*MessageDrivenBean接口由每一个动静驱动企业级组件类实现。
*这个容器利用MessageDrivenBean 要领来通知
*企业级Bean实例的实例生命周期事件
*/
public interface javax.ejb.MessageDrivenBean extends
javax.jms.MessageListener
{
/**
* 传送一个动静给监听者
*
* 参数 message :Message工具。
*/
public void onMessage(javax.jms.Message message);
/**
*容器在竣事动静驱动工具的生命周期之前,挪用这个要领。
*/
public void ejbRemove();
/**
*配置相关联的动静驱动上下文。
*容器在建设了实例后挪用这个要领。
* 企业版 Bean 实例将生存context工具的参考到一个实例工具中
*/
public void
setMessageDrivenContext(
javax.ejb.MessageDrivenContext context);
}
在代码段4中给出了一个MessageDrivenBean实例的实现,在个组件从一个JMS TextMessage中取得一条字符串,并输出,它是按照代码段2种的JMS动静监听者措施改编的。
代码段: 4
/**
* 这个类是 MessageDrivenBean的一个实现。
*/
public class MyListenerMDB implements MessageDrivenBean
{
/**
* 这是一个无参数结构器,这样 EJB容器可以利用Class.newInstance()要领来建设组件实例
*/
public MyListenerMDB()
{
}
/**
*这个要领接管动静实例并执动作静处理惩罚进程。
*
* 参数:message 。Message工具
*/
public void onMessage(Message message)
{
// onMessage 实现仍然未变:
// 从message工具中取出股票报价。
// StockQuoteProducer 发送 TextMessages
// 并在适合的时候放出该工具。
try
{
String quote = ((TextMessage)message).getText();
System.out.println("股票报价: " + quote);
}
catch(JMSException e)
{
System.out.println(
"不能处理惩罚动静: " + message);
}
}
/**
* 当MessageDrivenBean实例被从容器中抛出,该要领就被挪用。
*/
public void ejbRemove() throws javax.ejb.EJBException
{
System.out.println(
"StockListenerMDB: ejbRemove被挪用。");
}
/**
* 配置MessageDrivenContext实例。本要领将在组件实例化时被挪用
*动静驱动上下文答允组件开拓者会见EJB容器的东西
*
* 参数ctx : 动静驱动上下文
*/
public void setMessageDrivenContext(
MessageDrivenContext ctx) throws javax.ejb.EJBException
{
System.out.println(
"StockListenerMDB: setMessageDrivenContext 被挪用。");
}
/**
* ejbCreate with no args required by spec, though not
* enforced by interface
*/
public void ejbCreate()
{
System.out.println(
"StockListenerMDB: ejbCreate called.");
}
}
陈设描写器
MessageDrivenBean可以利用XML陈设描写器来指出受EJB处事器信息节制的运行时间行动,下面是陈设描写器中界说MessageDrivenBean的有效的DTD元素。
<!ELEMENT message-driven (description?,
display-name?, small-icon?,
large-icon?, ejb-name?, ejb-class,
transaction-type, transaction-scope?,
jms-message-selector?, jms-acknowledge-
mode?, message-driven-destination?,
env-entry*, ejb-ref*, security-
identity?, resource-ref*, resource-
env-ref*)>
需要留意的是,陈设描写器包括所有除用来陈设MessageDrivenBean组件的方针名以外的所有信息,方针名被配置在一个应用措施处事器提供商指定的设置文件中或作为一个系统属性。
在陈设描写器中,设置器可以指定组件是倾向于用于主题照旧用于行列,而且,假如倾向于用于主题那么组件是否应该继续耐久的签署者( durable subscriber)的身份。像行列一样,耐久的主题担保监听者将吸收到所有宣布到这个主题的动静,纵然监听者大概一段时间都不行用。 耐久的主题对应用措施的靠得住性很重要。
我们的给出的MessageDrivenBean的陈设描写器(见代码段5)汇报容器这个组件是特意侦听一个不耐久主题。这个组件有利用 NotSupported要领事务属性的容器打点事务限定。
代码段5:
<ejb-jar>
<enterprise-beans>
<message-driven>
<ejb-name>MessageListenerMDB</ejb-name>
<ejb-class>messageListenerMDB</ejb-class>
<transaction-type>Container</transaction-type>
<transaction-scope>Local</transaction-scope>
<jms-acknowledge-mode>auto-acknowledge</jms-
acknowledge-mode>
<message-driven-destination>
<jms-destination-type>javax.jms.Topic</jms-
destination-type>
<jms-subscription-durability>nondurable</jms-
subscription-durability>
</message-driven-destination>
</message-driven>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>MessageListenerMDB</ejb-name>
<method-name>onMessage</method-name>
<method-params>
<method-param>javax.jms.Message</method-param>
</method-params>
</method>
<trans-attribute>NotSupported</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
#p#分页标题#e#
利用MessageDrivenBean组件的另一个长处就是它的陈设的简捷性。 典范环境下,一个应用处事器供给商将提供界说组件陈设描写器的东西,发生 EJB jar文件,而且陈设组件。 一旦组件陈设好了, EJB处事器将处理惩罚 EJB容器类的注册而且开始 JMS毗连。 因为 JMS界说了一个尺度,一个基于 JNDI的机制,用于得到 JMS主题,行列,毗连以及一个可以或许利用任何JMS供给商实现的高端的应用措施处事器的引用。 这答允设计者在开拓贸易措施时最充实的操作应用措施处事器和JMS处事器。
MessageDrivenBean容器处事
EJB容器提供了下列处事,它们都是初级的EJB支持的处事,不能直接被利用EJB1.1的JMS开拓者利用。
1.打点生命周期:
MessageDrivenBean的生命周期与设置它的EJB处事器的寿命是一致的,因为MessageDrivenBean是无状态的,所以组件实例凡是被EJB处事器会合起来,而且当一条动静变得对付作为动静消费者的主题或行列可用的时候,会被容器取回,见图5。
2、处理惩罚异常:
MessageDrivenBean组件当处理惩罚动静的时候,大概不会抛出应用措施异常,这就意味着大概被MessageDrivenBean抛出的独一的异常指明一个系统错误的运行时间异常。容器将会处理惩罚这个异常,要领是删除这个组件实例,并返回任何组件实例或容器启动的事务处理惩罚。
3、线程或并发性:
MessageDrivenBean实例是在一个单独的节制线程中执行,这将大大的简化开拓者的任务。EJB处事器将确保这一特性,别的,EJB处事器大概提供一种操纵模式答允多动静被单独的组件实例同时处理惩罚,这个设置选项实用界说在JMS类型中的“expert level”类。JMS提供者并不必然带有这些类,所以EJB处事器大概不能操作每一种JMS实现的这种成果。
4、事务处理惩罚:
就像利用entity或session组件一样,MessageDrivenBean组件也可以有“contain-or-bean-managed”(容器或组件打点)事务处理惩罚。一个事务属性可以配置为MessageDrivenBean组件的onMessage()要领。因为没有客户端事务处理惩罚,所以只有一个事务处理惩罚属性的子集思量到entity和session组件与MessageDrivenBean组件有干系。利用了容器打点事务处理惩罚。容器将能支持MessageDrivenBean的Required和NotSupport事务处理惩罚属性。一个带有组件打点事务处理惩罚的MessageDrivenBean组件可以利用JTA UserTransaction工具。这个MessageDrivenBean组件在从onMessage()要领返回之前,必需先竣事事务处理惩罚。onMessage()要领很是简朴,它带有容器打点事务边界和NotSupported事务属性。容器将不会建设事务而且组件开拓者被克制会见 UserTransaction工具。
更有趣的事,当onMessage()要领被指定了Required事务属性,这时容器将建设一个全局事务处理惩罚收集任何可参考的资源并被通报到任何其他的正在处理惩罚动静的EJB处事器上。这是独一一种JMS主题或行列接口包括于EJB事务处理惩罚中的情景。
对付利用Required事务属性的组件,JMS处事器将成为XAResource,假如JMS处事器提供者不支持XA事务处理惩罚,JMS session凡是将与容器的全局事务处理惩罚的功效同步。这样,容器将不能包罗用于打点漫衍事务的两方面的委托处理惩罚的JMS处事器。JMS session的一个 rollback(反转)将告诫 JMS处事器,动静该当被从头发送。
5、动静确认:
容器老是处理惩罚MessageDrivenBean组件的动静收到简直认,对付组件来说,利用界说在 JMS类型中的客户端动静收到确认要领是犯科的。动静收到确承认以被配置为 DUPS_OK_ACKNOWLEDGE或 AUTO_ACKNOWLEDGE,前者答允在一次失败之后投递动静的副本,尔后者提供一个严格的担保机制,确保动静只能被投递一次。
6、安详性
#p#分页标题#e#
因为MessageDrivenBean组件没有客户端,所以在接管动静的时候容器根基上不会发生安详问题。EJB2.0类型中为组件要领执行一个声明指定的成果提供了利便。因此,MessageDrivenBean组件可以被配置来确保用于传送到其他正在处理惩罚动静的EJB处事器的安详。这就可以使MessageDrivenBean组件维护要领级安详性了。
应用措施处事器框架
当 MessageDrivenBean提供的成果在应用措施处事器内被陈设好后,它就会以指数形式膨胀开来。一个高端的应用措施处事器提供可扩展性,负载均衡,动态应用措施启动,附加的 EJB处事器实例的动态设置以及容错性,这些都是企业级应用措施的焦点要素。
1、可扩展性: 企业级应用措施处事器的一个要害元素就在它的提供一个布局来适应不绝增加的处理惩罚负载的本领。 跟着被 MessageDrivenBean处理惩罚的动静的数量的增加,应用措施处事器的 EJB处事器必需使适应这些增加而不会明明的增加处理惩罚时间。一般是提供应多应用措施处事器用于协调处理惩罚受到的动静的增多,这样应用措施处事器就有了可扩展性了。
2、负载均衡:当一个请求发送到应用措施处事器,一个负载均衡元素可以在实例之间平均分派负载(见图六)。举例来说,应用措施处事器每个实例都大概包括一个EJB处事器的实例。利用应用措施处事器的负载均衡特性,受到的动静就可以平均分派到所有的EJB处事器实例上了。
3、动态应用措施启动:应用措施处事器该当可以在负载增多时启动附加的预先配置的实例。
4、动态配置特另外EJB处事器实例:纵然一个应用措施被设定了牢靠的用于处理惩罚应用措施负载的实例数,可是也有大概信息流的激增造成系统处理惩罚速度变慢,为了处理惩罚这种环境,应用措施处事器必需具有设置附加应用措施实例的特性。好比说,应用措施处事器可以答允系统打点员动态界说附加的EJB处事器实例并把这些附加实例陈设到EJB处事器中。负载均衡元素将操作这些增补的实例来处理惩罚动静。
5、容错性:一个企业级的应用措施处事器必需也具有可以或许适应堕落的环境,像网络或硬件上的问题,可是前提是不能明明低落系统机能或是丢失数据,应用措施处事器利用的一种要领就是通过配置多应用措施处事器实例来分派状态信息,收到的动静通过智能化的负载均衡元素被分派到实例上。假如有一个或多个实例不能达到,负载均衡元素只要简朴的把动静从头分派给各个能用的实例就可以了。另外,负载均衡元素还可在打点组件的参加下重启那些因为某些原因而不能利用的实例。
逾越JMS
EJB的MessageDrivenBean组件部门给企业级处事器规模又添了一员虎将,它答允动静经过JMS接管在被一个简朴但又强有力的组件处理惩罚。然而,MessageDrivenBean的EJB组件事实上有本领酿成可以或许处理惩罚任何动静的组件模子,而不光是JMS处事器发来的动静。
JMS动静通过JMS实现被酿成可交互操纵,这就意味着JMS动静私地下是某一种特定的的实现,可是内容可以完全透明的被转换成另一种特定的JMS实现而不会在任何方面影响动静消费者。因此任何动静名目都可以转换成为JMS动静并被通报到MessageDrivenBean组件中。换句话说,一个MessageDrivenBean组件可以处理惩罚电子邮件、HTTP、FTP或其他任何协议发来的动静,以及又具有把这些协议转化成为JMS动静的应用措施处事器提供的动静。这就开启了一个尺度、简朴、精练且可以或许处理惩罚任何协议发过来的任何动静的本领的组件模子之门。假如动静被界说成为可开拓的、可扩展的语言向XML这样的及其强大的可交互操纵在宽松毗连络统以一种每小我私家都能领略的模式完成。下面我想简朴以一个独立于协议以外的动静处理惩罚的例子来验证一下这种技能的强大威力。
一个B2B的例子
一个典范的B2B情景就像一个“中枢-轮辐”形,多个宽松毗连的商家(即“轮幅”)与一个大型企业(即“中枢”)交互毗连,在某种环境之下,当执行交互操纵(像发送大的订单这样的事件)可以经过JMS应用措施在大企业与商家之间正常彼此浸染。对付一些小型商家来说,回收纯JMS大概在本钱上太高而承担不起,它大概会回收通过Web欣赏器中的HTTP或发送电子邮件的形式向大企业发送订单。为了向客户开放B2B系统,大型企业级处事器就必需可以或许处理惩罚这一系列差异的协议。
#p#分页标题#e#
并且,作为中枢的大型企业级处事器固然接管来自email或来自JMS等差异范例名目标订单,可是只有须要为每一种方法别离编写处理惩罚措施。利用MessageDrivenBean组件,任何协议都可以发送以XML名目描写的订单,如下面的例子。MessageDrivenBean组件只需要把留意力会合在处理惩罚来自应用措施处事器发送的JMS TextMessage中的XML就可以了,而不要去体贴到底所处理惩罚的动静时直接来自JMS处事器照旧作为一条来自邮件处事器的SMTP动静。
代码段六:
<purchase_order>
<client_number>101</client_number>
<date>August 8, 2001</date>
<billing_address>
<name>Wayne Zheng</name>
<address>xyz street</address>
<city>Hefei</city>
<province>Anhui</province>
<zip>230027</zip>
</billing_address>
<shipping_address>
<name>Zhang Tao</name>
<address>Huangshan Road</address>
<city>Hefei</city>
<state>Anhui</state>
<zip>230026</zip>
</shipping_address>
<items>
<item>
<quantity>1</quantity>
<product_number>324</product_number>
<description>Java Book</description>
<unitcost>19</unitcost>
</item>
<item>
<quantity>1</quantity>
<product_number>532</product_number>
<description>Java VM</description>
<unitcost>56</unitcost>
</item>
</items>
</purchase_order>
小结
EJB2.0提供了一种新的组件模子-MessageDrivenBean组件-用来处理惩罚异步动静,MessageDrivenBean组件可以简朴的开拓和操作与其他EJB组件模子沟通的容器处事,这种新的组件模子答允整合面向工具的中间件(MOM)的、优秀的以及新一代纯Java的JMS。而且,MessageDrivenBean组件尚有大概成为处理惩罚任何异步动静,出格是基于XML动静的家产尺度。因此,MessageDrivenBean组件是EJB2.0中的很是重要的一员,本文只是初探其脸孔,今后我还会越发深入的探讨其的坚守。