PDA

View Full Version : Async vs. Sync JMS Listener, MDB vs. Spring 2 MDB POJO's



Thomas.Heiss
Mar 8th, 2006, 09:47 AM
Hello all,

We need to use in a project Oracle AQ and BEA WLS 8.1.4 (it's not a web/servlet application!)

Using MDB's (2.0 EJBs) means asynchronous JMS listening, as far as I understand.

Question: What does Message Driven POJO's Spring 2 or Spring 1.2.x + Jencks mean? Async JMS or Sync JMS?

How does MDB POJO Spring 2 work in BEA WLS?

What benefits provides Spring 2 MDB POJO's over EJB MDB's and how would make Spring our life simpler?

To be honest. I know Spring very well. I will try to convince to use it for e.g JMS outbound and JDBC abstraction. But the production timeline would be 06/30/2006. Maybe not the best for a "non-production framework release like Spring 2".
But if I can prove it working, maybe nobody cares about that fact.

What could be the key elements to make the decision for Spring 2 MDB POJO's to convince Java development team and development leader?

We have at leaset three JMS queues in Oracle AQ/BEA JMS. Probably that means we would have to deploy the EJB MDB 3 times.
How does that work with Spring MDB POJO's? Better? Faster? Simpler?

What I really do NOT understand about JMS/Spring JMS:
1. How can I make sure JMS pooling is being used inside BEA WLS with MDB POJO's?
Isn't setMessageListener() forbidden in J2EE 1.3 EJB + J2EE Servlet + EJB container?! Or is it being used for Spring 2 MDB POJO's or Spring 1 + Jenks?
Is there a new thread started for each async JMS listener?

Pardon, if that has already been explained thousands of time, but I still don't get it!

2. How would MDB POJO's JMS pooling work with Oracle AQ JMS factories? Are BEA JMS wrappers not being used with Oracle AQ factories and so pooling/caching of JMS ressources is not possible?
If not, how does Spring support ressource pooling?

3. How do I start a MDB POJO? BEA T3StartupDef class?
We have no servlet. But still, the "JMS listener" should run inside BEA WebLogic.
We surely have to write for some propriatary product a plugin (SLSB). But still this is probably not the right point to start a MDB POJO?!

3. JMS ressource pooling for JMS publish
3.1 inside Stateless Session EJB
How can I make sure, that JMS ressources also get pooled for a JMS publish/queue send?
BEA JMS documentation notes for BEA JMS wrappers, that it is a strong requirement e.g inside an EJB to use <resource-ref> descriptors (are JNDI lookups enough too?)

So for what cases do BEA JMS wrappers come into play? Only with the BEA JMS internal server?
Would that mean that for Oracle AQ on the other hand, that I don't get a JMS pooling because BEA JMS wrappers can not work?

3.2 How does that work with Spring 1.2.x JmsTemplate102 class?
Is it enough to provide the JNDI BEA JMS factories / classes, to let ressource pooling come into play?

3.3 external Java application
Also we have to publish/send JMS messages to the Oracle AQ from an external standalone Java application.
Is it enough to get JMS references via JNDI from BEA WLS to enable BEA JMS wrappers and JMS ressource pooling?

Under what circumstances will that work with Spring JmsTemplate102?
I do not want to manually implement a JMS publish/send.
(I read already the JmsTemplate Gotcha information but unfortunately I seem not to understand 100%).

Best regards from Germany

Thomas

wpoitras
Mar 8th, 2006, 11:02 AM
Question: What does Message Driven POJO's Spring 2 or Spring 1.2.x + Jencks mean? Async JMS or Sync JMS?
I don't know much about Spring 2 JMS support. Using Jencks is a async JMS. But it requires a JMS implementation which fits within Jencks. I don't think WLS JMS is Jencks compatible. All examples I've seen use Jencks outside of a J2EE container with ActiveMQ.


What benefits provides Spring 2 MDB POJO's over EJB MDB's and how would make Spring our life simpler?POJO MDB (referred to as Message Driven POJOs or MDP) in general is useful when you want the functionality without the J2EE container. Also configuring a POJO should be simpler than having to write an actual Bean class and its descriptor.


1. How can I make sure JMS pooling is being used inside BEA WLS with MDB POJO's?
Isn't setMessageListener() forbidden in J2EE 1.3 EJB + J2EE Servlet + EJB container?! Or is it being used for Spring 2 MDB POJO's or Spring 1 + Jenks?
Is there a new thread started for each async JMS listener?
When using Jencks, people tend to use ActiveMQ, which has good JMS connection pooling.


2. How would MDB POJO's JMS pooling work with Oracle AQ JMS factories? Are BEA JMS wrappers not being used with Oracle AQ factories and so pooling/caching of JMS ressources is not possible?
If not, how does Spring support ressource pooling?WLS allows for specifying foreign JMS resources. Judging from the documentation I'm guessing if you use those foreign JMS resources in the same way you use the native ones (from within an EJB or Servlet, looking up the resources from java:comp/env, and specifying them as resource-refs) it should provide the wrappers. I would contact BEA for confirmation of this.


3. How do I start a MDB POJO? BEA T3StartupDef class?
We have no servlet. But still, the "JMS listener" should run inside BEA WebLogic.
We surely have to write for some propriatary product a plugin (SLSB). But still this is probably not the right point to start a MDB POJO?!
You are correct, you would probably initialize something like this with a T3StartupDef (although its deprecated, BEA has newer mechanisms for startup/shutdown). But I've mentioned above, you wouldn't get the JMS wrapper outside of EJB/Servlets.


3.1 inside Stateless Session EJB
How can I make sure, that JMS ressources also get pooled for a JMS publish/queue send?
BEA JMS documentation notes for BEA JMS wrappers, that it is a strong requirement e.g inside an EJB to use <resource-ref> descriptors (are JNDI lookups enough too?)My understand is yes, looking up a resource from JNDI that is specified in resource-ref should be sufficient.


So for what cases do BEA JMS wrappers come into play? Only with the BEA JMS internal server?
Would that mean that for Oracle AQ on the other hand, that I don't get a JMS pooling because BEA JMS wrappers can not work?
Wrappers come into play only when looking up resource-ref resources inside of EJBs and Servlets (which are the only places resource-refs can be specified). As for Oracle AQ, as I mentioned you might want to get clarification from BEA about this. I would assume foreign JMS providers configured on WLS should work just like native ones since they are bound in JNDI and can be specified in resource-ref.


3.2 How does that work with Spring 1.2.x JmsTemplate102 class?
Is it enough to provide the JNDI BEA JMS factories / classes, to let ressource pooling come into play?

As long as you use JMS templates in places where WLS JMS wrappers are created, you should be fine. But in client code you wouldn't have the pooling. The only support spring gives is SingleConnectionFactoryBean (http://springframework.org/docs/api/org/springframework/jms/connection/SingleConnectionFactory.html) which can cache a single JMS Connection. As far as I know there isn't a generic JMS pool that pools Sessions/receivers/senders/producers/consumers/etc.


I do not want to manually implement a JMS publish/send.
(I read already the JmsTemplate Gotcha information but unfortunately I seem not to understand 100%).

The problem with a non-pooling ConnectionFactory is that JmsTemplate does the following for each send:

getConnection
getSession
createMessage
createSender
send
close sender
close session
close connection

This is standard JMS. Unless you are using a ConnectionFactory which does intelligent pooling of the various objects created for this process, it can get very expensive very fast. Especially since on WLS JMS Session objects are expensive objects.

My best advice based on what you've talked about:
- Create your MessageListener based POJOs reguardless of what you do. Keeping them as POJOs makes them more testable (I use MockRunner (http://mockrunner.sourceforge.net/) to help me unit test JMS based code like this).
- Wrap your POJOs in real MDBs. This isn't very hard to do using Spring's EJB convenience classes. I'll post an example of what I'm talking about in another post.
- Connect to Oracle AQ use the foreign JMS support in WLS. I believe this will allow your WLS application to send and receive messages and still only require you to program for WLS. But this might require more research to make sure it meets your needs. Personally I've never done this.
- For client you either can write standard JMS or if your client can send or receive in batch, use one of the execute methods to handle multiple messages inside of one callback.

Tools like Jencks/ActiveMQ try to help you free yourself from an application server environment when you don't need one. I don't think now is the time to make that change in your application.

Spring is designed to make your J2EE application development easier. And can even help you create an application run on simpler software. But not every application can run on simpler software. I use WLS 8.1 and am trying to use Spring from within that environment to make my life easier. But I"m not moving away from MDBs anytime soon.

wpoitras
Mar 8th, 2006, 11:18 AM
Here is how I would easily configure an MDB. It relies on a base class I came up with which loads the name of the service which implements your MessageListener. It supports the standard method of specifying a Spring context (specifying the list of xml files in ejb/BeanFactoryPath env-entry) or supports using a SingletonBeanFactoryLocator by gettings its BeanFactoryLocatorKey from an env-entry:

public abstract class AbstractMDB extends AbstractMessageDrivenBean implements MessageListener {
// JNDI name used to look up name of MessageListener
private static final String SERVICE_NAME_PARAM = "serviceName";
/* If using a SingletonBeanFactoryLocator, JNDI name used in setBeanFactoryLocatorKey.
* If null, expects default behavior, which looks in ejb/BeanFactoryPath for list of context files.
*/
private static final String LOCATOR_FACTORY_KEY_PARAM = "contextKey";
MessageListener listener;
public void setMessageDrivenContext(MessageDrivenContext ctx) {
super.setMessageDrivenContext(ctx);
String factoryKey;
try {
Context ic = new InitialContext();
factoryKey = (String)ic.lookup("java:comp/env/"+LOCATOR_FACTORY_KEY_PARAM);
} catch (NamingException e) {
factoryKey = null;
}
if (factoryKey != null) {
setBeanFactoryLocatorKey(factoryKey);
setBeanFactoryLocator(SingletonBeanFactoryLocator. getInstance());
}
}

protected void onEjbCreate() {
String serviceName;
try {
Context ic = new InitialContext();
serviceName = (String)ic.lookup("java:comp/env/"+SERVICE_NAME_PARAM);
} catch (NamingException e) {
throw new EJBException("Couldn't lookup " + SERVICE_NAME_PARAM + " value", e);
}
listener = (MessageListener)getBeanFactory().getBean(serviceN ame);
}

public void onMessage(Message message) {
try {
listener.onMessage(message);
} catch (Exception e) {
logger.info("onMessage failed", e);
}
}
}


To implement your MDB, use something like the following. I have used xdoclet (http://xdoclet.sourceforge.net/) to generate the MDB descriptors. If you don't plan on using xdoclet you can delete the header and specify the properties by writing the deployment descriptor(s) by hand. Notice that the class is empty. All the logic is either handled in the base class or in the Spring context.

/**
* @ejb:bean name="MDBName"
* display-name="MDBDisplayName"
* destination-type="javax.jms.Queue"
* destination-jndi-name="queueName"
* transaction-type="Bean"
*
* @ejb:env-entry name="serviceName"
* type="java.lang.String"
* value="fluxListener"
*
* @ejb:env-entry name="ejb/BeanFactoryPath"
* type="java.lang.String"
* value="/applicationContext.xml"
*
* ADD RESOURCE REF FOR QUEUE AND CONNECTION FACTORY HERE
* @jboss.container-configuration name="Standard Message Driven Bean"
* @jboss.destination-jndi-name name="queue/queueName"
*
* @weblogic:pool max-beans-in-free-pool="15" initial-beans-in-free-pool="5"
* @weblogic.message-driven connection-factory-jndi-name="connectionFactoryJndiName"
* destination-jndi-name="queueName"
*
* @author william.poitras
*
*/
public class MyMDB extends AbstractMDB {
}

Thomas.Heiss
Mar 9th, 2006, 04:31 AM
Dear William,

Thank you very much for your input on this advanced topics and posting also a very nice example.

Today I know about these facts:
1. The MDB's need to listen to three different JMS queues.
Probably I need to deploy therefore three MDB's. But the difference for all of them will be only the queue name.
Inside the MDB I will have to access a backend (XML HTTP API), and also send a reply message to a JMS queue.
I will go for the Spring JMSTemplate102 class for the JMS part.

XDoclet alone for MDB/EJB seems not helping there :)

2. I will create a Generic EJB (SLSB), but depending on a Statemachine and InterfaceManager, the EJB will have to do different things (flexible, dynamic).

2x JDBC: (is planned to be implemented as a Spring JDBCTemplate (in the
Backend JDBC InterfaceImpl's, not the Generic EJB directly).
There are two different JDBC backends (two different Datasources).

1x JMS: usage for JMS send would also be a JMSTemplate (as in the MDB).

I'll also have to find a way to make MDB/EJB Deployment descriptors and Spring-config.xml as much as flexible as possible.

Key question would be then, if the complete Spring/EJB resource-ref's should be defined for the GenericMDB/GenericEJB and be put into .jar's/.ear's (resource-ref's seem to be the requirement for the usage of BEA JMS wrappers).

Just the backend names (MDB topic names, JDBC datasource name's, table's, pl/sql procedure names) are dynamically set, part of in the statemachine config, and be passed down to the interface manager/interface implementations (the MDB and the SLSB's call the statemachine/interface manager's).

Maybe it would be better to split all the Spring configuration for each MDB/EJB. Or maybe it doesn't hurt if Spring provides one config with JMS + 2x JDBC even the ONE deployed EJB only requires the one specific JDBC references.

3. Access to shared Spring factory over EJB layer
If Spring get's initialized by SingletonBeanFactoryLocator / ContextBeanFactoryLocator in 3 MDB (+pooling!) and 3-4 EJB's, will the ONE Spring config / factory instance be shared over the complete EJB container?
I guess MDB's and Stateless Session EJB's share the same EJB container JVM?

Despite the fact, that each SLSB will probably be in it's own ear (a so called propriatary "plugin" to a deployed J2EE EAR application), would that work?
Is BEA using a separate JVM so a Spring singleton is not available anymore?
Not sure already about the MDB part.

Or do all MDB + SLSB have to be in one EAR file so SingletonBeanFactoryLocator is able to share the one Spring config?
I think this has to be a MUST for the J2EE application.

Uh, I really have to test this all out :)

If you could give me further advice, you are very welcome!

Thomas

wpoitras
Mar 9th, 2006, 08:12 AM
1. The MDB's need to listen to three different JMS queues.
Probably I need to deploy therefore three MDB's. But the difference for all of them will be only the queue name.
This is one area where XDoclet comes in handy. Since XDoclet is designed to be run from Ant, you can specify Ant variables in your XDoclet comments. So you could put ${mdb.queue.name} for the queue name and run XDoclet for each of the MDBs, each with a different queue name, and it would generate the proper XML files.


Maybe it would be better to split all the Spring configuration for each MDB/EJB. Or maybe it doesn't hurt if Spring provides one config with JMS + 2x JDBC even the ONE deployed EJB only requires the one specific JDBC references.
There is where I've seen people have trouble in Spring: application instantiation strategies. You can either:
- Create multiple small Spring configurations, one for each bean. Each bean will gets its own instance of the bean it looks up.
- Create one Spring configuration, make all the beans lazy-init, so only the ones which actually get looked up will be created in a given EJB. Each EJB will gets its own instance of the bean it looks up.
- Create one Spring configuration and use a SingletonBeanFactoryLocator so that one context will be shared across a single appliation (.EAR). Each bean will only get created once per application.


Despite the fact, that each SLSB will probably be in it's own ear (a so called propriatary "plugin" to a deployed J2EE EAR application), would that work?
Is BEA using a separate JVM so a Spring singleton is not available anymore?
Not sure already about the MDB part.

Each application will create its own copy of the context, and as such each application will have its own copy.

I don't think BEA uses its own JVM for each EAR, but its possible it uses a different classloader for each. So a SingletonBeanFactoryLocator will have one copy of a particular context per application.

It looks like your application has a fairly complex initialization setup that I'm not sure Spring will be able to accomodate. I'm especially concerned about the EJB that provides the name of configuration information (like queue names, datasource names, etc). I believe MDBs need to have the queue name be specified at deployment time, not at run-time. I'm not sure how you would let the Spring contexts know what the name of datasources are. Especially since the normal way Spring intializes beans for a particular EJB is when the EJB is created (which happens at app server startup). If each EJB gets it own copy of a particular bean, it may be possible to set the dynamic properties after the bean is retrieved from Spring. But not if you decide to use a SingletonBeanFactoryLocator.

Runtime dynamic configuration is one those things that Spring doesn't always play well with. Spring works best when object properties are present at the time an application context is created. This is where I'd expect you to have the most trouble.

gonzalad
May 5th, 2010, 12:19 PM
Very interesting solution.

I've just integrated it in my projet (in need also to use Ejb Mdb and want to delegate the work to Spring's MessageListenerAdapter).

I've added support for Spring's SessionAwareMessageListener interface (adding reply feature support) to the AbstractMDB class (I've also changed its name).

You just have to create a environment reference to a connectionFactory (env-name would be 'connectionFactory'.

Here's the Mdb class :


package com.mycompany.sphinx.integration.jms.ejb.support;

import javax.ejb.EJBException;
import javax.ejb.MessageDrivenContext;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.springframework.beans.factory.access.Singleton BeanFactoryLocator;
import org.springframework.ejb.support.AbstractMessageDri venBean;
import org.springframework.jms.listener.SessionAwareMessa geListener;

@SuppressWarnings("serial")
public class DelegatingMessageListenerMessageDrivenBean extends AbstractMessageDrivenBean implements MessageListener {

/** JNDI name used to look up name of MessageListener */
private static final String LISTENER_NAME_PARAM = "listenerName";
/**
* If using a SingletonBeanFactoryLocator, JNDI name used in setBeanFactoryLocatorKey.
* If null, expects default behavior, which looks in ejb/BeanFactoryPath for list of context files.
*/
private static final String LOCATOR_FACTORY_KEY_PARAM = "contextKey";
/**
* Connection factory jndi name used for replies (optionnal).
*/
private static final String CONNECTION_FACTORY_KEY_PARAM = "connectionFactory";
private ConnectionFactory connectionFactory;

private MessageListener listener;

public void setMessageDrivenContext(MessageDrivenContext aMessageContext) {
super.setMessageDrivenContext(aMessageContext);
String factoryKey;
try {
Context ic = new InitialContext();
factoryKey = (String)ic.lookup("java:comp/env/"+LOCATOR_FACTORY_KEY_PARAM);
} catch (NamingException e) {
//TODO : logguer en debug
factoryKey = null;
}
if (factoryKey != null) {
setBeanFactoryLocatorKey(factoryKey);
setBeanFactoryLocator(SingletonBeanFactoryLocator. getInstance());
}
try {
Context ic = new InitialContext();
connectionFactory = (ConnectionFactory)ic.lookup("java:comp/env/"+CONNECTION_FACTORY_KEY_PARAM);
} catch (NamingException e) {
//TODO : logguer en debug
connectionFactory = null;
}
}

protected void onEjbCreate() throws EJBException {
String serviceName;
try {
Context ic = new InitialContext();
serviceName = (String)ic.lookup("java:comp/env/"+LISTENER_NAME_PARAM);
} catch (NamingException e) {
throw new EJBException("Couldn't lookup " + LISTENER_NAME_PARAM + " value", e);
}
listener = (MessageListener)getBeanFactory().getBean(serviceN ame);
}

@SuppressWarnings("unchecked")
public void onMessage(Message message) {
try {
if ((listener instanceof SessionAwareMessageListener) && connectionFactory!=null) {
Connection connection = connectionFactory.createConnection();
Session session=null;
try {
session = connection.createSession(true, Session.SESSION_TRANSACTED);
((SessionAwareMessageListener) listener).onMessage(message, session);
} finally {
if (session != null) {
session.close();
}
connection.close();
}
} else {
listener.onMessage(message);
}
} catch (Exception e) {
handleError(e);
}
}

protected void handleError(Exception e) {
logger.error("onMessage failed : "+e.toString(), e);
throw new RuntimeException (e);
}
}
Here's the ejb-jar.xml :


<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar id="ejb-jar_ID" version="2.1" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd">
<display-name>
aws-ejb</display-name>
<enterprise-beans>
<message-driven id="EchoMessageDriven">
<ejb-name>EchoMessageDriven</ejb-name>
<ejb-class>com.mycompany.sphinx.integration.jms.ejb.support.D elegatingMessageListenerMessageDrivenBean</ejb-class>
<messaging-type>javax.jms.MessageListener</messaging-type>
<transaction-type>Container</transaction-type>
<message-destination-type>javax.jms.Queue</message-destination-type>
<message-destination-link>destination1</message-destination-link>
<env-entry>
<description>
</description>
<env-entry-name>contextKey</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>com.mycompany.aws</env-entry-value>
</env-entry>
<env-entry>
<description>
</description>
<env-entry-name>listenerName</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>echoMessageDrivenBeanAdapter</env-entry-value>
</env-entry>
<resource-ref id="ResourceRef_1272996907074">
<description>
</description>
<res-ref-name>connectionFactory</res-ref-name>
<res-type>javax.jms.ConnectionFactory</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
</message-driven>
</enterprise-beans>
<assembly-descriptor>
<message-destination>
<description>
</description>
<message-destination-name>destination1</message-destination-name>
</message-destination>
</assembly-descriptor>
</ejb-jar>
Here's a sample beanRefFactory.xml :


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

<description>
Chargement de la configuration Spring utilisée par nos Ejbs.
</description>

<bean id="com.mycompany.aws"
class="org.springframework.context.support.ClassPathXmlAp plicationContext">
<constructor-arg>
<list>
<value>classpath:spring/aws-ejb.xml</value>
</list>
</constructor-arg>
</bean>

</beans>
The sample Spring configuration file :


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">


<bean id="echoMessageDrivenBeanAdapter"
class="org.springframework.jms.listener.adapter.MessageLi stenerAdapter">
<property name="delegate" ref="echoMessageListener"/>
<property name="defaultListenerMethod" value="echo"/>
</bean>

<bean id="echoMessageListener"
class="com.mycompany.aws.jms.server.EchoMessageListener"/>

</beans>
And the sample EchoMessageListener class :


package com.mycompany.aws.jms.server;

public class EchoMessageListener implements IEchoMessageListener {
public String echo(String aMessage) {
System.out.println("echo : "+aMessage);
return "echo : "+aMessage;
}
}

gonzalad
May 5th, 2010, 12:31 PM
Created http://jira.springframework.org/browse/SPR-7171

Thanks