2. JMS Basics
The application hands the message to be send to a
message broker.
The message broker ensures the message delivery to
the specified destination.
JMS messages are addressed with a destination.
Destination are of two types: queues (point-to-point
model) and topics (publish-subscribe model).
3. Point-to-point & Publish-subscribe
Point-to-Point:
Each message has exactly one sender and one receiver.
When a receiver asks for the next message in the queue, the
message is removed from the queue and delivered to the
receiver.
Each message in a message queue is delivered to only one
receiver.
Publish-Subscribe:
Messages are sent to a topic which is listened by multiple
receivers.
All subscribers to a topic will receive a copy of the message.
4. Benefits of JMS
The client doesn’t need to wait around for it to be
processed or even delivered.
JMS messages are data-centric and the client isn’t fixed
to a specific method signature.
JMS clients are unaware of service location and only
know the queue or topic through which the messages
will be sent.
JMS clients are assured of guaranteed message delivery
even if the service is unavailable when a message is
sent and is stored until the service is available again.
5. Setting up ActiveMQ in Spring
Add the incubator-activemq-4.1.0.jar to the application’s
classpath to access ActiveMQ’s API.
ActiveMQConnectionFactory is the JMS connection factory
for ActiveMQ, to send messages through the message
broker.
<bean id="connectionFactory"
class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL"
value="tcp://localhost:61616"/>
</bean>
The brokerURL property tells the connection factory where
the message broker is located
6. Declaring an ActiveMQ message
destination
A message destination is declared either a queue or topic, for
passing on the messages.
ActiveMQ queue Declaration:
<bean id="rantzDestination"
class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg index="0" value="rantz.marketing.queue"/>
</bean>
ActiveMQ topic Declaration:
<bean id="rantzDestination"
class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg index="0" value="rantz.marketing.topic"/>
</bean>
9. JMS Template
JMS Template handles creating a connection,
obtaining a session, and the actual sending and
receiving of messages.
It handles any clumsy JMSException that may be
thrown along the way.
When JMSException is thrown, JmsTemplate will
catch it and rethrow it as one of the unchecked
subclasses of JmsException in
org.springframework.jms.* package.
10. Wiring the JMS Template
JmsTemplate needs to get connections to the message
broker, and hence sets a connectionFactory property
with a bean that implements JMS’s ConnectionFactory
interface.
<bean id="jmsTemplate"
class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory"
ref="connectionFactory" />
</bean>
12. Setting a default destination
<bean id="jmsTemplate"
class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="defaultDestination" ref="rantzDestination" />
</bean>
The call to JmsTemplate’s send() method can be simplified slightly by
removing the first parameter:
jmsTemplate.send(
new MessageCreator() {
…
});
JmsTemplate assumes that the message sent to the default destination
when the send() method only takes a MessageCreator.
14. Consuming messages: Timeout
JmsTemplate’s receive() method is synchronous, blocking the call
until a message appears on the destination.
The receiveTimeout property can be used to configure
JmsTemplate to time out on receives after specified time interval.
<bean id="jmsTemplate"
class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="defaultDestination" ref="rantzDestination" />
<property name="receiveTimeout" value="60000" />
</bean>
15. Converting messages
Spring supports message conversion through its
MessageConverter interface:
public interface MessageConverter {
public Message toMessage(Object object, Session session);
public Object fromMessage(Message message);
}
For sending messages, the toMessage() method converts an
object to a Message.
On the receiving end, the fromMessage() method converts
an incoming Message into an Object.
16. Implementation of MessageConverter
public class MotoristMessageConverter implements MessageConverter {
public Object fromMessage(Message message) throws JMSException, MessageConversionException {
if(!(message instanceof MapMessage)) {
throw new MessageConversionException("Message isn't a MapMessage"); }
MapMessage mapMessage = (MapMessage) message;
SpammedMotorist motorist = new SpammedMotorist();
motorist.setFirstName(mapMessage.getString("firstName"));
return motorist;
}
public Message toMessage(Object object, Session session) throws JMSException,
MessageConversionException {
if(!(object instanceof Motorist)) {
throw new MessageConversionException("Object isn't a Motorist"); }
Motorist motorist = (Motorist) object;
MapMessage message = session.createMapMessage();
message.setString("firstName", motorist.getFirstName());
return message;
}
}
17. Sending/Receiving converted messages
The convertAndSend() method of JmsTemplate can be
simply called instead of explicitly calling the toMessage()
method before sending a message.
JmsTemplate’s convertAndSend() method automatically
calls the toMessage() method before sending the message
to the destination.
The receiveAndConvert() method of JmsTemplate can be
simply called instead of explicitly calling the fromMessage()
to convert the message returned from JmsTemplate’s
receive().
Unless specified, both the convertAndSend() and
receiveAndConvert(), send and receive messages
respectively from the default destination.
19. Spring JMS Gateway Support
public class RantzMarketingGatewayImpl extends JmsGatewaySupport
implements RantzMarketingGateway {
public void sendMotoristInfo(final Motorist motorist) {
getJmsTemplate().send("rantz.marketing.queue",
new MessageCreator() {
public Message createMessage(Session session)
throws JMSException {
MapMessage message = session.createMapMessage();
message.setString("firstName", motorist.getFirstName());
return message;
}
});
}
}
20. Spring JMS Gateway Support
A JmsTemplate object can be directly injected into the
jmsTemplate property, or by wiring the connection factory
into the connectionFactory property.
JmsGatewaySupport automatically creates a JmsTemplate
object based on the injected connection factory bean wired
into the connectionFactory property.
Shortcomings to wiring connection factory are as follows:
Can only specify a default destination on a JmsTemplate.
Can only wire a message converter into a JmsTemplate.
22. Message Listener Container
A message listener container watches a JMS destination for
messages and retrieves the messages on arrival.
The retrieved messages are passed on to a MessageListener
implementation by calling the onMessage() method.
onMessage() method is to receive and translate the message.
SimpleMessageListenerContainer is the simplest of the message
listener containers, configured as follows:
<bean class="org.springframework.jms.listener.
➥ SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="rantzDestination" />
<property name="messageListener" ref="rantzMdp" />
</bean>
23. Spring Message Listener Containers
Container class
org.springframework.jms.listener.*
Operation
SimpleMessageListenerContainer This is the simplest message listener
container. It works with a fixed number of
JMS sessions and does not support
transactions.
DefaultMessageListenerContainer This message listener container builds upon
SimpleMessageListenerContainer by
adding support for transactions
serversession.ServerSessionMessage
ListenerContainer
This is the most powerful of the message
listener containers. Like
DefaultMessageListenerContainer, it
supports transactions. However it is unique
in that it allows for dynamic management
of JMS sessions.
24. Transactional MDPs
DefaultMessageListenerContainer adds transaction support to the
SimpleMessageListenerContainer.
<bean class="org.springframework.jms.listener.➥ DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="rantzDestination" />
<property name="messageListener" ref="rantzMdp" />
<property name="transactionManager" ref="jmsTransactionManager" />
</bean>
The transactionManager property is wired with a reference to a transaction manager such
as JmsTransactionManager:
<bean id="jmsTransactionManager“ class="org.springframework.jms.connection.
➥ JmsTransactionManager">
<property name="connectionFactory" ref="connectionFactory" />
</bean>
The transactionManager property of DefaultMessageListenerContainer is optional.
25. Pure POJO MDP
The message listener container calls the onMessage() method
when a message arrives, when its messageListener property
being wired with an implementation of MessageListener.
<bean class="org.springframework.jms.listener.
➥ SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"
/>
<property name="destination" ref="rantzDestination" />
<property name="messageListener" ref="purePojoMdp" />
</bean>
26. MessageListenerAdapter
MessageListenerAdapter is a MessageListener that delegates to a bean and
method.
By default, MessageListenerAdapter calls a handleMessage() method
when a message arrives.
<bean id="purePojoMdp“
class="org.springframework.jms.listener.adapter.
➥ MessageListenerAdapter">
<property name="delegate" ref="rantzMdp" />
<property name="defaultListenerMethod“ value="processMotoristInfo" />
</bean>
The MessageListenerAdapter above calls the processMotoristInfo()
method (as it’s the defaultListenerMethod) on the rantzMdp bean when a
message arrives on the destination.
When MessageListenerAdapter receives a message, it considers the
message type and the value of the defaultListenerMethod and tries to find
an appropriate listener method signature to invoke.
27. Mapping of JMS Message to MDP
Message type Method parameter
TextMessage String or TextMessage
MapMessage java.util.Map or MapMessage
BytesMessage byte[] or BytesMessage
ObjectMessage java.io.Serializable or ObjectMessage
The listener method can be written to take either
the original JMS message or a simpler type.
28. Converting MDP messages
Spring message converters are used to translate messages to and
from domain-specific Java types.
MessageListenerAdapter has a messageConverter property to set
the corresponding message converter.
<bean id="purePojoMdp“
class="org.springframework.jms.listener.adapter.
➥ MessageListenerAdapter">
<property name="delegate" ref="rantzMdp" />
<property name="defaultListenerMethod“
value="processMotoristInfo" />
<property name="messageConverter" ref="motoristConverter" />
</bean>
29. Lingo
Lingo is a Spring-based remoting option that bridges
the gap between RPC and asynchronous messaging.
Lingo provides a service exporter to export bean
functionality as a Lingo service and a clientside proxy
to transparently wire a reference to a remote Lingo
service on the calling side.
Lingo remoting carries information over a JMS queue
or topic, making the service call held in the
queue/topic until the service is available again.
30. Exporting Lingo Service
Lingo provides JmsServiceExporter, a service exporter, were
services are made available through JMS.
The service implementation implements serviceInterface and is a
pure POJO wired into a MessageListenerAdapter to be used as
message-driven POJO.
<bean id="server"
class="org.logicblaze.lingo.jms.JmsServiceExporter">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="destination" />
<property name="service" ref="rantzMdp" />
<property name="serviceInterface"
value="com.roadrantz.marketing.MarketingService" />
</bean>
31. Lingo JMS Proxy
Lingo provides JmsProxyFactoryBean, a proxy factory bean that
produces proxies to remote Lingo-exported services.
<bean id="marketing"
class="org.logicblaze.lingo.jms.JmsProxyFactoryBean">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="destination" />
<property name="serviceInterface"
value="com.roadrantz.marketing.MarketingService" />
</bean>
Invoking a Lingo-exported service is no different from invoking an
RMI service, a web service, or even a method on another bean.