One of the new features of Spring 2.0 is message driven-POJO's or MDP's. These are like MDB's but do not require the EJB framework. In Spring 2.0 you get a lot of different options for creating MDP's but in general you simply need to implement a JMS message listener and wire it into Spring.
I have implemented an MDP sample attached to this arcticle. It requires maven2 to build, so make sure you download and install that first.
Take a look at MessageTraderBean.java in the sample. The guts of the implementation is onMessage() which simply receives a quote on a JMS topic and prints it out:
public void onMessage(Message msg) {
ObjectMessage tm = (ObjectMessage) msg;
try {
Quote quote = (Quote)tm.getObject();
if (quote.getShares() > tradeLimit) {
log("Trade " + quote.toString() + " was denied");
}
else {
lastQuote = quote.toString();
log("Received new quote : " + lastQuote);
}
}
catch(JMSException ex) {
ex.printStackTrace();
}
}Note how there is no extraneous framework code and indeed no references to Spring at all - the beauty of dependency injection!
In order to the wire up the MDP we need a Spring configuration file which is src/main/webapp/WEB-INF/applicationContext.xml. Inside you can see various Spring classes being wired together, in this instance I am using org.springframework.jms.listener.DefaultMessageListenerContainer. For more information on these consult the Spring documentation. The one important thing to note here is the use of commonJ WorkManagers (I'm not going to show the code here since putting XML in a blog entry is such a pain!). Since we are going to be running the sample on WebLogic Server we don't want to start creating threads inside the application. Fortunately Spring 2.0 provides a way of deferring the asynchronous execution required by MDP's to WorkManagers as well as simpler thread pooling schemes. The WorkManager we are referencing is defined in web.xml (in the same directory) with WebLogic specific configuration in weblogic.xml. For more information on configuring WorkManagers consult the WLS documentation. For more information on Spring 2.0's TaskExecutor support consult the reference manual.
In order to use the example you need to create an appropriate JMS topic for publishing messages to. So fire up a server and start the console.
First of all you will need to create a persistent store for the server, so select Services->Persistent Stores and create a new FileStore. The name doesn't matter. You can select "." as the directory.
Next, you will need to create a JMS server using that persistent store. This is where the names start getting important in order for the Spring application to find the right topic. You will notice that I have called the destinationName in the applicationContext.xml "myserverJMSServer/spring-jms!quotes" which in English is translated as "the JMS server myserverJMSServer with JMS module spring-jms and topic quotes", so make sure that you call the new JMS server "myserverJMSServer". So select Services->Messaging-> JMS Servers and create a new JMSServer with that name and targetted at the persistent store you just created. Make sure also that you select the running server as a target.
Now you need a JMS module targetted at the JMS server. So select Services->Messaging-> JMS Modules and create a new JMS module called "spring-jms" and targetted at the running server. When asked if you would like to add resources, accept and create a new Topic resource called "quotes" and target it at myserverJMSServer. You don't need to set the JNDI-name for the topic.
You are now ready to deploy the application. Copy target/mdp-1.0-SNAPSHOT.war into the server's autodeploy directory. You should get a flurry of debug messages, but no exceptions. If you get an exception most likely you got one of the names wrong when setting up JMS. You may have to kill the server and start again if this happens since there was a bug in earlier releases of Spring 2.0 that caused the server to spew endless exceptions if the deployment names did not match. Hopefully this will not happen to you!
Once you have the app deployed, all that remains to do is to run the client. You can see that the guts of the client is to publish a message to a topic:
public void example() throws RemoteException, JMSException, NamingException {
Topic newTopic = null;
TopicSession session = null;
try {
session =
m_topicConnection.createTopicSession(false, // non transacted
Session.AUTO_ACKNOWLEDGE);
newTopic = (Topic) m_context.lookup(TOPIC_JNDI_NAME);
}
catch(NamingException ex) {
newTopic = session.createTopic(TOPIC_NAME);
m_context.bind(TOPIC_JNDI_NAME, newTopic);
}
TopicPublisher sender = session.createPublisher(newTopic);
ObjectMessage tm = session.createObjectMessage();
String[] symbols = new String[] {
"BEAS", "SUNW", "IBM"
};
float[] price = new float[] {
13.10F, 4.50F, 89.89F
};
int[] shares = new int[] {
1000, 50, 3000
};
for (int i = 0; i < symbols.length; i++) {
tm.setObject( new Quote(symbols[i], price[i], shares[i]));
sender.publish(tm);
}
}
Note that here too there is no reference to Spring 2.0 in the code. The client does not even know it is interacting with a Spring application. To run the client you will need to include target/classes in your classpath to pick up the client classes, so something like:
eagle Andrew Piper> java -classpath "target/classes;$CLASSPATH" com.bea.spring.Client t3://eagle:7001Beginning message.Client...
Success! created topic: myserverJMSServer/spring-jms!quotes and published a message.
End message.Client...
You should also see a message in the server console window:
Received new quote : 1000 of BEAS@13.1 Received new quote : 50 of SUNW@4.5 Received new quote : 3000 of IBM@89.89Congratulations! Your message driven POJO works!
If you have problems with the example let me know. I ran this with Spring 2.0 rc2 which is available from the maven repositories as well as WebLogic Server 9.1. There shouldn't be any problem running the example on later releases of either Spring 2.0 or WebLogic Server.
Comments (2)
Does WebLogic provide the same kind of management options for MDPs as it does for MDBs (e.g.: the ability to deploy in pools that grow/shrink based on message volume)?
Posted by Nicholas Trandem | July 28, 2008 7:04 AM
Posted on July 28, 2008 07:04
The thread pool can be backed by commonJ which gives you the option of using the self-tuning weblogic workmanager. How configurable you can make this at runtime I don't know - you would have to experiment a little. Note too that the original blog entry was written a while ago, so things may have changed in 10.3.
Posted by Andy Piper | July 28, 2008 7:30 AM
Posted on July 28, 2008 07:30