1. Trang chủ >
  2. Công Nghệ Thông Tin >
  3. Kỹ thuật lập trình >

What’s New in JMS 2.0?

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (15.81 MB, 597 trang )


Chapter 13 ■ Messaging



JMS 1.0 made a clear difference between the point-to-point and publish-subscribe model. It defined two

domain-specific APIs, one for point-to-point (queues) and one for pub-sub (topics). That’s why you can find

QueueConnectionFactory and TopicConnectionFactory API instead of the generic ConnectionFactory for example.

Note also the different vocabulary; a consumer is called a receiver in P2P and a subscriber in pub-sub.

The JMS 1.1 API (referred to as the classic API) provided a unified set of interfaces that can be used with both P2P

and pub-sub messaging. Table 13-1 shows the generic name of an interface (e.g., Session) and the legacy names for

each model (QueueSession, TopicSession).

Table 13-1.  Interfaces Depending on JMS Version



Classic API



Simplified API



Legacy API (P2P)



Legacy API (Pub-Sub)



ConnectionFactory



ConnectionFactory



QueueConnectionFactory



TopicConnectionFactory



Connection



JMSContext



QueueConnection



TopicConnection



Session



JMSContext



QueueSession



TopicSession



Destination



Destination



Queue



Topic



Message



Message



Message



Message



MessageConsumer



JMSConsumer



QueueReceiver



TopicSubscriber



MessageProducer



JMSProducer



QueueSender



TopicPublisher



JMSException



JMSRuntimeException



JMSException



JMSException



But JMS 1.1 was still a verbose and low-level API compared to JPA or EJB. JMS 2.0 introduces a simplified API that

offers all the features of the classic API but requires fewer interfaces and is simpler to use. Table 13-1 highlights the

differences between these APIs (all located under the javax.jms package).

I will not discuss the legacy API but I need to introduce the classic API; first of all because you will still find

millions of lines of code using the JMS 1.1 classic API and second, because technically the simplified API relies on the

classical one.



Classic API

The JMS classic API provides classes and interfaces for applications that require a messaging system (see Figure 13-7).

This API enables asynchronous communication between clients by providing a connection to the provider, and a session

where messages can be created and sent or received. These messages can contain text or other different kinds of objects.



423

www.it-ebooks.info



Chapter 13 ■ Messaging



Figure 13-7.  JMS Classic API



ConnectionFactory

Connection factories are administered objects that allow an application to connect to a provider by creating a

Connection object programmatically. A javax.jms.ConnectionFactory is an interface that encapsulates the

configuration parameters that have been defined by an administrator.

To use an administered object such as a ConnectionFactory, the client needs to perform a JNDI lookup (or use

injection). For example, the following code fragment obtains the JNDI InitialContext object and uses it to look up a

ConnectionFactory by its JNDI name:



Context ctx = new InitialContext();

ConnectionFactory ConnectionFactory = 

(ConnectionFactory) ctx.lookup("jms/javaee7/ConnectionFactory");



The methods available in this interface (see Listing 13-1) are createConnection methods that return a

Connection object and new JMS 2.0 createContext methods that return a JMSContext. You can create a Connection

or a JMSContext either with the default user identity or by specifying a username and password.



424

www.it-ebooks.info



Chapter 13 ■ Messaging



Listing 13-1.  ConnectionFactory Interface

public interface ConnectionFactory {



Connection createConnection() throws JMSException;

Connection createConnection(String userName, String password) throws JMSException;

JMSContext createContext();

JMSContext createContext(String userName, String password);

JMSContext createContext(String userName, String password, int sessionMode);

JMSContext createContext(int sessionMode);

} 



Destination

A destination is an administered object that contains provider-specific configuration information such as the destination

address. But this configuration is hidden from the JMS client by using the standard javax.jms.Destination interface.

Like the connection factory, a JNDI lookup is needed to return such objects:



Context ctx = new InitialContext();

Destination queue = (Destination) ctx.lookup("jms/javaee7/Queue"); 



Connection

