| Sign In/My Account | View Cart |
So far, we've seen how inheritance can be used when calling an EJB directly through RMI. However, SOAP (web services) and JMS also allow you to invoke objects remotely. Recognizing this, the EJB committee introduced JMS consumer beans (message-driven beans) in version 2.0 of the specification, and, in version 2.1, a generic asynchronous mechanism allowing web service invocations.
This article discusses the steps involved in using inheritance in message-driven beans.
In order for the inheritance to occur, there has to be a key element present: object referencing. In the case of message-driven beans, there's no such thing! The main characteristic of message-driven beans is that they are not invoked synchronously through RMI, but rather asynchronously through JMS messages. These messages are processed by message-driven beans and are completely independent of their source. Plus, the JMS container makes the branching decision -- "which object processes which message?" -- not the programmer. Often, message-driven beans are managed as a pool of objects created in advance, to speed up processing.
|
A Note About the Example Source Code Download the source code for this article. This .zip file contains a WebLogic Server domain, EJB source code, and a PointBase database. Install this under C:\inherit. The start scripts contain the newest PointBase drivers. For the administration console, the username is still |
How can we take advantage of the abstraction, reuse, and maintainability promises of inheritance? If we can't control the way a message will be processed by creating objects, we can control this at the time of the bean invocation. I know of two techniques to control invocation:
In our example, we'll use a combination of techniques 1 and 2. We will have a Delegate message-driven bean, but this will re-post the message while setting a field for the message selection to occur. Our hierarchy of message-driven beans will pick up those messages.
The folks at RTM are very happy about the system so far, but the back-end ordering system is done manually, which is not a good feature for a web application. RTM needs an ordering subsystem.
RTM has exclusive contracts with the following fictive companies:
| Company | Coverage | Ordering Method |
| North-American Flights (NAF) |
• USA (except Hawaii) |
EDI |
| Pacific Ocean Air (POA) |
• Hawaii |
EDI |
| Continent Air (CA) |
• Europe |
Manually, through a broker. |
NAF handles the vast majority of flights, so it's the ideal candidate for modeling our base class. POA and CA are special cases, reusing some functionality from NAF; these will be the models of our subclasses. Figure 1 shows how the RTM ordering system will work.

Figure 1 -- ordering system flow
|
In This Series
EJB Free and Open Source Tools Summary
EJB Inheritance, Part 3
EJB Inheritance, Part 2
EJB Inheritance, Part 1 |
In this system, the following events occur:
company field is not set. The role of
the delegate is to figure out which company will handle the order.company field set to the proper value.company field.Before we go on, one last detail needs to be clarified. What will be the format for the messages? We'll use a class to represent our message. This class will contain the following information:
Figure 2 shows the traditional class diagram. Here we can see that the
message sent is an ObjectMessage, containing a Serializable
OrderMessage object.

