This chapter is divided into two subsections: Section 13.1 and Section 13.2. The first section describes the Java Message Service ( JMS) and its role as a resource that is available to any enterprise bean (session, entity, or message-driven). Readers unfamiliar with JMS should read the first section before proceeding to the second section.
If you’re already familiar with JMS, you can skip directly to the second section, which provides an overview of the new enterprise bean type—the message-driven bean. A message-driven bean is an asynchronous bean activated by message delivery. In EJB 2.0, vendors are required to support JMS-based message-driven bean, that receive JMS messages from specific topics or queues and process those messages as they are delivered.
All EJB 2.0 vendors must, by default, support a JMS provider. Most EJB 2.0 vendors have a JMS provider built in, but some may also support other JMS providers. Regardless of how the EJB 2.0 vendor provides the JMS service, having one is a requirement if the vendor expects to support message-driven beans. The advantage of this forced adoption of JMS is that EJB developers can expect to have a working JMS provider on which messages can be both sent and received.
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.
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.
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.[47]
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.
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();
The TopicSession
is used to create a
TopicPublisher
, which sends messages from the
TravelAgent EJB to the destination specified by the
Topic
object. Any JMS clients that subscribe to
that topic will receive a copy of the message:
TopicPublisher publisher = session.createPublisher(topic);
TextMessage textMsg = session.createTextMessage();
textMsg.setText(ticketDescription);
publisher.publish(textMsg);
In JMS, a message is a Java object with two parts: a
header and a message body. The header is composed of delivery
information and metadata, while the message body carries the
application data, which can take several forms: text, serializable
objects, byte streams, etc. The JMS API defines several message types
(TextMessage
,
MapMessage
,
ObjectMessage
,
and others) and provides methods for delivering messages to and
receiving messages from other applications.
For example, we can change the TravelAgent EJB so that it sends a
MapMessage
instead of a
TextMessage
:
TicketDO ticket = new TicketDO(customer,cruise,cabin,price); ... TopicPublisher publisher = session.createPublisher(topic);MapMessage mapMsg = session.createTextMessage();
mapMsg.setInt("CustomerID", ticket.customerID.intValue());
mapMsg.setInt("CruiseID", ticket.cruiseID.intValue());
mapMsg.setInt("CabinID", ticket.cabinID.intValue());
mapMsg.setDouble("Price", ticket.price);
publisher.publish(mapMsg);
The attributes of the MapMessage
(CustomerID
, CruiseID
,
CabinID
, and Price
) can be
accessed by name from those JMS clients that receive it.
As an alternative, the TravelAgent EJB could be modified to use the
ObjectMessage
type, which would allow us to send
the entire TicketDO
object as the message using
Java serialization:
TicketDO ticket = new TicketDO(customer,cruise,cabin,price); ... TopicPublisher publisher = session.createPublisher(topic);ObjectMessage objectMsg = session.createObjectMessage();
ObjectMsg.setObject(ticket);
publisher.publish(objectMsg);
In addition to the TextMessage
,
MapMessage
, and ObjectMessage
,
JMS provides two other message types:
StreamMessage
and BytesMessage
.
StreamMessage
can take the contents of an I/O
stream as its payload. BytesMessage
can take any
array of bytes, which it treats as opaque data.
When a JMS resource is used, it must be declared in the bean’s XML deployment descriptor, in a manner similar to the JDBC resource used by the Ship EJB in Chapter 10:
<enterprise-beans> <session> <ejb-name>TravelAgentBean</ejb-name> ... <resource-ref> <res-ref-name>jms/TopicFactory</res-ref-name> <res-type>javax.jms.TopicConnectionFactory</res-type> <res-auth>Container</res-auth> </resource-ref> <resource-ref> <res-ref-name>jdbc/titanDB</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> <resource-env-ref> <resource-env-ref-name>jms/TicketTopic</resource-env-ref-name> <resource-env-ref-type>javax.jms.Topic</resource-env-ref-type> </resource-env-ref> ... </session>
The
<resource-ref>
for the JMS
TopicConnectionFactory
is similar to the
<resource-ref>
declaration for the JDBC
DataSource
: it declares the JNDI ENC name,
interface type, and authorization protocol. In addition to the
<resource-ref>
, the TravelAgent EJB must
also declare the <resource-env-ref>
, which
lists any “administered objects” associated with a
<resource-ref>
entry. In this case, we
declare the Topic
used for sending a ticket
message. At deployment time the deployer will map the JMS
TopicConnectionFactory
and
Topic
declared by the
<resource-ref>
and
<resource-env-ref>
elements to a JMS factory and
topic.
To
get a better idea of how JMS is used, we can create a Java
application whose sole purpose is receiving and processing
reservation messages. We will develop a very simple JMS client that
simply prints a description of each ticket as it receives the
messages. We’ll assume that the TravelAgent EJB is using the
TextMessage
to send a description of the ticket to
the JMS clients. The following code shows how the JMS application
client might look:
import javax.jms.Message; import javax.jms.TextMessage; import javax.jms.TopicConnectionFactory; import javax.jms.TopicConnection; import javax.jms.TopicSession; import javax.jms.Topic; import javax.jms.Session; import javax.jms.TopicSubscriber; import javax.jms.JMSException; import javax.naming.InitialContext; public class JmsClient_1 implements javax.jms.MessageListener { public static void main(String [] args) throws Exception { if(args.length != 2) throw new Exception("Wrong number of arguments"); new JmsClient_1(args[0], args[1]); while(true){Thread.sleep(10000);} } public JmsClient_1(String factoryName, String topicName) throws Exception { InitialContext jndiContext = getInitialContext(); TopicConnectionFactory factory = (TopicConnectionFactory) jndiContext.lookup("TopicFactoryNameGoesHere
"); Topic topic = (Topic)jndiContext.lookup("TopicNameGoesHere
"); TopicConnection connect = factory.createTopicConnection(); TopicSession session = connect.createTopicSession(false,Session.AUTO_ACKNOWLEDGE); TopicSubscriber subscriber = session.createSubscriber(topic); subscriber.setMessageListener(this); connect.start(); } public void onMessage(Message message) { try { TextMessage textMsg = (TextMessage)message; String text = textMsg.getText(); System.out.println(" RESERVATION RECIEVED: "+text); } catch(JMSException jmsE) { jmsE.printStackTrace(); } } public static InitialContext getInitialContext() { // create vendor-specific JNDI context here } }
The constructor of JmsClient_1
obtains the
TopicConnectionFactory
and
Topic
from the JNDI
InitialContext
. This context is created with
vendor-specific properties so that the client can connect to the same
JMS provider as the one used by the TravelAgent EJB. For example, the
getInitialContext()
method for the WebLogic
application server would be coded as follows:[48]
public static InitialContext getInitialContext() { Properties env = new Properties(); env.put(Context.SECURITY_PRINCIPAL, "guest"); env.put(Context.SECURITY_CREDENTIALS, "guest"); env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory"); env.put(Context.PROVIDER_URL, "t3://localhost:7001"); return new InitialContext(env); }
Once the client has the TopicConnectionFactory
and
Topic
, it creates a
TopicConnection
and a
TopicSession
in the same way as the TravelAgent
EJB. The main difference is that the TopicSession
object is used to create a TopicSubscriber
instead
of a TopicPublisher
. The
TopicSubscriber
is designed specifically to
process incoming messages that are published to its specified
Topic
:
TopicSession session = connect.createTopicSession(false,Session.AUTO_ACKNOWLEDGE);TopicSubscriber subscriber = session.createSubscriber(topic);
subscriber.setMessageListener(this);
connect.start();
The TopicSubscriber
can receive messages directly,
or it can delegate the processing of the messages to a
javax.jms.MessageListener
. We chose to have
JmsClient_1
implement the
MessageListener
interface so that it can process the
messages itself. MessageListener
objects implement
a single method,
onMessage()
, which is invoked every time a new message is
sent to the subscriber’s topic. In this case, every time the
TravelAgent EJB sends a reservation message to the topic, the JMS
client will have its onMessage()
method invoked so
that it can receive a copy of the
message and
process it:
public void onMessage(Message message) { try { TextMessage textMsg = (TextMessage)message; String text = textMsg.getText(); System.out.println(" RESERVATION RECIEVED: "+text); } catch(JMSException jmsE) { jmsE.printStackTrace(); } }
Please refer to Workbook Exercise 13.1, JMS as a Resource. This workbook is available free, in PDF format, at http://www.oreilly.com/catalog/entjbeans3/workbooks.
One of the principal advantages of JMS messaging is that it’s asynchronous . In other words, a JMS client can send a message without having to wait for a reply. Contrast this flexibility with the synchronous messaging of Java RMI. RMI is an excellent choice for assembling transactional components, but is too restrictive for some uses. Each time a client invokes a bean’s method it blocks the current thread until the method completes execution. This lock-step processing makes the client dependent on the availability of the EJB server, resulting in a tight coupling between the client and enterprise bean.
In JMS, a client sends messages asynchronously to a destination (topic or queue), from which other JMS clients can also receive messages. When a JMS client sends a message, it doesn’t wait for a reply; it sends the message to a router, which is responsible for forwarding it to other clients. Clients sending messages are decoupled from the clients receiving them; senders are not dependent on the availability of receivers.
The limitations of RMI make JMS an attractive alternative for communicating with other applications. Using the standard JNDI environment naming context, an enterprise bean can obtain a JMS connection to a JMS provider and use it to deliver asynchronous messages to other Java applications. For example, a TravelAgent session bean can use JMS to notify other applications that a reservation has been processed, as shown in Figure 13-1.
In this case, the applications receiving JMS messages from the TravelAgent EJB may be message-driven beans, other Java applications in the enterprise, or applications in other organizations that benefit from being notified that a reservation has been processed. Examples might include business partners who share customer information or an internal marketing application that adds customers to a catalog mailing list.
JMS enables the enterprise bean to send messages without blocking. The enterprise bean doesn’t know who will receive the message, because it delivers the message to a virtual channel (destination) and not directly to another application. Applications can choose to receive messages from that virtual channel and receive notification of new reservations.
One interesting aspect of enterprise messaging is that the decoupled asynchronous nature of the technology means that the transactions and security contexts of the sender are not propagated to the receiver of the message. For example, when the TravelAgent EJB sends the ticket message, it may be authenticated by the JMS provider but its security context won’t be propagated to the JMS client that received the message. When a JMS client receives the message from the TravelAgent EJB, it will have no idea about the security context under which the message was sent. This is how it should be, because the sender and receiver often operate in different environments with different security domains.
Similarly, transactions are never propagated from the sender to the receiver. For one thing, the sender has no idea who the receivers of the message will be. If the message is sent to a topic there could be one receiver or thousands; managing a distributed transaction under such ambiguous circumstances is not tenable. In addition, the clients receiving the message may not get it for a long time after it is sent because they are temporarily down or otherwise unable to receive messages. One key strength of JMS is that it allows senders and receivers to be temporally decoupled. Transactions are designed to be executed quickly because they lock up resources; the possibility of a long transaction with an unpredictable end is also not tenable.
A JMS client can, however, have a distributed transaction with the JMS provider so that it manages the send or receive operation in the context of a transaction. For example, if the TravelAgent EJB’s transaction fails for any reason, the JMS provider will discard the ticket message sent by the TravelAgent EJB. Transactions and JMS are covered in more detail in Chapter 14.
JMS provides two types of messaging models: publish-and-subscribe and point-to-point. The JMS specification refers to these as messaging domains. In JMS terminology, publish-and-subscribe and point-to-point are frequently shortened to pub/sub and p2p (or PTP), respectively. This chapter uses both the long and short forms throughout.
In the simplest sense, publish-and-subscribe is intended for a one-to-many broadcast of messages, while point-to-point is intended for one-to-one delivery of messages (see Figure 13-2).
In publish-and-subscribe messaging, one producer can send a message to many consumers through a virtual channel called a topic. Consumers can choose to subscribe to a topic. Any messages addressed to a topic are delivered to all the topic’s consumers. Every consumer receives a copy of each message. The pub/sub messaging model is by and large a push-based model, where messages are automatically broadcast to consumers without them having to request or poll the topic for new messages.
In the pub/sub messaging model, the producer sending the message is not dependent on the consumers receiving the message. Optionally, JMS clients that use pub/sub can establish durable subscriptions that allow consumers to disconnect and later reconnect and collect messages that were published while they were disconnected.
The TravelAgent EJB in this chapter uses the pub/sub programming
model with a Topic
object as a destination.
The point-to-point messaging model allows JMS clients to send and receive messages both synchronously and asynchronously via virtual channels known as queues . The p2p messaging model has traditionally been a pull- or polling-based model, where messages are requested from the queue instead of being pushed to the client automatically.[49]
A queue may have multiple receivers, but only one receiver may receive each message. As shown earlier in Figure 13-2, the JMS provider will take care of doling out the messages among JMS clients, ensuring that each message is consumed by only one JMS client. The JMS specification does not dictate the rules for distributing messages among multiple receivers.
The messaging API for p2p is similar to that used for pub/sub. The following code listing shows how the TravelAgent EJB could be modified to use the queue-based p2p API instead of the topic-based pub/sub model used in the earlier example:
public TicketDO bookPassage(CreditCardDO card, double price) throws IncompleteConversationalState { ... TicketDO ticket = new TicketDO(customer,cruise,cabin,price);String ticketDescription = ticket.toString();
QueueConnectionFactory factory = (QueueConnectionFactory)
jndiContext.lookup("java:comp/env/jms/QueueFactory");
Queue queue = (Queue)
jndiContext.lookup("java:comp/env/jms/TicketQueue");
QueueConnection connect = factory.createQueueConneciton();
QueueSession session = connect.createQueueSession(true,0);
QueueSender sender = session.createSender(queue);
TextMessage textMsg = session.createTextMessage();
textMsg.setText(ticketDescription);
sender.send(textMsg);
connect.close();
return ticket; } catch(Exception e) { throw new EJBException(e); } }
The rationale behind the two models lies in the origin of the JMS specification. JMS started out as a way of providing a common API for accessing existing messaging systems. At the time of its conception, some messaging vendors had a p2p model and some had a pub/sub model. Hence JMS needed to provide an API for both models to gain wide industry support. The JMS 1.0.2 specification does not require a JMS provider to support both models. EJB 2.0 vendors, however, are required to support both messaging models.
Almost anything that can be done with the pub/sub model can be done with point-to-point, and vice versa. An analogy can be drawn to developers’ programming language preferences. In theory, any application that can be written with Pascal can also be written with C. Anything that can be written in C++ can also be written in Java. In some cases it comes down to a matter of preference, or which model you are already familiar with.
In most cases, the decision about which model to use depends on the distinct merits of each model. With pub/sub, any number of subscribers can be listening on a topic, and they will all receive copies of the same message. The publisher may not care if everybody is listening, or even if nobody is listening. For example, consider a publisher that broadcasts stock quotes. If any particular subscriber is not currently connected and misses out on a great quote, the publisher is not concerned. In contrast, a point-to-point session is likely to be intended for a one-on-one conversation with a specific application at the other end. In this scenario, every message really matters.
The range and variety of the data the messages represent can be a
factor as well. Using pub/sub, messages are dispatched to the
consumers based on filtering that is provided through the use of
specific topics. Even when messaging is being used to establish a
one-on-one conversation with another known application, it can be
advantageous to use pub/sub with multiple topics to segregate
different kinds of messages. Each kind of message can be dealt with
separately through its own unique consumer and
onMessage()
listener.
Point-to-point is more convenient when you want only a particular receiver to process a given message once. This is perhaps the most critical difference between the two models: p2p guarantees that only one consumer processes each message. This is extremely important when messages need to be processed separately but in tandem.
JmsClient_1
was designed to consume messages
produced by the TravelAgent EJB. Can another entity or session bean
receive those messages also? The answer is yes, but it’s a
really bad idea.
Entity and session beans respond to Java RMI calls from EJB clients and cannot be programmed to respond to JMS messages as do message-driven beans. That means it’s impossible to write a session or entity bean that will be driven by incoming messages. The inability to make EJBs respond to JMS messages was why message-driven beans were introduced in EJB 2.0. Message-driven beans are designed to consume messages from topics and queues. They fill an important niche; we’ll learn more about how to program them in the next section.
It is possible to develop an entity or session bean that can consume a JMS message from a business method, but the method must be called by an EJB client first. For example, when the business method on the Hypothetical EJB is called, it sets up a JMS session and then attempts to read a message from a queue:
public class HypotheticalBean implements javax.ejb.SessionBean {
InitialContext jndiContext;
public String businessMethod() {
try{
QueueConnectionFactory factory = (QueueConnectionFactory)
jndiContext.lookup("java:comp/env/jms/QueueFactory");
Queue topic = (Queue)
jndiContext.lookup("java:comp/env/jms/Queue");
QueueConnection connect = factory.createQueueConneciton();
QueueSession session = connect.createQueueSession(true,0);
QueueReceiver receiver = session.createReceiver(queue);
TextMessage textMsg = (TextMessage)receiver.receive();
connect.close();
return textMsg.getText();
} catch(Exception e) {
throws new EJBException(e);
}
}
...
}
The QueueReceiver
, which is a message consumer,
is used to proactively fetch a message from the queue. While this has
been programmed correctly, it is a dangerous operation because a call
to the
QueueReceiver.receive()
method blocks the thread until a message
becomes available. If a message is never delivered to the
receiver’s queue, the thread will be blocked indefinitely! In
other words, if no one ever sends a message to the queue, the
QueueReceiver
will just sit there waiting forever.
To be fair, there are other
receive()
methods that are less dangerous. For
example, receive(long
timeout)
allows you to specify a time after which the
QueueReceiver
should stop blocking the thread and
give up waiting for a message. There is also
receiveNoWait()
, which checks for a message and returns
null
if there are none waiting, thus avoiding a
prolonged thread block.
While the alternative receive()
methods are much
safer, this is still a dangerous operation to perform. There is no
guarantee that the less risky receive()
methods
will perform as expected, and the risk of programmer error (e.g.,
using the wrong receive()
method) is too great.
Besides, the message-driven bean provides you with a powerful and
simple enterprise bean that is specially designed to consume JMS
messages. This book recommends that you do not attempt to consume
messages from entity or session beans.
JMS (and enterprise messaging in general) represents a powerful paradigm in distributed computing. In my opinion, the Java Message Service is as important as Enterprise JavaBeans itself and should be well understood before it is used in development.
While this chapter has provided a brief overview of JMS, it has presented you only with enough material to prepare you for the discussion of message-driven beans in the next section. JMS has a cornucopia of features and details that are simply too extensive to cover in this book. To understand JMS and how it is used, you will need to study it independently.[50] Taking the time to learn JMS is well worth the effort.
[47] This analogy is
not perfect. One might also say that the
TopicSession
is analogous to the
DataSource
, since both represent
transaction-resources connections.
[48] JNDI
also allows the properties to be set in a
jndi.properties
file, which contains the
property values for the InitialContext
and can be
discovered dynamically at runtime. In this book, I chose to set the
properties explicitly.
[49] The JMS specification does not specifically state how the p2p and pub/sub models must be implemented. Either one may use push or pull, but at least conceptually pub/sub is push and p2p is pull.
[50] For a detailed treatment of JMS, see Java Message Service by Richard Monson-Haefel and David Chappell (O’Reilly).