Request-Reply JMS with Spring 2.0

Several months ago, I posted a blog entry introducing Spring 2.0's support for Message Driven POJOs. While many people are now familiar with that feature, Spring 2.0's JMS remoting features have received less attention. Essentially, this remoting functionality provides a JMS-based version of Spring's general approach to remoting as exhibited in its support for RMI, Hessian/Burlap, and its own HttpInvoker.
For those unfamiliar with Spring remoting, the general idea is to configure a non-invasive exporter on the server-side and a proxy generator (a Spring FactoryBean) on the client-side.
I will demonstrate this JMS remoting here with a code example – based on the same example as in my previous post.
First, there is the service itself. This time I am providing an interface (useful for the proxy generation and a good practice in general):
RegistrationReply processRequest(RegistrationRequest request);
}
…and the simple implementation class is the same as in the previous post:
private Map registrations = new HashMap();
private int counter = 100;
public RegistrationReply processRequest(RegistrationRequest request) {
int id = counter++;
if (id % 5 == 0) {
id = -1;
}
else {
registrations.put(new Integer(id), request);
}
return new RegistrationReply(request.getName(), id);
}
}
The RegistrationRequest and RegistrationReply objects are also the same as in the previous post:
private String name;
public RegistrationRequest(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
private String name;
private int confirmationId;
public RegistrationReply(String name, int confirmationId) {
this.name = name;
this.confirmationId = confirmationId;
}
public String toString() {
return (confirmationId >= 0)
? name + ": Confirmed #" + confirmationId
: name + ": Not Confirmed";
}
}
This time the 'shared-context.xml' file will be a bit simpler, because I am not defining a reply queue. Instead, the Spring-generated proxy will be leveraging a standard JMS QueueRequestor that relies upon temporary queues for handing the replies:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="requestQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="requestQueue"/>
</bean>
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616"/>
</bean>
</beans>
(as you may notice, I am also using ActiveMQ version 4.1.0 this time)
Now for the most interesting changes. On the server-side, I need an exporter. Specifically, I need to configure an instance of Spring's JmsInvokerServiceExporter. This will actually replace the message listener from the previous post's example. Notice that it requires two properties – the fully-qualified name of the 'serviceInterface' and the 'service' itself (a bean reference to the POJO service to be exported):
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<import resource="shared-context.xml"/>
<bean id="registrationService" class="blog.jms.remoting.RegistrationServiceImpl"/>
<bean id="listener" class="org.springframework.jms.remoting.JmsInvokerServiceExporter">
<property name="service" ref="registrationService"/>
<property name="serviceInterface" value="blog.jms.remoting.RegistrationService"/>
</bean>
<bean id="container" class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="messageListener" ref="listener"/>
<property name="destination" ref="requestQueue"/>
</bean>
</beans>
On the client side, the corresponding JmsInvokerProxyFactoryBean is quite simple. Also, notice that there is no longer a need for a jmsTemplate for sending messages as in the previous example. It only needs a reference to the 'connectionFactory', the 'queue', and of course the 'serviceInterface' that the proxy needs to implement:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<import resource="shared-context.xml"/>
<bean id="registrationService" class="org.springframework.jms.remoting.JmsInvokerProxyFactoryBean">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="queue" ref="requestQueue"/>
<property name="serviceInterface" value="blog.jms.remoting.RegistrationService"/>
</bean>
</beans>
The RegistrationServiceRunner is still necessary to bootstrap the service for this standalone example:
public static void main(String[] args) throws IOException {
new ClassPathXmlApplicationContext("/blog/jms/remoting/server-context.xml");
System.out.println("RegistrationService started");
System.in.read();
}
}
The client (RegistrationConsole in this case) is slightly different in this example than in the previous Message-Driven POJOs post:
public static void main(String[] args) throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("/blog/jms/remoting/client-context.xml");
RegistrationService registrationService = (RegistrationService) context.getBean("registrationService");
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
for (;;) {
System.out.print("To Register, Enter Name: ");
String name = reader.readLine();
RegistrationRequest request = new RegistrationRequest(name);
RegistrationReply reply = registrationService.processRequest(request);
System.out.println(reply);
}
}
}
Instead of sending an asynchronous message and having its reply handled by another listener, here the client simply invokes the method on the interface. Spring's exporter and proxy fully manage the correlation IDs.
Thanks to the Spring-generated proxy and its role in encapsulating the request/reply mechanism, the client invocation of this JMS-based remote service is completely decoupled from the JMS infrastructure.
As in the previous post, to run the example requires 3 steps: start the ActiveMQ broker, run the RegistrationServiceRunner's main(..) method, and then run the RegistrationConsole's main(..) method.
Similar Posts
- Message Driven POJOs!
- Spring Integration in 10 minutes
- Using a Hybrid Annotations & XML Approach for Request Mapping in Spring MVC
- XPath Support in Spring Web Services
- POJO Aspects in Spring 2.0: A Simple Example











