Receiving a message - Java-Springs

Synchronous Reception

While JMS is typically associated with asynchronous processing, it is possible to consume messages synchronously.The overloaded receive(..) methods provide this functionality. During a synchronous receive, the calling thread blocks until a message becomes available. This can be a dangerous operation since the calling thread can potentially be blocked indefinitely. The property receiveTimeout specifies how long the receiver should wait before giving up waiting for a message.

Asynchronous Reception - Message-Driven POJOs

In a fashion similar to a Message-Driven Bean (MDB) in the EJB world, the Message-Driven POJO (MDP) acts as a receiver for JMS messages. The one restriction (but see also below for the discussion of the MessageListenerAdapter class) on an MDP is that it must implement the javax.jms.MessageListener interface.Please also be aware that in the case where your POJO will be receiving messages on multiple threads, it is important to ensure that your implementation is thread-safe.

Below is a simple implementation of an MDP:

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
public class ExampleListener implements MessageListener {
public void onMessage(Message message) {
if (message instanceof TextMessage) {
try {
System.out.println(((TextMessage) message).getText());
}
catch (JMSException ex) {
throw new RuntimeException(ex);
}
}
else {
throw new IllegalArgumentException("Message must be of type TextMessage");
}
}
}

Once you've implemented your MessageListener, it's time to create a message listener container. Find below an example of how to define and configure one of the message listener containers that ships with Spring (in this case the Default Message Listener Container).

<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="jmsexample.ExampleListener" />
<!-- and this is the message listener container -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener" />
</bean>

The Session Aware Message Listener interface

The Session Aware Message Listener interface is a Spring-specific interface that provides a similar contract to the JMS Message Listener interface, but also provides the message handling method with access to the JMS Session from which the Message was received.

package org.springframework.jms.listener;
public interface SessionAwareMessageListener {
void onMessage(Message message, Session session) throws JMSException;
}

You can choose to have your MDPs implement this interface (in preference to the standard JMS Message Listener interface) if you want your MDPs to be able to respond to any received messages (using the Session supplied in the on Message(Message, Session) method). All of the message listener container implementations that ship wth Spring have support for MDPs that implement either the MessageListener or Session Aware Message Listener interface. Classes that implement the Session Aware Message Listener come with the caveat that they are then tied to Spring through the interface. The choice of whether or not to use it is left entirely up to you as an application developer or architect.

Please note that the 'onMessage(..)' method of the Session Aware Message Listener interface throws JMSException. In contrast to the standard JMS Message Listener interface, when using the Session Aware Message Listener interface, it is the responsibility of the client code to handle any exceptions thrown.

The MessageListenerAdapter

The Message Listener Adapter class is the final component in Spring's asynchronous messaging support: in a nutshell, it allows you to expose almost any class as a MDP (there are of course some constraints).

Consider the following interface definition. Notice that although the interface extends neither the Message Listener nor Session Aware Message Listener interfaces, it can still be used as a MDP via the use of the Message Listener Adapter class. Notice also how the various message handling methods are strongly typed according to the contents of the various Message types that they can receive and handle.

public interface MessageDelegate {
void handleMessage(String message);
void handleMessage(Map message);
void handleMessage(byte[] message);
void handleMessage(Serializable message);
}
public class DefaultMessageDelegate implements MessageDelegate {
// implementation elided for clarity...
}

In particular, note how the above implementation of the Message Delegate interface (the above Default Message Delegate class) has no JMS dependencies at all. It truly is a POJO that we will make into an MDP via the following configuration.

<!-- this is the Message Driven POJO (MDP) -->
<beanid="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg>
<bean class="jmsexample.DefaultMessageDelegate"/>
</constructor-arg>
</bean>
<!-- and this is the message listener container... -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener" />
</bean>

Below is an example of another MDP that can only handle the receiving of JMS Text Message messages. Notice how the message handling method is actually called 'receive' (the name of the message handling method in a MessageListenerAdapter defaults to 'handle Message'), but it is configurable (as you will see below). Notice also how the 'receive(..)' method is strongly typed to receive and respond only to JMS TextMessage messages.

public interface TextMessageDelegate {
void receive(TextMessage message);
}
public class DefaultTextMessageDelegate implements TextMessageDelegate {
// implementation elided for clarity...
}

The configuration of the attendant MessageListenerAdapter would look like this:

<beanid="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg>
<bean class="jmsexample.DefaultTextMessageDelegate"/>
</constructor-arg>
<property name="defaultListenerMethod" value="receive"/>
<!-- we don't want automatic message context extraction -->
<property name="messageConverter">
<null/>
</property>
</bean>

Please note that if the above 'message Listener' receives a JMS Message of a type other than Text Message, an Illegal State Exception will be thrown (and sub sequently swallowed).Another of the capabilities of the Message Listener Adapter class is the ability to automatically send back a response Message if a handler method returns a non-void value. Consider the interface and class:

public interface ResponsiveTextMessageDelegate {
// notice the return type...
String receive(TextMessage message);
}
public class DefaultResponsiveTextMessageDelegate implements ResponsiveTextMessageDelegate {
// implementation elided for clarity...
}

If the above Default Responsive Text Message Delegate is used in conjunction with a Message Listener Adapter then any non-null value that is returned from the execution of the 'receive(..)' method will (in the default configuration) be converted into a Text Message.The resulting Text Message will then be sent to the Destination (if one exists) defined in the JMS Reply-To property of the original Message, or the default Destination set on the Message Listener Adapter (if one has been configured); if no Destination is found then an Invalid Destination Exception will be thrown (and please note that this exception will not be swallowed and will propagate up the call stack).

Processing messages within transactions

Invoking a message listener within a transaction only requires reconfiguration of the listener container. Local resource transactions can simply be activated through the sessionTransacted flag on the listener container definition. Each message listener invocation will then operate within an active JMS transaction, with message reception rolled back in case of listener execution failure. Sending a response message (via Session Aware Message Listener) will be part of the same local transaction, but any other resource operations (such as database access) will operate independently. This usually requires duplicate message detection in the listener implementation, covering the case where database processing has committed but message processing failed to commit.

<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener"/>
<property name="sessionTransacted" value="true"/>
</bean>

For participating in an externally managed transaction, you will need to configure a transaction manager and use a listener container which supports externally managed transactions: typically Default Message Listener Container.

To configure a message listener container for XA transaction participation, you'll want to configure a Jta Transaction Manager (which, by default, delegates to the Java EE server's transaction subsystem). Note that the underlying JMS Connection Factory needs to be XA-capable and properly registered with your JTA transaction coordinator! (Check your Java EE server's configuration of JNDI resources.)This allows message recepton as well as e.g. database access to be part of the same transaction (with unified commit semantics, at the expense of XA transaction log overhead).

Then you just need to add it to our earlier container configuration. The container will take care of the rest.

<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener"/>
<property name="transactionManager" ref="transactionManager"/>
</bean>

All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd DMCA.com Protection Status

Java-Springs Topics