New York
University
Computer
Science Department
Courant
Institute of Mathematical Sciences
Understanding
Java Messaging and JMS
Course Title: Application Servers Course
Number: g22.3033-011
Instructor: Jean-Claude Franchitti Session: 5
Before building an application framework, you must first choose the messaging construct most appropriate to your needs. The JMS Sun specification and model environment has advantages and disadvantages compared to RMI, JavaSpaces, InfoBus, and JSDT.
In October 1998, JavaSoft released the Java Message Service (JMS) specification v1.0.1, an API for accessing enterprise messaging systems from Java. Because JMS is equipped to handle both synchronous and asynchronous messaging, it represents a possible alternative to strictly synchronous RMI.
This handout attempts both to define JMS and to compare it with other Java messaging or traditional message-oriented middleware (MOM) messaging paradigms. The main objective is to help you understand where JMS might fit into a Java-based, enterprise-wide application architecture.
JMS is a set of interfaces and associated semantics that define how a JMS client accesses the facilities of an enterprise messaging product. Enterprise messaging is recognized as an essential tool for building enterprise applications and ecommerce systems, and JMS provides a common way for Java programs to create, send, receive, and read an enterprise messaging system's messages.
With a solid implementation of JMS, one can implement client/server or server/server asynchronous applications without CORBA. However, most ISP implementations of JMS are by small firms that are trying to implement a fairly comprehensive spec. Sun, though, has clearly stated that a vendor does not need to implement the entire spec, only domains within the spec. Sun has also indicated that there are domains outside of the scope of the spec that should be implemented in order to provide a truly enterprise-wide and robust messaging environment.
EAI = MOM = JMS
Enterprise Application Integration (EAI) using MOM products is becoming an
essential component for integrating intracompany. While JMS can be used by
itself and not just as a wrapper for MOMs, Sun's primary intention is for
corporations to use it in conjunction with such MOM products as TIBCO or
MQSeries.
The strategic intention of some JMS vendors is to provide Java interfaces to MQSeries and TIBCO -- interfaces that allow the developer to program in pure JMS using Java APIs, while using MQSeries, TIBCO, and the like as the low-level transport. This means an organization might not need to change its underlying transport middleware; JMS gives developers a standard Java API so messages can be sent transparently across the organization via TIBCO or MQSeries.
JMS is clearly the missing piece in the enterprise Java architecture, and Sun has already introduced a number of major service APIs -- JDBC (for database access), JNDI (for naming and directory services), and a Java mail API -- that are required in order to write applications that integrate across the enterprise.
The JMS object model
The two dominant messaging approaches in use today and defined in the spec are
the Topic, or point-to-point messages, and the Queue, or publish-and-subscribe
messages. Because many message systems support only one of these styles, JMS
provides a separate domain and defines compliance for each. On an operational
basis, when an asynchronous Queue message is in flight, it goes to the JMS
server, which forwards it to all interested applications currently running and
stores it for those applications that aren't.
Figure 1. JMS Session, Connection, and Message common facilities
Figure 1 depicts the JMS objects necessary to provide JMS connectivity to a remote server. The ConnectionFactory is an administered object used by a client to create a Connection, which itself is an active connection to a JMS provider. Because of the authentication and communication setup processed when a Connection is created (most clients will do all their messaging with only one), it is a relatively heavyweight JMS object. The Destination encapsulates the identity of a message destination and contains provider-specific address and configuration information.
Sessions are the JMS entity that support transactions and asynchronous message consumption. JMS does not require that client code be used for asynchronous message consumption, or that it be capable of handling multiple, concurrent messages. Instead, transaction multiplexing within a connection is demarcated and encapsulated within a Session.
A Session is an atomic unit of work similar to a database transaction. It is very difficult to implement multithreaded transactions, and Sessions provide the throughput advantages of concurrency with the ease of single-threaded programming.
MessageProducer and MessageConsumer objects are created by a Session and are used for sending and receiving messages, respectively. In order to guarantee the delivery of a message, messages to and from the remote server object should be sent in persistent mode. The persistent mode instructs the JMS provider to log the message to stable storage as part of the client's send operation. This in turn ensures that the message will survive a JMS provider failure.
Session, MessageProducer, and MessageConsumer JMS objects do not support concurrent use, while ConnectionFactory, Destination, and Connection objects do. After these objects and common facilities are in place, a client application has the basic JMS setup needed to produce and consume messages.
JMS has five types of message, and while the type used depends on the requirements of the application, most client implementations use either MapMessage or ObjectMessage. ObjectMessages -- messages containing a serializable Java object -- make it clear to programmers which data type and field they're receiving and setting. MapMessages use a set of name-value pairs, where names are strings and values are Java primitive types. Alternatively, JMS TextMessages might be more appropriate for XML-based messages, where JMS is used for the transport layer, but not data definition.
All JMS messages support the acknowledged method for use; if a client uses automatic acknowledgment, calls to acknowledge are ignored. JMS messages sent by a Session to a Destination must be received in the order in which they were sent, so their implementations must provide for the sequencing of incoming messages.
The JMS specification does not address the following functionalities:
A wire protocol for messaging
A message metadata repository
Load balancing criteria
Fault tolerance mechanisms
Administration API or monitor security
Sun seems to have opened the door for these extensions to be implemented by JMS vendors on a proprietary basis.
The JMS model specification versus
implementation
The conceptual model shown in Figure 1 was developed using the Sun JMS
specification, and assumes that JMS vendor implementations provide the
appropriate functionality as described in that specification. It is recommended
that an organization run appropriate tests on multiple JMS implementations
before choosing one.
Test suites are extremely important because they gauge the quality of the JMS implementation an organization is planning to use, and also perform compliance, robustness, and reliability testing. For example, some JMS vendors have suites that include tests for each line of the draft specification. The strategy here should include test suites that check for durable subscriptions, persistent messages, commit/rollback of transactions, etc.
Synchronous messaging code fragment |
This code
fragment implements a synchronous receiver (subscriber) that synchronously
receives messages published on the topic (i.e., primaryTopic). Note that
no exception handling code is included, which makes it easier to see what's
happening. The fragment loosely implements the objects depicted in Figure 1. // 1. Create the InitialContext object used for looking up JMS administered objects located on // the default host. InitialContext ic = null; ic = new InitialContext (); ic.bind (); // 2. Lookup Connection Factory and Topic names TopicConnectionFactory tcf = (TopicConnectionFactory) ic.lookup ("primaryTCF"); Topic topic = (Topic)ic.lookup("primaryTopic"); // 3. Dispose of the InitialContext resources ic.dispose(); // 4. Create and start a topic connection System.out.println("Creating topic connection"); TopicConnection topicConnection = tcf.createTopicConnection(); topicConnection.start (); // 5. Create topic session on the connection just created System.out.println("Creating topic session: not transacted, auto ack"); TopicSession topicSession = topicConnection.createTopicSession(false,1); // 6. Create subscriber System.out.println("Creating topic, subscriber"); TopicSubscriber topicSubscriber = topicSession.createSubscriber(topic); // 7. Listen for messages System.out.println ("Ready to listen for messages synchronously (blocking receive)"); while (true) { TextMessage textmsg2 = (TextMessage)topicSubscriber.receive(); System.out.println("Received : " + textmsg2.getText() ); } ... |
JMS versus RMI
Remote method invocation (RMI) provides the semantics of object invocation
within a distributed object system. In such systems, a local surrogate (stub)
object manages the invocation on a remote object. While other RMI-type systems
(e.g., CORBA) can be adapted to handle Java objects, they fall short of
seamless integration due to their interoperability requirement with other
languages. In contrast, the Java RMI system assumes the homogeneous environment
of the Java Virtual Machine, and can therefore take advantage of the Java
object model whenever possible.
Because of RMI's inherent weaknesses -- limited client connections, poor performance due to lack of resource pooling, no store-and-forward or load balancing, lack of guaranteed messaging and security, as well as static clients and servers with location-dependent code -- asynchronous JMS messaging provides a better way to implement distributed solutions. Some of these architectural weaknesses are addressed within the JMS specification, while others are implemented by JMS vendors. For example, Fiorano Software's JMS implementation provides a form of load balancing through a server-clustering architecture that connects neighboring servers.
Consider JMS a robust alternative to RMI, but not a replacement. JMS can be implemented on top of RMI (java.rmi.server.RMISocketFactory) or Java sockets (java.net.Socket), and its implementations that use sockets directly will provide better message throughput than RMI-type JMS implementations.
JMS versus JavaSpaces
JavaSpaces is a distributed persistence and data exchange mechanism for Java.
With JavaSpaces, one writes data in entries that are in fact a typed grouping
of fields. Clients operate on a JavaSpace to write new entries, look up
existing entries, and remove entries from the space. This allows Java programs
to store their state and lets applets store state on servers when they can't
store it on the client because of security restrictions.
JavaSpaces is a simple and elegant API that runs on top of RMI and has a set of interfaces for leasing, transactions, and distributed events. Applications requiring these services could be written with JMS, but some developers opt to use JavaSpaces and its associated APIs rather than going out and buying a JMS implementation from another company. Sun has noted the possibility that JMS will define other messaging domains (like distributed events) in a future release; however, this is not the case with JMS 1.0.
JavaSpaces can also be seen as a set of APIs for making transient objects on a small scale, and as a Jini application confined to small local networks (since it can't travel through firewalls). You might also want to position JMS differently than JavaSpaces based on service level agreements (SLAs) with users, in which you guarantee certain performance and response times. JavaSpaces relies on RMI as the communications mechanism and doesn't lend itself to high-speed communications, like collaborative video and voice delivery.
JMS versus InfoBus
InfoBus is a solution for interconnecting such Java components as Lotus's Kona
applets; it is also a virtual traffic cop that polices the multiple components
executing on a Web page. It dynamically tags applets by content (e.g., name and
spreadsheet values), and also includes the data producer, data consumer, and
data controller modules. The tags allow information to be exchanged with
multiple components at runtime, and any JavaBeans component written to the
InfoBus API can exchange data with other applets.
The JavaBeans InfoBus Lotus specification is intended to provide standards by which a wide range of Java components, acting as data producers and consumers, can communicate data. Its architecture facilitates the creation of applications built from JavaBeans that exchange data asynchronously. InfoBus can also be used by arbitrary Java classes, including applets and servlets, and is designed for components working together in the same Java Virtual Machine (whereas JMS is designed for distributed messaging systems).
JMS versus JSDT (Java Shared Data Toolkit)
The Java Shared Data Toolkit is a development library that allows developers to
easily add collaboration features to applets and applications written in the
Java programming language. Enterprise developers can use JSDT to create
network-centric applications, such as shared whiteboards or chat environments.
It can also be used for remote presentations and shared simulations, and to
easily distribute data for enhanced group workflow.
While JSDT is designed for dealing with many simultaneous users of an application, JMS fits into the groupware category in a nonrealtime fashion. And, just as JSDT uses raw IP as its delivery mechanism, JMS can use higher-level protocols (SMTP, RMI, and Java sockets).
JSDT is designed for highly collaborative, high-bandwidth services, and to work across the Internet by using the HTTP transport mechanism. JSDT can also travel through firewalls, and may use one of many transport mechanisms and lightweight, high-speed protocols.
High on the wish list for JSDT is the implementation of a Jini-based transport mechanism, which would allow its true distributed nature to finally shine through.
Architectural implications
Which particular messaging system you choose to deploy depends on your
application requirements. In environments where CORBA has not been embraced,
and Java applications need to communicate with each other asynchronously
(without concern for whether the remote application is up or not), JMS is
probably the way to go. On the other hand, RMI is a viable choice for simple,
synchronous messages where performance is not an issue.
For ecommerce solutions, JMS can be used across the firewall through HTTP tunneling (with HTTP, connections die after data is transmitted once). Because synchronous data operations cannot be pushed from the server to the client, using HTTP connections between a JMS client and the JMS server results in major inefficiencies. The system becomes like RMI, based on polling, and although HTTP tunneling is possible with JMS, it's nonetheless inefficient.
The strategic purpose of JMS is to define a specification and an API, then let MOM vendors implement to it. Developers should be able to write Java messaging applications that work on top of all the different MOM providers, without having to change a line of their code. Pursuing this strategy keeps Java programmers productive by insulating them from low-level transport APIs while delivering enterprise-wide messaging.