This example shows an EJB application that combines an Enterprise Bean sending a JMS message and an Enterprise Bean writing a Database (an Entity Bean) within the same global transaction. It is composed of the following elements:
A Session Bean, EjbComp, with a method for sending a message to a JMS topic.
An Entity Bean, Account (the one used in the sample eb with container-managed persistence), which writes its data into a relational database table and is intended to represent a sent message (that is, each time the EjbComp bean sends a message, an Entity Bean instance will be created).
An EJB client, EjbCompClient, which calls the sendMsg method of the EjbComp bean and creates an Account entity bean, both within the same transaction. For a transaction commit, the JMS message is actually sent and the record corresponding to the entity bean in the database is created. For a rollback, the message is not sent and nothing is created in the database.
A pure JMS client MsgReceptor, outside the JOnAS server, the role of which is to receive the messages sent by the Enterprise Bean on the topic.
You can find the sample JMS application in $JONAS_ROOT/examples/src/jms/; it is described in Section 26.6 A JMS EJB Example.
The bean should contain code for initializing the references to JMS administered objects that it will use. To avoid repeating this code in each method performing JMS operations, it can be introduced in the ejbCreate method.
public class EjbCompBean implements SessionBean { ... ConnectionFactory cf = null; Topic topic = null; public void ejbCreate() { .... ictx = new InitialContext(); cf = (ConnectionFactory) ictx.lookup("java:comp/env/jms/conFactSender"); topic = (Topic) ictx.lookup("java:comp/env/jms/topiclistener"); } ... } |
All code that is not necessary for understanding the JMS logic (such as exception management) has been removed from the above example.
The JMS-administered objects ConnectionFactory and Topic have been made available to the bean by a resource reference in the first example, and by a resource environment reference in the second example.
The standard deployment descriptor should contain the following element:
<resource-ref> <res-ref-name>jms/conFactSender</res-ref-name> <res-type>javax.jms.ConnectionFactory</res-type> <res-auth>Container</res-auth> </resource-ref> <resource-env-ref> <resource-env-ref-name>jms/topiclistener</resource-env-ref-name> <resource-env-ref-type>javax.jms.Topic</resource-env-ref-type> </resource-env-ref> |
The JOnAS-specific deployment descriptor should contain the following element:
<jonas-resource> <res-ref-name>jms/conFactSender</res-ref-name> <jndi-name>TCF</jndi-name> </jonas-resource> <jonas-resource-env> <resource-env-ref-name>jms/topiclistener</resource-env-ref-name> <jndi-name>sampleTopic</jndi-name> </jonas-resource-env> |
Note that the EjbComp SessionBean will use the administered objects automatically created by JOnAS in the default JMS configuration.
Because the administered objects are now accessible, it is possible to perform JMS operations within a method. The following occurs in the sendMsg method:
public class EjbCompBean implements SessionBean { ... public void sendMsg(java.lang.String s) { // create Connection, Session and MessageProducer Connection conn = null; Session session = null; MessageProducer mp = null; try { conn = cf.createConnection(); session = conn.createSession(true, Session.AUTO_ACKNOWLEDGE); mp = session.createProducer((Destination)topic); } catch (Exception e) {e.printStackTrace();} // send the message to the topic try { ObjectMessage message; message = session.createObjectMessage(); message.setObject(s); mp.send(message); session.close(); conn.close(); } catch (Exception e) { e.printStackTrace(); } } ... } |
This method sends a message containing its String argument.
The example uses the simple Entity Bean Account for writing data into a database. Refer to the sample eb, which is described in Chapter 2 Getting Started with JOnAS and in the JOnAS Tutorial.
The client application calls the sendMsg method of the EjbComp bean and creates an AccountImpl Entity Bean, both within the same transaction.
public class EjbCompClient { ... public static void main(String[] arg) { ... utx = (UserTransaction) initialContext.lookup("javax.transaction.UserTransaction"); ... home1 = (EjbCompHome) initialContext.lookup("EjbCompHome"); home2 = (AccountHome) initialContext.lookup("AccountImplHome"); ... EjbComp aJmsBean = home1.create(); Account aDataBean = null; ... utx.begin(); aJmsBean.sendMsg("Hello commit"); // sending a JMS message aDataBean = home2.create(222, "JMS Sample OK", 0); utx.commit(); utx.begin(); aJmsBean.sendMsg("Hello rollback"); // sending a JMS message aDataBean = home2.create(223, "JMS Sample KO", 0); utx.rollback(); ... } } |
The result of this client execution will be that:
The "Hello commit" message will be sent and the [222, 'JMS Sample OK', 0] record will be created in the database (corresponding to the Entity Bean 109 creation).
The "Hello rollback" message will never be sent and the [223, 'JMS Sample KO', 0] record will not be created in the database (since the Entity Bean 110 creation will be canceled).
In this example, the messages sent by the EJB component are received by a simple JMS client that is running outside the JOnAS server, but listening for messages sent on the JMS topic "sampleTopic." It uses the ConnectionFactory automatically created by JOnAS named "JCF".
public class MsgReceptor { static Context ictx = null; static ConnectionFactory cf = null; static Topic topic = null; public static void main(String[] arg) { ictx = new InitialContext(); cf = (ConnectionFactory) ictx.lookup("JCF"); topic = (Topic) ictx.lookup("sampleTopic"); ... Connection conn = cf.createConnection(); Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageConsumer mc = session.createConsumer((Destination)topic); MyListenerSimple listener = new MyListenerSimple(); mc.setMessageListener(listener); conn.start(); System.in.read(); // waiting for messages session.close(); conn.close(); ... } } public MyListenerSimple implements javax.jms.MessageListener { MyListenerSimple() {} public void onMessage(javax.jms.Message msg) { try { if(msg==null) System.out.println("Message: message null "); else { if(msg instanceof ObjectMessage) { String m = (String) ((ObjectMessage)msg).getObject(); System.out.println ("JMS client: received message ======> " + m); } else if(msg instanceof TextMessage) { String m = ((TextMessage)msg).getText(); System.out.println ("JMS client: received message ======> " + m); } }catch(Exception exc) { System.out.println("Exception caught :" + exc); exc.printStackTrace(); } } } |