The javax.jms.Connection object, which you create using the createConnection() method of the connection

factory, encapsulates a connection to the JMS provider. Connections are thread-safe and designed to be shareable, as

opening a new connection is resource intensive. However, a session (javax.jms.Session) provides a single-threaded

context for sending and receiving messages, using a connection to create one or more sessions. Once you have a

connection factory, you can use it to create a connection as follows:



Connection connection = connectionFactory.createConnection();



Before a receiver can consume messages, it must call the start() method. If you need to stop receiving messages

temporarily without closing the connection, you can call the stop() method:



connection.start();

connection.stop();



When the application completes, you need to close any connections created. Closing a connection also closes its

sessions and its producers or consumers:



connection.close(); 



Session

You create a session from the connection using the createSession() method. A session provides a transactional

context in which a set of messages to be sent or received are grouped in an atomic unit of work, meaning that, if you

send several messages during the same session, JMS will ensure that either they all are sent or none. This behavior is

set at the creation of the session:



Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);





425

www.it-ebooks.info



Chapter 13 ■ Messaging



The first parameter of the method specifies whether or not the session is transactional. In the code, the parameter

is set to true, meaning that the request for sending messages won’t be realized until either the session’s commit()

method is called or the session is closed. If the parameter was set to false, the session would not be transactional,

and messages would be sent as soon as the send() method is invoked. The second parameter means that the session

automatically acknowledges messages when they have been received successfully. A session is single-threaded and is

used to create messages, producers, and consumers.



Messages

To communicate, clients exchange messages; one producer will send a message to a destination, and a consumer will

receive it. Messages are objects that encapsulate information and are divided in three parts (see Figure 13-8):





A header: contains standard information for identifying and routing the message.







Properties: are name-value pairs that the application can set or read. Properties also allow

destinations to filter messages based on property values.







A body: contains the actual message and can take several formats (text, bytes, object, etc.).



Figure 13-8.  Structure of a JMS message



Header

The header has predefined name-value pairs, common to all messages that both clients and providers use to identify

and route messages. They can be seen as message metadata as they give information about the message. Each

field has associated getter and setter methods defined in the javax.jms.Message interface. Some header fields are

intended to be set by a client, but many are set automatically by the send() or the publish() method. Table 13-2

describes each JMS message header field.



426

www.it-ebooks.info



Chapter 13 ■ Messaging



Table 13-2.  Fields Contained in the Header



Field



Description



Set By



JMSDestination



This indicates the destination to which the message is being sent.



send() or publish()

method



JMSDeliveryMode



send() or publish()

JMS supports two modes of message delivery. PERSISTENT mode

instructs the provider to ensure the message is not lost in transit due method

to a failure. NON_PERSISTENT mode is the lowest-overhead delivery

mode because it does not require the message to be

logged to a persistent storage.



JMSMessageID



This provides a value that uniquely identifies each message

sent by a provider.



send() or publish()

method



JMSTimestamp



This contains the time a message was handed off to a provider

to be sent.



send() or publish()

method



JMSCorrelationID



A client can use this field to link one message with another

such as linking a response message with its request message.



Client



JMSReplyTo



This contains the destination where a reply to the message

should be sent.



Client



JMSRedelivered



This Boolean value is set by the provider to indicate

whether a message has been redelivered.



Provider



JMSType



This serves as a message type identifier.



Client



JMSExpiration



When a message is sent, its expiration time is calculated and set

based on the time-to-live value specified on the send() method.



send() or publish()

method



JMSPriority



JMS defines a 10-level priority value, with 0 as the lowest priority

and 9 as the highest.



send() or publish()

method



Properties

In addition to the header fields, the javax.jms.Message interface supports property values, which are just like headers,

but explicitly created by the application, instead of being standard across messages. This provides a mechanism for

adding optional header fields to a message that a client will choose to receive or not via selectors. Property values can

be boolean, byte, short, int, long, float, double, and String. The code to set and get properties looks like this:



message.setFloatProperty("orderAmount", 1245.5f);

message.getFloatProperty("orderAmount"); 



