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
]