Figure 2 -- ordering system class diagram
Still following the tradition, here's the list of steps required to use inheritance in message-driven beans. We're following the second technique, using message selectors.
SessionBean. Write bean
subclasses extending the base bean class.setMessageDrivenContext(),
ejbCreate(), and ejbRemove() methods. These can be
inherited. Overridden methods can call the corresponding super method
first.onMessage() and any business logic methods. The base bean class implements the normal interfaces:
MessageDrivenBean and MessageListener. The extra
short subclasses extend this base class. Here I show all three class
skeletons:
Example 1 - OrderBean declaration
public class OrderBean implements MessageDrivenBean, MessageListener
{
public OrderBean() {}
...
}
Example 2 -- POAOrderBean declaration
public class POAOrderBean extends baseorder.OrderBean {
public POAOrderBean() {}
...
}
Example 3 -- CAOrderBean declaration
public class CAOrderBean extends baseorder.OrderBean {
public CAOrderBean() {}
...
}
When I say "lifecycle methods," I of course mean
setMessageDrivenContext(), ejbCreate(),
ejbRemove(), and the like. These usually support the bean without
actually doing any business processing. Here, we write these methods in the
base class, inheriting them in the subclasses. This is why I only show you the
class OrderBean:
Example 4 -- OrderBean lifecycle methods
public class OrderBean implements MessageDrivenBean, MessageListener
{
...
public void setMessageDrivenContext(MessageDrivenContext ctx) {
this.ctx = ctx;
}
public void ejbRemove() {}
public void ejbCreate() throws CreateException {}
...
}
These methods don't do much here, but they could maintain connections to EDI systems, set up the necessary configuration to get ready to send an email, or prepare anything else needed before the real processing occurs.
onMessage() and Business Logic Methods So far the code isn't very interesting. The real work happens in
onMessage() and the methods it calls. Let's start with the bulk of
the work, in the OrderBean class:
Example 5 -- OrderBean onMessage and business
logic methods
public class OrderBean implements MessageDrivenBean, MessageListener {
...
public void onMessage(Message m) {
ObjectMessage objMsg = (ObjectMessage) m;
OrderMessage order;
try {
order = (OrderMessage) objMsg.getObject();
processOrder(order);
} catch (JMSException e) {
System.out.println("Error getting a message object in CAOrderBean." + e);
e.printStackTrace();
}
}
static int transNum = 0;
public void processOrder(OrderMessage o) {
transNum ++;
String ediMessage = "EDI TRANSACTION " + transNum;
ediMessage += ", FOR COMPANY: '" + getCompany() + "'.";
ediMessage += " BUY TICKET FOR: '" + o.name + "', ";
ediMessage += "FROM: '" + o.departureAirport + "', ";
ediMessage += "TO: '" + o.arrivalAirport + "'.";
// This simulates the sending of an EDI message.
PrintStream ediProcessor = System.out;
ediProcessor.print(ediMessage);
}
protected String getCompany() {
// The default is North-American Flights.
return "NAF";
}
}
Although static variables are not recommended (as they can misbehave), this is just a simulation so I'll use one anyway.
Notice how electronic document interchange (EDI) is being simulated (very
poorly) by printing to System.out. Also worth noting is the
separation of the processing in three methods. Why did we do that? To allow for
more reuse. onMessage() is the same in all three classes.
processOrder() is the same in OrderBean and
POAOrderBean. Only getCompany() (a single line) is
different in all three classes. So now let's have a look at these two last (and
extremely small) classes:
Example 6 -- POAOrderBean business logic methods
public class POAOrderBean extends baseorder.OrderBean {
...
protected String getCompany() {
return "POA";
}
}
Example 7 -- CAOrderBean business logic methods
public class CAOrderBean extends baseorder.OrderBean {
...
public void processOrder(OrderMessage o) {
// This order is handled by sending an email to this address:
String orderingBroker = "broker@FictiveCompany.com";
String messageBody = "Dear Fictive Company,\n\n";
messageBody += "Please send a ticket to this customer: " + o.name + "\n";
messageBody += " Going from: " + o.departureAirport + "\n";
messageBody += " To: " + o.arrivalAirport + "\n";
messageBody += " Aboard a " + getCompany() + " flight.\n\n";
messageBody += "Bill me to this account number: 3920938402192.\n\n";
messageBody += "Thank you so much,\n\n";
messageBody += "RTM automatic ordering system.";
// Here we would use the Mail API to send this email but this
// is not the purpose of this example so we'll just print the message.
System.out.println("\n============");
System.out.println("Sent this message to " + orderingBroker + " : ");
System.out.println(messageBody);
System.out.println("\n============");
}
protected String getCompany() {
return "CA";
}
}
Forgive my poor simulation of sending an email.
All classes are now written. Lastly, we write the deployment descriptors.
In particular, the content of ejb-jar.xml interests us, because we
want to do branching based on the content of a JMS message field. This is done
with the <message-selector> tag. A message selector is a
string resembling the inside of an SQL WHERE clause. It contains a condition. When a message arrives, the JMS server will find a message listener for which the message fulfills the condition. For more information about message selector syntax, check out Sun's
documentation for the Message class.
Now I'll just list the three beans' <message-driven>
tags, with the message selector in red.
Example 8 -- ejb-jar.xml, message-driven bean descriptors
<!-- Base Order (Message-Driven) -->
<message-driven>
<ejb-name>OrderEJB</ejb-name>
<ejb-class>baseorder.OrderBean</ejb-class>
<transaction-type>Container</transaction-type>
<message-selector>
<![CDATA[ company = 'NAF' </XMLCDATA>
</message-selector>
<message-driven-destination>
<destination-type>javax.jms.Queue</destination-type>
</message-driven-destination>
</message-driven>
<!-- POA Order (Message-Driven) -->
<message-driven>
<ejb-name>POAOrderEJB</ejb-name>
<ejb-class>poaorder.POAOrderBean</ejb-class>
<transaction-type>Container</transaction-type>
<message-selector>
<![CDATA[ company = 'POA' </XMLCDATA>
</message-selector>
<message-driven-destination>
<destination-type>javax.jms.Queue</destination-type>
</message-driven-destination>
</message-driven>
<!-- CA Order (Message-Driven) -->
<message-driven>
<ejb-name>CAOrderEJB</ejb-name>
<ejb-class>caorder.CAOrderBean</ejb-class>
<transaction-type>Container</transaction-type>
<message-selector>
<![CDATA[ company = 'CA' </XMLCDATA>
</message-selector>
<message-driven-destination>
<destination-type>javax.jms.Queue</destination-type>
</message-driven-destination>
</message-driven>
The OrderDelegate bean is a delegate; it forwards messages to
the real workers. We use it only to inject the needed message selection fields
in the message. This delegate is really a factory for creating messages. There
are many ways to implement such message factories:
Business Delegate or Service Locator
(client-side object that handles the message creation).Service Activator (a session bean that serves a similar
purpose to our OrderDelegate). I don't want to get too deep into the details of writing the
OrderDelegate object, mainly because the data is hardcoded in the
bean, which is a bad practice. The point is this: a message arrives, the data is
analyzed. Then, a new message is sent with with one JMS message field added to
the existing content.
Let's not forget a small but important detail. We don't want our delegate to
process the same message over and over again. For this, attach a mutually-exclusive message selector to this delegate. Here's the one we use for our
OrderDelegate:
<message-selector>
<![CDATA[ company IS NULL </XMLCDATA>
</message-selector>
This is the last article of the series. Or is it? There's a lot more to be said about EJB inheritance than there is space in these articles, but I think at least I provided you with a good push in the right direction. If you have ideas that you feel are interesting, or if you find I overlooked something, feel free to drop me a line. Your technical tips are welcome. In particular, I'm looking for:
In the future, I may continue this series if I gather enough interesting content. EJB inheritance is a fun concept to play with. It requires some work, but can be very useful. I hope this has been fun, engaging, and useful for you too.
Emmanuel Proulx is an expert in J2EE and Enterprise JavaBeans, and is a certified WebLogic Server 7.0 engineer. He works in the fields of telecommunications and web development.
|
Related Reading Enterprise JavaBeans |
Return to ONJava.com.
Maybe you have additional EJB inheritance articles that you could let me know about?
Thank you again.
Wayne.