在前面的文章中给出的SessionBean的例子都是同步调用SessionBean方法的,也就是说,只有当方法中的代码都执行完,才能返回到客户端。但在某些情况下,由于SessionBean方法的执行时间比较长,这就需要异步地调用该方法,否则客户端就需要等待比较长的时间。要实现异步调用,就需要使用本要讲的消息驱动Bean。消息驱动Bean的基本原理是客户端向消息服务器发送一条消息后,消息服务器会将该消息保存在消息队列中。在这时消息服务器中的某个消费者(读取并处理消息的对象)会读取该消息,并进行处理。发送消息的客户端被称为消息生产者。
本文给出的消息驱动Bean的例子的基本功能是客户端向消息服务器发送一条消息(该消息实际上是一个实体Bean的对象实例),然后消息消费者读取这条消息后,将消息中的实体Bean持久化。实现消息驱动Bean的步骤如下:
一、实现实体Bean
二、编写消息驱动Bean
消息驱动Bean必须实现MessageListener接口,当该消息驱动Bean接收到一个消息后,EJB容器就会调用MessageListener接口的onMessage方法来理该消息。消息驱动Bean的代码如下:
消息驱动Bean需要使用 @MessageDriven进行注释。要注意的是 destination 属性的值是 queue/MDBQueue。JBoss不会自已建立一个Queue对象,因此,需要手工来配置Queue对象。读者可以<JBoss5.x安装目录>\server\default\deploy目录中建立一个xxx-service.xml文件,其中xxx可以任意取值,但必须跟“-service”后缀,例如,abc-service.xml。该文件可以放在deploy或其子目录(可以是多层子目录)中。该文件的内容如下:
要注意的是,<mbean>元素的name属性值中的name必须是MDBQueue,要与queue/MDBQueue中的/后面的部分一致。如果不进行上面的配置,在启动JBOSS时就会抛出如下的异常:
javax.naming.NameNotFoundException: MDBQueue not bound
也可以将<mbean>元素放在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
在上面的代码中使用ObjectMessage对象来包装要向消息服务器发送的实体Bean的对象实例。
除了可以在SessionBean中访问消息驱动Bean外,还可以在不同的机器上通过jndi来查找并调用消息驱动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;
}
}
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 objmsg = (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);
}
}
}
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 objmsg = (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对象。读者可以<JBoss5.x安装目录>\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 >
< 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 >
要注意的是,<mbean>元素的name属性值中的name必须是MDBQueue,要与queue/MDBQueue中的/后面的部分一致。如果不进行上面的配置,在启动JBOSS时就会抛出如下的异常:
javax.naming.NameNotFoundException: MDBQueue not bound
也可以将<mbean>元素放在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;
@Stateless
public 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 objectMessage = session.createObjectMessage();
objectMessage.setObject(db);
messageProducer.send(objectMessage);
connection.close();
System.out.println( " 成功发送消息! " );
}
catch (Exception e)
{
System.out.println( " 发送消息失败! " );
}
return " 方法成功返回 " ;
}
}
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;
@Stateless
public 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 objectMessage = 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 objectMessage = session.createObjectMessage();
DateBean db = new DateBean();
db.setMyDate( new Date());
objectMessage.setObject(db);
messageProducer.send(objectMessage);
connection.close();
System.out.println( " 成功发送消息! " );
}
}
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 objectMessage = session.createObjectMessage();
DateBean db = new DateBean();
db.setMyDate( new Date());
objectMessage.setObject(db);
messageProducer.send(objectMessage);
connection.close();
System.out.println( " 成功发送消息! " );
}
}
本文转自 androidguy 51CTO博客,原文链接:http://blog.51cto.com/androidguy/214419,如需转载请自行联系原作者