Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
CPAN423 Enterprise Java Programming Lecture #9: Introduction to Messaging with JMS Java Message Services (JMS): JMS is a communication service that enables applications to create, send, receive and read messages asynchronously (no waiting). The main advantage of JMS is to decouple clients that cannot wait for lengthy business method to complete. The sender and the receiver do not know about each other. Also the sender and receiver do not have to be connected at the same time. The sender connects to the JMS provider and sends the messages to a destination. When the receiver connects to the JMS provider, it can read the messages from that destination. JMS was originally created to provide an interface between Java applications and Message Oriented Middleware (MOM). MOM is a specialized type of applications that coordinate the sending and receiving of messages. It’s most suitable for connecting heterogeneous system. The JMS provider is the messaging system that handles the routing and delivery of the messages. JMS clients that send messages are called producers and JMS clients that receive messages are called consumers. A client application can be producer and consumer in the same time. JMS applications use JDBC to maintain messages persistence. JMS supports two messaging models: point-to-point and publish/subscribe. The point-to-point model is one-to-one model. The sender sends messages that are received by one client only. The sender sends messages to a particular queue which processes them on First-in-First-Out basis. Only once receiver can receive a message from the queue. The queue retains all messages until they are consumed or expired. The publish/subscribe model enable clients to publish messages on one-to-many or many-many basis. One or more publishers can publish messages that can be received by one or more subscribers. The publisher publishes a message to a topic. Interested subscribers can connect and receive messages from the topic. The topic sends messages to all subscribers. There are two kinds of subscriptions: durable and non durable. In a non durable subscription, the subscriber must be connected when a message is published in order to receive this message. The message is destroyed if there are no subscribers online. In a durable subscription, messages are stored until they can be delivered to the subscriber(s). Following is a description of the most important interfaces in the package javax.jms: 1 ConnectionFactory : Used to create a connection with the JMS provider. This interface has one method called getConnection and has two sub interfaces: TopicConnectionFactory and QueueConnectionFactory. TopicConnectionFactory is used to create a connection with publish/subscribe JMS provider and the QueueConnectionFactory is used to create a connection to a point-to-point JMS provider. Destination: Encapsulates a provider specific address. It has no methods and has two sub interfaces: Topic and Queue. Topic represents a destination in a publish/subscribe model and Queue represent a destination in a point-to-point model. Destination and ConnectionFactory objects are called administrator objects. The administrated objects are created by the administrator of the JMS provider and placed in a JNDI. Connection: represents clients to connect to a JMS provider. There are two kinds of connections: QueueConnection and TopicConnection. Session: single-threaded context for producing and consuming messages. The sub interface QueueSession provides methods for working with messages in a queue. The interface TopicSession provides methods for working with messages in a topic. MessageProducer: A message producer is used by the client to send messages to a destination. It has two sub interfaces: QueueSender and TopicPublisher MessageConsumer: A message consumer is used by the client to receive messages from a destination. It has two sub interfaces: QueueReceiver and TopicSubscriber Message: A message is a bundle of information sent from a process running on one computer to another running on the same computer or a different computer. There are three parts in a Message: header, properties and body. The header is required while the body and properties are optional. The header contains number of fields used to identify and rout the message. For example the header field JMSDeliveryMode indicates whether the deliver is persistent or not. The JMSExpiration indicates the length of time a message exists before it expires. If set to zero, the message will not expire. The properties part is used to hold application-specific values. The body is the content of the message based on the message type. There are 5 types of message body format in JMS: 1. TextMessage: message containing text formatted as a Java string. 2. MapMessage: A message containing a set of name/value pairs. Each name is a string and each value is a Java primitive type. 3. ByteMessage: A message containing uninterrupted stream of bytes. 4. StreamMessage: A message containing a stream of Java primitive values. 2 5. ObjectMessage: A message containing a serializable Java object or a collection of serializanble Java objects. ExceptionListener: used to communicate the JMS provider problems to the JMS client. To handle exceptions. You need to create a listener object that implements ExceptionListener and hence implement the OnException method. MessageListener: An object that implements this interface can receive messages from a Queue or a Topic asynchronously. MessageSelector: Used by the client to filter out received messages. Following are the steps to communicate with a Queue in a point-to-point model: Obtain a Queue via JNDI lookup. Obtain a QueueConnectionFactory via JNDI lookup. Obtain a QueueConnection to the provider. Obtain a QueueSession with the provider. Create either a QueueSender or a QueueReceiver Send or receive messages. Close the QueueSender/QueueReceiver, the QueueSession and the Connection. Following are the steps to communicate with a Topic in a publish/subscribe model: Obtain a Topic via JNDI lookup. Obtain a TopicConnectionFactory via JNDI lookup. Obtain a TopicConnection to the provider. Obtain a TopicSession with the provider. Create either a TopicPublisher or a TopicSubscriber. Publish or subscribe messages. Close the TopicPublisher/ TopicSubscriber, the TopicSession and the Connection. Message-Driven Beans: MDB is a new type of beans added to the EJB 2.0 specifications to enable an enterprise bean to be asynchronously invoked to handle incoming JMS. With entity bean and session beans messages are send and received synchronously. This means that the request is blocked until a response is received or the request is timed out. A MDB is an asynchronous message consumer. The MDB is created and managed by the EJB container. It is different from entity and session bean in that it has no home or component interface. Therefore Message-Driven Beans are hidden from the clients. A client cannot access the MDB directly. It can access MDB indirectly by sending messages to a JMS destination and the MDB will 3 consume the messages from that destination. MDB provide concurrent mechanism for processing messages sent to a certain destination. MDBs are stateless; they are not allowed to store state information for a particular client. This is because the client cannot make a direct call to a MDB. However, MDBs still can have instance variables and instance state. The EJB container interacts with the MDB through a set of methods in a similar way used with Session and Entity beans. Message-Driven Beans have tow states: Does Not Exist and Ready. The bean transit from Does Not Exist to Ready state when the container instantiate the bean with the Class.newInstance and the call of its methods :setMessageDrivenContext and ejbCreate . When in Ready state and when a message arrives, the container calls onMessage method of the bean to process the message. The bean transits from Ready state to Does Not Exist when the container calls ejbRemove method of the bean. MDB must be declared as public and must provide a no-argument constructor. It must implement the interface javax.ejb.MessageDrivenBean and the interface javax.jms.MessageListener. On that base, the MDB must provide an implementation to onMessage method. MDBs cannot be declared as final or abstract and must implement the finalize method. Examples: Ex1: Point-to-point JMS example In this example we will create two clients to a point-to-point JMS provider. The first client is called PTPSenderServlet.java and is used to send messages to a Queue. The Queue is managed by Resin’s JMS provider: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import javax.naming.*; import javax.jms.*; public class PTPSendingServlet extends HttpServlet { Queue queue; QueueConnectionFactory factory; public void init() throws ServletException 4 { super.init(); try { Context env = (Context) new InitialContext().lookup("java:comp/env"); queue = (Queue) env.lookup("jms/queue"); factory = (QueueConnectionFactory) env.lookup("jms/queueconnection-factory"); } catch (NamingException ex) { throw new ServletException(ex); } } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int count = 5; try { QueueConnection connection = factory.createQueueConnection(); QueueSession session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); QueueSender sender = session.createSender(queue); connection.start(); for (int i = 0; i <= count; i++) { String text = "message # " + String.valueOf(i); Message message = session.createTextMessage(text); sender.send(message); } connection.close(); } catch (JMSException ex) { throw new ServletException(ex); } PrintWriter out = response.getWriter(); out.print(count + " messages was sent."); } } The second client is called PTPReceiverServlet.java and is used to receive messages from the Queue: 5 import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import javax.naming.*; import javax.jms.*; public class PTPReceivingServlet extends HttpServlet { Queue queue; QueueConnectionFactory factory; public void init() throws ServletException { super.init(); try { Context env = (Context) new InitialContext().lookup("java:comp/env"); queue = (Queue) env.lookup("jms/queue"); factory = (QueueConnectionFactory) env.lookup("jms/queueconnection-factory"); } catch (NamingException ex) { throw new ServletException(ex); } } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out=null; try { out=response.getWriter(); QueueConnection connection = factory.createQueueConnection(); QueueSession session = connection.createQueueSession(false,Session.AUTO_ACKNOWLEDGE); QueueReceiver receiver = session.createReceiver(queue); connection.start(); if (receiver !=null) { // receive a message with 1 second timeout TextMessage msg= (TextMessage)receiver.receive(1000); out.println(msg.getText()); } connection.close(); } catch (Exception ex) { out.println("No more messages are sent"); 6 } } } Following is the configuration file web.xml for this example: <web-app xmlns="http://caucho.com/ns/resin"> <resource jndi-name="jms/queue-connection-factory" type='com.caucho.jms.JVMQueueConnectionFactory'/> <resource jndi-name="jms/queue" type='com.caucho.jms.memory.MemoryQueue'/> <servlet servlet-name="send" servlet-class="PTPSendingServlet"> </servlet> <servlet-mapping url-pattern="/send" servlet-name="send"/> <servlet servlet-name="receive" servlet-class="PTPReceivingServlet"> </servlet> <servlet-mapping url-pattern="/receive" servlet-name="receive"/> </web-app> We will deploy our application under webapps folder of Resin web server. The following steps illustrate the required steps: Create a folder called JMSEX under webapps folder. Any HTML or JSP file should be stored in JMSEX folder. Under JMSEX folder, create a folder called WEB-INF. WEB-INF contain the web application deployment descriptor files: web.xml o Under WEB-INF create the following folders: classes: contains the servlet classes, the EJB classes and any other supporting classes. lib: Contain any JAR file used in the web application. JARs can contain EJB, servlets and other supporting classes. The resulting structure should look as follow: webapps JMSEX WEB-INF classes lib The files PTPSendingServlet.java and PTPReceivingServlet.java should be stored under classes folder. 7 Ex2: publish/subscribe JMS example In this example we will create three clients to a publish/subscribe JMS provider. The first client is called PublisherServlet.java and is used to send messages to a Topic. The Topic is managed by Resin’s JMS provider: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import javax.naming.*; import javax.jms.*; import java.util.*; public class PublisherServlet extends HttpServlet { Topic topic; TopicConnectionFactory factory; public void init() throws ServletException { super.init(); try { Context env = (Context) new InitialContext().lookup("java:comp/env"); topic = (Topic) env.lookup("jms/topic"); factory = (TopicConnectionFactory) env.lookup("jms/topic-connectionfactory"); } catch (NamingException ex) { throw new ServletException(ex); } } public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException { PrintWriter out=null; try { out=response.getWriter(); TopicConnection connection = factory.createTopicConnection(); 8 TopicSession jmsSession = connection.createTopicSession(false,Session.AUTO_ACKNOWLEDGE); TopicPublisher publisher = jmsSession.createPublisher(topic); publisher.setDeliveryMode(DeliveryMode.PERSISTENT); connection.start(); TextMessage message = jmsSession.createTextMessage(); Date date=new Date(); message.setText("messaage published on" +date.toString()); publisher.publish(topic, message); out.println(message.getText()); connection.close(); // } // connection.close(); } catch (Exception ex) { // throw new ServletException(ex); out.println(ex.toString()); } } } The second client is called SubscriberServlet.java and is used to receive messages from the Topic: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import javax.naming.*; import javax.jms.*; public class SubscriberServlet extends HttpServlet { 9 Topic topic; TopicConnectionFactory factory; public void init() throws ServletException { super.init(); try { Context env = (Context) new InitialContext().lookup("java:comp/env"); topic = (Topic) env.lookup("jms/topic"); factory = (TopicConnectionFactory) env.lookup("jms/topic-connectionfactory"); } catch (NamingException ex) { throw new ServletException(ex); } } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out =null; try { TopicConnection connection = factory.createTopicConnection(); TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); TopicSubscriber subscribe = session.createDurableSubscriber(topic,"s1"); connection.start(); out = response.getWriter(); TextMessage msg=(TextMessage)subscribe.receive(1000); out.print("receive " + msg.getText()); connection.close(); } catch (Exception ex) { 10 out.println("No more messages published"); } } } The third client is called SubscriberServlet1.java and is used to receive messages from the Topic: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import javax.naming.*; import javax.jms.*; public class SubscriberServlet1 extends HttpServlet { Topic topic; TopicConnectionFactory factory; public void init() throws ServletException { super.init(); try { Context env = (Context) new InitialContext().lookup("java:comp/env"); topic = (Topic) env.lookup("jms/topic"); factory = (TopicConnectionFactory) env.lookup("jms/topic-connectionfactory"); } catch (NamingException ex) { throw new ServletException(ex); } } 11 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out =null; try { TopicConnection connection = factory.createTopicConnection(); TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); TopicSubscriber subscribe = session.createDurableSubscriber(topic,"s2"); connection.start(); out = response.getWriter(); TextMessage msg=(TextMessage)subscribe.receive(1000); out.print("receive " + msg.getText()); connection.close(); } catch (Exception ex) { out.println("No more messages published"); } } } We will deploy our example under the folder JMSEX created in example 1. The files PublisherServlet.java, SubscriberServlet.java and SubscriberServlet1.java must be stored under the folder JMSEX /classes. You also need to add the following configuration to web.xml under JMSEX /WEB-INF <resource jndi-name="jms/topic-connection-factory" type='com.caucho.jms.JVMTopicConnectionFactory'/> <resource jndi-name="jms/topic" 12 type='com.caucho.jms.memory.MemoryTopic'/> <servlet servlet-name="publish" servlet-class="PublisherServlet"> </servlet> <servlet-mapping url-pattern="/publish" servlet-name="publish"/> <servlet servlet-name="subscribe" servlet-class="SubscriberServlet"> </servlet> <servlet-mapping url-pattern="/subscribe" servlet-name="subscribe"/> <servlet servlet-name="subscribe1" servlet-class="SubscriberServlet1"> </servlet> <servlet-mapping url-pattern="/subscribe1" servlet-name="subscribe1"/> Ex3: MDB example In this example we will create a MDB called MessageBeanEx.java that receives messages from a Queue identified by the JNDI “jms/queue“ and forward them to another Queue identified by the JNDI “jms/queue1“. The MDB will also log the messages to text file. package MDB; import java.util.logging.Level; import java.util.logging.Logger; import javax.ejb.*; import javax.jms.*; import javax.naming.*; public class MessageBeanEx implements MessageDrivenBean, MessageListener { static protected final Logger log = Logger.getLogger(MessageBeanEx.class.getName()); MessageDrivenContext context = null; QueueConnection connection; QueueSession session; public void setMessageDrivenContext(MessageDrivenContext context) { } public void ejbCreate() throws EJBException { } public void ejbRemove() { } public void onMessage(Message msg) { try { TextMessage message = (TextMessage) msg; log.fine(message.getText()); 13 InitialContext initContext = new InitialContext(); QueueConnectionFactory factory = (QueueConnectionFactory) initContext.lookup("java:comp/env/jms/queue-connection-factory"); connection = factory.createQueueConnection(); session = connection.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE); Queue queue = (Queue) initContext.lookup("java:comp/env/jms/queue1"); QueueSender sender = session.createSender(queue); TextMessage message2 = session.createTextMessage(message.getText()+"Forwarded from MDB"); sender.send(message2); sender.close(); } catch(Exception e) { e.printStackTrace(); } } } The example will use two clients. The first client is called MessageSenderServlet.java: and is used send message to the queue identified by the JNDI “jms/queue“: import java.io.*; import javax.jms.*; import javax.naming.*; import javax.servlet.*; import javax.servlet.http.*; public class MessageSenderServlet extends HttpServlet { Queue queue; QueueConnectionFactory factory; public void init() throws ServletException { super.init(); 14 try { Context env = (Context) new InitialContext().lookup("java:comp/env"); queue = (Queue) env.lookup("jms/queue"); factory = (QueueConnectionFactory) env.lookup("jms/queueconnection-factory"); } catch (NamingException ex) { throw new ServletException(ex); } } public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException { int count = 6; try { QueueConnection connection = factory.createQueueConnection(); QueueSession session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); QueueSender sender = session.createSender(queue); TextMessage message = session.createTextMessage(); for (int i = 1; i <= count; i++) { message.setText( "message #" + String.valueOf(i)); sender.send(message); } } catch (JMSException ex) { throw new ServletException(ex); } PrintWriter out = response.getWriter(); out.print("Sent " + String.valueOf(count) + " messages."); } } 15 The second client is called MessageRecieverServlet.java and is used to receive messages from the queue identified by the JNDI “jms/queue1“ import java.io.*; import javax.jms.*; import javax.naming.*; import javax.servlet.*; import javax.servlet.http.*; public class MessageReceiverServlet extends HttpServlet { Queue queue; QueueConnectionFactory factory; public void init() throws ServletException { super.init(); try { Context env = (Context) new InitialContext().lookup("java:comp/env"); queue = (Queue) env.lookup("jms/queue1"); factory = (QueueConnectionFactory) connection-factory"); env.lookup("jms/queue- } catch (NamingException ex) { throw new ServletException(ex); } } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); try { 16 QueueConnection connection = factory.createQueueConnection(); QueueSession session = connection.createQueueSession(false,Session.AUTO_ACKNOWLEDGE); QueueReceiver receiver = session.createReceiver(queue); connection.start(); if (receiver !=null) { // receive a message with 1 second timeout TextMessage msg= (TextMessage)receiver.receive(1000); out.println(msg.getText()); } } catch (Exception ex) { out.println("No more messages "); } } } We will deploy our MDB under webapps folder of Resin web server under a folder called MDBEX. The following steps illustrate the required steps: Create a folder called MDBEX under webapps folder. Any HTML or JSP file should be stored in MDBEX folder. Under MDBEX folder create a folder called WEB-INF. WEB-INF contains the web application deployment descriptor files: web.xml and one EJB descriptor file for each beans deployed under MDBEX folder. o Under WEB-INF create the following folders: classes: contains the servlet classes, the EJB classes and any other supporting classes. We will package our EJB classes into a folder called MDB. lib: Contain any JAR file used in the web application. JARs can contain EJB, servlets and other supporting classes. The resulting structure should look as follow: webapps EntityEJB WEB-INF 17 classes MDB lib The files MessageBeanEx.java should be stored under MDB folder. MessageSenderServlet.java and MessageReceiverServlet.java. should be stored under classes folder. Following is an EJB descriptor file called MDB.ejb that should be stored under WEB-INF folder: <ejb-jar xmlns="http://caucho.com/ns/resin"> <enterprise-beans> <message-driven> <ejb-name>MessageBeanEx</ejb-name> <ejb-class>MDB.MessageBeanEx</ejb-class> <destination>jms/queue</destination> </message-driven> </enterprise-beans> </ejb-jar> Following is the web application deployment descriptor for this application (web.xml) that should be stored under WEB-INF folder: <web-app xmlns="http://caucho.com/ns/resin"> <log name="MDB" level="fine" path="WEB-INF/debug.log" timestamp="[%M:%S.%s] " format="${log.loggerName} ${log.message}"/> <resource jndi-name="jms/queue-connection-factory" type='com.caucho.jms.JVMQueueConnectionFactory'/> <resource jndi-name="jms/queue" type='com.caucho.jms.memory.MemoryQueue'/> <resource jndi-name="jms/queue1" type='com.caucho.jms.memory.MemoryQueue'/> <ejb-server config-directory="WEB-INF"> <jms-connection-factory>jms/queue-connection-factory</jmsconnection-factory> </ejb-server> <servlet> <servlet-name>MessageSenderServlet</servlet-name> <servlet-class>MessageSenderServlet</servlet-class> </servlet> <servlet-mapping> <url-pattern>/sender</url-pattern> 18 <servlet-name>MessageSenderServlet</servlet-name> </servlet-mapping> <servlet> <servlet-name>MessageReceiverServlet</servlet-name> <servlet-class>MessageReceiverServlet</servlet-class> </servlet> <servlet-mapping> <url-pattern>/receiver</url-pattern> <servlet-name>MessageReceiverServlet</servlet-name> </servlet-mapping> </web-app> Ex4: MDB with Entity EJB example In this example, we will use a CMP Entity bean to maintain the state of messages processed by a Message-Driven bean. First we will create the CMP Entity EJB. The entity bean represent the name of the favorite programming language and the vote count for this language. We will create a local interface called Candidate. This interface defines the database related business methods that will be available to the clients. package votes; import javax.ejb.*; public interface Candidate extends EJBLocalObject { public void incrementVoteCount() ; public Integer getVoteCount() ; public String getCandidateName() ; } Then we will write a Local Home interface called CandidateHome. This interface defines the database access methods: package votes; import java.util.*; import javax.ejb.*; public interface CandidateHome extends EJBLocalHome { public Candidate findByPrimaryKey( String candidateName ) throws FinderException; public Collection findAllCandidates() throws FinderException; public Candidate create( String candidateName ) throws CreateException; } 19 Then we will provide an implementation class called CandidateEJB. Since we are using CMP, abstract access methods must be provided for each table’s field. This also means that the class itself must be declared an abstract. The EJB container will generate the required persistence code based on these methods. package votes; import javax.ejb.*; public abstract class CandidateEJB implements EntityBean { private EntityContext entityContext; public Integer voteCount; public String name; public void incrementVoteCount() { int newVoteCount = getVoteCount().intValue() + 1; voteCount = new Integer( newVoteCount ); setVoteCount(voteCount); } public abstract void setVoteCount(Integer cnt); public abstract Integer getVoteCount(); public abstract String getCandidateName(); public abstract void setCandidateName(String name); public String ejbCreate( String candidateName ) throws CreateException { setCandidateName (candidateName); voteCount = new Integer( 0 ); setVoteCount(voteCount); return null; } public void ejbPostCreate( String candidateName ) {} public void setEntityContext( EntityContext context ) { entityContext = context; } public void unsetEntityContext() { entityContext = null; } public void ejbActivate() { name = ( String ) entityContext.getPrimaryKey(); } 20 public void ejbPassivate() { name = null; } public void ejbLoad() {} public void ejbStore() {} public void ejbRemove() {} } After we have created the Entity bean, we will create the Message-Driven bean to process messages send to the queue. The MDB will use the Entity bean to store the messages to a database. Messages contain the names of the client favorite programming languages. package votes; import java.util.*; import java.rmi.*; import javax.ejb.*; import javax.rmi.*; import javax.jms.*; import javax.naming.*; public class VoteCollectorEJB implements MessageDrivenBean, MessageListener { private MessageDrivenContext messageDrivenContext; public void onMessage( Message message ) { TextMessage voteMessage; try { if ( message instanceof TextMessage ) { voteMessage = ( TextMessage ) message; String vote = voteMessage.getText(); countVote( vote ); } } 21 catch ( JMSException jmsException ) { jmsException.printStackTrace(); } } private void countVote( String vote ) { CandidateHome home = null; try { Context ctx = new InitialContext(); home = (CandidateHome) ctx.lookup("java:comp/env/cmp/client"); /* You need to have the candidate Names added to the votes table. This way you limit the no of candidates. If the number of candidate is unlimited, then you can uncomment the following line home.create(vote); */ Candidate candidate = home.findByPrimaryKey( vote ); candidate.incrementVoteCount(); } catch ( Exception e ) { throw new EJBException( e ); } 22 } public void setMessageDrivenContext( MessageDrivenContext context ) { messageDrivenContext = context; } public void ejbCreate() {} public void ejbRemove() {} } The MDB will process incoming messages to the Queue and uses the Entity bean to store the messages to a database table. Following is the SQL statement used to create a table called votes: Create table votes( candidateName varchar2(30), voteCount number(6), primary key(candidateName)); Also we will create a client called BMSender.java used to send message to the application’s queue: import java.io.*; import javax.jms.*; import javax.naming.*; import javax.servlet.*; import javax.servlet.http.*; public class BMSender extends HttpServlet { Queue queue; QueueConnectionFactory factory; public void init() throws ServletException 23 { super.init(); try { Context env = (Context) new InitialContext().lookup("java:comp/env"); queue = (Queue) env.lookup("jms/queue"); factory = (QueueConnectionFactory) env.lookup("jms/queueconnection-factory"); } catch (NamingException ex) { throw new ServletException(ex); } } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { QueueConnection connection = factory.createQueueConnection(); QueueSession session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); QueueSender sender = session.createSender(queue); TextMessage message = session.createTextMessage(); message.setText("C"); sender.send(message); message.setText("Java"); sender.send(message); message.setText("VB"); sender.send(message); message.setText("HTML"); sender.send(message); } catch (JMSException ex) { 24 throw new ServletException(ex); } PrintWriter out = response.getWriter(); out.print("Sent messages."); } } The application will use another client called Client.java that will connect the Entity bean to retrieve the name of the programming languages and the vote counts. import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import javax.naming.*; import javax.ejb.*; import java.util.*; import javax.rmi.*; import votes.*; public class Client extends HttpServlet { private CandidateHome home = null; public void setCandidateHome(CandidateHome h) { home = h; } public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { PrintWriter out = res.getWriter(); try { res.setContentType("text/html"); 25 Context initialContext = new InitialContext(); Collection candidates = home.findAllCandidates(); Iterator iterator = candidates.iterator(); while ( iterator.hasNext() ) { Candidate candidate = ( Candidate ) iterator.next(); out.println( candidate.getCandidateName() +" : "); out.println(candidate.getVoteCount().intValue() ); out.println("<br/>"); } } catch (Exception e) { throw new ServletException(e); } } } EJB applications are deployed under webapps folder of Resin web server. We will deploy our EJB under a folder called MDBwithEntityEJB. The following steps illustrate the required steps: Create a folder called MDBwithEntityEJB under webapps folder. Any HTML or JSP file should be stored in MDBwithEntityEJB folder. Under MDBwithEntityEJB folder create a folder called WEB-INF. WEBINF contain the web application deployment descriptor files: web.xml and one EJB descriptor file for each beans deployed under MDBwithEntityEJB folder. o Under WEB-INF create the following folders: classes: contains the servlet classes, the EJB classes and any other supporting classes. We will package our EJB classes into a folder called votes. lib: Contain any JAR file used in the web application. JARs can contain EJB, servlets and other supporting classes. The resulting structure should look as follow: webapps MDBwithEntityEJB WEB-INF classes votes 26 lib The files Candidate.java, CandidateHome.java, CandidateEJB.java and VoteCollectorEJB.java should be stored under votes folder. BMSender.java, BMReceiver.java should be stored under classes folder. Following is an EJB descriptor file called candidate.ejb for the Candidate Entity EJB that should be stored under WEB-INF folder: <?xml version="1.0"?> <ejb-jar xmlns="http://caucho.com/ns/resin"> <enterprise-beans> <entity> <ejb-name>client</ejb-name> <local-home>votes.CandidateHome</local-home> <local>votes.Candidate</local> <ejb-class>votes.CandidateEJB</ejb-class> <persistence-type>Container</persistence-type> <prim-key-class>java.lang.String</prim-key-class> <reentrant>True</reentrant> <abstract-schema-name>votes</abstract-schema-name> <sql-table>votes</sql-table> <cmp-field> <field-name>candidateName</field-name> <sql-column>candidateName</sql-column> </cmp-field> <cmp-field> <field-name>voteCount</field-name> <sql-column>voteCount</sql-column> </cmp-field> <primkey-field>candidateName</primkey-field> <query> <query-method> <method-name>findAllCandidates</method-name> </query-method> 27 <ejb-ql> <![CDATA[SELECT o FROM votes o]]> </ejb-ql> </query> </entity> </enterprise-beans> <assembly-descriptor> <container-transaction> <method> <ejb-name>client</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> </assembly-descriptor> </ejb-jar> Following is an EJB descriptor file called message.ejb for the MDB that should be stored under WEB-INF folder: <ejb-jar xmlns="http://caucho.com/ns/resin"> <enterprise-beans> <message-driven> <ejb-name>VoteCollectorEJB</ejb-name> <ejb-class>votes.VoteCollectorEJB</ejb-class> <destination>jms/queue</destination> </message-driven> </enterprise-beans> </ejb-jar> Following is the web application deployment descriptor for this application (web.xml) that should be stored under WEB-INF folder: 28 <web-app xmlns="http://caucho.com/ns/resin"> <resource jndi-name="jms/queue-connection-factory" type='com.caucho.jms.JVMQueueConnectionFactory'/> <resource jndi-name="jms/queue" type='com.caucho.jms.memory.MemoryQueue'/> <database jndi-name="jdbc/test"> <driver type="oracle.jdbc.driver.OracleDriver"> <url>jdbc:oracle:thin:@munro.humber.ca:1521:msit</url> <user>user</user> <password>password</password> </driver> </database> <ejb-server jndi-name="cmp"> <config-directory>WEB-INF</config-directory> <data-source>jdbc/test</data-source> <create-database-schema/> <jms-connection-factory>jms/queue-connection-factory</jmsconnection-factory> </ejb-server> <servlet servlet-name="client" servlet-class="Client"> <init> <candidate-home>${jndi:lookup("cmp/client")}</candidate-home> </init> </servlet> <servlet-mapping url-pattern="/client" servlet-name="client"/> <servlet servlet-name="sender" servlet-class="BMSender"> </servlet> <servlet-mapping url-pattern="/sender" servlet-name="sender"/> </web-app> 29