Message-Driven Beans
Version 2.0 of the EJB specification introduced a new kind of EJB: the message-driven bean. Unlike entity or session beans, message-driven beans don't have a public interface. The only way you can communicate with message-driven beans is via messages.
Message-driven beans are much easier to write than either session beans or entity beans. You must still write ejbCreate and ejbRemove to initialize the bean and clean up when you're done. You must also implement ejbActivate and ejbPassivate just because they're required by the EJB specification, but they are not a part of the message-driven bean lifecycle. Be cause message-driven beans have no public interface, they have no Home or Remote interfaces.
You must implement the setMessageDrivenContext method so the container can give you your entity bean context and, finally, you must implement the onMessage method from the MessageListener interface to receive messages.
One of the advantages of message-driven beans as compared to non-EJB message listeners is that the EJB container might create multiple beans to handle incoming messages. The EJB container dispatches queue messages to as many message-driven beans as it sees fit (the EJB container might only use a single bean if it desires).
One of the disadvantages of message-driven beans is that they can only listen to a single queue or topic. A single message-driven bean can't listen to messages from two different queues.
When you create a QueueSession, you must specify an acknowledgement mode. The three possible modes are Session.AUTO_ACKNOWLEDGE, Session.CLIENT_ACKNOWLEDGE, and Session.DUPS_OK_ACKNOWLEDGE. The AUTO_ACKNOWLEDGE mode causes the session to acknowledge messages automatically. The CLIENT_ACKNOWLEDGE mode requires the consumer to acknowledge each message manually by calling the message's acknowledge method. The DUPS_OK_ACKNOWLEDGE mode causes the session to acknowledge the messages lazily-that is, it acknowledges the messages when it sees it. In DUPS_OK_ACKNOWLEDGE mode, it's possible for a consumer to receive a message twice if the message service has a problem. Although the mode can allow messages to flow through the system faster, you should only use it if you can tolerate the presence of duplicate messages.
Listing 19.7 shows a simple message-driven bean that echoes messages back to the sender. It uses the JMSReplyTo property to figure out where to send the reply. Also, notice that it creates a QueueSender with a null Queue. This creates an anonymous producer that can send to any queue.
Listing 19.7 Source Code for EchoMessageBean.java
package usingj2ee.messages; import javax.ejb.*; import javax.jms.*; import javax.naming.*; public class EchoMessageBean implements MessageDrivenBean { private MessageDrivenContext mdContext; // The following Queue objects are only needed because this bean // sends a reply message back to the sender. For a message-driven bean, // the EJB container handles the Queue and Topic objects for the incoming // messages. protected QueueConnection qConn; protected QueueSession qSession; protected QueueSender qSender; public EchoMessageBean() { } public void ejbCreate() throws CreateException { try { // Locate the JNDI naming instance InitialContext context = new InitialContext(); // Locate the Queue Connection Factory QueueConnectionFactory qcFact = (QueueConnectionFactory) context.lookup("javax.jms.QueueConnectionFactory"); // Create a Queue Connection qConn = qcFact.createQueueConnection(); // Create a non-transactional Queue Sender qSession = qConn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); // Create an unidentified producer (a producer that isn't associated // with a particular queue) qSender = qSession.createSender(null); } catch (Exception exc) { exc.printStackTrace(); throw new CreateException( "Error initializing EchoMessageBean: "+ exc.toString()); } } public void setMessageDrivenContext(MessageDrivenContext ctx) { mdContext = ctx; } public void ejbActivate() { } public void ejbPassivate() { } public void ejbRemove() { try { // Close the Queue connection qConn.close(); } catch (Exception exc) { exc.printStackTrace(); } } public void onMessage(Message message) { try { // Set a property that lets the client know for sure that the // message bean saw the message message.setStringProperty("MessageBeanACK", "The message bean saw this message"); // Figure out where to send the reply Destination replyTo = message.getJMSReplyTo(); if ((replyTo != null) && (replyTo instanceof Queue)) { // Send the reply qSender.send((Queue) replyTo, message); } } catch (Exception exc) { exc.printStackTrace(); } } }