Introduction

This plugin makes it easy to both send and receive JMS messages inside a Grails application.

Installation

The plugin is available on Maven Central and should be a dependency like this:

dependencies {
    implementation 'io.github.gpc:jms:4.0.2'
}

In older versions of Gradle, replace implementation with compile

Examples

The following are some simple examples to give you a feel for the plugin.

Service Queue Listeners

class ListeningService {

    static exposes = ['jms']

    def onMessage(message) {
        assert message == 1
    }
}
class SomeController {

    def jmsService

    def someAction() {
        jmsService.send(service: 'listening', 1)
    }
}

Service Method Queue Listeners

import grails.plugin.jms.Queue

class ListeningService {

    static exposes = ['jms']

    @Queue
    def receive(message) {
        assert message == 1
    }
}
class SomeController {

    def jmsService

    def someAction() {
        jmsService.send(service: 'listening', method: 'receive', 1)
    }
}

Topic Listeners

import grails.plugin.jms.Subscriber

class ListeningService {

    static exposes = ['jms']

    @Subscriber
    def newMessages(message) {
        assert message == 1
    }
}
class SomeController {

    def jmsService

    def someAction() {
        jmsService.send(topic: 'newMessages', 1)
    }
}

Post Processing Messages

import javax.jms.Message

class SomeController {

    def jmsService

    def someAction() {
        jmsService.send(service: 'initial', 1) { Message msg ->
            msg.JMSReplyTo = createDestination(service: 'reply')
            msg
        }
    }
}

Spring JMS

This plugin is built on top of Spring’s JMS support There are some core Spring JMS concepts that you should at least be aware of.

JmsTemplate

Spring provides JmsTemplate which is what this plugin uses to send messages.

MessageConverter

The MessageConverter abstraction conveniently allows pluggable message conversion. By default, this plugin uses Spring’s SimpleMessageConverter which handles standard message payloads and JMS Message types.

MessageListenerContainer

A listener container polls a JMS destination for messages. Each listener (i.e. each service method that receives JMS messages) has its own listener container.

This plugin uses the DefaultMessageListenerContainer implementation.

MessageListenerAdapter

A listener adapter connects a listener container to the actual destination of the message. It handles message conversion amongst other things.

By default, this plugin uses a MessageListenerAdapter subclass that is Grails aware and sets up the necessary Grails environment for listener methods (e.g. Hibernate session).

Plugging In A JMS Provider

The plugin does not include a JMS provider so you must install and configure your own.

All you need to provide is one or more javax.jms.ConnectionFactory beans and the plugin takes care of the rest.

The plugin looks for a connection factory bean named jmsConnectionFactory.

The default SpringBoot configuration has caching enabled, which results in a bean named cachingJmsConnectionFactory being defined rather than jmsConnectionFactory.

To turn caching off, set the configuration property spring.jms.cache.enabled in application.yml like so.

spring:
    jms:
        cache:
            enabled: false

With caching disabled, there will be a jmsConnectionFactory bean defined.

If you wish to use this plugin with caching enabled, you can add the following line in resources.groovy to use a spring bean alias.

beans = {

    // ...
    springConfig.addAlias('jmsConnectionFactory', 'cachingJmsConnectionFactory')
}

ActiveMQ Example

Getting ActiveMQ up and running as your provider is very simple. All that is required is adding compile dependencies on the spring-boot-starter-activemq library and pooled-jms library in build.gradle.

// ...

dependencies {

    // ...
    compile 'io.github.gpc:jms:4.0.2'
    compile 'org.springframework.boot:spring-boot-starter-activemq'
    compile 'org.messaginghub:pooled-jms'
}

