Enterprise Messaging With Spring JMS Bruce Snyder, Senior Software Engineer, SpringSource Friday, July 8, 2011
Enterprise Messaging With Spring JMS
Bruce Snyder, Senior Software Engineer, SpringSource
Friday, July 8, 2011
Agenda
• Very brief introduction to JMS • Synchronous JMS With Spring • Asynchronous JMS With Spring
2
Friday, July 8, 2011
What is JMS?
• JMS is:– An API for client-side communications with a JMS provider – Included in Java EE
• Also stand alone
• JMS is not: – A spec for a message broker implementation
3
Friday, July 8, 2011
JMS is an Abstraction
4
Friday, July 8, 2011
JMS Message
5
Friday, July 8, 2011
Point-to-Point Messaging
6
Friday, July 8, 2011
Publish/Subscribe Messaging
7
Friday, July 8, 2011
Typical JMS Use
8
Friday, July 8, 2011
Raw JMS
9
Friday, July 8, 2011
JMS With Spring
10
Friday, July 8, 2011
Managed vs. Non-Managed JMS
• Managed– JMS provider in a Java EE container – JMS resource pooling – Transaction support – Support for EJBs
• Non-Managed– Stand alone JMS provider – Manual setup of JMS resources – No guarantee of transactions
• Spring supports both environments
11
Friday, July 8, 2011
JMS With Spring
12
Friday, July 8, 2011
Spring JMS
• JMS Template– Send and receive messages
synchronously
• Message Listener Container – Receive messages asynchronously – Message-Driven POJOs (MDPs)
13
Friday, July 8, 2011
JmsTemplate
• browse() – Browse messages in a queue
• convertAndSend() – Send messages synchronously– Convert a Java object to a JMS message
• send() – Send a message synchronously using a MessageCreator
• receive() and receiveAndConvert() – Receive messages synchronously
• execute() – Provides access to callbacks for more complex scenarios
• receiveSelected() and receiveSelectedAndConvert()– Receive filtered messages synchronously
14
Synchronous
Friday, July 8, 2011
The Spring JmsTemplate
15
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory" p:brokerURL="tcp://localhost:61616" /> <bean id="destination" class="org.apache.activemq.command.ActiveMQQueue"> <constructor-arg value="FOO.TEST" /> </bean> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate" p:connectionFactory-ref="connectionFactory" p:defaultDestination-ref="destination" />
<bean id="messageProducer" class="org.bsnyder.spring.jms.producer.SimpleMessageProducer" p:jmsTemplate-ref="jmsTemplate" />
Synchronous
Friday, July 8, 2011
The Spring JmsTemplate
16
// Use the default destination jmsTemplate.convertAndSend("Hello World!");
// Use a different destinationjmsTemplate.convertAndSend(“TEST.BAR”, “Hello World!”);
// Use a different destinationString textMessage1 = (String) jmsTemplate.receiveAndConvert();
// Use a different destinationString textMessage2 = (String) jmsTemplate.receiveAndConvert(“TEST.BAR”);
Synchronous
Friday, July 8, 2011
The Spring JmsTemplate
• Using send() with a MessageCreator
17
// Use a MessageCreator callback jmsTemplate.send(destination, new MessageCreator() { public Message createMessage(Session session) throws JMSException { TextMessage message = session.createTextMessage("Hello World!"); message.setIntProperty("someBusinessId", 22); return message; } });
// Receive raw JMS message TextMessage message = jmsTemplate.receive();
Synchronous
Friday, July 8, 2011
The Spring JmsTemplate
• Using execute() and the SessionCallback
18
// Use a SessionCallbackjmsTemplate.execute(new SessionCallback() { public Object doInJms(Session session) throws JMSException { Queue queue = session.createQueue("MY.TEST.QUEUE"); MessageProducer producer = session.createProducer(queue); TextMessage message = session.createTextMessage("Hello World!"); producer.send(message); }});
Synchronous
Friday, July 8, 2011
The Spring JmsTemplate
• Using execute() with the ProducerCallback
19
// Use a ProducerCallbackjmsTemplate.execute(new ProducerCallback() { public Object doInJms(Session session, MessageProducer producer) throws JMSException { TextMessage message = session.createTextMessage("Hello World!"); producer.send(destination, message); } return null; }});
Synchronous
Friday, July 8, 2011
The Spring JmsTemplate
• Using JMS selector expression
20
// Using a selector expression String selectorExpression = “Timestamp BETWEEN 1218048453251 AND 1218048484330”;
jmsTemplate.receiveSelected(destination, selectorExpression);
Synchronous
Friday, July 8, 2011
The Spring JmsTemplate
• Resolving JMS destinations – DynamicDestinationResolver (default)
• Look up destinations via a simple text name • Just calls session.createQueue() and session.createTopic()
– JndiDestinationResolver• Option to fall back to DynamicDestinationResolver
– BeanFactoryDestinationResolver• Look up beans that are javax.jms.Destination objects
21
Synchronous
Friday, July 8, 2011
The Spring JmsTemplate
• MessageConverter – SimpleMessageConverter
• String <-> javax.jms.TextMessage• Map <-> javax.jms.MapMessage• Serializable object <-> javax.jms.ObjectMessage• byte[] <-> javax.jms.BytesMessage
22
Synchronous
Friday, July 8, 2011
The Spring JmsTemplate
• JmsException hierarchy – Spring-specific unchecked exceptions – Corresponds to JMSException
• Advantage – Automatic clean up of JMS resources
23
Synchronous
Friday, July 8, 2011
The Spring JmsTemplate
• Automatically participates in transactions • Provides support for:
– Java EE transactions – Spring local transactions (Spring JmsTransactionManager)– Spring global transactions (Spring JtaTransactionManager)
• XA requires an XA capable ConnectionFactory • XA resource enlistment is provider specific
24
Synchronous
Friday, July 8, 2011
The Spring JmsTemplate
• JmsTemplate does not provide resource pooling – Utilizes fresh connection/session for every invocation
• JMS resource pooling is responsibility of JMS provider
• Spring provides support – SingleConnectionFactory
• Returns same connection for all calls to createConnection()• Ignores all calls to close()
– CachingConnectionFactory• Extends SingleConnectionFactory to add Session caching and
automatic Connection recovery
25
Synchronous
Friday, July 8, 2011
Spring JMS
• JMS Template– Send and receive messages
synchronously
• Message Listener Container – Receive messages asynchronously – Message-Driven POJOs (MDPs)
26
Friday, July 8, 2011
Managed vs. Non-Managed JMS
• Non-Managed – JMS MessageConsumer registers a MessageListener – Manual lifecycle management
• Managed – EJB Message-Driven Beans
27
Asynchronous
Friday, July 8, 2011
JMS Transaction Support
• Non-Managed XA Transactions – A JMS MessageConsumer can use various acknowledge
modes • AUTO_ACKNOWLEDGE• CLIENT_ACKNOWLEDGE• DUPS_OK_ACKNOWLEDGE• local JMS transaction
– Standard JMS does not support asynchronous message consumption as part of a XA transaction
• Managed XA Transactions – Officially supported only by EJBs
28
Asynchronous
Friday, July 8, 2011
Spring Message-Driven POJOs
• DefaultMessageListenerContainer– Most commonly used container – Allows for dynamic scaling of queue consumers– Participates in external transactions
• SimpleMessageListenerContainer– Very basic – Static configuration– No external transaction support
29
Asynchronous
Friday, July 8, 2011
DefaultMessageListenerContainer
• Highly configurable – Dynamic scale up/down of consumers
• Threads managed by the container – Customizable via the Spring TaskExecutor
• Resource caching– Connection, Session, MessageConsumer – Default is to cache nothing so as work in Java EE
environments– See the setCacheLevel() method for more info
• Works in managed and non-managed environments• Supports XA message consumption
30
Asynchronous
Friday, July 8, 2011
SimpleMessageListenerContainer
• No dynamic scaling of consumers • No support for XA transactions
31
Asynchronous
Friday, July 8, 2011
Supported Types of Message Listeners
• javax.jms.MessageListener interface – Standard Java EE interface – Threading is up to you
• SessionAwareMessageListener interface– Spring-specific interface – Provides access to the Session object
• Very useful for request-response messaging• MessageListenerAdapter interface
– Spring-specific interface – Allows for type-specific message handling – No JMS dependencies whatsoever
32
Asynchronous
Friday, July 8, 2011
MessageListener
• Standard JMS MessageListener • Uses an onMessage() method
33
public class MyMessageListener implements MessageListener { private static Logger LOG = Logger.getLogger(MyMessageListener.class); public void onMessage(Message message) throws JMSException { try { LOG.info("Consumed message: “ + message); // Do some processing here } catch (JMSException e) { LOG.error(e.getMessage(), e); } }
Asynchronous
Friday, July 8, 2011
DefaultMessageListenerContainer
• XML configuration
34
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory" p:brokerURL="tcp://localhost:61616" />
<bean id="messageListener" class="org.bsnyder.spring.jms.listener.SimpleMessageListener" />
<jms:listener-container concurrency="5-10"> <jms:listener destination="FOO.TEST" ref="messageListener"/> </jms:listener-container>
Asynchronous
Friday, July 8, 2011
DefaultMessageListenerContainer
• Use more than one listener
35
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory" p:brokerURL="tcp://localhost:61616" />
<bean id="widgetListener" class="org.bsnyder.spring.jms.listener.WidgetMessageListener" />
<bean id="gadgetListener" class="org.bsnyder.spring.jms.listener.GadgetMessageListener" />
<jms:listener-container concurrency="5-10"> <jms:listener destination="PRODUCTS.WIDGETS" ref="widgetListener"/> <jms:listener destination="PRODUCTS.GADGETS" ref="gadgetListener"/> </jms:listener-container>
Asynchronous
Friday, July 8, 2011
SessionAwareMessageListener
• Provides access to the session• Uses an onMessage() method
36
public class MySessionAwareMessageListener implements SessionAwareMessageListener { private static Logger LOG = Logger.getLogger(MySessionAwareMessageListener.class); public void onMessage(Message message, Session session) throws JMSException { try { LOG.info("Consumed message: “ + ((TextMessage)message).getText()); // Send a reply message TextMessage newMessage = session.createTextMessage(“This is a test”); MessageProducer producer = session.createProducer(message.getJMSReplyTo()); LOG.info("Sending reply message "); producer.send(newMessage); } catch (JMSException e) { LOG.error(e.getMessage(), e); } }}
Asynchronous
Friday, July 8, 2011
DefaultMessageListenerContainer
• XML configuration
37
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory" p:brokerURL="tcp://localhost:61616" />
<bean id="messageListener" class="org.bsnyder.spring.jms.listener.MySessionAwareMessageListener" />
<jms:listener-container concurrency="5-10"> <jms:listener destination="FOO.TEST" ref="messageListener"/> </jms:listener-container>
Asynchronous
Friday, July 8, 2011
MessageListenerAdapter
• Handles all message content types• No reply message is sent (void return)
38
public interface MessageDelegate { void processMessage(String text); void processMessage(Map map); void processMessage(byte[] bytes); void processMessage(Serializable obj); }
Asynchronous
Friday, July 8, 2011
MessageListenerAdapter
39
<bean id="messageDelegate" class="org.springframework.jms.listener.adapter.MessageListenerAdapter" p:defaultListenerMethod="processMessage"> <constructor-arg> <bean class="org.bsnyder.spring.jms.adapter.MessageDelegateImpl" /> </constructor-arg> <property name="messageConverter"> <null/> </property> </bean>
<jms:listener-container concurrency="5-10"> <jms:listener destination="FOO.TEST" ref="messageDelegate" /> </jms:listener-container>
Asynchronous
Friday, July 8, 2011
MessageListenerAdapter
• Handles all raw JMS message types• No reply message is sent (void return)
40
public interface MessageDelegate { void processMessage(TextMessage message); void processMessage(MapMessage message); void processMessage(BytesMessage message); void processMessage(ObjectMessage message); }
Asynchronous
Friday, July 8, 2011
MessageListenerAdapter
41
<bean id="messageDelegate2" class="org.springframework.jms.listener.adapter.MessageListenerAdapter" p:defaultListenerMethod="processMessage"> <constructor-arg> <bean class="org.bsnyder.spring.jms.adapter.MessageDelegate2Impl" /> </constructor-arg> <property name="messageConverter"> <null/> </property> </bean>
<jms:listener-container concurrency="5-10"> <jms:listener destination="FOO.TEST2" ref="messageDelegate2" /> </jms:listener-container>
Asynchronous
Friday, July 8, 2011
Local Transactions
42
Asynchronous
<bean id="messageDelegate" class="org.springframework.jms.listener.adapter.MessageListenerAdapter" p:defaultListenerMethod="processMessage"> <constructor-arg> <bean class="org.bsnyder.spring.jms.adapter.MessageDelegateImpl" /> </constructor-arg> <property name="messageConverter"> <null/> </property> </bean>
<jms:listener-container concurrency="5-10" acknowledge=”transacted”> <jms:listener destination="FOO.TEST" ref="messageDelegate" /> </jms:listener-container>
Friday, July 8, 2011
Global Transactions
43
Asynchronous
<bean id="activemqXaConnectionFactory" class="org.apache.activemq.ActiveMQXAConnectionFactory" p:brokerURL="tcp://localhost:61616" />
<bean id="atomikosConnectionFactory" class="com.atomikos.jms.AtomikosConnectionFactoryBean" init-method="init" destroy-method="close"> <property name="uniqueResourceName" value="simpleTransaction"/> <property name="xaConnectionFactory" ref="activemqXaConnectionFactory"/> </bean>
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.J2eeTransactionManager" /> <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.J2eeUserTransaction" /> <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" p:transactionManager-ref="atomikosTransactionManager" p:userTransaction-ref="atomikosUserTransaction"> </bean>...
Friday, July 8, 2011
Global Transactions
44
Asynchronous
...
<bean id="messageDelegate" class="org.springframework.jms.listener.adapter.MessageListenerAdapter" p:defaultListenerMethod="processMessage"> <constructor-arg> <bean class="org.bsnyder.spring.jms.adapter.MessageDelegateImpl" /> </constructor-arg> <property name="messageConverter"> <null/> </property> </bean>
<jms:listener-container concurrency="5-10" connection-factory="atomikosConnectionFactory" transaction-manager="transactionManager" acknowledge="transacted"> <jms:listener destination="FOO.TEST" ref="messageDelegate" /> </jms:listener-container>
Friday, July 8, 2011
Q&A
Thank You!
Friday, July 8, 2011