In order to send and receive any messages in JMS, an application requires a handle to a JMS provider.
A JMS client connects to a JMS provider through administrative objects. These administrative objects can be injected into the application if the developer uses annotations or they can be retrieved by navigating to the Java Native Directory Interface in a Java EE application server or by directly looking up the name and programming in the JMS API.
Inside a Java EE application server, the connection details of the administrative objects are established through external configuration. The JMS specification does not explicitly define how the administration objects are set up. Java EE application servers allow applications to set up administrative objects through XML files. Most products, including GlassFish, have a separate web-based administration console where operations can set up connection factories and connection pools.. In the Java EE specification, this is the role of the Deployer and Administrator (see Online Chapter, Moving Java EE.next to the Cloud).
JMS defines two administrative objects: javax.jms.ConnectionFactory and javax.jms.Destination.
The connection factory is the provider object that creates a connection to the JMS provider and a destination address in order to send a message. The interface responsible for supplying connections is called javax.jms.ConnectionFactory
. The connection factory is rather similar to javax.sql.DataSource
in the JDBC specification. DataSource
provides a database connection to a relational database. ConnectionFactory
provides a connection to the messaging system conduit.
Here is the interface definition:
package javax.jms; public interface ConnectionFactory { Connection createConnection() throws JMSException; Connection createConnection( String userName, String password) throws JMSException; /* Since JMS 2.0 */ JMSContext createContext(); JMSContext createContext( String userName, String password); JMSContext createContext( String userName, String password, int sessionMode); JMSContext createContext(int sessionMode); }
In the JMS 1.1 standard, the connection factory provides access to the javax.jms.Connection
object, which is the connection that represents either a queue (P2P) or topic (pub-sub).
Since Java EE 7 and JMS 2.0, connections to the messaging systems are now preferred through the new overloaded methods on createContext()
, which returns javax.jms.JMSContext
. The other methods are overloaded variations of them. The createContext(
method creates or retrieves a default JMS connection for the application server, which is really only useful for testing. The other take a combination of the login credentials and optionally accept a connection mode. See the later section on JMSContexts for the modes.
It is worth noting that javax.jms.Connection
are now compatible with Java SE 7 try-resource syntax as they extend java.lang.AutoCloseable
.
JMS 2.0 defines a default connection factory for Java EE full-provide application servers which your application can use, which means all Java EE 7 applications have definite access to a JMS connection. The object is called JNDI (java:comp/jmsDefaultConnectionFactory)
. Your application can always assume the default connection factory is there and use it. Retrieving the default connection factory is as simple as injecting the @Inject
annotation on the JMSContext
object.
This is a code snippet that injects JMSConnectionFactory
into a bean:
@Inject @JMSConnectionFactory("java:comp/defaultJMSConnectionFactory") private JMSContext context;
The message destination is the abstraction over the conduit that connect the Java application to the messaging system. A message destination has provider address to identify its location in the messaging system. The address allows other endpoint to send message over the channel. A JMS destination is called a queue for point-to-point communications, otherwise it is called topic for the pub-sub model.
In JMS, this representation is the marker interface javax.jms.Destination
, which happens to be a super interface of javax.jms.Queue
and javax.jms.Topic
.
Here is a condensed definition of these interfaces:
package javax.jms; public interface Destination { } public interface Queue extends Destination { String getQueueName() throws JMSException; String toString(); } public interface Topic extends Destination { String getTopicName() throws JMSException; String toString(); }
Message destinations were introduced in JMS 1.1 in order to unify the two messaging models.
javax.jms.JMSContext
is the main interface in the simplified JMS 2.0 specification. The intention of this new interface was to merge the two different ways of configuring and administrating JMS clients in the previous specifications.
In the earlier specification, there were a lot of different objects involved in order to make a connection to the JMS provider, create a message destination, create a message, and send the message on the connection.
For new applications with JMS 2.0, a developer simply injects JMSContext
into their code, especially if it runs inside a supporting Java EE 7 application server.
The Java interface JMSContext
defines a number of important methods:
The JMSContext
interface also declares static constants, which are useful for controlling message acknowledgement behavior in the downstream system. These are called
delivery modes:
The JMSContext
is a very useful instance in a server-side application because it allows the developer to rely on annotations. The best way to get a JMSContext
is to inject it into the application logic. Inside a Java EE environment, there are restrictions on calling certain methods that would interfere with the application server that can cause undefined behavior. An exception is thrown if you attempt to call commit()
, rollback(),
or recover()
on a message destination. If these calls were allowed, then they could be detrimental to the operation of the Java EE server, because suddenly the application could take ownership over internal managed resources when it is not supposed to.
On the client side, the developer must create or retrieve a JMSContext manually. Java EE 7 does not define standalone behavior. Perhaps this is a weakness of the current JMS 2.0 specification. It should be possible to inject a JMSContext
in a Java SE application by using a standalone CDI container such as JBoss Weld.
JMSContext in a Java EE server
JMSContext
behaves differently inside an application running on a Java EE server in comparison to a Java SE environment. Inside a Java EE application server or EJB server, the JMSContext
can be injected into the application. For an application running under the web profile server with a JMS provider extension, the JMSContext
can also be injected into the application. The benefit of having only one object is obviously beneficial to dependency injection. The application server takes care of creating, opening or closing the context.
There are two ways to retrieve a JMSContext
:
JMSContext
from ConnectionFactory
by simply calling the createContext()
method. This allows a Java SE application to only have one object to get a JMS connection. Once the application is finished with the context, simply call close().
@javax.annotation.Inject
to inject the JMSContext
into the class.18.188.152.136