Tomitribe supports many of organizations that use ActiveMQ, JMS, and Message-Driven Beans in production with TomEE. This post is derived from experience working with those organizations to improve their system performance and software architecture.
The idea behind yesterday’s MDB tutorial was to divide up the work of spidering a web page among three different MDBs each with a specific purpose. This division of labor is an important concept in messaging in general and MDBs in particular. This article shows how MDBs can communicate with outside applications as well as other MDBs in a Java EE application server. This latter capability, MDB-to-MDB messaging, allows for powerful, asynchronous architectures and is a major benefit of MDBs.
The Workflow as a river delta
A workflow (any business process) is naturally thought of as a chain where each task must be performed in a regimented order, but often that is not the case. Sometimes tasks branch off like a river delta that breaks up into streams some of which just end and others that continue to branch or flow back together.
With branching tasks like this you have a couple of options. You can use threads or different process to handle separate streams by passing information in memory or over network connections. Frequently, however, a better solution is to use MDBs. Why? Because you get the efficiency of threads without the complexity and threat of deadlocks and the scalability enjoyed with separate processes without the latency of network communications. The following figure illustrates a flow of messages thorough many different MDBs. Notice the resemblance to the photo of the river delta.
The MdbExample as a river delta
The MdbExample demonstrates the branching illustrated above but on a small scale where the work of spidering a web page is broken up into three processes.
The SpiderMDB
subscribes to the SpiderTopic
. When it receives a web address from the JMS client it extracts the URL from the text message and downloads the web page. Next the SpiderMDB
parses the HTML web page extracting all the URLs for images and hyperlinks. The result is two lists, one for the image URLs and one for the hyperlinks.
Below is a snippet of code from the actual SpiderMDB
. The full source code with a plethora of comments can be found in your MdbExample
directory or at GitHub here.
package com.example;
import com.example.Parser;
import java.awt.Color;
import java.net.URL;
import java.util.List;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.*;
import org.apache.activemq.ActiveMQConnectionFactory;
@MessageDriven(
activationConfig = {
@ActivationConfigProperty(
propertyName = "destinationType",
propertyValue = "javax.jms.Topic"),
@ActivationConfigProperty(
propertyName = "destination",
propertyValue = "SpiderTopic")
}
)
public class SpiderMDB implements MessageListener {
public void onMessage(Message message) {
try {
TextMessage msg = (TextMessage)message;
Out.println(this, msg, Color.blue);
URL url = new URL(msg.getText());
Parser p = new Parser(url);
List imgUrls = p.imgSrc;
Out.println(this,imgUrls.size() + " Images",Color.blue);
this.forwardList(imgUrls, "ImageQueue");
List links = p.aHrefs;
Out.println(this,links.size() + " Links",Color.blue);
this.forwardList(links, "LinkQueue");
} catch (Exception e) {
e.printStackTrace();
return;
}
}
private void forwardList(List items, String queueName) throws Exception {
//forwarding code goes here
}
}
The SpiderMDB
loops through both URL lists within the forwardList()
method sending image URLs in messages to the ImageQueue
and hyperlink URLs in messages to the LinkQueue
downstream in the workflow.
private void forwardList(List items, String queueName) throws Exception {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection connection = factory.createConnection("admin", "password");
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue(queueName);
MessageProducer producer = session.createProducer(destination);
for(String item : items) {
TextMessage itemMsg = session.createTextMessage(item);
producer.send(itemMsg);
}
if (session != null) session.close();
if (connection != null) connection.close();
}
The ImageMDB
and LinkMDB
break up the processing of image links and hyperlinks, between them but also among instances so the MDB container might use five ImageMDB
instances to process 15 image links and seven LinkMDB
instances to process 30 hyperlinks. Here is the code for the ImageMDB
which is the same as the LinkMDB
. The actual source code, with lots of comments, can be found in the MdbExample
directory on your desktop or the at GitHub here.
package com.example;
import java.awt.Color;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.*;
@MessageDriven(
activationConfig = {
@ActivationConfigProperty(
propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(
propertyName = "destination",
propertyValue = "ImageQueue")
}
)
public class ImageMDB implements MessageListener {
public void onMessage(Message message) {
TextMessage msg = (TextMessage)message;
Out.println(this, msg, Color.orange);
}
}
If you take a look at the output again (see below) noticed that blue, orange and green lines are output from different MDB types (SpiderMDB
, ImageMDB
, and LinkMDB
respectively). You should notice that the identifier at the start of each line, the “@nn
“, identifies a specific MDB instance showing that each bean type (e.g. ImageMDB
and LinkMDB
) can scale by using multiple instances each of which processes a single URL.
The MDB architecture can greatly improve both efficiency and throughput compared to a rigid sequential process without the risk of threading issues or the latency of network connections.
Conclusion
The MDB Tutorial helps to illustrate how Message-driven beans can form the foundation of a very powerful, asynchronous architecture where each component has many instances each of which can consume a message from queue or topic. The ability to send messages from one MDB to the next allows the system enormous flexibility because messages are sent to specific topics and queues not to specific components. You can change the topics and queues that MDBs subscribe to as well as the ones they send messages to, this creates an incredibly flexible system where components can be added, replaced and removed without disrupting the work of the rest of the system.