Rick says:
Added on April 4th, 2007 at 7:55 amLingo is a related library that might be of interest.
Barry Kaplan says:
Added on April 4th, 2007 at 8:12 amIs it required to only send/receive a single object via the remote method? In others, does the spring impl support arbitrary method signatures as Lingo does? Does it support exceptions? Both async and sync?
Mark Fisher (blog author) says:
Added on April 4th, 2007 at 9:48 amBarry,
Arbitrary method signatures are supported (it passes the context as an invocation object). Also, any Exception (runtime or checked) can be thrown from the service method just as if it were a local call. Of course, JMSExceptions may occur as well – and will be wrapped in Spring's runtime RemoteAccessException.
Barry Kaplan says:
Added on April 4th, 2007 at 4:22 pmVery nice. Thanks!
Davide Baroncelli says:
Added on April 5th, 2007 at 2:13 amIs there a way to plug different serializability methods (ex: XML binding) instead of relying on standard java serialization?
Indrit Selimi says:
Added on April 6th, 2007 at 5:36 pmHi, I want to implement 'event isolation' or message ordering in base of some key property in the message, can in someway Spring remoting help achieving this?
Mark Spitzer says:
Added on May 4th, 2007 at 3:41 pmGreat article! I second Davide's motion for pluggable serialization. I'm thinking both the service exporter and the invoker proxy could both be modified to use a MessageConverter. A default message converter could apply the same java serialization behavior that you have right now.
Thai says:
Added on May 17th, 2007 at 2:45 pmHi Mark,
Could you please post your whole example here or send it to me at dxxvi@hatrang.net? I don't know how to write the Spring application context xml file (actually, I wrote one but it was syntactically wrong).
Regards.
Richard says:
Added on June 20th, 2007 at 5:09 pmI used the example above to implement the jms request/reply with tibco as the jms provider. It worked fine when I used spring for the client like the example above but when I had a Tibco requestor, the server (listener) never received the message. Does the client have to use the JmsInvokerProxyFactoryBean for this to work? Thanks for any insight!
Mark Fisher (blog author) says:
Added on June 21st, 2007 at 8:05 am[quote comment="28562"]Does the client have to use the JmsInvokerProxyFactoryBean for this to work?[/quote]
Richard,
The JmsInvokerServiceExporter is expecting a JMS ObjectMessage where the payload is an instance of Spring's RemoteInvocation (basically a serializable wrapper for an AOP MethodInvocation), and the JmsInvokerProxyFactoryBean expects a RemoteInvocationResult. If you would like to create an alternative implementation, I would recommend looking at the source for those two. You may find that you can accomplish what you want through subclassing and/or leveraging a custom implementation of the MessageConverter interface to handle translation to/from the remote invocation wrappers.
Hope that helps,
Mark
Richard says:
Added on June 21st, 2007 at 8:15 amThanks Mark, I will take a look at the source.
Mark Fisher (blog author) says:
Added on June 21st, 2007 at 8:18 am[quote comment="17494"]Is there a way to plug different serializability methods (ex: XML binding) instead of relying on standard java serialization?[/quote]
[quote comment="21505"]…both the service exporter and the invoker proxy could both be modified to use a MessageConverter.[/quote]
A custom MessageConverter implementation can be provided for both exporter and invoker as of version 2.0.5.
Mark Fisher (blog author) says:
Added on June 21st, 2007 at 8:28 am[quote comment="17742"]Hi, I want to implement 'event isolation' or message ordering in base of some key property in the message, can in someway Spring remoting help achieving this?[/quote]
Indrit,
Could you elaborate on this requirement a bit? My initial reaction is that it would require some customization – if you are talking about a splitter/aggregator model. For that scenario, QueueRequestor is too fine-grained – each request/reply unit-of-work is operating on a single message.
anek says:
Added on October 4th, 2007 at 6:56 amGreat post,
But what about performance?
When testing the performance of this request/reply solution, it turns out that only about 14 requests per second gets processed at full speed on a "yesteryear"-machine.
(Just using a loop that sends the next request when the reply arrives)
Is this somehow due to lack of pooling?
Or is it maybe that the proxy creates a new temporary reply queue for each request?
Andrea says:
Added on December 15th, 2007 at 7:47 amHi I'm testing spring 2.5. So I've been porting the sample using the new JMS namespace. So, while the original sample is working fine, when I change the content of server-context.xml like here:
I get the following exception
Caused by: java.lang.NoSuchMethodException: blog.jms.remoting.RegistrationServiceImpl.processRequest(org.springframework.remoting.support.RemoteInvocation)
I think I've to write a custom message converter but I do not understand why
Thanks
Andrea
Andrea says:
Added on December 15th, 2007 at 7:49 amSorry, it's me again because I've forgot to quote the xml…
[quote post="147"]
[/quote]
Andrea says:
Added on December 15th, 2007 at 7:57 amLet's try again…
<jms:listener-container connection-factory="connectionFactory"
container-type="default" destination-type="queue" >
<jms:listener destination="requestQueue"
ref="registrationService" method="processRequest" />
</jms:listener-container>
Rolando says:
Added on May 12th, 2008 at 8:39 pmGreat stuff!
But I have a question:
When the connectionFactory brokerUrl is changed to ssl://192.168.0.2:443, where 192.168.0.2 could be any valid address other than localhost, and the environment variable ACTIVEMQ_OPTS is set to
-Xmx512M -Dorg.apache.activemq.UseDedicatedTaskRunner=true -Dderby.storage.fileSyncTransactionLog=true -Dcom.sun.management.jmxremote -Djavax.net.ssl.keyStore=C:\work\ActiveMQ401\broker.ks -Djavax.net.ssl.keyStorePassword=secret -Djavax.net.ssl.trustStore=c:\work\ActiveMQ401\broker.ts -Djavax.net.ssl.trustStorePassword=secret
performance slows down to approximately 1/2 min. per message/response compared to less than 1 sec. when set to tcp://192.168.0.2:61616.
Does it mean this is unusable in production environments?
Thanks,
Rolando
forex currency trading says:
Added on July 24th, 2008 at 7:07 amI would like to thanks you for ur sincere efforts in writing this post.
Michael says:
Added on July 30th, 2008 at 8:25 amAnyone know if there is a way to prefix the names of the queues that are created dynamically.
echo says:
Added on December 27th, 2008 at 11:17 ami love it
Ajax says:
Added on May 22nd, 2009 at 3:08 pmCool, nice news!
Price Comparison says:
Added on November 21st, 2009 at 5:49 amI can understand the effort behind the posting the code
this will request for the queue