3. 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
4. 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
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
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>
8. 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
11. 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" />
12. 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
13. 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>
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
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
16. 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>
17. 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…
18. 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());}