Body

The body of a message is optional, and contains the data to send or receive. Depending on the interface that you use,

it can contain different formats of data, as listed in Table 13-3.



427

www.it-ebooks.info



Chapter 13 ■ Messaging



Table 13-3.  Types of Messages



Interface



Description



StreamMessage



A message whose body contains a stream of Java primitive values. It is filled and read

sequentially.



MapMessage



A message whose body contains a set of name-value pairs where names are strings

and values are Java primitive types.



TextMessage



A message whose body contains a string (for example, it can contain XML).



ObjectMessage



A message that contains a serializable object or a collection of serializable objects.



BytesMessage



A message that contains a stream of bytes.



It is possible to create your own message format, if you extend the javax.jms.Message interface. Note that, when

a message is received, its body is read-only. Depending on the message type, you have different methods to access its

content. A text message will have a getText() and setText() method, an object message will have a getObject() and

setObject(), and so forth:



textMessage.setText("This is a text message");

textMessage.getText();

bytesMessage.readByte();

objectMessage.getObject();



Note that since JMS 2.0, the new method T getBody(Class c) returns the message body as an object of

the specified type.



Sending and Receiving a Message with Classic API

Now let’s take a look at a simple example to get an idea of how to use the classic JMS API to send and receive a

message. JMS employs producers, consumers, and destinations. The producer sends a message to the destination,

where the consumer is waiting for the message to arrive. Destinations can be of two kinds: queues (for point-to-point

communication) and topics (for publish-subscribe communication). In Listing 13-2, a producer sends a text message

to a queue to which the consumer is listening.

Listing 13-2.  The Producer Class Produces a Message into a Queue using the Classic API

public class Producer {



public static void main(String[] args) {



try {

// Gets the JNDI context

Context jndiContext = new InitialContext();



// Looks up the administered objects

ConnectionFactory connectionFactory = (ConnectionFactory) 

jndiContext.lookup("jms/javaee7/ConnectionFactory");

Destination queue = (Destination) jndiContext.lookup("jms/javaee7/Queue");



// Creates the needed artifacts to connect to the queue

Connection connection = connectionFactory.createConnection();

Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

MessageProducer producer = session.createProducer(queue);





428



www.it-ebooks.info



Chapter 13 ■ Messaging



// Sends a text message to the queue

TextMessage message = session.createTextMessage("Text message sent at " + new Date());

producer.send(message);







connection.close();







} catch (NamingException | JMSException e) {

e.printStackTrace();

}

}

}



The code in Listing 13-2 represents a Producer class that has a main() method only. In this method, the first

thing that occurs is that a JNDI context is instantiated and used to obtain a ConnectionFactory and a Destination.

Connection factories and destinations (queues and topics) are called administered objects; they have to be created

and declared in the message provider (in our case, OpenMQ in GlassFish). They both have a JNDI name (e.g., the

queue is called jms/javaee7/Queue) and need to be looked up in the JNDI tree.

When the two administered objects are obtained, the Producer class uses the ConnectionFactory to create a

Connection from which a Session is obtained. With this session, a MessageProducer and a message are created on the

destination queue (session.createProducer(queue)). The producer then sends this message (of type text). Note that

this main class catches the JNDI NamingException as well as the checked JMSException.

Fortunately, once you’ve written this code to send a message, the code to receive it looks almost the same. In fact,

the first lines of the Consumer class in Listing 13-3 are exactly the same: create a JNDI context, lookup for the connection

factory and the destination, and then connect. The only differences are that a MessageConsumer is used instead of a

MessageProducer, and that the receiver enters an infinite loop to listen to the queue (you’ll later see that this loop can be

avoided by using the more standard message listener). When the message arrives, it is consumed and the content displayed.

Listing 13-3.  The Consumer Class Consumes a Message from a Queue using the Classic API

