Spring JMS with
ActiveMQ
ENTERPRISE MESSAGING
Point-to-Point messaging
Queue’s
Messages are delivered once and only once
◦ Kept in the queue when listener is not available
◦ Sent to only one consumer when multiple are available
◦ Client acknowledge: wait for consumer to acknowledge before removing the message
◦ Prefetch: multiple messages can be reserved for a consumer
◦ Client timeout: if no ack or reject is received, queue will consider the consumer dead
◦ Order not guaranteed with multiple queue’s
◦ Redelivery Policy
◦ Set on client side
◦ Retry after reject or use Dead Letter Queue
Selectors and Message Groups
Selectors: Server side filtering
◦ Consumer can register a filter when connecting
◦ Filtering will be done on the server
◦ ActiveMQ supports selectors for JMS Headers and XPaths for XML Messages.
◦ No JSON
Message Groups: guaranteed ordering
◦ If messages are correlated and relative order is important
◦ Add JMSXGroupID Header
◦ All messages with the same groupId will be processed by the same consumer
◦ Group mappings are kept in-memory on the server.
◦ Need to close groups properly
◦ Could fail after a failover
Publish/Subscribe
Topics
Write one message, will be received by all subscribers.
◦ Looser coupling between producer and consumer
When no subscribers are available, the message is not saved.
Perfect for frontend – backend communication
◦ Backend post a teaserChange on a topic
◦ All frontends showing the teaserList are subscribed on that topic
Not suited for server – to – server communication
◦ Clustered service will receive each messages on every node
CompositeTopic a.k.a. VirtualTopic
Configure queue’s on the server which listen to a topic
◦ Queue’s will buffer messages when subscribers are temporary offline
◦ Messages will be received exactly once per Queue
◦ Queue per interested service
◦ Decouple producer from consumer
◦ Unless queue’s are full :-)
<virtualDestinations>
<compositeTopic name="redsys.publishing.publishfeed">
<forwardTo>
<queue physicalName="redsys.moonriser.consumer.publishing.publishfeed"/>
<queue physicalName="redsys.sitemanagement.consumer.publishing.publishfeed"/>
<queue physicalName="redsys.publishingeventprocessor.consumer.publishing.publisheventfeed"/>
</forwardTo>
</compositeTopic>
ActiveMQ Storage configuration
ActiveMQ will try to keep as much messages in memory as possible
Flushes to disk if one queue goes over 69% mem, or total mem usage goes over 80%
If disk storage gets over 80%, producers are first slowed down, then blocked
Selectors don’t work on flushed messages
◦ Need to wait until other messages are consumed
JMS API
Spring JMS
Synchronous Messaging
// Use the default destination
jmsTemplate.convertAndSend("Hello World!");
// Use a different destination
jmsTemplate.convertAndSend(“TEST.BAR”, “Hello World!”);
// Use a default destination
String textMessage1 = (String) jmsTemplate.receiveAndConvert();
// Use a different destination
String textMessage2 = (String) jmsTemplate.receiveAndConvert(“TEST.BAR”);
<bean id="connectionFactory"
class="org.apache.activemq.ActiveMQConnectionFactory"
p:brokerURL="tcp://localhost:61616" />
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"
p:connectionFactory-ref="connectionFactory"
p:defaultDestination-ref="destination" />
MessageConvertors
Maps java objects to message payloads
◦ ObjectMessage: java serialization
◦ TextMessage: Jackson (JSON) or Jaxb (XML)
◦ Jackson inserts a JMS Header with the fully qualified classname by default
◦ Can reuse Spring MVC ObjectMapper
Asynchronous Messaging
class MyMessageListener{
public Result onMessage(Action a){ // input parameters will be unmarshalled if necessary (jackson/jaxb)
// can also receive message headers as input parameters
// non-void results will be sent to a response queue
// auto-acknowledges if no exceptions thrown
}
}
<bean id="connectionFactory"
class="org.apache.activemq.ActiveMQConnectionFactory"
p:brokerURL="tcp://localhost:61616" />
<bean id="messageListener“ class="org.bsnyder.spring.jms.listener.MyMessageListener" />
<jms:listener-container concurrency="5-10“container-type=“simple|default”>
<jms:listener destination="FOO.TEST" ref="messageListener“ method=“onMessage”/>
</jms:listener-container>
MessageListenerContainer options
• DefaultMessageListenerContainer
– Spring launches threads
◦ while(…){ consumer.receive(); }
– Allows for dynamic scaling of queue consumers
– Participates in external transactions
• SimpleMessageListenerContainer
– consumer.setMessageListener(myListener);
◦ JMS Provider manages ThreadPool
– No external transaction support
– Works better with MockRunner JMS
JMS Resource Pooling
Managed ConnectionFactory
◦ JCA Resource Adapter in JBoss
◦ ConnectionFactory bound in JNDI, no pooling in application
Unmanaged broker
◦ Define pure ActiveMQConnectionFactory in Spring
◦ Wrap in org.apache.activemq.pool.PooledConnectionFactory
◦ Also possible: Spring CachingConnectionFactory
◦ Caches Sessions, too
ActiveMQ Client Configuration
ActiveMQ namespace for spring configuration
Same configuration format as server
<connectionFactory xmlns="http://activemq.apache.org/schema/core"
brokerURL="${activeMQ.brokerURL}"
userName="${activeMQ.username}"
password="${activeMQ.password}">
<redeliveryPolicyMap>
<redeliveryPolicyMap>
<defaultEntry>
<redeliveryPolicy maximumRedeliveries="2"/>
</defaultEntry>
</redeliveryPolicyMap>
</redeliveryPolicyMap>
</connectionFactory>
Transactions
JmsTransactionManager
◦ Injected in JmsTemplate and (Default)MessageListenerContainers
◦ Transactional behaviour across JMS Senders/Receivers
◦ All calls use the same JMS Session
◦ Acknowledgements are only processed when whole session is committed
JtaTransactionManager
◦ JMS Sessions are synchronized with XA Database transactions
◦ Here be dragons…
Testing
Unit tests
◦ Call message listeners directly
◦ Mockito.mock(JmsTemplate.class)
Component Integration Tests
◦ Use mockrunner-jms
System Tests
◦ Use embedded ActiveMQ broker
<amq:broker persistent="false" useJmx="false" id="embeddedBroker">
<amq:transportConnectors>
<amq:transportConnector uri="tcp://localhost:#{freePortSelector}"/>
</amq:transportConnectors>
</amq:broker>
<amq:connectionFactory id="jmsFactory" brokerURL="vm://localhost"/>
@Bean
public JMSMockObjectFactory jmsMockObjectFactory() { return new JMSMockObjectFactory();}
@Bean
public ConnectionFactory connectionFactory() { return jmsMockObjectFactory().getMockConnectionFactory();}
@Bean
public JMSTestModule jmsTestModule() { return new JMSTestModule(jmsMockObjectFactory());}
Q&A

