Skip to content

Toro Cloud Dev Center


JMS listener endpoint

The JMS listener endpoint enables developers to write applications that react to messages received via Java Message Service (JMS) - a specification for implementing reliable, asynchronous, loosely-coupled communications. Apache ActiveMQ is Martini's default message broker, however it can be configured to use ActiveMQ Artemis or RabbitMQ instead.

Ad hoc message publishing

JMS messages can be sent by right-clicking on an instance in the Navigator view or Martini Desktop or Online, and choosing Send JMS Message. This prompts a dialog where you can choose a destination and the content you would like to send.

Send JMS Message dialog

Properties

General configuration

Property Default Description
Name (required) The name of the endpoint.
Service (required) The service to execute when the endpoint is triggered.
Run As Anonymous The user to run the service in behalf of. This is logged to Tracker.
Document Type <Name of endpoint type> The document type to be used when adding documents to Tracker as this endpoint is triggered.
Auto Start true Whether or not to automatically start the endpoint upon package startup.
Log To Tracker false Flag determining whether executions should be logged to Tracker.
Replicated true If this endpoint is configured on a Martini instance that's running in a cluster, replicated will determine whether to run the endpoint on all instances, or only the elected leader node in the cluster. When this is checked, all instances will run the endpoint. When it's unchecked, only the leader node will run the endpoint.

JMS listener-specific configuration

Properties Default Description
Destination (required) The JMS destination this endpoint should listen to.
Transaction Safe false Flag determining whether the service invocation should be wrapped in a transaction.
Acknowledgement Auto acknowledge For non-transacted JMS session, this property determines how JMS messages are acknowledged.
Durable false Durable subscription ensures that this endpoint receives messages across restarts.
Concurrent Consumers 1 The number of concurrent consumers to create for the specified destination.
Max Concurrent Consumers 1 The number of maximum concurrent consumers to create for the specified destination. If this is higher than the Concurrent Consumers, new consumers will be dynamically created when enough incoming messages are encountered.

Multiple concurrent consumers for a topic may consume the same message

When the total number of concurrent consumers is greater than 1 for a topic destination, it may lead to concurrent consumption of the same message if your broker is not JMS 2.0 compliant. In JMS 1.1, a topic subscription was not permitted to have more than one consumer at a time. Check your vendor setup measures if it clearly allows this setting.

Multiple concurrent consumers may lose order of messages

Increasing the number of concurrent consumers is recommended to scale the consumption of JMS messages. However, guaranteed ordering of messages is lost when configured to do so. For low-volume queues, it is advisable to stick with 1 consumer.

Service

When the endpoint is triggered, the following variables are exposed to the configured service:

General parameters

Name Type Description
$trackerId java.lang.String The Tracker document internal ID. If the endpoint was configured to not track, this value will be null.
$tracker io.toro.martini.tracker.Tracker The Tracker object. If the endpoint was configured to not track, this value will be null.
martiniPackage MartiniPackage The Martini package that contains the endpoint.
parameters java.util.Map A map containing all the endpoint specific parameters.
properties java.util.Map A map containing containing all the properties associated with the endpoint.

JMS listener-specific parameters

Name Type Description
message javax.jms.Message The message that triggered this endpoint.
destination javax.jms.Destination The destination of the message.
destinationName java.lang.String The destination of the message, in String.
replyTo javax.jms.Destination The reply destination, or null.
correlationId java.lang.String The message correlation ID, or null.

Depending on the type of message received by the endpoint, Martini exposes additional variables:

Example

Gloop as service

Consider this application using Gloop. This is invoked when the JMS destination configured for the endpoint receives a Message.

JMS-replying service

(4) This line sends back the received message as the reply.

Want more examples?

The distribution ships with a Martini package called examples, which contains services (including the above example) demonstrating more use cases.

Groovy script as service

Consider this Groovy script that simply prints the available variables in the context:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
[
    'properties',
    'parameters',
    'martiniPackage',
    'properties',
    'destination',
    'destinationName',
    'message',
    'replyTo',
    'correlationId',
    '$trackerId',
    '$tracker',

    'content'].each {

    println "$it\t : " + this[it]
}

When the endpoint is triggered - in this case, a JMS message sent to the configured destination - the console will show logs similar to the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
properites   : [ <omitted> ]
parameters   : [ <omitted> ]
martiniPackage   : martiniPackage [name=examples]
properties   : [ <omitted> ]
destination  : topic://toroMartini.universe-questions
destinationName  : toroMartini.universe-questions
message  : ActiveMQTextMessage {commandId = 1260, responseRequired = false, messageId = ID:TORO-MBP06-53671-1523846101995-2:1:7:4:2, originalDestination = null, originalTransactionId = null, producerId = ID:TORO-MBP06-53671-1523846101995-2:1:7:4, destination = topic://toroMartini.universe-questions, transactionId = null, expiration = 0, timestamp = 1523851868500, arrival = 0, brokerInTime = 1523851868500, brokerOutTime = 1523851868502, correlationId = null, replyTo = null, persistent = true, type = null, priority = 4, groupID = null, groupSequence = 0, targetConsumerId = null, compressed = false, userID = null, content = null, marshalledProperties = null, dataStructure = null, redeliveryCounter = 0, size = 1036, properties = null, readOnlyProperties = true, readOnlyBody = true, droppable = false, jmsXGroupFirstForConsumer = false, text = "What's the answer to life?"}
replyTo  : null
correlationId    : null
$trackerId   : null
$tracker     : null
content  : "What's the answer to life?"

Groovy method as service

We can configure the endpoint to use Groovy methods as services as well. Consider the following example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class DeepThought {

  void respond(def message) {
    if (message.getReplyTo() == null) {
      "I can't answer without introducing yourself.".info()
      return
    }
    message.replyWith( "42" )
  }
}

Assuming the service DeepThought#respond is configured in the endpoint, we can trigger it by sending a message to the destination topic://toroMartini.universe-questions. By invoking the following publisher:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Hitchhiker {

  final def destination = "topic://universe-questions"

  void ask() {
      def question = "What's the answer to the ultimate question of life, the universe and everything?"
      question.publishTo(destination) { reply ->
          "Uhm..'${reply.text}'?".info()
      }
  }
}

Like the info() method, replyWith() and publishTo() are extension methods for JMS operations. The ask() method publishes a JMS message, and waits for a reply - which DeepThought complies with "42".

Destination prefixes

Martini uses the configured application property jms.destination-prefix as the prefix (or "toroMartini", if none provided) for its JMS destinations. While publishing, the prefix should be omitted - like in this case: topic://universe-questions.