public class Consumer {



public static void main(String[] args) {



try {

// Gets the JNDI context

Context jndiContext = new InitialContext();



// Looks up the administered objects

ConnectionFactory connectionFactory = (ConnectionFactory) 

jndiContext.lookup("jms/javaee7/ConnectionFactory");

Destination queue = (Destination) jndiContext.lookup("jms/javaee7/Queue");



// Creates the needed artifacts to connect to the queue

Connection connection = connectionFactory.createConnection();

Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

MessageConsumer consumer = session.createConsumer(queue);



connection.start();



// Loops to receive the messages

while (true) {

TextMessage message = (TextMessage) consumer.receive();

System.out.println("Message received: " + message.getText());

}





429

www.it-ebooks.info



Chapter 13 ■ Messaging



} catch (NamingException | JMSException e) {

e.printStackTrace();

}

}

}



Simplified API

As you can see, the code in Listing 13-2 and 13-3 is quite verbose and low level. You need several artifacts to be able to

produce or consume a message (ConnectionFactory, Connection, Session . . .). On top of that you also need to deal

with the JMSException which is a checked exception (JMSException has several sub classes). This API was created

with JMS 1.1 in 2002 and was not changed until JMS 2.0.

JMS 2.0 introduces a new simplified API, which consists mainly of three new interfaces (JMSContext,

JMSProducer and JMSConsumer). These interfaces rely internally on the ConnectionFactory and other classic APIs but

leave the boilerplate code aside. Thanks to the new JMSRuntimeException, which is an unchecked exception, the code

to send or receive a message is now much easier to write and read (code examples to follow).

Figure 13-9 shows a simplified class diagram of this new API. Note that the legacy, classic, and simplified APIs are

all under the javax.jms package.



Figure 13-9. JMS Simplified API

The simplified API provides the same messaging functionality as the classic API but requires fewer interfaces and

is simpler to use. These main interfaces are:





JMSContext: active connection to a JMS provider and a single-threaded context for sending

and receiving messages







JMSProducer: object created by a JMSContext that is used for sending messages to a queue or topic







JMSConsumer: object created by a JMSContext that is used for receiving messages sent to a

queue or topic



430

www.it-ebooks.info



Chapter 13 ■ Messaging



JMSContext

The JMSContext is the main interface in the simplified JMS API introduced by JMS 2.0. It combines the functionality of

two separate objects from the JMS 1.1 classic API: a Connection (the physical link to the JMS provider) and a Session

(a single-threaded context for sending and receiving messages).

A JMSContext may be created by the application by calling one of several createContext methods on a

ConnectionFactory (see Listing 13-1) and then closed (i.e., application-managed). Alternatively, if the application

is running in a container (EJB or Web), the JMSContext can be injected using the @Inject annotation

(i.e., container-managed).

When an application needs to send messages it uses the createProducer method to create a JMSProducer,

which provides methods to configure and send messages. Messages may be sent either synchronously or

asynchronously. To receive messages, an application can use one of several createConsumer methods to create a

JMSConsumer. Table 13-4 shows you a subset of the JMSContext API.

Table 13-4.  Subset of the JMSContext API



Property



Description



void start()



Starts (or restarts) delivery of incoming messages



void stop()



Temporarily stops the delivery of incoming messages



void close()



Closes the JMSContext



void commit()



Commits all messages done in this transaction and

releases any locks currently held



void rollback()



Rolls back any messages done in this transaction and

releases any locks currently held



BytesMessage createBytesMessage()



Creates a BytesMessage object



MapMessage createMapMessage()



Creates a MapMessage object



Message createMessage()



Creates a Message object



ObjectMessage createObjectMessage()



Creates an ObjectMessage object



StreamMessage createStreamMessage()



Creates a StreamMessage object



TextMessage createTextMessage()



Creates a TextMessage object



Topic createTopic(String topicName)



Creates a Topic object



Queue createQueue(String queueName)



Creates a Queue object



JMSConsumer createConsumer(Destination destination)



Creates a JMSConsumer for the specified destination



JMSConsumer createConsumer(Destination destination,

String messageSelector)



Creates a JMSConsumer for the specified destination,

using a message selector



JMSProducer createProducer()



Creates a new JMSProducer object which can be used

