Tuesday, September 27, 2011

JMS - 1

Messaging systems are
composed of messaging clients and some kind of messaging middleware server. The clients send messages to the messaging server, which then distributes those messages
to other clients. The client is a business application or component that is using the
messaging API (in our case, JMS).


Centralized Architectures


Enterprise messaging systems that use a centralized architecture rely on a message
server. A message server, also called a message router or broker, is responsible for delivering messages from one messaging client to other messaging clients.


Decentralized Architectures


All decentralized architectures currently use IP multicast at the network level. A messaging system based on multicasting has no centralized server.

Messaging Models


JMS supports two types of messaging models: point-to-point and publish-andsubscribe.
These messaging models are sometimes referred to as messaging domains.
Point-to-point messaging and publish-and-subscribe messaging are frequently shortened
to p2p and pub/sub, respectively.

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.


From a JMS perspective, messaging clients are called JMS clients, and the messaging system is called the JMS provider. A JMS application is a business system composed of many JMS clients and, generally, one JMS provider.

In addition, a JMS client that produces a message is called a message producer, while a
JMS client that receives a message is called a message consumer. A JMS client can be both a message producer and a message consumer. When we use the term consumer or producer, we mean a JMS client that consumes messages or produces messages, respectively.

Point-to-Point


The point-to-point messaging model allows JMS clients to send and receive messages
both synchronously and asynchronously via virtual channels known as queues. In the point-to-point model, message producers are called senders and message consumers
are called receivers.

One of the distinguishing characteristics of point-to-point messaging is that messages sent to a queue are received by one and only one receiver, even though there may be many receivers listening on a queue for the same message.

Point-to-point messaging supports asynchronous “fire and forget” messaging as well
as synchronous request/reply messaging. Point-to-point messaging tends to be more
coupled than the publish-and-subscribe model in that the sender generally knows how
the message is going to be used and who is going to receive it.

Publish-and-Subscribe
In the publish-and-subscribe model, messages are published to a virtual channel called
a topic. Message producers are called publishers, whereas message consumers are called
subscribers. Unlike the point-to-point model, messages published to a topic using the publish-and-subscribe model can be received by multiple subscribers. This technique is sometimes referred to as broadcasting a message. Every subscriber receives a copy of
each message. The publish-and-subscribe 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.

There are many different types of subscribers within the pub/sub messaging model.
Nondurable subscribers are temporary subscriptions that receive messages only when
they are actively listening on the topic. Durable subscribers, on the other hand, will
receive a copy of every message published, even if they are “offline” when the message
is published. There is also the notion of dynamic durable subscribers and administered durable subscribers.

JMS General API



Within the JMS general API, there are seven main JMS API interfaces related to sending
and receiving JMS messages:
• ConnectionFactory
• Destination
• Connection
• Session
• Message
• MessageProducer
• MessageConsumer

In JMS, the Session object holds the transactional unit of work for messaging, not the
Connection object. This is different from JDBC, where the Connection object holds the
transactional unit of work. This means that when using JMS, an application will typically have only a single Connection object but will have a pool of Session objects.

Point-to-Point API


The interfaces used for sending and
receiving messages from a queue are as follows:
• QueueConnectionFactory
• Queue
• QueueConnection
• QueueSession
• Message
• QueueSender
• QueueReceiver


Publish-and-Subscribe API


The interfaces used within the pub/sub
messaging model are as follows:
• TopicConnectionFactory
• Topic
• TopicConnection
• TopicSession
• Message
• TopicPublisher
• TopicSubscriber


SOA has given rise to a new breed of middleware known as an Enterprise Service
Bus, or ESB.




An InitialContext is the starting point for any JNDI lookup—it’s similar in concept to the root of a filesystem. The InitialContext provides a network connection to the directory service that acts as a root for accessing JMS administered objects. The properties used to create an InitialContext depend on which JMS directory service you are using. You could configure the initial context properties using the Properties Object directly in your source code, or preferably using an external jndi.properties file located in the classpath of the application.

