EJB Container IntegrationThe JMS API has recently become an integral part of the J2EE architecture. JMS messages can be used to drive processing in a J2EE container via Message Driven Beans (MDB). Furthermore, all the Enterprise Beans in a J2EE application can send messages to JMS destinations by accessing the JMS API via a JCA resource adapter. Both, Message Driven Beans and the JMS JCA resource adapter will be covered in this chapter. Message Driven BeansA Message Driven Bean is a Enterprise Bean which has no remote or home interfaces. The purpose of an MDB is to process messages that get delivered from a JMS destination. The MDB must implement the JMS MessageListener interface. The messages are delivered asynchronously to the onMessage(...) method of the MDB. AdvantagesThere are many advantages that a MDB brings to the table when processing JMS messages. One of most important is allowing the J2EE container manage the transaction. Allowing the container to manage transaction removes the responsibility of correctly doing transaction management from the application developer and places that responsibility on the J2EE container. When an MDB is used to process JMS messages, the container controls the number of threads executing used to deliver messages to MDB. Thread management is an integral part of building scalable applications. JBoss has another great reason to use a MDB to process JMS messages. It's typically orders of magnitude faster! When the MDB is co-located on the server that is running JBossMQ, an optimized connection is used to deliver the messages to the MDB. MDB InterfaceAn MDB must implement both MessageDrivenBean and MessageListener interfaces. Listing 4.1 is the source code to one of the simplest Message Driven Beans that can be created. The onMessage(...) method extracts the text of the message and display it to the console. Listing 4.1 The source to a simple MDB which displays TextMessages on the console. import javax.ejb.*; import javax.jms.*; /** * A MDB which receives TextMessages and displays them on the console. */ public class TextMDB implements MessageDrivenBean, MessageListener { public void setMessageDrivenContext(MessageDrivenContext ctx) throws EJBException { } public void ejbCreate() { } public void ejbRemove() { } public void onMessage(Message msg) { try { TextMessage message = (TextMessage)msg; System.out.println("MDB received message: "+message.getText()); } catch (JMSException e) { e.printStackTrace(); } } } Deployment DescriptorA MDB like all other Enterprise Java Beans must use a deployment descriptor to configure the details of how the J2EE container will manage the MDB. The deployment descriptor configures the type of destination that will be used as the source of the delivered messages. It configures the type of transaction management that the MDB will be using. It also sets the durable subscription and/or message selector that will be used if any. Listing 4.2 The /META-INF/ejb-jar.xml deployment descriptor for the TextMDB. "1.0"?> "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/j2ee/dtds/ejb-jar_2_0.dtd"> The jar file that your MDB gets packaged in should have a XML file called /META-INF/ejb-jar.xml. You may need to generate additional container specific deployment descriptors in addition to the ejb-jar.xml file. Refer to your J2EE container's user guides for more information. The additional deployment descriptor that is needed for JBoss should be stored as /META-INF/jboss.xml. The sample below configures our example bean to use the standard MDB container configuration and for the messages to get delivered from the testQueue destination. Listing 4.3 The /META-INF/jboss.xml deployment descriptor for the TextMDB. "1.0" encoding="utf-8"?> If you deploy the sample we have been describing, the JBoss sever console should display messages similar to the following: 23:10:38,887 INFO [MainDeployer] Starting deployment of package: file:/C:/Java/JBoss-3.2.1/server/default/deploy/mdb-sample.jar 23:10:39,238 INFO [EjbModule] Creating 23:10:39,248 INFO [EjbModule] Deploying Example 23:10:39,288 INFO [MessageDrivenContainer] Creating 23:10:39,288 INFO [MessageDrivenInstancePool] Creating 23:10:39,288 INFO [MessageDrivenInstancePool] Created 23:10:39,298 INFO [JMSContainerInvoker] Creating 23:10:39,308 INFO [JMSContainerInvoker] Created 23:10:39,308 INFO [MessageDrivenContainer] Created 23:10:39,308 INFO [EjbModule] Created 23:10:39,308 INFO [EjbModule] Starting 23:10:39,308 INFO [MessageDrivenContainer] Starting 23:10:39,318 INFO [JMSContainerInvoker] Starting 23:10:39,318 INFO [DLQHandler] Creating 23:10:39,438 INFO [DLQHandler] Created 23:10:39,548 INFO [DLQHandler] Starting 23:10:39,558 INFO [DLQHandler] Started 23:10:39,558 INFO [JMSContainerInvoker] Started 23:10:39,558 INFO [MessageDrivenInstancePool] Starting 23:10:39,558 INFO [MessageDrivenInstancePool] Started 23:10:39,558 INFO [MessageDrivenContainer] Started 23:10:39,558 INFO [EjbModule] Started 23:10:39,578 INFO [EJBDeployer] Deployed: file:/C:/Java/JBoss-3.2.1/server/default/deploy/mdb-sample.jar 23:10:39,638 INFO [MainDeployer] Deployed package: file:/C:/Java/JBoss-3.2.1/server/default/deploy/mdb-sample.jar And if you send the ?testQueue? a couple of text messages, you should see the MDB display them on the console: 23:10:39,718 INFO [STDOUT] MDB received message: Hello World! 23:10:39,728 INFO [STDOUT] MDB received message: Hello World! 23:10:39,728 INFO [STDOUT] MDB received message: Hello World! JCA JMS ConnectionsEnterprise Java Bean developers eventual come across a problem where they need to to use the JMS API within an EJB. The developer may need to check to see how many messages are sitting in queue or they may need to send a new message to a topic. In either case, it is highly recommended that JCA based JMS connection be used by the EJB instead of the standard JMS connection. This section will examine a MDB that uses JCA JMS Connections. The MDB's purpose is to relay the message that was received in the onMessage(...) method and send it to an output queue that is defined at deployment time. The code for the RelayMDB is shown in Listing 4.4. AdvantagesJCA based connections allow the J2EE container to:
Looking up the Resource AdapterThe EJB should lookup the JCA JMS connection factory using the bean environment mechanism. In other words, it should look up the connection factory in a JNDI entry under the Java:comp/env JNDI context. At application assembly time, the deployment descriptor will be configured so that a JCA JMS Connection Factory is bound to that entry. The RelayMDB in Listing 4.4 is initialized by looking up the a QueueConnectionFactory and a Queue in the bean environment. It is important to note that the connection to the JMS provider is being established in the ejbCreate() method call. This avoids doing the JNDI look up call every time a message is processed. public void ejbCreate() { try { InitialContext ictx = new InitialContext(); output = (Queue)ictx.lookup("Java:comp/env/jms/output"); QueueConnectionFactory factory = (QueueConnectionFactory)ictx.lookup( "Java:comp/env/jms/connectionfactory"); connection = factory.createQueueConnection(); } catch (Exception e) { e.printStackTrace(); throw new EJBException(e.toString()); } } Since the connection is established in the ejbCreate() method call, the connection should conversely be closed in the ejbRemove() method call. public void ejbRemove() { try { connection.close(); } catch (Throwable e) { } ctx = null; } Sending a Message Using a JCA ConnectionOnce relay RelayMDB has been initialized, it is ready to process messages. The main part of the onMessage(Message) method is straightforward. A new session should be established as usual. The transaction arguments to the session will be ignored since the JCA session will use the J2EE container to manage transaction. The sender object is created and is then used to send the message that was just received to the output queue. public void onMessage(Message msg) { ... try { session = connection.createQueueSession(true, Session.AUTO_ACKNOWLEDGE); sender = session.createSender(output); System.out.println("Relaying message " + msg); sender.send(msg); The error handing should be explained in a little more detail. If a JMSException occurs, a message is displayed and then current transaction is marked for rollback. This would rollback the message that was sent using the JCA adapter and potentially (depending on the deployment descriptor) the transaction that delivered the message to RelayMDB. catch (JMSException ex) { System.out.println("Rolling back message due to error."); ctx.setRollbackOnly(); throw new EJBException(ex.toString()); } The finally block ensures that the session is closed properly. It is important that the session always be closed after it is used. The session object is pooled by the JCA Resource Adapter so your EJB should not try to cache it. Creating new session objects is a relatively cheap operation for the JCA Resource Adapter. finally { try { session.close(); } catch (Throwable e) { } } Listing 4.4 The source code for a MDB that relays messages to another queue. import javax.ejb.*; import javax.jms.*; import javax.naming.*; /** * A MDB which relays the received message to another Queue */ public class RelayMDB implements MessageDrivenBean, MessageListener { private MessageDrivenContext ctx = null; public void setMessageDrivenContext(MessageDrivenContext ctx) throws EJBException { this.ctx = ctx; } Queue output; QueueConnection connection; public void ejbCreate() { try { InitialContext ictx = new InitialContext(); output = (Queue)ictx.lookup("Java:comp/env/jms/output"); QueueConnectionFactory factory = (QueueConnectionFactory)ictx.lookup("Java:comp/env/jms/connectionfactory"); connection = factory.createQueueConnection(); } catch (Exception e) { e.printStackTrace(); throw new EJBException(e.toString()); } } public void ejbRemove() { try { connection.close(); } catch (Throwable e) { } ctx = null; } public void onMessage(Message msg) { QueueSession session = null; QueueSender sender = null; try { session = connection.createQueueSession(true, Session.AUTO_ACKNOWLEDGE); sender = session.createSender(output); System.out.println("Relaying message " + msg); sender.send(msg); } catch (JMSException ex) { System.out.println("Rolling back message due to error."); ctx.setRollbackOnly(); throw new EJBException(ex.toString()); } finally { try { session.close(); } catch (Throwable e) { } } } } Deployment DescriptorThe RelayMDB like all EJBs requires a deployment descriptor. It is very similar to the deployment descriptor in Listing 4.2. The main difference to note are the Listing 4.5 The /META-INF/ejb-jar.xml deployment descriptor for the RelayMDB. "1.0"?> "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/j2ee/dtds/ejb-jar_2_0.dtd"> Creating the JBoss specific deployment descriptor is similar to the TextMDB deployment descriptor used to deploy the shown in Listing 4.3. Once of the biggest differences is that it has a Later in in the deployment descriptor, the Listing 4.6 The /META-INF/jboss.xml deployment descriptor for the RelayMDB. "1.0" encoding="utf-8"?> |
|
|||||
© 2003 Core Developers Network Ltd "Core Developers Network", the stylized apple logo and "Core Associates" are trademarks of Core Developers Network Ltd. All other trademarks are held by their respective owners. Core Developers Network Ltd is not affiliated with any of the respective trademark owners. |