to configure and send messages



JMSContext createContext(int sessionMode)



Creates a new JMSContext with the specified

session mode



431

www.it-ebooks.info



Chapter 13 ■ Messaging



JMSProducer

A JMSProducer is used to send messages on behalf of a JMSContext. It provides various send methods to send a

message to a specified destination. An instance of JMSProducer is created by calling the createProducer method on a

JMSContext. It also provides methods to allow send options, message properties and message headers (see Figure 13-8)

to be specified prior to sending the message. Table 13-5 shows a subset of the JMSProducer API.

Table 13-5.  Subset of the JMSProducer API



Property



Description



get/set[Type]Property



Sets and returns a message property where [Type] is the type of

the property and can be Boolean, Byte, Double, Float, Int, Long,

Object, Short, String



JMSProducer clearProperties()



Clears any message properties set



Set getPropertyNames()



Returns an unmodifiable Set view of the names of all the

message properties that have been set



boolean propertyExists(String name)



Indicates whether a message property with the specified name

has been set



get/set[Message Header]



Sets and returns a message header where [Message Header]

can be DeliveryDelay, DeliveryMode, JMSCorrelationID,

JMSReplyTo, JMSType, Priority, TimeToLive



JMSProducer send(Destination destination,

Message message)



Sends a message to the specified destination, using any send options,

message properties and message headers that have been defined



JMSProducer send(Destination destination,

String body)



Sends a TextMessage with the specified body to the specified

destination



JMSConsumer

A JMSConsumer is used to receive messages from a queue or topic. It is created with one of the createConsumer

methods on a JMSContext by passing a Queue or a Topic. As you will later see, a JMSConsumer can be created with a

message selector so it can restrict messages delivered.

A client may receive a message synchronously or asynchronously as they arrive. For asynchronous delivery, a

client can register a MessageListener object with a JMSConsumer. As messages arrive, the provider delivers them by

calling the MessageListener's onMessage method. Table 13-6 shows you a subset of the JMSConsumer API.



432

www.it-ebooks.info



Chapter 13 ■ Messaging



Table 13-6.  Subset of the JMSConsumer API



Property



Description



void close()



Closes the JMSConsumer



Message receive()



Receives the next message produced



Message receive(long timeout)



Receives the next message that arrives within the

specified timeout interval



T receiveBody(Class c)



Receives the next message produced and returns its

body as an object of the specified type



Message receiveNoWait()



Receives the next message if one is immediately available



void setMessageListener(MessageListener listener)



Sets the MessageListener



MessageListenergetMessageListener()



Gets the MessageListener



String getMessageSelector()



Gets the message selector expression



Writing Message Producers

The new JMS simplified API allows you to write producers and consumers in a less verbose manner than with the

classic API. But it still needs both of the administered objects: ConnectionFactory and Destination. Depending if

you are running outside or inside a container (EJB, Web or ACC) you will either use JNDI lookups or injection.

As you’ve seen previously, the JMSContext API is the central API to produce and consume messages. If your

application runs outside a container you will need to manage the lifecycle of the JMSContext (by creating and closing

it programmatically). If you run inside a container you can just inject it and leave the container to manage its lifecycle.



Producing a Message outside a Container

A message producer (JMSProducer) is an object created by the JMSContext and is used to send messages to a

destination. The following steps explain how to create a producer that sends a message to a queue (see Listing 13-4)

outside any container (in a pure Java SE environment):





Obtain a connection factory and a queue using JNDI lookups







Create a JMSContext object using the connection factory (notice the try-with-resources

statement that will automatically close the JMSContext object)







Create a javax.jms.JMSProducer using the JSMContext object







Send a text message to the queue using the JMSProducer.send() method



Listing 13-4.  The Producer Class Produces a Message into a Queue

public class Producer {



public static void main(String[] args) {



try {

// Gets the JNDI context

Context jndiContext = new InitialContext();





433

www.it-ebooks.info



Xem Thêm
Tải bản đầy đủ (.pdf) (597 trang)

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×