Spring JMS and ActiveMQ

  • 1.
  • 2.
  • 3.
    Queue’s Messages are deliveredonce and only once ◦ Kept in the queue when listener is not available ◦ Sent to only one consumer when multiple are available ◦ Client acknowledge: wait for consumer to acknowledge before removing the message ◦ Prefetch: multiple messages can be reserved for a consumer ◦ Client timeout: if no ack or reject is received, queue will consider the consumer dead ◦ Order not guaranteed with multiple queue’s ◦ Redelivery Policy ◦ Set on client side ◦ Retry after reject or use Dead Letter Queue
  • 4.
    Selectors and MessageGroups Selectors: Server side filtering ◦ Consumer can register a filter when connecting ◦ Filtering will be done on the server ◦ ActiveMQ supports selectors for JMS Headers and XPaths for XML Messages. ◦ No JSON Message Groups: guaranteed ordering ◦ If messages are correlated and relative order is important ◦ Add JMSXGroupID Header ◦ All messages with the same groupId will be processed by the same consumer ◦ Group mappings are kept in-memory on the server. ◦ Need to close groups properly ◦ Could fail after a failover
  • 5.
  • 6.
    Topics Write one message,will be received by all subscribers. ◦ Looser coupling between producer and consumer When no subscribers are available, the message is not saved. Perfect for frontend – backend communication ◦ Backend post a teaserChange on a topic ◦ All frontends showing the teaserList are subscribed on that topic Not suited for server – to – server communication ◦ Clustered service will receive each messages on every node
  • 7.
    CompositeTopic a.k.a. VirtualTopic Configurequeue’s on the server which listen to a topic ◦ Queue’s will buffer messages when subscribers are temporary offline ◦ Messages will be received exactly once per Queue ◦ Queue per interested service ◦ Decouple producer from consumer ◦ Unless queue’s are full :-) <virtualDestinations> <compositeTopic name="redsys.publishing.publishfeed"> <forwardTo> <queue physicalName="redsys.moonriser.consumer.publishing.publishfeed"/> <queue physicalName="redsys.sitemanagement.consumer.publishing.publishfeed"/> <queue physicalName="redsys.publishingeventprocessor.consumer.publishing.publisheventfeed"/> </forwardTo> </compositeTopic>
  • 8.
    ActiveMQ Storage configuration ActiveMQwill try to keep as much messages in memory as possible Flushes to disk if one queue goes over 69% mem, or total mem usage goes over 80% If disk storage gets over 80%, producers are first slowed down, then blocked Selectors don’t work on flushed messages ◦ Need to wait until other messages are consumed
  • 9.
  • 10.
  • 11.
    Synchronous Messaging // Usethe default destination jmsTemplate.convertAndSend("Hello World!"); // Use a different destination jmsTemplate.convertAndSend(“TEST.BAR”, “Hello World!”); // Use a default destination String textMessage1 = (String) jmsTemplate.receiveAndConvert(); // Use a different destination String textMessage2 = (String) jmsTemplate.receiveAndConvert(“TEST.BAR”); <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory" p:brokerURL="tcp://localhost:61616" /> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate" p:connectionFactory-ref="connectionFactory" p:defaultDestination-ref="destination" />
  • 12.
    MessageConvertors Maps java objectsto message payloads ◦ ObjectMessage: java serialization ◦ TextMessage: Jackson (JSON) or Jaxb (XML) ◦ Jackson inserts a JMS Header with the fully qualified classname by default ◦ Can reuse Spring MVC ObjectMapper
  • 13.
    Asynchronous Messaging class MyMessageListener{ publicResult onMessage(Action a){ // input parameters will be unmarshalled if necessary (jackson/jaxb) // can also receive message headers as input parameters // non-void results will be sent to a response queue // auto-acknowledges if no exceptions thrown } } <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory" p:brokerURL="tcp://localhost:61616" /> <bean id="messageListener“ class="org.bsnyder.spring.jms.listener.MyMessageListener" /> <jms:listener-container concurrency="5-10“container-type=“simple|default”> <jms:listener destination="FOO.TEST" ref="messageListener“ method=“onMessage”/> </jms:listener-container>
  • 14.
    MessageListenerContainer options • DefaultMessageListenerContainer –Spring launches threads ◦ while(…){ consumer.receive(); } – Allows for dynamic scaling of queue consumers – Participates in external transactions • SimpleMessageListenerContainer – consumer.setMessageListener(myListener); ◦ JMS Provider manages ThreadPool – No external transaction support – Works better with MockRunner JMS
  • 15.
    JMS Resource Pooling ManagedConnectionFactory ◦ JCA Resource Adapter in JBoss ◦ ConnectionFactory bound in JNDI, no pooling in application Unmanaged broker ◦ Define pure ActiveMQConnectionFactory in Spring ◦ Wrap in org.apache.activemq.pool.PooledConnectionFactory ◦ Also possible: Spring CachingConnectionFactory ◦ Caches Sessions, too
  • 16.
    ActiveMQ Client Configuration ActiveMQnamespace for spring configuration Same configuration format as server <connectionFactory xmlns="http://activemq.apache.org/schema/core" brokerURL="${activeMQ.brokerURL}" userName="${activeMQ.username}" password="${activeMQ.password}"> <redeliveryPolicyMap> <redeliveryPolicyMap> <defaultEntry> <redeliveryPolicy maximumRedeliveries="2"/> </defaultEntry> </redeliveryPolicyMap> </redeliveryPolicyMap> </connectionFactory>
  • 17.
    Transactions JmsTransactionManager ◦ Injected inJmsTemplate and (Default)MessageListenerContainers ◦ Transactional behaviour across JMS Senders/Receivers ◦ All calls use the same JMS Session ◦ Acknowledgements are only processed when whole session is committed JtaTransactionManager ◦ JMS Sessions are synchronized with XA Database transactions ◦ Here be dragons…
  • 18.
    Testing Unit tests ◦ Callmessage listeners directly ◦ Mockito.mock(JmsTemplate.class) Component Integration Tests ◦ Use mockrunner-jms System Tests ◦ Use embedded ActiveMQ broker <amq:broker persistent="false" useJmx="false" id="embeddedBroker"> <amq:transportConnectors> <amq:transportConnector uri="tcp://localhost:#{freePortSelector}"/> </amq:transportConnectors> </amq:broker> <amq:connectionFactory id="jmsFactory" brokerURL="vm://localhost"/> @Bean public JMSMockObjectFactory jmsMockObjectFactory() { return new JMSMockObjectFactory();} @Bean public ConnectionFactory connectionFactory() { return jmsMockObjectFactory().getMockConnectionFactory();} @Bean public JMSTestModule jmsTestModule() { return new JMSTestModule(jmsMockObjectFactory());}
  • 19.