After summarizing the JMS Tutorial, I wrote a number of code samples to try to cover the essentials and experiment to make sure things worked the way I thought. This included at least the following:
- Using queues and topics
- Implementing a durable topic subscriber
- Using local transactions and experimenting with rolling back
- Using non-transaction mode and experimenting with what happens when messages not acknowledged
- Sending multiple messages with different priorities
- Sending messages with a timeout and making sure it actually does
- Using receive in blocking mode, blocing mode with a timeout, and listeners for synchronous and asynchronous
- Playing with persistent and non-persistent modes and restarting ActiveMQ to see how it is handled
- Having multiple consumers for topics vs. queues and seeing if all or some of them get the messages
- Playing with the request/reply pattern
It is that last item that took a little longer to wrap my head around as I was mixing up the various destinations.
Invoking a method and getting the return value back is one example of the request/reply pattern. Invoking a RPC via web services or even old-style CORBA is another example. All of these are synchronous. As JMS is inherently asynchronous, you can use it to create asynchronous requests and replies.
One approach would be to set up a queue that the requester can send the request message to, and another queue that the replier can use to send the reply message back to the requester. However, this does not scale. What if you have need to add another requester? Another reply queue would need to be set up administratively, and the replier logic would need to be updated to differentiate between the two requesters.
Addtionally, what if the one requester sends three requests to the replier before any of the replies come back? There needs to be some way to be able to correlate the replies back to the original requests.
JMS offers the ability to create temporary queues and topics. Here is how it works:
- The requester creates the temporary queue, and adds the queue info to the request message
- The requester sends the request message to the queue used by the replier
- The requester waits for incoming reply messages on the temporary queue it created
- The replier receives the request message on the normal queue it is listening on, processes it, creates the reply message, and uses the message’s temporary queue information to know what queue to send the reply message to
- The replier includes the original id of the request message so that the requester can correlate this reply with a particular request, in case the requester has made other requests to this replier
- The replier sends the reply message to the temporary queue
- The requester retrieves the request from the temporary queue, and uses the original request id from the reply to properly correlate the reply with its request
- Once done using the system, the requester can then delete the temporary queue
Here is part of the requester code:
requester = session.createProducer(replierDestination); TextMessage requestMessage = session.createTextMessage(); requestMessage.setText("This is the request"); TemporaryQueue temporaryQueue = session.createTemporaryQueue(); MessageConsumer responseConsumer = session.createConsumer(temporaryQueue); requestMessage.setJMSReplyTo(temporaryQueue); request.send(requestMessage); TextMessage reply = (TextMessage) responseConsumer.receive(); String correlationId = reply.getJMSCorrelationID();
Here is part of the replier code:
TextMessage request = (TextMessage)replier.receive(); producer = session.createProducer(request.getJMSReplyTo()); Message response = session.createTextMessage("processed message: " + request.getText()); response.setJMSCorrelationID(request.getJMSMessageID()); producer.send(response);