The runtime will recognize that activemq-spring is available and will auto configure the jmsConnectionFactory in the Spring application context. The org.springframework.boot.autoconfigure.jms.activemq.ActiveMQConnectionFactoryConfiguration class is what is actually configuring the jmsConnectionFactory bean. If the default factory settings are not sufficient the factory may be configured with any of the properties defined in the org.springframework.boot.autoconfigure.jms.activemq.ActiveMQProperties class by defining corresponding properties in application.yml with corresponding property names defined under spring.activmeq as shown below.

spring:
    activemq:
        brokerUrl: vm://localhost
        pool:
            enabled: true
    jms:
        cache:
            enabled: false

Note for those who want to use connection pooling: when spring.activemq.pool.enabled is true then SpringBoot will create a pooledJmsConnectionFactory bean rather than jmsConnectionFactory. You can add the following line in resources.groovy to use a spring bean alias in order to get around this.

beans = {

    // ...
    springConfig.addAlias('jmsConnectionFactory', 'pooledJmsConnectionFactory')
}

The Configuration Mechanism

JMS is a complicated topic. There are different consumption and configuration patterns. While this plugin does set some reasonable defaults, it’s very likely that you are going to need to customise these settings either globally or for specific senders or listeners.

To support this, the plugin makes configuration options available to you should you need to set it. This is achieved through the use of Spring’s abstract beans and Grails' configuration mechanism.

How it works

The configuration is controlled by the Grails application configuration under the key jms. This is merged against plugin provided defaults.

Here is what the defaults look like…​

templates:
    standard:
        connectionFactoryBean: jmsConnectionFactory
        messageConverterBean: standardJmsMessageConverter

...

That creates a map of "bean definitions" that get processed into real bean definitions.

The default config creates our standard (i.e. default) converters, jms templates for sending, and listener containers and adapters for receiving.

When sending messages with the jmsService you can specify which template to use to send the message. If none is specified, "standard" is used.

Likewise, listeners can specify which container and/or adapter bean definition to base themselves on. If none are specified, "standard" is used in both cases.

Changing Defaults

You can override the configuration defaults very easily.

Let’s suppose you do not want any message conversion on listeners. If a listener container has no messageConverter listeners will receive raw messages. So we want to override the standard listener container definition to set the messageConverter property to null.

In your application’s application.yml

jms:
    containers:
        standard:
            messageConverter: null

This definition will get merged against the plugin provided defaults to produce a standard listener container definition with messageConverter set to null.

Disabling the default dependency on the Persistence Interceptor.

If you are not using any GORM implementation such as Grails Hibernate Plugin (i.e. you uninstalled the hibernate plugin) or the GORM implementation you are using doesn’t provide a Persistence Interceptor Bean, you will have to disable the default dependency to the Persistence Interceptor Bean. You can do this by setting in the application.yml the jms.adapters.standard.persistenceInterceptorBean to null .

jms:
    adapters:
        standard:
            persistenceInterceptorBean: null

Syntax Notes

There are some noteworthy things about this config syntax.

Bean names

The beans created automatically get suffixes applied to them. Template bean names get suffixed with JmsTemplate, container beans get suffixed with JmsListenerContainer and adapter beans get suffixed with JmsListenerAdapter.

Setting Beans

To set a property to another Spring bean, simply append Bean to the property name and set the property to the name of the bean.

Here is how the standard template is defined to use the bean named jmsConnectionFactory as it’s connection factory…​

templates {
    standard {
        connectionFactoryBean = "jmsConnectionFactory"
    }
}

Setting Class

To set the class of a bean, you must use the following syntax

templates {
    standard {
        meta {
            clazz = my.org.CustomJmsTemplate
        }
    }
}

Extending Definitions

Bean definition can inherit from parents and selectively override settings.

templates {
    other {
        meta {
            parentBean = 'standardJmsTemplate'
        }
        connectionFactoryBean = "someOtherJmsConnectionFactory"
    }
}

This creates an "other" template, that inherits all of the standard settings but uses a custom connectionFactory.

Sending Messages

This plugin adds a service called jmsService to your application that can be used to send JMS messages.

