ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

EJB Message-Driven Beans
Pages: 1, 2, 3, 4, 5, 6, 7

JMS as a Resource

JMS is a standard vendor-neutral API that is part of the J2EE platform and can be used to access enterprise messaging systems. Enterprise messaging systems (a.k.a. message-oriented middleware) facilitate the exchange of messages among software applications over a network. JMS is analogous to JDBC: whereas JDBC is an API that can be used to access many different relational databases, JMS provides the same vendor-independent access to enterprise messaging systems. Many enterprise messaging products currently support JMS, including IBM's MQSeries, BEA's WebLogic JMS service, Sun Microsystems' iPlanet Message Queue, and Progress' SonicMQ, to name a few. Software applications that use the JMS API for sending or receiving messages are portable across brands of JMS vendors.



Java applications that use JMS are called JMS clients, and the messaging system that handles routing and delivery of messages is called the JMS provider. A JMS application is a business system composed of many JMS clients and, generally, one JMS provider.

A JMS client that sends a message is called a producer, while a JMS client that receives a message is called a consumer. A single JMS client can be both a producer and a consumer. When we use the terms consumer and producer, we mean a JMS client that receives messages or sends messages, respectively.

In EJB, enterprise beans of all types can use JMS to send messages to various destinations. Those messages are consumed by other Java applications or message-driven beans. JMS facilitates sending messages from enterprise beans by using a messaging service, sometimes called a message broker or router. Message brokers have been around for a couple of decades--the oldest and most established being IBM's MQSeries--but JMS is fairly new and is specifically designed to deliver a variety of message types from one Java application to another.

Reimplementing the TravelAgent EJB with JMS

We can modify the TravelAgent EJB developed in Chapter 12 so that it uses JMS to alert some other Java application that a reservation has been made. The following code shows how to modify the bookPassage() method so that the TravelAgent EJB will send a simple text message based on the description information from the TicketDO object:

public TicketDO bookPassage(CreditCardDO card, double price)
  throws IncompleteConversationalState {
         
  if (customer == null || cruise == null || cabin == null) {
    throw new IncompleteConversationalState();
  }
  try {
    ReservationHomeLocal resHome = (ReservationHomeLocal)
      jndiContext.lookup("java:comp/env/ejb/ReservationHomeLocal");
 
    ReservationLocal reservation =
      resHome.create(customer, cruise, cabin, price, new Date());
        
    Object ref = jndiContext.lookup
      ("java:comp/env/ejb/ProcessPaymentHomeRemote");
 
    ProcessPaymentHomeRemote ppHome = (ProcessPaymentHomeRemote)
      PortableRemoteObject.narrow(ref, ProcessPaymentHomeRemote.class);
      
    ProcessPaymentRemote process = ppHome.create();
    process.byCredit(customer, card, price);
 
    TicketDO ticket = new TicketDO(customer,cruise,cabin,price);
 
    String ticketDescription = ticket.toString();
 
    TopicConnectionFactory factory = (TopicConnectionFactory)
      jndiContext.lookup("java:comp/env/jms/TopicFactory");
    
    Topic topic = (Topic)
      jndiContext.lookup("java:comp/env/jms/TicketTopic");
 
    TopicConnection connect = factory.createTopicConnection();
 
    TopicSession session = connect.createTopicSession(true,0);
 
    TopicPublisher publisher = session.createPublisher(topic);
 
    TextMessage textMsg = session.createTextMessage();
    textMsg.setText(ticketDescription);
    publisher.publish(textMsg);
    connect.close();
 
    return ticket;
  } catch(Exception e) {
    throw new EJBException(e);
  }
}

We needed to add a lot of new code to send a message. However, while it may look a little overwhelming at first, the basics of JMS are not all that complicated.

TopicConnectionFactory and Topic

In order to send a JMS message we need a connection to the JMS provider and a destination address for the message. The connection to the JMS provider is made possible by a JMS connection factory; the destination address of the message is identified by a Topic object. Both the connection factory and the Topic object are obtained from the TravelAgent EJB's JNDI ENC:

TopicConnectionFactory factory = (TopicConnectionFactory)
    jndiContext.lookup("java:comp/env/jms/TopicFactory");
        
Topic topic = (Topic)
    jndiContext.lookup("java:comp/env/jms/TicketTopic");

The TopicConnectionFactory in JMS is similar in function to the DataSource in JDBC. Just as the DataSource provides a JDBC connection to a database, the TopicConnectionFactory provides a JMS connection to a message router. (This analogy is not perfect. One might also say that the TopicSession is analogous to the DataSource, since both represent transaction-resources connections.)

The Topic object itself represents a network-independent destination to which the message will be addressed. In JMS, messages are sent to destinations--either topics or queues--instead of directly to other applications. A topic is analogous to an email list or newsgroup; any application with the proper credentials can receive messages from and send messages to a topic. When a JMS client receives messages from a topic, the client is said to subscribe to that topic. JMS decouples applications by allowing them to send messages to each other through a destination, which serves as virtual channel.

JMS also supports another destination type, called a Queue. The difference between topics and queues is explained in more detail later.

TopicConnection and TopicSession

The TopicConnectionFactory is used to create a TopicConnection, which is an actual connection to the JMS provider:

TopicConnection connect = factory.createTopicConnection();
 
TopicSession session = connect.createTopicSession(true,0);

Once a TopicConnection is obtained, it can be used to create a TopicSession. A TopicSession allows the Java developer to group the actions of sending and receiving messages. In this case you will need only a single TopicSession. However, having more than one TopicSession object is frequently helpful: if you wish to produce and consume messages using multithreading, a different Session needs to be created by each thread accessing that thread. This is because JMS Session objects use a single-threaded model, which prohibits concurrent accessing of a single Session from multiple threads. The thread that creates a TopicSession is usually the thread that uses that Session's producers and consumers (i.e., TopicPublisher and TopicSubscriber objects). If you wish to produce and consume messages using multithreading, a different Session should be created and used by each thread.

The createTopicSession() method has two parameters:

createTopicSession(boolean transacted, int acknowledgeMode)

According to the EJB 2.0 specification, these arguments are ignored at runtime because the EJB container manages the transaction and acknowledgment mode of any JMS resource obtained from the JNDI ENC. The specification recommends that developers use the arguments true for transacted and 0 for acknowlegeMode, but since they are supposed to be ignored, it should not matter what you use. Unfortunately, not all vendors adhere to this part of the specification. Some vendors ignore these parameters while some do not. Consult your vendor's documentation to determine the proper values for these parameters in both container-managed and bean-managed transactions.

It's good programming practice to close a TopicConnection after its been used, in order to conserve resources:

TopicConnection connect = factory.createTopicConnection();
...
 
connect.close();

Pages: 1, 2, 3, 4, 5, 6, 7

Next Pagearrow