The corresponding source code using the properties object would be as follows:
Properties env = new Properties();
env.put(Context.SECURITY_PRINCIPAL, "system");
env.put(Context.SECURITY_CREDENTIALS, "manager");
env.put(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.activemq.jndi.ActiveMQInitialContextFactory");
env.put(Context.PROVIDER_URL, "tcp://localhost:61616");
InitialContext ctx = new InitialContext(env);


Once a JNDI InitialContext object is instantiated, it can be used to look up the Topic
ConnectionFactory in the messaging server’s naming service:
TopicConnectionFactory conFactory =
(TopicConnectionFactory)ctx.lookup(topicFactory);

The TopicConnectionFactory provides two overloaded versions of the createTopicCon
nection() method:

public TopicConnection createTopicConnection()
throws JMSException, JMSSecurityException;
public TopicConnection createTopicConnection(String username,
String password) throws JMSException, JMSSecurityException;


The TopicConnection is created by the TopicConnectionFactory:
// Look up a JMS connection factory and create the connection
TopicConnectionFactory conFactory =
(TopicConnectionFactory)ctx.lookup(topicFactory);
TopicConnection connection = conFactory.createTopicConnection();

The TopicConnection is an interface that extends javax.jms.Connection interface. It defines several generalpurpose methods used by clients of the TopicConnection. Among these methods are the start() , stop(), and close() methods:

public interface Connection {
public void start() throws JMSException;
public void stop() throws JMSException;
public void close() throws JMSException;
...
}

public interface TopicConnection extends Connection {
public TopicSession createTopicSession(boolean transacted,
int acknowledgeMode)
throws JMSException;


Closing a TopicConnection closes all the objects associated with the connection,
including the TopicSession, TopicPublisher, and TopicSubscriber.

After the TopicConnection is obtained, it’s used to create TopicSession objects:
// Create two JMS session objects
TopicSession pubSession = connection.createTopicSession(
false, Session.AUTO_ACKNOWLEDGE);
TopicSession subSession = connection.createTopicSession(
false, Session.AUTO_ACKNOWLEDGE);


The boolean parameter in the createTopicSession() method indicates whether the
Session object will be transacted.
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 TopicSession objects are used to create the TopicPublisher and TopicSubscriber.
The TopicPublisher and TopicSubscriber objects are created with a Topic identifier and
are dedicated to the TopicSession that created them; they operate under the control of
a specific TopicSession:
TopicPublisher publisher =
pubSession.createPublisher(chatTopic);
TopicSubscriber subscriber =
subSession.createSubscriber(chatTopic);


The TopicSession is also used to create the Message objects that are delivered to the
topic.
TextMessage message = pubSession.createTextMessage();

JNDI is used to locate a Topic object, which is an administered object like the Topic
ConnectionFactory:
InitialContext jndi = new InitialContext(env);
...
// Look up a JMS topic
Topic chatTopic = (Topic)jndi.lookup(topicName);

A Topic object 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.

The Topic object has one method, getName(), the name identifier for the physical topic it represents.

// Create a JMS publisher and subscriber
TopicSubscriber subscriber =
subSession.createSubscriber(chatTopic, null, true);

A TopicSubscriber receives messages from a specific topic. The Topic object argument
used in the createSubscriber() method identifies the topic from which the TopicSub
scriber will receive messages. The second argument contains the message selector used
to filter out only those messages we want to receive based on certain criteria. In this
case, we set this value to null, indicating that we want to receive all messages. The third argument contains a boolean value indicating whether or not we want to receive messages we publish ourselves. In this case, we are setting the value to true, indicating that, as a subscriber to the topic, we do not want to see messages we publish.

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.‡ An object simply implements the listener interface, in this case the MessageLis
tener, and then is registered with the TopicSubscriber. A TopicSubscriber may have
only one MessageListener object. Here is the definition of the MessageListener

interface
used in JMS:
package javax.jms;
public interface MessageListener {
public void onMessage(Message message);
}

A message has three parts: a header, properties, and payload.

Message
This type has no payload. It is useful for simple event notification.

TextMessage
This type carries a java.lang.String as 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 Message types. 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.Properties object, except the values must be Java primitives or their
wrappers. The MapMessage is useful for delivering keyed data.


JMS consumers
can choose to receive messages based on the values of certain headers and
properties, using a special filtering mechanism called message selectors.

Automatically assigned headers



There are two types of delivery modes in JMS: persistent and nonpersistent. A persistent message should be delivered once-and-only-once, which means that if the JMS provider fails, the message is not lost; it will be delivered after the server recovers. A nonpersistent message is delivered at-most-once, which means that it can be lost permanently if the JMS provider fails.

JMSDeliveryMode and the JMSPriority headers

int deliverymode = message.getJMSDeliveryMode();
if (deliverymode == javax.jms.DeliveryMode.PERSISTENT) {
...
} else { // equals DeliveryMode.NON_PERSISTENT
...
}

// Set the JMS delivery mode on the message producer
TopicPublisher topicPublisher = topicSession.createPublisher(topic);
topicPublisher.setDeliveryMode(DeliveryMode.NON_PERSISTENT);

String messageid = message.getJMSMessageID();

long timestamp = message.getJMSTimestamp();

long timeToLive = message.getJMSExpiration();

TopicPublisher topicPublisher = topicSession.createPublisher(topic);
// Set time to live as 1 hour (1000 millis x 60 sec x 60 min)
topicPublisher.setTimeToLive(3600000);

boolean isRedelivered = message.getJMSRedelivered()

There are two categories of message priorities: levels 0–4 are gradations of normal priority, and levels 5–9 are gradations of expedited priority. The message servers may use a message’s priority to prioritize delivery of messages to consumers—messages with an expedited priority are delivered ahead of normal priority messages:
int priority = message.getJMSPriority();

The priority of messages can be declared by the JMS client using the setPriority()
method on the producer:

TopicPublisher topicPublisher = TopicSession.createPublisher(someTopic);
topicPublisher.setPriority(9);

Any direct programmatic invocation of the setJMSPriority() method will be ignored
when the message is sent.

Developer assigned headers


message.setJMSReplyTo(topic);
...
Topic topic = (Topic) message.getJMSReplyTo();

message.setJMSCorrelationID(identifier);
...
String correlationid = message.getJMSCorrelationID();

Properties



There are three basic categories of message properties: application-specific properties,JMS-defined properties, and provider-specific properties. Application properties are defined and applied to Message objects by the application developer; the JMS extension and provider-specific properties are additional headers that are, for the most part, automatically added by the JMS provider.

message.setStringProperty("username",username);
Property values can be any boolean, byte, short, int, long, float, double, or String.



Message
CF - connection - session - /_ MessageProducer ---|
\ MessageConsumer ---|
| |
| |
Destination --------------------------------------


Point-to-Point API
Message
QCF - Qconnection - Qsession - /_ QueueSender ---|
\ QueueReceiver--|
| |
| |
Queue -------------------------------------------

Publish-and-Subscribe API

Message
TCF - TConnection - TSession - /_ TopicPublisher--|
\ TopicSubscriber-|
| |
| |
Topic---------------------------------------------


public interface Message{
....
public short getShortProperty(String name)
throws JMSException, MessageFormatException;

public void setShortProperty(String name, short value)
throws JMSException, MessageNotWriteableException;

public Object getObjectProperty(String name)
throws JMSException, MessageFormatException;

public void setObjectProperty(String name, Object value)
throws JMSException, MessageNotWriteableException;

public void clearProperties()
throws JMSException;

public Enumeration getPropertyNames()
throws JMSException;

public boolean propertyExists(String name)
throws JMSException;
}

The properties can, however, be changed on that message by calling the method
clearProperties(), which removes all the properties from the message so that new ones
can be added.


Note that in the Message interface you will not find corresponding setJMSXERTY>() and getJMSX() methods defined; when used, they must be set in the
same manner as application-specified properties:

message.setStringProperty("JMSXGroupID", "ERF-001");
message.setIntProperty("JMSXGroupSeq", 3);

Provider-Specific Properties


Every JMS provider can define a set of proprietary properties that can be set by the
client or the provider automatically. Provider-specific properties must start with the
prefix JMS followed by the property name (JMS_). The purpose
of the provider-specific properties is to support proprietary vendor features.

Message Types


The six message interfaces are Message and its five subinterfaces:
TextMessage, StreamMessage, MapMessage, ObjectMessage, and BytesMessage.

-------------------
As shown below, the Message type can be created and
used as a JMS message with no payload:
// Create and deliver a Message
Message message = session.createMessage();
publisher.publish(message);
...
// Receive a message on the consumer
public void onMessage(Message message) {
// No payload, just process event notification
}

This type of message contains only JMS headers and properties and is used in event
notification.
-------------------
TextMessage textMessage = session.createTextMessage();
textMessage.setText("Hello!");
topicPublisher.publish(textMessage);
...

TextMessage textMessage = session.createTextMessage("Hello!");
queueSender.send(textMessage);
-------------------
// Order is a serializable object
Order order = new Order( );
...
ObjectMessage objectMessage = session.createObjectMessage();
objectMessage.setObject(order);
queueSender.send(objectMessage);
...
ObjectMessage objectMessage = session.createObjectMessage(order);
topicPublisher.publish(objectMessage);

-------------------
BytesMessage bytesMessage = session.createBytesMessage();
bytesMessage.writeChar('R');
bytesMessage.writeInt(10);
bytesMessage.writeUTF("OReilly");
queueSender.send(bytesMessage);

On the surface, the StreamMessage strongly resembles the BytesMessage, but they are
not the same. The StreamMessage keeps track of the order and types of primitives written.For example, an exception would be
thrown if you tried to read a long value as a short:

-------------------
StreamMessage streamMessage = session.createStreamMessage();
streamMessage.writeLong(2938302);
// The next line throws a JMSException
short value = streamMessage.readShort()
-------------------
MapMessage mapMessage = session.createMapMessage();
mapMessage.setInt("Age", 88);
mapMessage.setFloat("Weight", 234);
mapMessage.setString("Name", "Smith");
mapMessage.setObject("Height", new Double(150.32));
....
int age = mapMessage.getInt("Age");
float weight = mapMessage.getFloat("Weight");
String name = mapMessage.getString("Name");
Double height = (Double)mapMessage.getObject("Height");

To avoid reading nonexistent name-value pairs, the MapMessage provides an
itemExists() test method. In addition, the getMapNames() method lets a JMS client
enumerate the names and use them to obtain all the values in the message. For example:

public void onMessage(Message message) {
MapMessage mapMessage = (MapMessage)message;
Enumeration names = mapMessage.getMapNames();
while(names.hasMoreElements()){
String name = (String)names.nextElement();
Object value = mapMessage.getObject(name);
System.out.println("Name = "+name+", Value = "+value);
}
}


Read Only Messages


There are three acknowledgment modes that may be set by the JMS consumer when its session is created: AUTO_ACKNOWLEDGE, DUPS_OK_ACKNOWLEDGE, and CLIENT_ACKNOWLEDGE. Here is how a pub/sub consumer sets one of the three acknowledgment modes:

TopicSession topic = topicConnection.createTopicSession(false, Session.CLIENT_ACKNOWLEDGE);

In CLIENT_ACKNOWLEDGE mode, the JMS consumer (client) explicitly acknowledges each message as it is received. The acknowledge() method on the Message interface is used
for this purpose. For example:

public void onMessage(Message message) {
message.acknowledge();
...
}

Chapter 4 - Point-to-Point Messaging



Chapter 5 - Publish-and-Subscribe Messaging



Each message is delivered to multiple message consumers, called subscribers.
There are many types of subscribers, including durable, nondurable, and dynamic.

Another major difference between the pub/sub and p2p models is that, with the pub/
sub model, message selectors are applied when the message is copied to each subscriber;
whereas with the p2p model, message selectors are applied after the message has been
added to the queue.

The important point here is that multiple consumers may consume the message.

No comments: