X

Simple JMS 2.0 Sample - @JMSDestinationDefinition and Synchronous Message Receive: TOTD #191

Guest Author


href="https://blogs.oracle.com/arungupta/entry/jms_2_0_early_draft">JMS
2.0 Early Draft - Simplified API Sample Code explained some of
the changes that are made in JMS 2.0 to "catch up" with all the
changes in the Java SE and EE platform over the past few years. The
main goals of JMS 2.0 are:
  • Changes to improve ease-of-development
  • Clarification of relationship between JMS and other Java EE
    specifications
  • Definition of a new mandatory API to allow any JMS provider to
    be integrated with any other Java EE Application Server
  • Other enhancements as requested by the community


This Tip Of The Day (TOTD) will
explain a simple sample showing how to send a message and receive it
synchronously. The complete source code for the sample can be href="//cdn.app.compendium.com/uploads/user/e7c690e8-6ff9-102a-ac6d-e4aebca50425/f4a5b21d-66fa-4885-92bf-c4e81c06d916/File/14fac9dfbcc0132808096e88d5f80a87/totd191_send_message_sample.zip">downloaded
here.



This is a Stateless EJB that has a single method to send a message.


@Stateless
public class MessageSender {

@Inject
JMSContext context;

@Resource(mappedName="java:global/jms/myQueue")
Queue queue;

public void sendMessage(String message) {
context.createProducer().send(queue, message);
}
}

In this code:
  • JMSContext is a new interface introduced in the
    simplified API in JMS 2.0. This combines in a single object the
    functionality of two separate objects from the JMS 1.1 API: a Connection
    and a Session.



    A JMSContext may be injected in the Java EE web
    and EJB containers using the @Inject annotation. A
    JMSContext created in this way is described as
    being container-managed. A container-managed JMSContext
    will be closed automatically by the container. Applications
    running in the Java EE web and EJB containers are not permitted
    to create more than one active session on a connection. This
    allows to combine them in a single object offering a simpler
    API. This is the recommended way for creating JMS context in
    Java EE applications.



    A JMSContext may be created by calling one of the
    createContext methods on a ConnectionFactory.
    A JMSContext that is created this way is described
    as being application-managed. An application-managed JMSContext
    must be closed when no longer needed by calling its close
    method. Applications running in a Java SE environment or in the
    Java EE application client container are permitted to create
    multiple active sessions on the same connection. This allows the
    same physical connection to be used in multiple threads
    simultaneously. The createContext method is
    recommended to create JMSContext in such
    applications.

  • The Java EE Platform requires a preconfigured JMS ConnectionFactory
    accessible to the application under the JNDI name:
    java:comp/DefaultJMSConnectionFactory

    The annotation @JMSConnectionFactory may be used
    to specify the JNDI lookup name of the ConnectionFactory
    used to create the JMSContext as:
    @Inject
    @JMSConnectionFactory("java:comp/DefaultJMSConnectionFactory")
    JMSContext context;

    If no lookup name is specified or the @JMSConnectionFactory
    is omitted then the platform default JMS connection factory will
    be used. The above code fragment is equivalent to:
    @Inject
    JMSContext context;

  • @Resource defines a dependency on a resource
    needed by the application. In this case, it specifies the JNDI
    name of the destination to which the message needs to be sent.

  • When an application needs to send messages it uses the createProducer
    method to create JMSProducer which provides
    methods to configure and send messages. The various setProperty
    methods are used to set property on the message being sent.
    There are several other methods like setPriority,
    setDeliveryDelay, and setTimeToLive
    to set other quality-of-service attributes for the message being
    sent.



    Messages may be sent synchronously or asynchronously.
    Synchronous messages are sent using one of the send
    methods. Asynchronous messages are sent using setAsync
    method and assigning a CompletionListener. When
    the message has been successfully sent the JMS provider invokes
    the callback method onCompletion on the CompletionListener
    object. If the message is not sent for some reason or an
    acknowledgement from the remote JMS server is not received then
    onException callback method is called. An
    asynchronous send is not permitted in a Java EE application.




This is a Stateless EJB that has a single method to receive the
message synchronously.


@Stateless
public class MessageReceiverSync {

@Inject
private JMSContext context;

@Resource(mappedName="java:global/jms/myQueue")
Queue myQueue;

public String receiveMessage() {
String message = context.createConsumer(myQueue).receiveBody(String.class, 1000);
return "Received " + message;
}
}



In this code:
  • JMSContext referring to the preconfigured JMS ConnectionFactory
    is injected by the container.
  • @Resource defines a dependency on the JMS
    destination on which the message is received.
  • When an application needs to receive messages it uses one of
    the several createConsumer or createDurableConsumer
    methods to create a JMSConsumer. createConsumer
    creates a non-durable subscription on the specified destination.
    This means that a client will only see the messages published on
    the destination when the subscriber is active. If the subscriber
    is not active, it is missing messages published on the
    destination. createDurableConsumer creates an
    unshared durable subscription of a specified topic and creates a
    consumer on that subscription. This allows the subscriber will
    receive all messages published on a topic, including the ones
    published when there is no active consumer associated with it.
    The JMS provider retains a record of this durable subscription
    and ensures that all messages from the topic's publishers are
    retained until they are delivered to, and acknowledged by, a
    consumer on this durable subscription or until they have
    expired.



    A JMSConsumer provides methods to receive messages
    either synchronously or asynchronously. receive
    methods are used for synchronous delivery of the messages. A MessageListener
    object may be registered with the client for asynchronous
    delivery of the messages. onMessage method of the
    MessageListener object are called as messages are
    received. Asynchronous delivery of messages will not work until
    MQ-264 is
    fixed.


Next is a Servlet that ties all the pieces together. It defines the
JMS Destination and send and receive the message using the
previously defined EJBs.


@JMSDestinationDefinition(name = "java:global/jms/myQueue",
resourceAdapterName = "jmsra",
className = "javax.jms.Queue",
destinationName="queue1234",
description="My Queue")
@WebServlet(urlPatterns = {"/TestServlet"})
public class TestServlet extends HttpServlet {

@EJB MessageSender sender;

@EJB MessageReceiverSync receiver;


void doGet(HttpServletRequest request, HttpServletResponse response) {
. . .
String m = "Hello there";
sender.sendMessage(m);
out.format("Message sent: %1$s.<br>", m);
out.println("Receiving message...<br>");
String message = receiver.receiveMessage();
out.println("Message received: " + message);
. . .
}

In this code:
  • @JMSDestinationDefinition defines the JMS destination. The name
    attribute defines the JNDI name of the destination being
    defined, destinationName attribute defines the
    name of the queue or topic, and className
    attribute defines the JMS destination implementation class name.
  • doGet method uses the injected EJBs to send and
    receive the message.


The complete source code for the sample can be href="//cdn.app.compendium.com/uploads/user/e7c690e8-6ff9-102a-ac6d-e4aebca50425/f4a5b21d-66fa-4885-92bf-c4e81c06d916/File/14fac9dfbcc0132808096e88d5f80a87/totd191_send_message_sample.zip">downloaded
here. "mvn package" and deploy the generated WAR file.



Download href="http://dlc.sun.com.edgesuite.net/glassfish/4.0/promoted/">GlassFish
4 build 68 onwards and try this sample today!



The latest progress on JMS 2.0 can be tracked at:
  • jms-spec.java.net
  • JSR 343 EG archive at href="http://java.net/projects/jms-spec/lists/jsr343-experts/archive">jsr343-experts
  • Discussion at href="http://java.net/projects/jms-spec/lists/users/archive">users@jms-spec
  • JSR 343
    • href="http://java.net/projects/jms-spec/pages/Home#Latest_draft_specification_and_javadocs">Latest
      Specification

    • Latest Javadocs (href="http://java.net/projects/jms-spec/sources/repository/content/jms2.0/target/jms-2.0-javadoc.jar">download,
      href="http://jms-spec.java.net/2.0-SNAPSHOT/apidocs/index.html">online)

Help us make JMS 2.0 better, simpler, easier to use. Join href="http://java.net/projects/jms-spec/lists/users/archive">users@jms-spec
and contribute!


Join the discussion

Comments ( 8 )
  • guest Thursday, December 27, 2012

    Thanks for this post Arun.

    A couple of mont ago I proposed to also have a default Queue and Topic so we could write the following without any other declaration (http://java.net/projects/javaee-spec/lists/users/archive/2012-11/message/29). But it looks like it wasn't approved. A shame, I still think it would have make JMS even easier. Hope we will have more default producers in Java EE :

    @Stateless

    public class MessageReceiverSync {

    @Inject

    private JMSContext context;

    @Inject

    Queue myQueue;

    public String receiveMessage() {

    String message = context.createConsumer(myQueue).receiveBody(String.class, 1000);

    return "Received " + message;

    }

    }


  • guest Thursday, December 27, 2012

    Excellent! Good Job!


  • Arun Gupta Thursday, December 27, 2012

    Thanks Antonio!

    Following up with Nigel and gang and will let you know.


  • Sean Landsman Saturday, March 9, 2013

    Hi Arun

    I've just tried this on a new install of Glassfish b78 and I get the following error when I try to deploy the code above:

    ~/development/glassfish4_b78/glassfish/bin/asadmin deploy target/send-message-1.0-SNAPSHOT.war

    remote failure: Error occurred during deployment: Exception while loading the app : CDI deployment failure:Exception List with 2 exceptions:

    Exception 0 :

    org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [JMSContext] with qualifiers [@Default] at injection point [[BackedAnnotatedField] @Inject @JMSConnectionFactory org.sample.sendmessage.MessageSender.context]

    Any ideas how to resolve this issue? Should I file a bug report?

    thanks,

    Sean


  • Arun Gupta Sunday, March 10, 2013

    Sean,

    I noticed this issue on 78 as well couple of days back and asked around. It seems it'll be fixed in Beta6 coming in about 2 weeks. Please go ahead and file a bug for tracking purpose.

    Also add me in the CC list (java.net id: arungupta).

    Arun


  • guest Wednesday, March 13, 2013

    Hi Arun,

    Looks like the issue has already been raised - and fixed: http://java.net/jira/browse/GLASSFISH-19846


  • Miles Elam Wednesday, March 20, 2013

    Don't see the point in the createProducer() and createConsumer(Queue) methods. Why not context.send(queue, message) and receiveBody(queue, String.class, 1000)? Why the extra boilerplate?


  • Murali Thursday, June 27, 2013
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha