Table of Contents
Java Message Service (JMS)
is vendor-agnostic Java API that one can use to integrate heterogeneous systems. This API is implemented by many vendors in their Message-Oriented Middleware (MOM) products, so when using any of these products (JMS providers) you can actually use the same API – the JMS API.
JMS offers many advantages, main of them are:
- heterogeneous integration – some messaging systems allow for communication between different programming languages by having JMS support along with some native API for the non-Java languages.
- asynchronous processing of requests
- decoupling of sender and receiver
- increase the degree of architectural flexibility
- increase scalability – is accomplished by adding multiple message receivers, the only limit for this is the database ability to process that big amount of requests
A JMS application is composed of:
- a JMS Provider (server/router/broker)
- many JMS clients (applications that communicate through messages)
Messages are sent using (actually to) destinations which are virtual channels between applications. These destinations are divided in two types:
- Topics – are used in pub/sub models* (explained further)
- Queues – are used in p2p models* (explained further)
Connection factories and Destinations are maintained administratively (rather than programmatically), these objects are called administered objects.
And the application on the other end (the receiver of the message) checks for messages from these sources. This way producers of the messages are decoupled from the consumers.
Messages contain all the required info for the JMS provider to understand where and when to deliver it. So this too helps for the whole system to be decoupled.
Messaging models
JMS has two main messaging models (messaging domains):
- Pub/sub – publish and subscribe – one topic may have more subscribers (basically one to many). Message producers are called publishers. Messages according to this model are “broadcasted”.
- P2p – point to point – messages are delivered from one producer to one consumer by some queue (one to one). In p2p we call producer sender and consumer receiver. Even if more receivers are listening only the first one will receive the message (this is done by JMS provider).
Messaging model | Message destination type | Producer alias | Consumer alias |
Publish and subscribe | Topic | Publisher | Subscriber |
Point to point | Queue | Sender | Receiver |
JMS API
The JMS API can be divided in 3 main parts:
- general API
- pub/sub API
- p2p API
General API | Pub/sub API | P2P API | *comments |
ConnectionFactory | TopicConnectionFactory | QueueConnectionFactory | must be obtained from JMS provider using JNDI |
Destination | Topic | Queue | |
Connection | TopicConnection | QueueConnection | a JMS app will have only one of these |
Session | TopicSession | QueueSession | usually more than one; holds the transactional unit of work |
Message | |||
MessageProvider | TopicPublisher | QueueSender | |
MessageConsumer | TopicSubscriber | QueueReceiver |
The order of creation is the following:
ConnectionFactory creates:
Connection which in turn creates:
Session which in turn creates:
– Message
– MessageProducer (TopicPublisher, QueueSender)
– MessageConsumer (TopicSubscriber, QueueReceiver)
JMS provides guaranteed delivery. JMS provider can store the messages that should have been delivered to a destination (that was unavailable) to some storage. As soon as the destination app becomes available JMS provider retrieves the message from the storage and sends it to the destination. The mechanism used for this is called store-and-forward.
JNDI for JMS
JNDI is usually used for ConnectionFactory and Destination lookup. The actual properties names from the JNDI may vary depending on the JMS provider vendor.
JNDI properties may be passed in 2 different ways:
- Using jndi.properties file
- Using a Properties object
For jndi.properties file way we have to place a file with such name in the classpath. And for the Properties object way we have to do something like:
Properties jndiProperties = new Properties(); jndiProperties.put(Context.SECURITY_PRINCIPAL, "system"); jndiProperties.put(Context.SECURITY_CREDENTIALS, "manager"); jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.activemq.jndi.ActiveMQInitialContextFactory"); jndiProperties.put(Context.PROVIDER_URL, "tcp://localhost:61616"); InitialContext context = new InitialContext(jndiProperties);
The only difference in using either of these 2 methods for JNDI properties declaration is that in case of Properties object way you initialize the InitialContext object like:
InitialContext context = new InitialContext(propertiesObject);
Whilst in case of the jndi.properties file way you initialize it like:
InitialContext context = new InitialContext();
and it finds the jndi.properties file on its own.
Please read https://docs.oracle.com/javase/7/docs/api/javax/naming/Context.html#RESOURCEFILES for further info.