`
zscomehuyue
  • 浏览: 402392 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论
阅读更多

所有EJB3.0开发商都必须提供一个JMS provider的实现,JMS provider对于message-driven bean而言绝对是必须的。JMS是一套用于访问企业消息系统的开发商中立的API。JMS在其中扮演的角色与JDBC很相似:JDBC提供一套用于访问各种不同关系数据库的公共API,JMS也提供了独立于特定厂商的企业消息系统访问方式。JMS使用消息服务(messaging service)来帮助enterprise bean发送信息,消息服务有时也称为消息代理。JMS是专门为不同java应用系统之间传递各类信息而设计的。

使用JMS重新实现TravelAgentEJB
修改11章开发的TravelAgentEJB,使其能在预订完成时利用JMS来通知其他java应用程序。
以下相关类(如Connection)属于JSM API
@Resource(mappedName=”connectionFactoryNameGoesHere”)
private ConnectionFactory connectionFactory;//注入
@Resource(mappedName=”TicketTopic”)//注入
private Topic topic;
@Remove
public TicketDO bookPassage(CreditCardDO card,double price){
    ………..
    Connection connect= connectionFactory.createConnection();//创建JMS provider连接
    Session session=connect.createSession(true,0);//创建Session对象
    // createSession(boolean transacted,int acknowledgeMode);
    //这两个参数会被容器忽略,EJB规范建议分别设置为true和0
    MessageProducer producer=session.createProducer(topic);//创建消息生产者
    TextMessage textMsg=session.createTextMessage();//创建文本消息
    textMsg.setText(ticketDescription);
    producer.send(textMsg);
    connect.close();
    ………..
}

ConnectionFactory和Topic
为了发送JMS,我们需要一个指向JMS provider的连接和一个消息目标地址。使用JMS连接工厂(JMS connection factory)可以获得JMS provider连接;而消息目标地址则可以由一个Topic(主题)对象来表示,也可以由queue(队列)来表示。

消息的类型
在JMS中,消息是一个java对象,它包含两个部分:消息头和消息正文。JMS API定义了多种不同的消息类型(TextMessage、MapMessage、ObjectMessage、StreamMessage和BytesMessage),并提供了不发送和接收这些消息的方法。
使用MapMessage的例子:
MapMessage mapMsg=session.createMapMessage();
mapMsg.setInt(“CustomerID”,ticket.customerID.intValue());
mapMsg.setInt(“Price”,ticket.price);
producer.send(mapMsg);
使用ObjectMessage的例子:
ObjectMessage objectMsg=session.createObjectMessage();
objctMsg.setObject(ticket);
producer.send(objectMsg);

JMS应用客户端
用于接收和处理乘客的预订信息,它在收到消息之后将每和票据的信息输出到屏幕
public class JmsClient_TicketConsumer implements javax.jms.MessageListener{//监听消息

   public static void main(String [] args) throws Exception{
      new JmsClient_TicketConsumer();
      while(true) { Thread.sleep (10000); }    
   }
 
   public JmsClient_TicketConsumer () throws Exception{
      Context jndiContext = getInitialContext ();
    
      ConnectionFactory factory = (ConnectionFactory)
      jndiContext.lookup ("ConnectionFactory");
    
      Queue ticketQueue = (Queue)
      jndiContext.lookup ("queue/titan-TicketQueue");
    
      Connection connect = factory.createConnection();    
      Session session = connect.createSession(false,Session.AUTO_ACKNOWLEDGE);    
      MessageConsumer receiver = session.createConsumer(ticketQueue); //消息消费者
      receiver.setMessageListener(this);

      connect.start ();//开始监听是否有信息到达
   }
 
   public void onMessage (Message message){//每当有新消息送达,该方法被调用
      try{
         ObjectMessage objMsg = (ObjectMessage)message;//强制转换成发送信息时的信息类型
         TicketDO ticket = (TicketDO)objMsg.getObject ();
         System.out.println ("********************************");
         System.out.println (ticket);
         System.out.println ("********************************");
      } catch (JMSException displayed){
         displayed.printStackTrace ();
      }
   }
 
   public static Context getInitialContext ()throws javax.naming.NamingException{
    Properties env=new Properties();
    env.put(…….);//使用开发商专有属性来配置InitialContext,也可以在jndi.properties文件中设置
          return new InitialContext (env);
   }
}

JMS的异步性
JMS消息处理的一个主要优势它的异步性。当JMS客户端发送消息时,它并不等待回应,只是将消息发送到消息路由,由消息路由来负责将消息传递给其他客户端。而使用java RMI和JAX-RPC时,客户端以同步方式调用bean方法,当前线程会被阻塞直到方法执行完毕为止。当需要与其他应用程序时行通信时,RMI的种种不足使JMS成为一种极富吸引力的替代方案。同时发送方的事务与安全上下文是不会传播到接收方的,不过JMS客户端和JMS provider可以共同拥有一个分布式事务。

JMS的消息传递模型
JMS provider提供了两种消息传递模型:发布-订阅模型(publicsh-and-subscribe,pub/sub)和点对点模型(point-to-point,p2p),JMS规范将其称为消息传递域(messaging domain)。通过情况下,发布-订阅主要用于一对多的消息广播,而点对点则用于一对一的消息传递。

发布-订阅
在发布-订阅消息传递模型中,生产者可以通过一个被称为主题的虚拟通道,将消息发送给多个消费者。任何发送到某一主题的消息都会被转送给订阅了该主题的所有消费者。此模型基本上是一个推模型(push-based model),消息会自动广播,消费者无须通过主动请求或轮询主题方式获得新的消息。

点对点
点对点的消息传递模型允许JMS客户端通过一个被称为队列的虚拟通道,以同步或异步的方式收发消息。它是一个拉模型(pull-based model)或轮询模型,在此类模型中,消息浊自动推送给客户端的,而是由客户端从队列中获得。一个队列可以有多个接收者,但是每条消息只能由一个接收者接收。

应该选用哪一种消息传递模型
两者实际上都可以使用“推”或“拉”的方式。JMS提供了一套同时适用于这两种模型的API。采用pub/sub模型所能完成的工作几乎都可以采用点对点来完成,反之亦然。某些情况下,这取决于个人偏好,或熟悉的程度。利用pub/sub模式,JMS通过使用专门的主题,可以先对消息进行过滤,而后再分发给消费者;而p2p保证每条消息只由一个消费者处理,当消息需要被分别处理,同时又要保证消息的先后顺序时,这一能力尤为重要。

Session Bean不应该接收消息
Session Bean响应来自EJB客户端的调用,但是它们不能像message-driven bean那样响应JMS消息(消息驱动,也就是接收到消息时,一些方法自动执行),我们可以编写一个业务方法中消费JMS消息的session bean,但是这个方法必须由EJB客户端来调用。如
Connection connect = factory.createConnection();    
Session session = connect.createSession(true,0);    
MessageConsumer receiver = session.createConsumer(queue);      //消息消费者
TextMessage textMsg=( TextMessage) receiver.receive();

connect.close();
可见使用session bean编写代码来接收信息是不明智的。

了解更多有关JMS的内容
JMS代表了分布式计算环境下一种功能强大的范式,以上对其的简介的目的是为讨论message-driven bean作铺垫的。

基于JMS的Message-Driven Bean
MDB是无状态的,事务感知的服务器组件,用于处理通过JMS进行递送的异步消息。MDB负责处理消息,而容器则负责管理包括事务、安全、资源、并发和消息确认在内的组件外环境。MDB是用来响应异步消息的,它不有远程或本地接口(方法自动调用,而非由客户端调用)

ReservationProcessor EJB
ReservationProcessor EJB是一个message-driven bean,其作用是接收代表新到预订通知的JMS消息,当它收到消息时,会创建一个新的Reservatin EJB,然后使用ProcessPayment EJB处理支付,并送出一张票据,它是TravelAgent EJB的一个自动化版本。源码如下:
@MessageDriven(activationConfig={//含义见下一节
       @ActivationConfigProperty(propertyName="destinationType",
                             propertyValue="javax.jms.Queue"),
       @ActivationConfigProperty(propertyName="acknowledgeMode",
                             propertyValue="Auto-acknowledge")})
public class ReservationProcessorBean implements javax.jms.MessageListener {
//MessageListener 监听器接口只定义了onMessage()方法
   @PersistenceContext(unitName= "titanDB")//注入
   private EntityManager em;

   @EJB
   private ProcessPaymentLocal process;

   @Resource(mappedName="ConnectionFactory")
   private ConnectionFactory connectionFactory;

   public void onMessage(Message message) {
   //类似于TravelAgentEJB的bookPassage()方法,此方法会自动执行,所以称为自动化
      System.out.println("Received Message");
      try {
        //从收到的消息中取出数据,类似于得到请求参数
        MapMessage reservationMsg = (MapMessage)message;    
         int customerPk = reservationMsg.getInt("CustomerID");
         int cruisePk = reservationMsg.getInt("CruiseID");
         int cabinPk = reservationMsg.getInt("CabinID");
   
         double price = reservationMsg.getDouble("Price");
   
         // 获取信用卡
         Date expirationDate =  new Date(reservationMsg.getLong("CreditCardExpDate"));
         String cardNumber = reservationMsg.getString("CreditCardNum");
         String cardType = reservationMsg.getString("CreditCardType");
         CreditCardDO card = new CreditCardDO(cardNumber, expirationDate, cardType);
       
         Customer customer = em.find(Customer.class, customerPk);
         Cruise cruise = em.find(Cruise.class, cruisePk);
         Cabin cabin = em.find(Cabin.class, cabinPk);
         //创建代表舱位预订记录的Reservation
         Reservation reservation = new Reservation(customer, cruise, cabin, price,
                 new Date());
         em.persist(reservation);
            //处理信用卡支付
         process.byCredit(customer, card, price);
   
         TicketDO ticket = new TicketDO(customer,cruise,cabin,price);
         deliverTicket(reservationMsg, ticket);//发送信息给客户端,见下文
      }
      catch(Exception e) {
         throw new EJBException(e);
      }
   }
    //MDB也可以使用JMS来发送信息
   public void deliverTicket (MapMessage reservationMsg, TicketDO ticket) throws JMSException, NamingException{
      Queue queue = (Queue)reservationMsg.getJMSReplyTo();//从接收的信息中得到回复消息目标地址
      Connection connect = connectionFactory.createConnection();
      Session session = connect.createSession(true,0);
      MessageProducer sender = session.createProducer(queue);
      ObjectMessage message = session.createObjectMessage();
      message.setObject(ticket);//把ticket发送到客户端
      sender.send(message);
      connect.close();
   }
}

@MessageDriven
MDB使用@javax.ejb.MessageDriven注解。

@ActivationConfigProperty
@Message-Driven.activationConfig()属性接受一组@ ActivationConfigProperty注解作为参数,用一组简单的“名/值”对来描述MDB配置.EJB3.0为基于JMS的message-driven bean定义了一组固定属性,它们是:acknowledgeMode、messageSelector、destinationType和subscriptionDurability

消息选择器(message Selector)
消息选择器让MDB选择性地接收来自特定主题或队列的消息,它以SQL-92条件表达式语法(SQL中的where子句)的一个子集为基础的,如:
@ActivationConfigProperty(propertyName="messageSelector", propertyValue="MessageFormat=’Versin 3.4’")
JMS消息生产者通过以下代码设置Message的MessageFormat属性
Message message=session.createMapMessage();
message.setStringProperty(“MessageFormat”,”Versin 3.4”);

确认模式
JMS确认是指JMS客户端通知JMS provider确认消息已经到达的一种机制。在EJB中,收到信息后发送确认是MDB容器的职责。
@ActivationConfigProperty(propertyName="acknowledgeMode", propertyValue="Auto-acknowledge")
确认模式有两个取值:Auto-acknowledge和Dups-ok-acknowledge。前者告诉容器,在消息交给MDB实例处理后,容器应该立即向JMS provider发送确认。后者容器任何时间发送确认都是可接受的,由于MDB容器可能延迟很长时间才发送确认,JMS provider可能认为容器没有收到消息而重发信息,所以使用这种模式时,MDB必须能够正确处理重复消息。在实际上大多数确认模式都会被忽略,如果事务由容器来管理,确认行为是在事务上下文中执行:如果事务成功,消息就会被确认;如果事务失败,消息就不会被确认

订阅的持续性
当基于JMS的MDB使用javax.jms.Topic时,我们必须在部署描述文件中声明:这一订阅是Durable或NonDurable。如果EJB容器由于某些原因失去与JMS provider的连接,Durable表示,在此期间Bean本应收到的消息是不会丢失的,当容器重新获得连接后,这些消息会由容器发送给MDB,这种行为称为保存-转发消息传递机制。声明为NonDurable的MDB,会丢失连接期间本应收到的消息,所以它可以改善JMS provier的性能,但降低MDB的可靠性。声明方式:
@ActivationConfigProperty(propertyName="subscriptionDurability", propertyValue="Durable")

ReservationProcessor EJB的部署描述文件
<?xml version="1.0"?>
<ejb-jar …...>
   <enterprise-beans>
      <message-driven>
             <ejb-name>ReservationProcessorBean</ejb-name>
             <ejb-class>com.titan.reservationprocessor. ReservationProcessorBean </ejb-class>
             <message -type>javax.jms.MessageListener</ message -type>
    <transacton-type>Container</transacton-type >
    <message-destination-type>javax.jms.Queue</message-destination-type>
    <activation-config>
        <activation-property>
            <activation-config-property-name>destinationType</activation-config-property-name>
            <activation-config-property-value>javax.jms.Queue</activation-config-property-value>
        <activation-property>
        …………
<activation-config>
             <ejb-local-ref>
                 <ejb-ref-name>ejb/PaymentProcessor</ejb-ref-name>
                 <ejb-ref-type>Session</ejb-ref-type>
                 <local>com.titan.processpayment.ProcessPaymentLocal</local>
                 <injection-target>
                        <injection-target-class>
                               com.titan.reservationprocessor. ReservationProcessorBean
                        </injection-target-class>
                    <injection-target-name>process</injection-target-name>
                 </injection-target>
          </ejb-local-ref>
          <persistence-context-ref>
                 <persistence-context-ref-name>persistence/titan</persistence-context-ref-name>
                <persistence-unit-name>titan</persistence-unit-name>
                 <injection-target>
                         <injection-target-class>
                                com.titan.reservationprocessor. ReservationProcessorBean
                         </injection-target-class>
                         <injection-target-name>em</injection-target-name>
                 </injection-target>
              </persistence-context-ref>
    <resource-ref>
                 <res-ref-name>jms/ConnectionFactory</res-ref-name>
                 <res-type>javax.jms.ConnectonFactory </res-type>
                 <res-auth>Container</res-auth>
                 <mapped-name>ConnectionFactory</mapped-name>
                 <injection-target>
                        <injection-target-class>
                               com.titan.reservationprocessor. ReservationProcessorBean
                        </injection-target-class>
                        <injection-target-name>connectionFactory</injection-target-name>
                 </injection-target>
          </resource-ref>
       </message-driven>
   </enterprise-beans>
</ejb-jar>

ReservationProcessor客户端
发送预订消息的客户端
代码实现见前面的章节“使用JMS重新实现TravelAgentEJB”

接收票据信息的客户端
代码实现见前面的章节“JMS应用客户端”
            
Message-driven bean的生命週期
MDB实例的生命周期有两个状态:Does Not Exist和Method-Ready Pool.
Does Not Exist状态
MDB没有被实例化。

Method-Ready Pool状态
和stateless session bean对应状态相似,也使用了实例池。EJB服务器首次启动时,它可能创建一些MDB实例,当MDB实例不足以处理发来的消息时,容器会调用bean class的Class.newInstance()方法创建一个新的bean实例,然后根据bean的元数据或XML声明注入相关资源,如果bean定义了PostConstruction回调函数,容器最后会调用该方法。当有消息送递MDB时,容器会将这条消息委派给任何一个处于MRP状态的bean实例。当服务器移除一部分内存对象以减少处于MRP状态的bean总数时,MDB将到达Does Not Exist状态,这时可能触发@PreDestroy回调事件。

基于连接器的Message-Driven Bean
基于JMS的MDB非常有用,但它也存在局限:EJB开发商往往只支持少量的JMS provider(消息收发双方都使用相同的JMS API)。它只支持JMS编程模型而不支持其他类型的消息系统。EJB3.0还支持一个可扩展的message-driven bean定义,使之可以对任何开发商的任何消息系统提供服务,即基于JCA的MDB,JCA定义了EJB容器和异步连接器之间的规则,从而使用MDB可以自动处理来自EIS的消息。JCA连接器可以从开发商X处购得,使用JCA连接器的MDB,可以自动处理开发商X开发的EIS系统发送过来的消息。例子,一个处理email的MDB
package com.vendorx.email;//来自开发商X的JCA
public inteface EmilListener{
    public void onMessage(javax.mail.Message message);
//这个方法可以有不同形式的方法名称和签名,并允许拥有返回值,由JCA开发商决定
}
@MessageDriven(activationConfig={//属性依赖于连接器的类型及具体要求
       @ActivationConfigProperty(propertyName="mailServer",
                             propertyValue="mail.ispx.com"),
       @ActivationConfigProperty(propertyName="serverType",
                             propertyValue="POP3")})
public class EmailBean implements com.vendorx.email.EmilListener{//实现JCA开发商的接口
    public void onMessage(javax.mail.Message message){
        javax.mial.internet.MimeMessage msg=( javax.mial.internet.MimeMessage)message;
        Address[] addressed=msg.getFrom();//来自那里的email.
        ……..
}
}

消息连接
允许由任一enterprise bean发出的消息,能够最终路由到位于 同一部署环境中的指定message-driven bean处,通过使用消息连接,可以在同一应用程序内的不同组件之间控制消息流转。可以定义一个TicketDistributor EJB分发由TravelAgent EJB发送到同一个JMS主题的消息,专门定义一个MDB来处理分发工作可以获得列好的灵活性和系统性能。EJB规范没有为消息连接提供注解,必须使用XML部署描述文件来使用这个功能。
<session>
    …..
    <message-destination-link>Distributor</message-destination-link>
…..
</session>
<message-driven>
    <ejb-name>TicketDistributorEJB</ejb-name>
    <message-destination-link>Distributor</message-destination-link>
</ message-driven >
<assembly-descriptor>
    <message-destination>
        <message-destination-name>Distributor</message-destination-name>
    </message-destination>
<assembly-descriptor>
这时由session bean 发送到目标地址的消息会由MDB TicketDistributorEJB收到转发。session bean可以发送消息,但不要接收消息。

分享到:
评论

相关推荐

    EBJ3.0实例教程

    EBJ3.0实例教程

    ejb.jar.zip(javax.ebj/javax.ebj.spi) 下载

    ejb.jar.zip(javax.ebj/javax.ebj.spi) 下载

    ebj设计模式

    ebj设计模式,关于EJB的设计模式~!经典呀~

    EBJ Register

    EJB 注册,结合JBuilder

    EBJ120-TP_掘进机

    EBJ120-TP_掘进机的结构、工作原理等

    EBJ120掘进机行走机构的设计

    以EBJ120型悬臂式掘进机为例,首先依据EBJ120型掘进机行走机构的组成和功能,介绍了其设计要求以及设计掘进机的主要技术参数。然后,确定掘进机行走机构的主要技术参数,最后对行走机构主要部件履带架等进行了设计和计算...

    EBj0dFwm.html

    EBj0dFwm.html

    MyEclipse+EJB3+JBoss4部署EBJ+web项目HelloWorld详解

    1,下载安装JDK1.6; 2,下载安装MyEclispse8.5; 3,下载安装JBoss4; 4,在MyEclispse8.5环境下配置JBoss4; 5,新建一个EJB3项目; 6,新建一个远程的无状态的SessionBean; 远程接口代码如下:

    ejb.rar_ebj_ejb

    ebj设计模式....大家下载看看.内容也不错的

    清晰的技术资料学习笔记

    很好的技术资料,内含有关于SQL ORACLE JAVA JSF EBJ ECLIPSE等的学习笔记,希望对你很有用。

    EBJ-120TP掘进机运输机驱动装置试验的分析

    运输机驱动装置作为掘进机的关键部件起着转载、运输的作用,如其发生故障直接影响井下掘进生产,因此,通过对EBJ-120TP掘进机运输机驱动装置试验结果的分析,使得能及早发现其潜在的隐患,及时排查,减少故障的发生,为运输...

    EBJ120TP型掘进机液压系统温度控制的研究

    针对EBJ120TP型掘进机液压系统在井下长时间连续作业时,其油温过高导致掘进机无法使用的情况,全面的分析了其温升过快的主要原因,提出了改进液压系统和水路强制冷却系统的方案,并在实际应用中取得了良好的效果。

    EBJ-120TP型掘进机机载临时支护装置的研究

    煤巷临时支护技术的落后是制约煤巷综合机械化掘进的关键因素之一,通过对EBJ-120TP型掘进机临时支护的研究,为掘进工作面提供临时顶板支护,提供安全的作业环境,并减轻人工顶梁支护的体力劳动,提高支护效率,同时大大...

    ebj-ppt

    ejb的ppt。。。。。

    EBJ-160悬臂式重型掘进机截割部减速器的设计与研究

    针对EBJ-160悬臂式重型掘进机具有结构紧凑、机身矮、重心低等特点,结合行星齿轮减速器传动的优点,设计一种符合该型掘进机截割部的减速器。设计所采用的方法是根据结构尺寸初选齿轮的相关参数,再按照齿面接触疲劳强度...

    JBOSS使用指南

    文档目录: 一.JBOSS入门 1.下载和安装JBoss 2.JBoss的目录结构 3.启动服务器 4.JMX控制台 5.停止服务器 6.JBoss中的部署 二.JBOSS的配置 1. JBoss日志设置 2. web 服务的端口号的修改 ...4.实体Bean(Entity Bean)

    图解eclipse+myelcipse开发EJB

    图解eclipse+myelcipse开发EJB

Global site tag (gtag.js) - Google Analytics