The send(destination, message, jmsTemplateName, postProcessor) method.

destination

An instance of javax.jms.Destination, javax.jms.Topic , a String or a Map .

A String destination argument will be interpreted as the name of a destination queue .

A Map destination argument can be used in the following ways:

jmsService.send(queue: "aQueue", msg, "standard", null) // send to a literal queue

jmsService.send(topic: "aTopic", msg, "standard", null) // send to a literal topic

jmsService.send(service: "person", msg, "standard", null) // send to the queue '«appname».person'

jmsService.send(service: "person", method: "doIt", msg, "standard", null) // send to the queue '«appname».person.doIt'

jmsService.send(app: "remote", service: "person", method: "doIt", msg, "standard", null) // send to the queue 'remote.person.doIt'

The app/service/method convention makes a lot more sense if you read the section below on service method listener queue subscribers.

message

This is the message payload. By default this can be any Java/Groovy object or a javax.jms.Message. How it gets converted into a message is handled by the underlying jms template’s message converter.

jmsTemplateName

The name of the template that should be used to send the message. If this value is null , the standard template will be used (called "standard").

postProcessor

An optional closure that can be used to "post process" the message after it has been converted into a message but before it has been sent. This closure acts as the implementation of the postProcessMessage() method of the MessagePostProcessor class.

send() method variants

There are variations of the send() method for convenience…​

jmsService.send(destination, message) // use the standard template and no post processor
jmsService.send(destination, message, postProcessor) // use the standard template

Post Processing Messages

Message post processors can either augment the passed Message object, or create a new one. Because of this, the post processor must return the message object that is to be sent.

import javax.jms.Message

jmsService.send(topic: 'somethingHappened', 1) { Message msg ->
    msg.JMSCorrelationID = "correlate"
    msg
}

Setting destinations

Post processors can use the createDestination() method in post processor implementations to create destinations using the same API style as jmsService.send() method

jmsService.send(service: 'initial', 1) {
    it.JMSReplyTo = createDestination(service: 'reply')
    it
}

Using Other Templates

Here is an example of using a custom template that uses a different connection factory.

Example

resources.groovy

import org.apache.activemq.ActiveMQConnectionFactory
import org.springframework.jms.connection.SingleConnectionFactory

beans = {
    // used by the standard template by convention
    jmsConnectionFactory(SingleConnectionFactory) {
        targetConnectionFactory = { ActiveMQConnectionFactory cf ->
            brokerURL = 'vm://localhost'
        }
    }

    otherJmsConnectionFactory(SingleConnectionFactory) {
        targetConnectionFactory = { ActiveMQConnectionFactory cf ->
            brokerURL = // ... something else
        }
    }
}

application.yml

jms:
    templates:
        other:
            meta:
                parentBean: standardJmsTemplate
            connectionFactoryBean: otherJmsConnectionFactory // use different connection factory

Sending messages

jmsService.send(topic: "stuffHappened", message, "other")

The third argument of "other" to the send() method specifies to use the "other" template.

Receiving Messages

Service Listeners

Service Listeners

Service listeners are a convenient way to define one handler for JMS messages. The simplest service listener looks like…

class PersonService {
    static exposes = ["jms"]
    def onMessage(msg) {
        // handle message
    }
}

This will register the onMessage method as a listener for the JMS queue named «application name».person , where «application name» is the app.name key from the application.properties file.

Configuration

The following configuration parameters can be set as static variables on the service class…

|==

Property Name,Type,Default,Description destination,String,«app name».«service name»,The named destination of the listener isTopic,boolean,false,is the destination a topic ( true ) or a queue ( false ) selector,String,null,See the “Message Selector” section of http://java.sun.com/j2ee/1.4/docs/api/javax/jms/Message.html adapter,String,"standard",The adapter to use for this listener container,String,"standard",The container to use for this listener |==

