HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML

Inheritance and polymorphism are two of the most fundamental concepts in the object-oriented design world. They are used extensively in all Java applications, except J2EE apps using EJBs. Oh sure, developers implement various bean, remote, and home interfaces but often that's it. No significant class hierarchies. No polymorphic beans. Occasional reuse. Why is that?

In this article we'll first review why this is the norm in EJB-based applications and then demonstrate techniques you can use to reclaim some of these goals within the constraints imposed by the EJB standards. (We are assuming you're familiar with the basics of EJBs.) The crux of this article focuses on an application that takes the classic object-oriented design example of a hierarchy of vehicle types and translates it into polymorphic EJBs. The aim is to combine some advantages of inheritance such as code reuse and polymorphism with the advantages of EJBs such as distributed transactional components.

While this is not a universally applicable technique, you should by the end see how there's a suitable subset of problems that can be aptly solved with this approach. We'll also review interesting Java Naming and Directory Interfaces (JNDI) and other coding techniques you may find useful. A complete set of working examples (EJBs, client, descriptors, and scripts) are available below and have been tested under WebLogic 6.1 and JBoss 2.4.

Implementation Inheritance and Polymorphism
In class hierarchies you often want to treat an object instance as its base type rather than the specific specialized subtype that it is. This is a fundamental element of abstraction and is often manifest through polymorphic behavior you define a method that expects a graphics object of type Shape on which you'll invoke the draw method but aren't concerned at runtime if this Shape is a circle, square, or polygon subtype. You see this daily in Java development.

To achieve this behavioral characteristic there are two classic approaches: inheritance or interfaces. In the inheritance model there would be a base Shape class, likely defined as abstract, from which a set of subtypes would be derived. The base defines a set of methods and attributes and probably implements some shared behavior used by all subclasses. This implementation inheritance is typically an "is-a" approach: a circle is a shape.

In the interface model an interface such as shape would be defined, and any class that wants to "act-as-a" shape would implement the methods in this interface (although for "shapes" the typical design is "is-a"). This interface would define one or more methods that any implementer is required to define. Also note that interfaces themselves can form hierarchies and thereby achieve interface inheritance. Again, you see this throughout the various JDKs and your own code.

The key tradeoff between these approaches is often seen through a design model that differentiates between these "is-a" and "acts-as-a" behaviors, as well as the fact that Java supports only single inheritance and interfaces provide a mechanism that allows objects to fulfill multiple roles. One of the benefits of implementation inheritance is code reuse in which common code exists or is refactored into base classes and shared by all derived types. In interface inheritance this is possible only through other design techniques such as containment and delegation. (And it's fair to say that there isn't always a right or wrong approach to choose a healthy ongoing debate exists as to the design tradeoffs between the implementation versus interface inheritance.)

In EJBs, much of what occurs along these lines uses the interface-based approach. Many of the key APIs in the javax.ejb package are interfaces that your classes must define: EJBObject, EJBHome, SessionBean, EntityBean, etc. Within most EJBs the interactions with other objects typically occur through containment, delegation, and other "uses" relationships rather than through inheritance. This is reflected in most of the common EJB design patterns: session facade, value objects, and business delegates. Let's look into why.

Vehicle Hierarchy Example
The classic introductory example of inheritance and polymorphism a hierarchy of vehicle types typically looks like Figure 1.

Figure 1
Figure 1

In the Vehicle base class we want to define a set of methods and attributes that all vehicles will support. Subsequently, at runtime we want to be able to treat instances of the vehicle hierarchy equally so we can invoke the startEngine() without concern for whether the exact subtype being used is a car, truck, or other derived type. In addition, the base class will implement some of these methods so they need not be redefined for each subtype, and some common attributes, such as fuelLevel, will be defined in the base class as well. This is all fairly basic.

In the world of EJB this isn't nearly so simple. With EJBs a component is not one object, it's three: the bean, its remote interface, and its home factory. It's these three parts that cause nearly all the complications. It's only by virtue of the Java language that each of these parts may be base or derived types, but the EJB as a whole is not "inheritable." (As a matter of fact, the final EJB 2.0 specification in Appendix D.4 states, "The current EJB specification does not specify the concept of component inheritance. There are complex issues that would have to be addressed in order to define component inheritance." But it does go on to say, "however, the bean provider can take advantage of the Java language support for inheritance" using interface inheritance and implementation inheritance both of which we do here.)

Figure 2 shows the bare-bones vehicle hierarchy and the core EJB design that we'll discuss later.

