ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


EJB Inheritance, Part 4

by Emmanuel Proulx
01/29/2003

Message-Driven Bean Inheritance

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.

The Problem with JMS

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 inherit and the password is still password.

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:

  1. Use a single type of message-driven bean as a Delegate, which branches to a hierarchy of objects.
  2. Use message selectors to let JMS do the branching for us, sending the message to the proper object in a hierarchy of message-driven beans.

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.

RTM Ordering System

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:

CompanyCoverageOrdering Method
North-American Flights (NAF)

• USA (except Hawaii)
• Canada
• Mexico

EDI
Pacific Ocean Air (POA)

• Hawaii
• Asia
• Oceania

EDI
Continent Air (CA)

• Europe
• Africa
• Other

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
What's the best platform for J2EE development? Emmanuel Proulx finds himself answering that question time after time. In this article, he explores several free-as-in-speech and free-as-in-beer EJB 2.0 tools and gives his suggestions for choosing an application server.

EJB Inheritance, Part 3
Session beans can take advantage of inheritance, just like entity beans. Indeed, implementing session bean inheritance is nowhere near as hard as it is with entity beans. Part 3 of this series shows the proper technique for implementing inheritance in session beans and addresses the use of factories.

EJB Inheritance, Part 2
Part two of this series on inheritance with Entity Java Beans focuses on the various options for table mapping.

EJB Inheritance, Part 1
The principles of object-oriented programming are encapsulation and inheritance. Enterprise JavaBeans handle encapsulation just fine, but what about inheritance? In this article, the author attempts to apply inheritance to EJBs.

In this system, the following events occur:

  1. An order message is received from the RTM application to the order JMS queue.
  2. There are four listeners for this queue. One of them is the delegate, which is invoked when the company field is not set. The role of the delegate is to figure out which company will handle the order.
  3. Once this has been decided, the delegate re-sends the message with the company field set to the proper value.
  4. Finally, one of the other listeners picks up the order, based on the value in the 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

Overview of the Steps

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.

  1. Create a base bean class implementing SessionBean. Write bean subclasses extending the base bean class.
  2. Write the classes' setMessageDrivenContext(), ejbCreate(), and ejbRemove() methods. These can be inherited. Overridden methods can call the corresponding super method first.
  3. Write the onMessage() and any business logic methods.
  4. Choose a message selector condition for each bean of the hierarchy.

Bean Classes

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() {}
   ...
}

Lifecycle Methods

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.

Message Selectors

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>

Delegate or Factory

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:

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>

The Last Episode?

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.

Enterprise JavaBeans

Related Reading

Enterprise JavaBeans
By Richard Monson-Haefel

Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.