class PersonService {
    static exposes = ["jms"]
    static destination = "somethingHappened"
    static isTopic = true
    static adapter = "custom"

    def onMessage(msg) {
        // handle message
    }
}

Service Method Listeners

Service Method Listeners

Another avenue is to expose specific methods as message listeners via annotations. This looks like…

import grails.plugin.jms.*

class PersonService {
   static exposes = ["jms"]

   @Queue
   def addPerson(msg) {
   }

   @Subscriber
   def somethingHappened(msg) {
   }
}

The above configuration binds the personService.addPerson() method to a queue named «app name».person.addPerson and binds the method personService.somethingHappened() as a listener to the topic named somethingHappened .

Note that you still need to expose the class via static exposes = \["jms"] .

@Queue Configuration

The following configuration parameters can be set as annotation parameters…

|==

Property Name,Type,Default,Description name,String,«app name».«service name».«method name»,The destination name for the queue selector,String,null,The message selector to apply (See the “Message Selector” section of http://java.sun.com/j2ee/1.4/docs/api/javax/jms/Message.html) adapter,String,"standard",The adapter to use for this listener container,String,"standard",The container to use for this listener |==

Example…

import grails.plugin.jms.*

class PersonService {
   static exposes = ["jms"]

   @Queue(
      name = "myQueue",
      selector = "name IS NOT NULL"
   )
   def addPerson(msg) {
   }
}

@Subscriber Configuration

The following configuration parameters can be set as annotation parameters…

|==

Property Name,Type,Default,Description topic,String,«method name»,The name of the topic to subscribe to selector,String,null,The message selector to apply (See the “Message Selector” section of [http://java.sun.com/j2ee/1.4/docs/api/javax/jms/Message.html]) adapter,String,"standard",The adapter to use for this listener container,String,"standard",The container to use for this listener |==

Example…

import grails.plugin.jms.*

class PersonService {
   static exposes = ["jms"]

   @Subscriber(topic = "aTopic")
   def somethingHappened(msg) {
   }
}

Defining the Queue names and Subscriber topics through configuration.

You can specify the names of the given destinations , queues and topics , described through the Queue and Subscriber annotations by prefixing the key with a Dollar sign ( $ ). The key needs to be available through the Config.groovy file in the jms.destinations space, if its not available an error will be thrown.

Example…

PersonService.groovy

import grails.plugin.jms.*

class PersonService {
   static exposes = ["jms"]

   @Subscriber(topic = '$topic.key.in.config')
   def somethingHappened(msg) {
   }

   @Queue(name = '$queue.key.in.config')
   def someWorkToDo(msg) {
   }
}

Config.groovy

jms {
    destinations {
        //Name of the topic in the JMS server will be person.somethingHappened
        topic.key.in.config = 'person.somethingHappened'

        //Name of the queue in the JMS server will be person.sendSomeWork
        queue.key.in.config = 'person.sendSomeWork'
    }
}

Listener Return Values

Spring’s MessageListenerAdapter adds some special handling of listener method return values.

From MessageListenerAdapter’s JavaDoc: "If a target listener method returns a non-null object (typically of a message content type such as String or byte array), it will get wrapped in a JMS Message and sent to the response destination (either the JMS "reply-to" destination or a specified default destination)."

Be careful with Groovy’s implicit return mechanism; ensure that you return null explicitly if you want nothing to be sent to the reply destination. If you accidentally return a value that cannot be sent to the reply destination, you may have odd side effects like messages never being removed from the queue (due to implicit rollbacks!).

Using Other Containers Or Adapters

Here is an example of using a container and adapter other than standard.

Example

resources.groovy

import org.apache.activemq.ActiveMQConnectionFactory
import org.springframework.jms.connection.SingleConnectionFactory

beans = {
    // used by the standard template by convention
    jmsConnectionFactory(SingleConnectionFactory) {
        targetConnectionFactory = { ActiveMQConnectionFactory cf ->
            brokerURL = 'vm://localhost'
        }
    }

    otherJmsConnectionFactory(SingleConnectionFactory) {
        targetConnectionFactory = { ActiveMQConnectionFactory cf ->
            brokerURL = // ... something else
        }
    }
}

Config.groovy

jms {
    containers {
        other {
            meta {
                parentBean = 'standardJmsListenerContainer'
            }
            concurrentConsumers = 5
            connectionFactoryBean = "otherJmsConnectionFactory"
        }
    }
    adapters {
        other {
            meta {
                parentBean = 'standardJmsListenerAdapter'
            }
            messageConverter = null // do no message conversion
        }
    }
}

Sending messages

class ListeningService {
    static exposes = ["jms"]
    static adapter = "other"
    static container = "other"

    def onMessage(msg) {
        // handle message
    }
}
import grails.plugin.jms.*

class ListeningService {
    static exposes = ["jms"]

    @Queue(adapter = "other", container = "other")
    def receive(msg) {
        // handle message
    }
}

Receiving Messages With Selectors

As mentioned in Sending Messages this plugin adds a service called jmsService to your application. In addition to the methods already described in other chapters the jmsService has the following methods that can be used to receive a selected message as a single operation without a Service Listener.

The receiveSelected(destination, selector, timeout, jmsTemplateBeanName)

destination

An instance of javax.jms.Destination , javax.jms.Topic , a String or a Map .

A String destination argument will be interpreted as the name of a destination queue .

A Map destination argument can be used in the following ways:

// Expect/Receive a message with a *selector* on a literal queue waiting up to the given *timeout*.
// Will return the converted message or null if the message was not available.
jmsService.receiveSelected(queue: "aQueue", selector, timeout, "standard")


// Expect/Receive a message with a *selector* on a literal topic waiting up to the given *timeout*.
// Will return the converted message or null if the message was not available.
jmsService.receiveSelected(topic: "aTopic", selector, timeout, "standard")


// Expect/Receive a message with a *selector* on the queue '«appname».person' waiting up to the given *timeout*.
// Will return the converted message or null if the message was not available.
jmsService.receiveSelected(service: "person", selector, timeout, "standard")

// Expect/Receive a message with a *selector* on the queue '«appname».person.doIt' waiting up to the given *timeout*.
// Will return the converted message or null if the message was not available.
jmsService.receiveSelected(service: "person", method: "doIt", selector, timeout, "standard")


// Expect/Receive a message with a *selector* on the queue 'remote.person.doIt' waiting up to the given *timeout*.
// Will return the converted message or null if the message was not available.
jmsService.receiveSelected(app: "remote", service: "person", method: "doIt", selector, timeout, "standard")

selector

This is the message selector as described by the JMS Specification. In a nutshell a message selector lets a client specify a statement, which is similar to an SQL92 statement, that will be used to filter messages through the values of their message headers and message properties. "Only messages whose header and property values match the selector are delivered". As described in the JMS Specification what it means for a message not to be delivered depends on the MessageConsumer being used. It is important to mention that the selectors can only access header or properties but will not be able to access any message body values.

References

timeout

A long value that specifies the amount of milliseconds that this call should wait until desisting and returning null.

jmsTemplateName

The name of the template that should be used to send the message. If this value is null , the standard template will be used (called "standard").

There are variations of the receiveSelected() method for convenience…​

receiveSelected() method variants

jmsService.receiveSelected(destination, selector) // use the default timeout and standard template
jmsService.receiveSelected(destination, selector, timeout) // use the standard template

Specifying a timeout through configuration or the template.

If no timeout is specified the JmsService uses a 500 millisecond timeout. You can also specify a timeout through the Config.groovy file.

//Specifying a 100 milliseconds timeout
jms.receiveTimeout=100l

Or if you are providing a custom JmsTemplate through its receiveTimeout attribute.

**Note: Both timeouts will be ignored if set to zero, the only way of setting a zero timeout would be by passing such timeout as an argument to the call.

The receiveSelectedAsync(destination, selector, timeout, jmsTemplateBeanName)

This methods provides a variant to the receiveSelected method, the difference is that this method will execute the request asynchronously by wrapping a call to the receiveSelected within an Executor Service (see java.util.concurrent.ExecutorService in your JDK API 1.5+ ).

Some examples..

// Expect/Receive a message with a *selector* on a literal queue waiting up to the given *timeout*.
// Will return a java.util.concurrent.Future that holds the result of the asynchronous execution.
java.util.concurrent.Future afuture = jmsService.receiveSelectedAsync(queue: "aQueue", selector, timeout, "standard")

// Expect/Receive a message with a *selector* on a literal topic waiting up to the given *timeout*.
// Will return a java.util.concurrent.Future that holds the result of the asynchronous execution.
java.util.concurrent.Future afuture = jmsService.receiveSelectedAsync(topic: "aTopic", selector, timeout, "standard")

Receiving Methods Added To Controllers And Services

The methods described below are not supported in the current 2.0.0 milestone but will be added soon.

The plugin will inject the following methods to Controllers and Services.

Synchronous calls.

// Expect/Receive a message with a *selector* on a literal queue waiting up to the given *timeout*.
// Will return the converted message or null if the message was not available.
def msg =receiveSelectedJMSMessage(queue: "aQueue", selector, timeout, "standard")


// Expect/Receive a message with a *selector* on a literal topic waiting up to the given *timeout*.
// Will return the converted message or null if the message was not available.
def msg =receiveSelectedJMSMessage(topic: "aTopic", selector, timeout, "standard")


// Expect/Receive a message with a *selector* on the queue '«appname».person' waiting up to the given *timeout*.
// Will return the converted message or null if the message was not available.
def msg =receiveSelectedJMSMessage(service: "person", selector, timeout, "standard")

// Expect/Receive a message with a *selector* on the queue '«appname».person.doIt' waiting up to the given *timeout*.
// Will return the converted message or null if the message was not available.
def msg =receiveSelectedJMSMessage(service: "person", method: "doIt", selector, timeout, "standard")


// Expect/Receive a message with a *selector* on the queue 'remote.person.doIt' waiting up to the given *timeout*.
// Will return the converted message or null if the message was not available.
def msg = receiveSelectedJMSMessage(app: "remote", service: "person", method: "doIt", selector, timeout, "standard")

Asynchronous calls.

// Expect/Receive a message with a *selector* on a literal queue waiting up to the given *timeout*.
// Will return a java.util.concurrent.Future wrapping the result the task.
def afuture = receiveSelectedAsyncJMSMessage(queue: "aQueue", selector, timeout, "standard")


// Expect/Receive a message with a *selector* on a literal topic waiting up to the given *timeout*.
// Will return a java.util.concurrent.Future wrapping the result the task.
def afuture = receiveSelectedAsyncJMSMessage(topic: "aTopic", selector, timeout, "standard")


// Expect/Receive a message with a *selector* on the queue '«appname».person' waiting up to the given *timeout*.
// Will return a java.util.concurrent.Future wrapping the result the task.
def afuture = receiveSelectedAsyncJMSMessage(service: "person", selector, timeout, "standard")

// Expect/Receive a message with a *selector* on the queue '«appname».person.doIt' waiting up to the given *timeout*.
// Will return a java.util.concurrent.Future wrapping the result the task.
def afuture = receiveSelectedAsyncJMSMessage(service: "person", method: "doIt", selector, timeout, "standard")


// Expect/Receive a message with a *selector* on the queue 'remote.person.doIt' waiting up to the given *timeout*.
// Will return a java.util.concurrent.Future wrapping the result the task.
def afuture = receiveSelectedAsyncJMSMessage(app: "remote", service: "person", method: "doIt", selector, timeout, "standard")

*Note: a afuture.get() will return the *message.

Specifying your own Executor for Async. Receivers using Spring IoC.

beans = {
     jmsAsyncReceiverExecutor( java.util.concurrent.Executors ) { executors ->
        executors.factoryMethod = "newFixedThreadPool"
        executors.constructorArgs = << 5 >>
    }
}

Browsing Messages In A Queue

If you are looking on ways to obtain the contents of a given javax.jms.Queue without changing its state the JmsService offers a set of methods designed for this task.

The browse(destination, jmsTemplateName, browserCallback) method.

Will retrieve all messages inside the given queue at that time without changing its state i.e messages will not be consumed. This method will convert the javax.jms.Message using the JmsTemplate. If you need the javax.jms.Message you should use the browseNoConvert() method and its variants as described further on.

destination

An instance of javax.jms.Queue , a String or a Map . Needs to be a queue .

A String destination argument will be interpreted as the name of a destination queue .

A Map destination argument can be used in the following ways:

// browse literal queue
List messages = jmsService.browse(queue: "aQueue", "standard", null)

// browse the queue '«appname».person'
List messages = jmsService.browse(service: "person", "standard", null)

// browse the queue '«appname».person.doIt'
List messages = jmsService.browse(service: "person", method: "doIt", "standard", null)

// browse the queue 'remote.person.doIt'
List messages = jmsService.browse(app: "remote", service: "person", method: "doIt", "standard", null)

jmsTemplateName

The name of the template that should be used to send the message. If this value is null , the standard template will be used (called "standard").

browserCallback

An optional closure that can be used to "process" the message before being added to the returning message list. The value returned by this callback will be the one added to the returning list if such value is not null.

browse() method variants

There are variations of the browse() method for convenience…​

List messages = jmsService.browse(queue)

List messages = jmsService.browse(queue, browserCallback)

List messages = jmsService.browse(queue, jmsTemplateBeanName)

The browseNoConvert(destination, jmsTemplateName, browserCallback) method.

This method will not convert the javax.jms.Message. In other words the browserCallback:Closure will receive a javax.jms.Message or if no callback is defined a list containing javax.jms.Message instances will be returned. You can’t update the returned javax.jms.Message objects, they are read-only instances.

List messages = jmsService.browseNoConvert(queue)

//You can do the following to filter messages or use a selector through the browseSelected* methods
List messages = jmsService.browseNoConvert(queue){ javax.jms.Message msg ->
    ( msg.getStringProperty('aproperty') ? msg : null )
}

List messages = jmsService.browseNoConvert(queue, jmsTemplateBeanName)
messages.each {
    assert it instanceof javax.jms.Message
}

The browseSelected(destination, selector, jmsTemplateName, browserCallback) method.

Will retrieve messages that match the selector inside the given queue at the time of the call without changing its state i.e messages will not be consumed. This method will convert the javax.jms.Message using the JmsTemplate. If you need the javax.jms.Message you should use the browseSelectedNotConvert() method and its variants as described further on.

selector

This is the message selector as described by the JMS Specification. In a nutshell a message selector lets a client specify a statement, which is similar to an SQL92 statement, that will be used to filter messages through the values of their message headers and message properties. "Only messages whose header and property values match the selector are delivered". As described in the JMS Specification what it means for a message not to be delivered depends on the MessageConsumer being used. It is important to mention that the selectors can only access header or properties but will not be able to access any message body values.

List messages = jmsService.browseSelected(queue, " anIntProperty > 0 AND anotherProperty='a Value'")

//filtering through body content.
List messages = jmsService.browseSelected(queue, " anIntProperty > 0 AND anotherProperty='a Value'"){
    ( msg == 'avalue' ?: null )
}

List messages = jmsService.browseSelected(queue, " anIntProperty > 0 AND anotherProperty='a Value'", jmsTemplateBeanName)

The browseSelectedNotConvert(destination, selector, jmsTemplateName, browserCallback) method.

Will retrieve messages that match the selector inside the given queue at the time of the call without changing its state i.e messages will not be consumed. As the browseNoConvert this method will not convert the javax.jms.Message.

List messages = jmsService.browseSelectedNotConvert(queue, " anIntProperty > 0 AND anotherProperty='a Value'")

List messages = jmsService.browseSelectedNotConvert(queue, " anIntProperty > 0 AND anotherProperty='a Value'"){ javax.jms.Message msg ->
    return msg.JMSCorrelationID
}

List messages = jmsService.browseSelectedNotConvert(queue, " anIntProperty > 0 AND anotherProperty='a Value'", jmsTemplateBeanName)
messages.each {
    assert it instanceof javax.jms.Message
}

Message Conversion

Both templates and adapters use a MessageConverter to convert objects into messages. By default, this plugin configures templates and adapters to use a http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/jms/support/converter/SimpleMessageConverter.html.[SimpleMessageConverter] This can be changed via the config mechanism…

jms {
    converters {
        other {
            meta {
                clazz = my.custom.MessageConverter
            }
        }
    }
    adapters {
        other {
            meta {
                parentBean = 'standardJmsListenerAdapter'
            }
            messageConverterBean = "otherJmsMessageConverter"
        }
    }
}

This would configure the “other” listener adapter to use our special message converter.

To globally use a custom message converter, you can augment the standard definition…

jms {
    converters {
        standard {
            meta {
                clazz = my.custom.MessageConverter
            }
        }
    }
}

This would cause all templates and adapters to use your custom converter.

Logging

All logging is done under the namespace grails.plugin.jms.

Disabling And Reloading

Disabling

You can globally disable all JMS functionality by setting jms.disabled to true in your application config.

For example, you could turn JMS for testing with:

environments {
    test {
        jms.disabled = true
    }
}

If JMS is disabled then no listeners are registered (so no messages will be received).

If an attempt is made to send a message while JMS is disabled you will only get a log message alerting you that the message will not be sent because JMS is disabled. This allows you to still use the sendMessage() methods or jmsService even if JMS is disabled.

Reloading

The JMS plugin has good support for hot reloading during development.

Listeners

If you make a change to a service class that is a listener during development, all existing listeners for that service will be shutdown. The service is then re-inspected for listeners that are then registered. This means you can change listener config and have it take effect without restarting your application.

Config

If any change to the JMS config is detected, all JMS functionality is torn down and then re-established with the new config. This allows you to change bean definitions (such as container or template options) and have them take effect without restarting your application.

Disabled/Enabled

You can also temporarily disable or enable JMS functionality by changing the jms.disabled config option during development and have it take effect without restarting your application.

Reference

Service

sendJMSMessage

sendJMSMessage

Purpose

Examples

foo.sendJMSMessage(object,object)

Description

Arguments:

[* object , * object ]

sendTopicJMSMessage

sendTopicJMSMessage

Purpose

Examples

foo.sendTopicJMSMessage(object,object)

Description

Arguments:

[* object , * object ]

receiveSelectedAsyncJMSMessage

receiveSelectedAsyncJMSMessage

Purpose

Examples

foo.receiveSelectedAsyncJMSMessage(object,object)

Description

Arguments:

[* object , * object ]

receiveSelectedJMSMessage

receiveSelectedJMSMessage

Purpose

Examples

foo.receiveSelectedJMSMessage(object,object)

Description

Arguments:

[* object , * object ]

sendPubSubJMSMessage

sendPubSubJMSMessage

Purpose

Examples

foo.sendPubSubJMSMessage(object,object)

Description

Arguments:

[* object , * object ]

sendQueueJMSMessage

sendQueueJMSMessage

Purpose

Examples

foo.sendQueueJMSMessage(object,object)

Description

Arguments:

[* object , * object ]