eclipse+JBoss5+EJB3开发指南(14):消息驱动Bean

在前面的文章中给出的SessionBean的例子都是同步调用SessionBean方法的,也就是说,只有当方法 中的代码都执行完,才能返回到客户端。但在某些情况下,由于SessionBean方法的执行时间比较长,这 就需要异步地调用该方法,否则客户端就需要等待比较长的时间。要实现异步调用,就需要使用本要讲的 消息驱动Bean。消息驱动Bean的基本原理是客户端向消息服务器发送一条消息后,消息服务器会将该消息 保存在消息队列中。在这时消息服务器中的某个消费者(读取并处理消息的对象)会读取该消息,并进行 处理。发送消息的客户端被称为消息生产者。

本文给出的消息驱动Bean的例子的基本功能是客户端向消息服务器发送一条消息(该消息实际上是一 个实体Bean的对象实例),然后消息消费者读取这条消息后,将消息中的实体Bean持久化。实现消息驱动 Bean的步骤如下:

一、实现实体Bean

package entity;import java.io.Serializable;import java.util.Date;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;import javax.persistence.Table;@Entity@Table(name="t_date")public class DateBean implements Serializable{    private int id;    private Date myDate;    @Id    @GeneratedValue(strategy=GenerationType.IDENTITY)    public int getId()    {        return id;    }        public void setId(int id)    {        this.id = id;    }    @Column(name="mydate")    public Date getMyDate()    {        return myDate;    }    public void setMyDate(Date myDate)    {        this.myDate = myDate;    }    }

二、编写消息驱动Bean

消息驱动Bean必须实现MessageListener接口,当该消息驱动Bean接收到一个消息后,EJB容器就会调 用MessageListener接口的onMessage方法来理该消息。消息驱动Bean的代码如下:

package service;import javax.ejb.ActivationConfigProperty;import javax.ejb.EJBException;import javax.ejb.MessageDriven;import javax.jms.Message;import javax.jms.MessageListener;import javax.jms.ObjectMessage;import javax.persistence.EntityManager;import javax.persistence.PersistenceContext;import entity.DateBean;@MessageDriven( activationConfig =  {                @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),        @ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/MDBQueue")      })public class DateMessageBean implements MessageListener{    @PersistenceContext(unitName = "myentity1")    private EntityManager em;    @Override    public void onMessage(Message message)    {        try        {            if(message instanceof ObjectMessage)            {                ObjectMessage bjmsg = (ObjectMessage) message;                DateBean dateBean = (DateBean) objmsg.getObject();                em.persist(dateBean);                System.out.println("成功持久化DateBean对象!");            }            else            {                System.out.println("消息类型错误!");            }        }        catch (Exception e)        {            throw new EJBException(e);        }    }}

消息驱动Bean需要使用@MessageDriven进行注释。要注意的是destination属性的值是queue/MDBQueue 。JBoss不会自已建立一个Queue对象,因此,需要手工来配置Queue对象。读者可以/server/default/deploy目录中建立一个xxx-service.xml文件,其中xxx可以任意取值,但必须跟 “-service”后缀,例如,abc-service.xml。该文件可以放在deploy或其子目录(可以是多层子目录) 中。该文件的内容如下:

<?xml version="1.0" encoding="UTF-8"?><server>  <mbean code="org.JBoss.mq.server.jmx.Queue" name="JBoss.mq.destination:service=Queue,name=MDBQueue">    <depends optional-attribute-name="DestinationManager">JBoss.mq:service=DestinationManager</depends>  </mbean></server>

要注意的是,元素的name属性值中的name必须是MDBQueue,要与queue/MDBQueue中的/ 后面的部分一致。如果不进行上面的配置,在启动JBoss时就会抛出如下的异常:

javax.naming.NameNotFoundException: MDBQueue not bound

也可以将元素放在deploy目录中的其他以-service.xml结尾的文件中。

如果不设置destination属性的值,在启动JBoss是会抛出如下的异常:

org.JBoss.deployers.spi.DeploymentException: Required config property RequiredConfigPropertyMetaData@174098f[name=destination descriptions= [DescriptionMetaData@4ca30b[language=zh]]] for messagingType ‘javax.jms.MessageListener’ not found in activation config [ActivationConfigProperty(destinationType=javax.jms.Queue), ActivationConfigProperty(connectionFacToryJndiName=MyQueueConnectionFacTory), ActivationConfigProperty(destinationName=MyRequestQueue)] ra=JBoss.jca:service=RARDeployment,name=’jms-ra.rar’

… …

三、编写调用消息驱动Bean的SessionBean

package service;import java.util.ArrayList;import java.util.Date;import java.util.List;import javax.annotation.Resource;import javax.ejb.Stateless;import javax.jms.Connection;import javax.jms.ConnectionFacTory;import javax.jms.MessageProducer;import javax.jms.ObjectMessage;import javax.jms.Queue;import javax.jms.Session;import javax.persistence.EntityManager;import entity.DateBean;import entity.Greeting;@Statelesspublic class GreeterBean implements Greeter{    @Resource(mappedName = "ConnectionFacTory")    private ConnectionFacTory cf;    @Resource(mappedName = "queue/MDBQueue")    private Queue queue;    @Override    public String greet(String message)    {        try        {            DateBean db = new DateBean();            db.setMyDate(new Date());            Connection connection = cf.createConnection();            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);            MessageProducer messageProducer = session.createProducer(queue);            ObjectMessage bjectMessage = session.createObjectMessage();            objectMessage.setObject(db);            messageProducer.send(objectMessage);            connection.close();            System.out.println("成功发送消息!");        }        catch (Exception e)        {            System.out.println("发送消息失败!");        }        return "方法成功返回";    }}

在上面的代码中使用ObjectMessage对象来包装要向消息服务器发送的实体Bean的对象实例。

除了可以在SessionBean中访问消息驱动Bean外,还可以在不同的机器上通过jndi来查找并调用消息驱 动Bean,代码如下:

package test;import java.util.Date;import javax.ejb.EJB;import javax.jms.Destination;import javax.jms.MessageProducer;import javax.jms.ObjectMessage;import javax.jms.Queue;import javax.jms.QueueConnection;import javax.jms.QueueConnectionFacTory;import javax.jms.QueueSession;import javax.jms.TextMessage;import javax.naming.InitialContext;import entity.DateBean;import service.Greeter;public class Client{    public static void main(String[] args) throws Exception    {        InitialContext ctx = new InitialContext();        QueueConnection connection = null;        QueueSession session = null;        QueueConnectionFacTory facTory = (QueueConnectionFacTory) ctx.lookup("ConnectionFacTory");        connection = facTory.createQueueConnection();        session = connection.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);        Destination destination = (Queue) ctx.lookup("queue/MDBQueue");        MessageProducer messageProducer = session.createProducer(destination);        ObjectMessage bjectMessage = session.createObjectMessage();        DateBean db = new DateBean();        db.setMyDate(new Date());        objectMessage.setObject(db);        messageProducer.send(objectMessage);        connection.close();        System.out.println("成功发送消息!");    }}

有的事情现在不做,就一辈子也不会做了。

eclipse+JBoss5+EJB3开发指南(14):消息驱动Bean

相关文章:

你感兴趣的文章:

标签云: