Nice article from O'reilly's JMS book covers using JMS for a chat application.
...Chapter 2 of the book is free to read on the website.
Java Message Service
By Richard Monson-Haefel & David Chappell
December 2000
0-596-00068-5, Order Number: 0685
240 pages, $34.95
Chapter 2
In this chapter:
Developing a Simple Example
The Chat ApplicationNow that you understand Message-Oriented Middleware and some JMS concepts, you are ready to write your first JMS application. This chapter provides a gentle introduction to JMS using the publish-and-subscribe messaging model. You will get your feet wet with JMS and learn some of the basic classes and interfaces. Chapter 4, Publish-and-Subscribe Messaging, covers publish-and-subscribe in detail, and Chapter 5, Point-to-Point Messaging, covers the point-to-point message model.
As with all examples in this book, example code and instructions specific to several vendors is provided in the book download at O'Reilly's web site (see the Preface for details). You will need to install and configure your JMS provider according to the instructions provided by your vendor.
The Chat Application
Internet chat provides an interesting application for learning about the JMS pub/sub messaging model. Used mostly for entertainment, web-based chat applications can be found on thousands of web sites. In a chat application, people join virtual chat rooms where they can "chat" with a group of other people.
To illustrate how JMS works, we will use the JMS pub/sub API to build a simple chat application. The requirements of Internet chat map neatly onto the publish-and-subscribe messaging model. In this model, a producer can send a message to many consumers by delivering the message to a single topic. A message producer is also called a publisher and a message consumer is also called a subscriber. In reality, using JMS for a chat application would be overkill, since chat systems don't require enterprise quality service.
The following source code is a JMS-based chat client. Every participant in a chat session uses this
Chatprogram to join a specific chat room (topic), and deliver and receive messages to and from that room:package chap2.chat;import javax.jms.*;import javax.naming.*;import java.io.*;import java.io.InputStreamReader;import java.util.Properties;public class Chat implements javax.jms.MessageListener{private TopicSession pubSession;private TopicSession subSession;private TopicPublisher publisher;private TopicConnection connection;private String username;/* Constructor. Establish JMS publisher and subscriber */public Chat(String topicName, String username, String password)throws Exception {// Obtain a JNDI connectionProperties env = new Properties( );// ... specify the JNDI properties specific to the vendorInitialContext jndi = new InitialContext(env);// Look up a JMS connection factoryTopicConnectionFactory conFactory =(TopicConnectionFactory)jndi.lookup("TopicConnectionFactory");// Create a JMS connectionTopicConnection connection =conFactory.createTopicConnection(username,password);// Create two JMS session objectsTopicSession pubSession =connection.createTopicSession(false,Session.AUTO_ACKNOWLEDGE);TopicSession subSession =connection.createTopicSession(false,Session.AUTO_ACKNOWLEDGE);// Look up a JMS topicTopic chatTopic = (Topic)jndi.lookup(topicName);// Create a JMS publisher and subscriberTopicPublisher publisher =pubSession.createPublisher(chatTopic);TopicSubscriber subscriber =subSession.createSubscriber(chatTopic);// Set a JMS message listenersubscriber.setMessageListener(this);// Intialize the Chat applicationset(connection, pubSession, subSession, publisher, username);// Start the JMS connection; allows messages to be deliveredconnection.start( );}/* Initialize the instance variables */public void set(TopicConnection con, TopicSession pubSess,TopicSession subSess, TopicPublisher pub,String username) {this.connection = con;this.pubSession = pubSess;this.subSession = subSess;this.publisher = pub;this.username = username;}/* Receive message from topic subscriber */public void onMessage(Message message) {try {TextMessage textMessage = (TextMessage) message;String text = textMessage.getText( );System.out.println(text);} catch (JMSException jmse){ jmse.printStackTrace( ); }}/* Create and send message using topic publisher */protected void writeMessage(String text) throws JMSException {TextMessage message = pubSession.createTextMessage( );message.setText(username+" : "+text);publisher.publish(message);}/* Close the JMS connection */public void close( ) throws JMSException {connection.close( );}/* Run the Chat client */public static void main(String [] args){try{if (args.length!=3)System.out.println("Topic or username missing");// args[0]=topicName; args[1]=username; args[2]=passwordChat chat = new Chat(args[0],args[1],args[2]);// Read from command lineBufferedReader commandLine = newjava.io.BufferedReader(new InputStreamReader(System.in));// Loop until the word "exit" is typedwhile(true){String s = commandLine.readLine( );if (s.equalsIgnoreCase("exit")){chat.close( ); // close down connectionSystem.exit(0);// exit program} elsechat.writeMessage(s);}} catch (Exception e){ e.printStackTrace( ); }}}
Getting Started with the Chat Example
To put this client to use, compile it like any other Java program. Then start your JMS server, setting up whatever topics, usernames, and passwords you want. Configuration of a JMS server is vendor-dependent, and won't be discussed here.
The
Chatclass includes amain( )method so that it can be run as a standalone Java application. It's executed from the command line as follows:
java chap2.chat.Chat topic username password
The topic is the destination that we want to publish-and-subscribe to; username and password make up the authentication information for the client. Run at least two chat clients in separate command windows and try typing into one; you should see the text you type displayed by the other client.
Before examining the source code in detail, a quick explanation will be helpful. The chat client creates a JMS publisher and subscriber for a specific topic. The topic represents the chat room. The JMS server registers all the JMS clients that want to publish or subscribe to a specific topic. When text is entered at the command line of one of the chat clients, it is published to the messaging server. The messaging server identifies the topic associated with the publisher and delivers the message to all the JMS clients that have subscribed to that topic. As Figure 2-1 illustrates, messages published by any one of the JMS clients are delivered to all the JMS subscribers for that topic.
Figure 2-1. The Chat application
Examining the Source Code
Running the
Chatexample in a couple of command windows demonstrates what theChatapplication does. The rest of this chapter examines the source code for theChatapplication so that you can see how theChatapplication works.
Bootstrapping the JMS client
The
main( )method bootstraps the chat client and provides a command-line interface. Once an instance of theChatclass is created, themain( )method spends the rest of its time reading text typed at the command line and passing it to theChatinstance using the instance'swriteMessage( )method.
The
Chatinstance connects to the topic and receives and delivers messages. TheChatinstance starts its life in the constructor, which does all the work to connect to the topic and set up theTopicPublisherandTopicSubscribersfor delivering and receiving messages.
Obtaining a JNDI connection
The chat client starts by obtaining a JNDI connection to the JMS messaging server. JNDI is an implementation-independent API for directory and naming systems. A directory service provides JMS clients with access to
ConnectionFactoryandDestinations(topics and queues) objects.ConnectionFactoryandDestinationobjects are the only things in JMS that cannot be obtained using the JMS API--unlike connections, sessions, producers, consumers, and messages, which are manufactured using the JMS API. JNDI provides a convenient, location-transparent, configurable, and portable mechanism for obtainingConnectionFactoryandDestinationobjects, also called JMS administered objects because they are established and configured by a system administrator.
Using JNDI, a JMS client can obtain access to a JMS provider by first looking up a
ConnectionFactory. TheConnectionFactoryis used to create JMS connections, which can then be used for sending and receiving messages.Destinationobjects, which represent virtual channels (topics and queues) in JMS, are also obtained via JNDI and are used by the JMS client. The directory service can be configured by the system administrator to provide JMS administered objects so that the JMS clients don't need to use proprietary code to access a JMS provider.
JMS servers will either work with a separate directory service (e.g., LDAP) or provide their own directory service that supports the JNDI API. For more details on JNDI, see the sidebar "Understanding JNDI."
The constructor of the
Chatclass starts by obtaining a connection to the JNDI naming service used by the JMS server:
JNDI is a standard Java extension that provides a uniform API for accessing a variety of directory and naming services. In this respect, it is somewhat similar to JDBC. JDBC lets you write code that can access different relational databases such as Oracle, SQLServer, or Sybase; JNDI lets you write code that can access different directory and naming services, such as LDAP, Novell Netware NDS, CORBA Naming Service, and proprietary naming services provided by JMS servers.
In JMS, JNDI is used mostly as a naming service to locate administered objects. Administered objects are JMS objects that are created and configured by the system administrator. Administered objects include JMS
ConnectionFactoryandDestinationobjects like topics and queues.
Administered objects are bound to a name in a naming service. A naming service associates names with distributed objects, files, and devices so that they can be located on the network using simple names instead of cryptic network addresses. An example of a naming service is the DNS, which converts an Internet hostname like www.oreilly.com into a network address that browsers use to connect to web servers. There are many other naming services, such as COSNaming in CORBA and the Java RMI registry. Naming services allow printers, distributed objects, and JMS administered objects to be bound to names and organized in a hierarchy similar to a filesystem. A directory service is a more sophisticated kind of naming service.
JNDI provides an abstraction that hides the specifics of the naming service, making client applications more portable. Using JNDI, JMS clients can browse a naming service and obtain references to administered objects without knowing the details of the naming service or how it is implemented. JMS servers are usually be used in combination with a standard JNDI driver (a.k.a. service provider) and directory service like LDAP, or provide a proprietary JNDI service provider and directory service.
JNDI is both virtual and dynamic. It is virtual because it allows one naming service to be linked to another. Using JNDI, you can drill down through directories to files, printers, JMS administered objects, and other resources following virtual links between naming services. The user doesn't know or care where the directories are actually located. As an administrator, you can create virtual directories that span a variety of different services over many different physical locations.
JNDI is dynamic because it allows the JNDI drivers for specific types of naming and directory services to be loaded dynamically at runtime. A driver maps a specific kind of naming or directory service into the standard JNDI class interfaces. Drivers have been created for LDAP, Novell NetWare NDS, Sun Solaris NIS+, CORBA COSNaming, and many other types of naming and directory services, including proprietary ones. Dynamically loading JNDI drivers (service providers) makes it possible for a client to navigate across arbitrary directory services without knowing in advance what kinds of services it is likely to find.
// Obtain a JNDI connectionProperties env = new Properties( );// ... specify the JNDI properties specific to the vendorInitialContext jndi = new InitialContext(env);
Creating a connection to a JNDI naming service requires that a
javax.naming.InitialContextobject be created. AnInitialContextis the starting point for any JNDI lookup--it's similar in concept to the root of a filesystem. TheInitialContextprovides a network connection to the directory service that acts as a root for accessing JMS administered objects. The properties used to create anInitialContextdepend on which JMS directory service you are using. The code used to create a JNDIInitialContextin BEA's Weblogic naming service, for example, would look something like this:
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");InitialContext jndi = new InitialContext(env);
When SonicMQ is used in combination with a third party LDAP directory service, the connection properties would be very different. For example, the following shows how a SonicMQ JMS client would use JNDI to access JMS administered objects stored in a LDAP directory server:
Properties env = new Properties( );env.put(Context.SECURITY_PRINCIPAL, "guest");env.put(Context.SECURITY_CREDENTIALS, "guest");env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");env.put(Context.PROVIDER_URL,"ldap://localhost:389/o=acme.com");InitialContext jndi = new InitialContext(env);
NOTE: Alternatively, the
InitialContext( )can be created without properties (no-arg constructor). In this case JNDI will read the vendor-specific JNDI properties from a special file in the classpath named jndi.properties. This eliminates provider-specific code in JMS clients, making them more portable.
The TopicConnectionFactory
Once a JNDI
InitialContextobject is instantiated, it can be used to look up theTopicConnectionFactoryin the messaging server's naming service:
TopicConnectionFactory conFactory =(TopicConnectionFactory)jndi.lookup("TopicConnectionFactory");
The
javax.jms.TopicConnectionFactoryis used to manufacture connections to a message server. ATopicConnectionFactoryis a type of administered object, which means that its attributes and behavior are configured by the system administrator responsible for the messaging server. TheTopicConnectionFactoryis implemented differently by each vendor, so configuration options available to system administrators vary from product to product. A connection factory might, for example, be configured to manufacture connections that use a particular protocol, security scheme, clustering strategy, etc. A system administrator might choose to deploy several differentTopicConnectionFactoryobjects, each configured with its own JNDI lookup name.
The
TopicConnectionFactoryprovides two overloaded versions of thecreateTopicConnection( )method:
package javax.jms;public interface TopicConnectionFactory extends ConnectionFactory {public TopicConnection createTopicConnection( )throws JMSException, JMSSecurityException;public TopicConnection createTopicConnection(String username,String password) throws JMSException, JMSSecurityException;}
These methods are used to create
TopicConnectionobjects. The behavior of the no-arg method depends on the JMS provider. Some JMS providers will assume that the JMS client is connecting under anonymous security context, while other providers may assume that the credentials can be obtained from JNDI or the current thread.[1] The second method provides the client with a username-password authentication credential, which can be used to authenticate the connection. In our code, we choose to authenticate the connection explicitly with a username and password.
The TopicConnection
The
TopicConnectionis created by theTopicConnectionFactory:
// Look up a JMS connection factoryTopicConnectionFactory conFactory =(TopicConnectionFactory)jndi.lookup("TopicConnectionFactory");// Create a JMS connectionTopicConnection connection =conFactory.createTopicConnection(username, password);
The
TopicConnectionrepresents a connection to the message server. EachTopicConnectionthat is created from aTopicConnectionFactoryis a unique connection to the server.[2] A JMS client might choose to create multiple connections from the same connection factory, but this is rare as connections are relatively expensive (each connection requires a network socket, I/O streams, memory, etc.). Creating multiple Session objects (discussed later in this chapter) from the same connection is considered more efficient, because sessions share access to the same connection. TheTopicConnectionis an interface that extendsjavax.jms.Connectioninterface. It defines several general-purpose methods used by clients of theTopicConnection. Among these methods are thestart( ),stop( ), andclose( )methods:
// javax.jms.Connection the super interfacepublic interface Connection {public void start( ) throws JMSException;public void stop( ) throws JMSException;public void close( ) throws JMSException;...}// javax.jms.TopicConnection extends javax.jms.Connectionpublic interface TopicConnection extends Connection {public TopicSession createTopicSession(boolean transacted,int acknowledgeMode)throws JMSException;...}
The
start( ),stop( ), andclose( )methods allow a client to manage the connection directly. Thestart( )method turns the inbound flow of messages "on," allowing messages to be received by the client. This method is used at the end of the constructor inChatclass:
...// Intialize the Chat applicationset(connection, pubSession, subSession, publisher, username);connection.start( );}
It is a good idea to start the connection after the subscribers have been set up, because the messages start to flow in from the topic as soon as
start( )is invoked.
The
stop( )method blocks the flow of inbound messages until thestart( )method is invoked again. Theclose( )method is used to close theTopicConnectionto the message server. This should be done when a client is finished using theTopicConnection; closing the connection conserves resources on the client and server. In theChatclass, themain( )method callsChat.close( )when "exit" is typed at the command line. TheChat.close( )method in turn calls theTopicConnection.close( )method:
public void close( ) throws JMSException {connection.close( );}
Closing a
TopicConnectioncloses all the objects associated with the connection including theTopicSession,TopicPublisher, andTopicSubscriber.
The TopicSession
After the
TopicConnectionis obtained, it's used to createTopicSessionobjects:
// Create a JMS connectionTopicConnection connection =conFactory.createTopicConnection(username,password);// Create two JMS session objectsTopicSession pubSession =connection.createTopicSession(false,Session.AUTO_ACKNOWLEDGE);TopicSession subSession =connection.createTopicSession(false,Session.AUTO_ACKNOWLEDGE);
A
TopicSessionobject is a factory for creatingMessage,TopicPublisher, andTopicSubscriberobjects. A client can create multipleTopicSessionobjects to provide more granular control over publishers, subscribers, and their associated transactions. In this case we create twoTopicSessionobjects,pubSessionandsubSession. We need two objects because of threading restrictions in JMS, which are discussed in the "Sessions and Threading" section later in the chapter.
The
booleanparameter in thecreateTopicSession( )method indicates whether theSessionobject will be transacted. A transactedSessionautomatically manages outgoing and incoming messages within a transaction. Transactions are important but not critical to our discussion at this time, so the parameter is set tofalse, which means theTopicSessionwill not be transacted. Transactions are discussed in more detail in Chapter 6, Guaranteed Messaging, Transactions, Acknowledgments, and Failures.
The second parameter indicates the acknowledgment mode used by the JMS client. An acknowledgment is a notification to the message server that the client has received the message. In this case we chose
AUTO_ACKNOWLEDGE, which means that the message is automatically acknowledged after it is received by the client.
The
TopicSessionobjects are used to create theTopicPublisherandTopicSubscriber. TheTopicPublisherandTopicSubscriberobjects are created with aTopicidentifier and are dedicated to theTopicSessionthat created them; they operate under the control of a specificTopicSession:
TopicPublisher publisher =pubSession.createPublisher(chatTopic);TopicSubscriber subscriber =subSession.createSubscriber(chatTopic);
The
TopicSessionis also used to create theMessageobjects that are delivered to the topic. ThepubSessionis used to createMessageobjects in thewriteMessage( )method. When you type text at the command line, themain( )method reads the text and passes it to theChatinstance by invokingwriteMessage( ). ThewriteMessage( )method (shown in the following example) uses thepubSessionobject to generate aTextMessageobject that can be used to deliver the text to the topic:
protected void writeMessage(String text) throws JMSException{TextMessage message = pubSession.createTextMessage( );message.setText(username+" : "+text);publisher.publish(message);}
Several
Messagetypes can be created by aTopicSession. The most commonly used type is theTextMessage.
The Topic
JNDI is used to locate a
Topicobject, which is an administered object like theTopicConnectionFactory:
InitialContext jndi = new InitialContext(env);..// Look up a JMS topicTopic chatTopic = (Topic)jndi.lookup(topicName);
A
Topicobject is a handle or identifier for an actual topic, called a physical topic, on the messaging server. A physical topic is an electronic channel to which many clients can subscribe and publish. A topic is analogous to a news group or list server: when a message is sent to a news group or list server, it is delivered to all the subscribers. Similarly, when a JMS client delivers aMessageobject to a topic, all the clients subscribed to that topic receive theMessage.
The
Topicobject encapsulates a vendor-specific name for identifying a physical topic in the messaging server. TheTopicobject has one method,getName( ), which returns the name identifier for the physical topic it represents. The name encapsulated by aTopicobject is vendor-specific and varies from product to product. For example, one vendor might use dot (".") separated topic names, like "oreilly.jms.chat", while another vendor might use a completely different naming system, similar to LDAP naming, "o=oreilly,cn=chat". Using topic names directly will result in client applications that are not portable across brands of JMS servers. TheTopicobject hides the topic name from the client, making the client more portable.
As a convention, we'll refer to a physical topic as a topic and only use the term "physical topic" when it's important to stress its difference from a
Topicobject.
The TopicPublisher
A
TopicPublisherwas created using thepubSessionand thechatTopic:
// Look up a JMS topicTopic chatTopic = (Topic)jndi.lookup(topicName);// Create a JMS publisher and subscriberTopicPublisher publisher =pubSession.createPublisher(chatTopic);
A
TopicPublisheris used to deliver messages to a specific topic on a message server. TheTopicobject used in thecreatePublisher( )method identifies the topic that will receive messages from theTopicPublisher. In theChatexample, any text typed on the command line is passed to theChatclass'swriteMessage( )method. This method uses theTopicPublisherto deliver a message to the topic:
protected void writeMessage(String text) throws JMSException{TextMessage message = pubSession.createTextMessage( );message.setText(username+" : "+text);publisher.publish(message);}
The
TopicPublisherobjects deliver messages to the topic asynchronously. Asynchronous delivery and consumption of messages is a key characteristic of Message-Oriented Middleware; theTopicPublisherdoesn't block or wait until all the subscribers receive the message. Instead, it returns from thepublish( )method as soon as the message server receives the message. It's up to the message server to deliver themessageto all the subscribers for that topic.
The TopicSubscriber
The
TopicSubscriberis created using thesubSessionand thechatTopic:
// Look up a JMS topicTopic chatTopic = (Topic)jndi.lookup(topicName);// Create a JMS publisher and subscriberTopicPublisher publisher =pubSession.createPublisher(chatTopic);TopicSubscriber subscriber =subSession.createSubscriber(chatTopic);
A
TopicSubscriberreceives messages from a specific topic. TheTopicobject argument used in thecreateSubscriber( )method identifies the topic from which theTopicSubscriberwill receive messages.
The
TopicSubscriberreceives messages from the message server one at a time (serially). These messages are pushed from the message server to theTopicSubscriberasynchronously, which means that theTopicSubscriberdoes not have to poll the message server for messages. In our example, each chat client will receive any message published by any of the other chat clients. When a user enters text at the command line, the text message is delivered to all other chat clients that subscribe to the same topic.
The pub/sub messaging model in JMS includes an in-process Java event model for handling incoming messages. This is similar to the event-driven model used by Java beans.[3] An object simply implements the listener interface, in this case the
MessageListener, and then is registered with theTopicSubscriber. ATopicSubscribermay have only oneMessageListenerobject. Here is the definition of theMessageListenerinterface used in JMS:
package javax.jms;public interface MessageListener {public void onMessage(Message message);}
When the
TopicSubscriberreceives a message from its topic, it invokes theonMessage( )method of itsMessageListenerobjects. TheChatclass itself implements theMessageListenerinterface and implements theonMessage( )method:
public class Chat implements javax.jms.MessageListener{...public void onMessage(Message message){try{TextMessage textMessage = (TextMessage)message;String text = textMessage.getText( );System.out.println(text);} catch (JMSException jmse){jmse.printStackTrace( );}}...}
The
Chatclass is aMessageListenertype, and therefore registers itself with theTopicSubscriberin its constructor:
TopicSubscriber subscriber = subSession.createSubscriber(chatTopic);subscriber.setMessageListener(this);
When the message server pushes a message to the
TopicSubscriber, theTopicSubscriberinvokes theChatobject'sonMessage( )method.
It's fairly easy to confuse the Java Message Service with its use of a Java event model. JMS is an API for asynchronous distributed enterprise messaging that spans processes and machines across a network. The Java event model is used to synchronously deliver events by invoking methods on one or more objects in the same process that have registered as listeners. The JMS pub/sub model uses the Java event model so that a
TopicSubscribercan notify itsMessageListenerobject in the same process that a message has arrived from the message server.
The Message
In the
chatexample, theTextMessageclass is used to encapsulate the messages we send and receive. ATextMessagecontains ajava.lang.Stringas its body and is the most commonly used message type. TheonMessage( )method receivesTextMessageobjects from theTopicSubscriber. Likewise, thewriteMessage( )method creates and publishesTextMessageobjects using theTopicPublisher:
public void onMessage(Message message){try{TextMessage textMessage = (TextMessage)message;String text = textMessage.getText( );System.out.println(text);} catch (JMSException jmse){jmse.printStackTrace( );}}protected void writeMessage(String text) throws JMSException{TextMessage message = pubSession.createTextMessage( );message.setText(username+" : "+text);publisher.publish(message);}
A message basically has two parts: a header and payload. The header is comprised of special fields that are used to identify the message, declare attributes of the message, and provide information for routing. The difference between message types is determined largely by their payload, i.e., the type of application data the message contains. The
Messageclass, which is the superclass of all message objects, has no payload. It is a lightweight message that delivers no payload but can serve as a simple event notification. The other message types have special payloads that determine their type and use:
Message- This type has no payload. It is useful for simple event notification.
TextMessage- This type carries a
java.lang.Stringas its payload. It is useful for exchanging simple text messages and also for more complex character data, such as XML documents.
ObjectMessage- This type carries a serializable Java object as its payload. It's useful for exchanging Java objects.
BytesMessage- This type carries an array of primitive bytes as its payload. It's useful for exchanging data in an application's native format, which may not be compatible with other existing
Messagetypes. It is also useful where JMS is used purely as a transport between two systems, and the message payload is opaque to the JMS client.
StreamMessage- This type carries a stream of primitive Java types (int, double, char, etc.) as its payload. It provides a set of convenience methods for mapping a formatted stream of bytes to Java primitives. It's an easy programming model when exchanging primitive application data in a fixed order.
MapMessage- This type carries a set of name-value pairs as its payload. The payload is similar to a
java.util.Propertiesobject, except the values must be Java primitives or their wrappers. TheMapMessageis useful for delivering keyed data.
Sessions and Threading
The
Chatapplication uses a separate session for the publisher and subscriber,pubSessionandsubSession, respectively. This is due to a threading restriction imposed by JMS. According to the JMS specification, a session may not be operated on by more than one thread at a time. In our example, two threads of control are active: the default main thread of theChatapplication and the thread that invokes theonMessage( )handler. The thread that invokes theonMessage( )handler is owned by the JMS provider. Since the invocation of theonMessage( )handler is asynchronous, it could be called while the main thread is publishing a message in thewriteMessage( )method. If both the publisher and subscriber had been created by the same session, the two threads could operate on these methods at the same time; in effect, they could operate on the sameTopicSessionconcurrently--a condition that is prohibited.
A goal of the JMS specification was to avoid imposing an internal architecture on the JMS provider. Requiring a JMS provider's implementation of a
Sessionobject to be capable of safely handling multiple threads was specifically avoided. This is mostly due to one of the intended uses of JMS--that the JMS API be a wrapper around an existing messaging system, which may not have multithreaded delivery capabilities on the client.
The requirement imposed on the JMS provider is that the sending of messages and the asynchronous receiving of messages be processed serially. It is possible to publish-and-subscribe using the same session, but only if the application is publishing from within the
onMessage( )handler. An example of this will be covered in Chapter 4.
1. Thread-specific storage is used with the Java Authentication and Authorization Service ( JAAS) to allow security credentials to transparently propagate between resources and applications.
2. The actual physical network connection may or may not be unique depending on the vendor. However, the connection is considered to be logically unique so authentication and connection control can be managed separately from other connections.
3. Although the in-process event model used by
TopicSubscriberis similar to the one used in Java beans, JMS itself is an API and the interfaces it defines are not Java beans.
Java Message Service: Chapter 2: Developing a Simple Example

No comments:
Post a Comment