Figure 2
Figure 2

See how quickly the boxes multiply. Yet from an EJB perspective this is quite a simple picture. On the client side (for the home and remote interfaces) there's no extra inheritance here; it's just the bare minimum that's required for any EJB. On the bean side there's just a small hierarchy of those two derived types inheriting from a base (this time VehicleEJB), which in turn implements the standard SessionBean interface.

We'll continue with our example in more detail later, but it's instructive to first look at how our simple pieces in Figure 2 fit into the final bigger picture (see Figure 3).

Figure 3
Figure 3

As you can see, this is more complex still. What this figure demonstrates is the greater whole into which all EJBs fit. At the top are the base interfaces, such as java.rmi.Remote and java.io.Serializable, while at the bottom are the actual container-generated final implementation classes. (Note that each server is free to define and construct these actual implementations as it sees fit for example, JBoss dynamically constructs implementations whereas WebLogic builds these when the EJBC compiler is run.)

Because of the potential complications in utilizing inheritance with EJBs, we've chosen a relatively simple design pattern for our approach. The key is that we perform inheritance only on the bean implementation side, expose a common remote interface, and use JNDI lookups to grab the appropriate binding at runtime.

To focus on polymorphism and object behavior, our design centers on stateful session beans (and is equally applicable to stateless session beans). With entity beans there are additional complications, such as returning differing primary keys, which we'll discuss in more detail later. As for message-driven beans, because they have no homes or remotes and a single asynchronous onMessage() point of entry, our model doesn't really apply. (As a side note, IBM, in their VisualAge for Java product, provides extensions for EJB inheritance, but because these are proprietary we don't cover them here.)

The Code
Our vehicle example consists of a single client application with the following:

  1. A main() method taking arguments to specify vehicle type, color, etc.
  2. A buyVehicle() method that obtains home and remote references to an EJB of the appropriate runtime type (a car or truck)
  3. A testDrive() method to drive the generic vehicle
The first thing the client's main() method does, after validating the command line, is obtain the appropriate JNDI context. This context can be specified on the command line or use default values (we go into more background on JNDI shortly).

To connect to the JNDI tree, a context has to be created with an optional Hashtable as a parameter. The Hashtable values specify the provider's JNDI context factory as well as the URL to the JNDI tree. The code below shows how this is retrieved for JBoss (note that WebLogic's JNDI connection factory is a class called "weblogic.jndi.WLInitialContextFactory" and the default URL is "t3://localhost:7001").

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory ");
env.put(Context.PROVIDER_URL,
"jnp://localhost:1099");
Context context = new InitialContext(env);
Leveraging JNDI
With that context in hand, the client can call its own buyVehicle() method to get a VehicleRemote based on the command-line options. It's here that things get a little interesting.

First of all, the EJB specification requires a session bean to define an EJBHome object that contains factory-like create methods to construct the bean. In our case, this is a simple two-argument create() that takes the vehicle color and an air-conditioning flag as arguments and returns a VehicleRemote reference. The remote interface contains our shared "business" methods: startEngine(), accelerate(), brake(), and getFuelLevel(). All vehicle implementations use the same home and remote interface.

You might now ask: How can I access a specific subtype vehicle implementation on a J2EE server when the home and remote interfaces are the same for all vehicles?

This happens with the magic of JNDI. Within an EJB container, and a J2EE server more generally, the required JNDI services provide a mechanism to store object instances within a globally accessible namespace (a JNDI tree). These objects can then be remotely retrieved using the JNDI name as a key. In addition, the EJB specs require application servers to publish deployed EJB home interfaces using JNDI names as defined in each EJB's deployment descriptor.

This means we can use JNDI as a level of indirection. First, each concrete vehicle implementation gets bound to the JNDI tree under its own unique JNDI name (to be more accurate, it's the home interface that's looked up by name). This VehicleHome object is then used to create a VehicleRemote reference that in turn instantiates and manages (with the assistance of the container) the designated EJB instance. It's this association between a home/remote pair and an EJB class, which is made in the deployment descriptor, that allows us to use the same home and remote interfaces but create different subtypes on the server. If this sounds a bit confusing, the next section may help clarify things.

The mechanism for this binding is specified in the following two deployment descriptors: the generic ejb-jar.xml used to describe the session beans (see Figure 4) and the vendor-specific decriptor used to assign a JNDI name to each EJB jboss.xml or weblogic-ejb-jar.xml for JBoss and WebLogic, respectively (see Figure 5). (Note that in this example we've chosen to assign the beans JNDI names that are the same as their EJB names in these descriptor files this is not required, we could have chosen differing JNDI names without breaking the rules of J2EE or our polymorphism.)

Figure 4
Figure 4

Figure 5
Figure 5

In our example, the result is that we can use the exact same code on the client side, but at runtime can construct and interact with different EJB subtypes by simply changing the JNDI name we look up.

Here's an example of the code that does this using the context we obtained earlier. First a VehicleHome is obtained from JNDI based on the binding name specified:

VehicleHome vehicleHome = (VehicleHome) context.lookup("Car");

Now we can create a specific vehicle (a CarEJB) using the home interface's create method that takes two arguments, a color and an air-conditioning boolean flag:

VehicleRemote vehicle = vehicleHome.create("silver", false);

Finally we get to drive: we start the vehicle, get up to speed, slow down for a curve, and then turn:

vehicle.startEngine();
vehicle.accelerate(55);
vehicle.brake(30);
vehicle.turnSteeringWheel(angle);
This block of code can work equally well if its remote reference is to a CarEJB or a TruckEJB.

Figure 6 shows a similar example in which a client first does a JNDI lookup on a "Car", gets the home, creates the remote, and then drives the car. It then does a lookup on "Truck" and can perform the identical steps.

Figure 6
Figure 6

Issues and Alternatives
As we've shown, our vehicle example capitalizes on some of the inheritance wiggle room left in the EJB specifications. However, there are additional constraints defined by the specifications that hinder what otherwise might seem viable approaches to using inheritance.

For example, it might seem that creating a hierarchy of home interfaces that parallels the beans would be nice. But the create() method of an EJBHome poses an interesting problem. In particular, the Java language does not allow overloading method signatures based on return type. Why does this matter? Because it means you can't do this:

public BaseHome extends EJBHome {
public BaseRemote ejbCreate() throws RemoteException;
}

public DerivedHome extends BaseHome {
public DerivedRemote ejbCreate() throws RemoteException;
}
In addition, the ejbCreate() method of bean-managed entity beans must return a primary key and derived classes must return that same type. This is true for single object finder methods as well. With finders, whether returning single objects or Collections, it would be nice if, for a hierarchy of EJBs, the base class finder could return the right subtype(s) for the given search criteria, but unfortunately this can't happen automatically with the current standard. Which in the end is why home interface inheritance is particularly problematic and why our examples avoid it too messy.

Not to say that there aren't other EJB inheritance alternatives to consider.

A more elaborate approach is to use interface inheritance on the client side, where each derived type on the bean side gets a corresponding derived remote interface on the client side (see Figure 7).

Figure 7
Figure 7

The advantage to this approach is that it allows the client to access methods defined only in the derived types, not just those in the base Vehicle class. For example, if TruckEJB adds an unhookTrailer() method, that can be added to the TruckRemote interface. The disadvantage is that the client, at some point in the code, must now explicitly differentiate between derived types. It can no longer just pass in a different JNDI name and get a VehicleHome; it must create distinct home types (remember, we can't have a single home create different remote types). Still, at most points the client code can benefit by interacting with the more general VehicleRemote objects anywhere specific subtype methods are not needed.

(Keep in mind that in our primary example, the base VehicleEJB also adds a bit of value by fulfilling the role of an adapter pattern by providing default implementations of the EJB life-cycle methods such as ejbRemove(), ejbPassivate(), and setSessionContext() in which we cache the context in a protected member variable.)

Another technique, one that isn't so much an alternative as it is a minor modification to the first design, is to define a business interface called Vehicle and have both VehicleRemote and VehicleEJB implement this interface (see Figure 8). This provides a degree of consistency and explicit signature enforcement across both sides.

Figure 8
Figure 8

In the end, if and how you choose to use polymorphism and inheritance with your EJBs will naturally vary according to your needs. Over time, the EJB specifications will inevitably evolve and the ways in which this can be done will evolve along with them.

Author Bios
John Musser is a lead architect on an e-logistics system and teaches software development at Columbia University. Over the past 20 years he has built software ranging from Wall Street trading systems to games for Electronic Arts. [email protected]

Bernhard Lenz is a senior consultant with over eight years of experience in distributed, component-oriented, and multitiered systems. His clients include a varieity of major Wall Street firms in insurance, banking, and brokerage. He is currently working with John Musser on a large-scale J2EE e-logistics system. [email protected]

Source Code for this Article (~ 25.5 KB ~zip format )

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.