Skip to main content

Component Inheritance in EJB 2.0

October 25, 2005

{cs.r.title}









Contents
Just What Is So Bad about
Not Having Component
Inheritance?
Getting Our Bearings: Picking Our Tools
   Components
   Component Inheritance
What Are We Trying to Solve with
Component-Oriented Programming
(COP) that
Object-Oriented Programming (OOP) Cannot?
Our Implementation of
Component Inheritance in EJB 2.0
What is Missing from Our Solution?
Looking Forward:
Incorporating It into the Specification
   Defining Applications
   Changes to ejb-jar.xml
   Scenario One
   Scenario Two
Conclusion
Resources

Over the last few years, while "http://java.sun.com/products/ejb/">Enterprise JavaBeans (EJB)
has gained in fame, it has simultaneously received much criticism.
Numerous articles and blogs have expounded on why EJBs should not
be used, lamenting their poor design. Perhaps the biggest complaint
has been that EJB does not allow component inheritance for entity
beans. The EJB
2.0 specification
itself appears to confirm this criticism,
declaring "the data model for container-managed persistence does
not currently support inheritance. Therefore, entity objects or
value classes of different types cannot be compared." Despite this
declaration, EJB 2.0 can support component inheritance.

Just What Is So Bad about Not Having Component
Inheritance?

One of Sun's main goals in creating EJBs was to accomplish for
Java enterprise software what ActiveX/COM had achieved for
Microsoft's development platform. The first goal of the EJB
specification is to make it possible to build applications from
custom components used alongside components purchased from
different vendors. Sound similar to COM and ActiveX? The
specification aimed to take components even a step further and
enable deploying the application and components onto multiple
platforms.

However, without component inheritance and polymorphism, those
components would be too rigid and unable to properly describe the
business context of each potential customer. Take the example of a
company that sells store components, such as
ProductBean. The bean has a set of predefined
attributes like productId. A customer, Small
Company, Inc., wishes to add attributes and functions to better
represent their current product schema. Without inheritance and
polymorphism, their beans would be incompatible with the store
components that expect ProductBeans. They would lose
the ability to integrate custom and third-party software, and to
properly represent their business rules.

Getting Our Bearings: Picking Our Tools

Components

In the world of component-oriented development, there is much
disagreement over what constitutes a component and thus what
component inheritance is, arising in part from confusion about the
difference between objects and components. There are similarities
between the two; for example, both have interfaces and types, can
participate in inheritance and polymorphism, and result in
instances within a system. This has led to the erroneous assumption
that entity beans are objects (see the article " "http://www.onjava.com/pub/a/onjava/2002/09/04/ejbinherit.html">EJB
Inheritance
" for an example), but they are actually components
that have and support instances.

Since entity beans are constituted by and acting through
objects, it seems like entity beans violate one of the tenets of
components; that they have no externally visible state. The entity
bean component does not have an externally observable state;
rather, the objects by which it is constituted have state. An entity
bean component is not simply the object returned by
beanHome.findByPrimaryKey(). It is the home, the remote
and local interfaces, the bean class, the XML descriptor file, any
remote stubs and any objects, and classes or other items used to
constitute the component. This is why ejbHome methods can be
implemented in the same class file as "EJBObject instance" methods.
This distinction is what Clemens Szyperski calls "the separation
of the immutable 'plan' from the mutable 'instances.'" (See
Szyperski's "http://www.amazon.com/exec/obidos/tg/detail/-/0201745720/">Component
Software: Beyond Object-Oriented Programming
.)

The differences between objects and components start with their
approaches to software. The object-oriented approach is concerned
first with design and development, while the component-oriented
approach aims towards deployment (see Stuart Halloway's "http://www.amazon.com/exec/obidos/tg/detail/-/0201753065/">Component
Development for the Java Platform
for more on this
distinction). As a result, a component's type (as defined by the
interfaces it supports) and its implementation are separated. The
separation allows a deployed component to change its
implementation, or code, without breaking its contract with other
components. In contrast, once an object's class is defined
in a class loader, it cannot be redefined in that class loader; the
implementation becomes permanent for the runtime.

summary=
"This table holds another table. It's for formatting only.">
summary=
"This table lists the differences between objects and components.">
"#FFFFFF">Comparing Objects and Components
Objects Components
  • Have a unique identity
  • Encapsulate/express state and behavior
  • Have behavior and structure defined by classes
  • Are designed (through classes) for development contexts
  • Are units of instantiation
  • Have no externally observable state
  • Have type and implementation separated
  • Conform to standards set by a component model
  • Are built for deployment contexts
  • Are units of independent deployment

Building on those attributes, a component is a typed, deployable
unit of functionality that adheres to a contract of specified
interfaces and deployment rules with a component model framework. A
component has no observable state despite any instances or data it
may create. Further, a component such as an entity bean is not
simply its interfaces, EntityBean class, and descriptor
files, but also any objects, code, or other resources created by the
container in its deployment.

Component Inheritance

Inheritance actually consists of two concepts: inheritance and
inclusion polymorphism. Inheritance is the incorporation of
abstraction aspects in an interface or implementation by one entity
of another. Polymorphism is the ability for one thing to appear in
multiple forms depending on context, and the ability of different
things to appear the same in a certain context. Inclusion
polymorphism is more frequently referred to as sub-typing.
For this article, we will use "component inheritance" to refer to
the idea of both inheritance and inclusion polymorphism of
components.

Component inheritance can be confusing and difficult to grasp
for OOP developers due to choices in implementation and
demarcation. Some implementations, such as ours, rely in part on
object inheritance. Other choices might use delegation, proxies, or
other devices that may not fit the object-oriented inheritance
definition.
The hierarchy mapping is also specified in a less
overt manner. In Java's object inheritance, the relationship is
clearly marked by the key word extends. Component
inheritance might be declared in an XML file, as a metadata key, or
via some other manner--it may not be evident in the code at
all.

Components' separation of type and implementation offer a new
inheritance paradigm for OO developers. In OO, if an object
Magazine's class inherited from Product
and was defined in a class loader, the class loader could not load
a new Product implementation without breaking
Magazine. Further, Magazine had to be
compiled against Product code--Product's code had to be available at compile time.
Components, on the other hand, do not require the base component's
code at compile time. This is one of the most powerful advantages
for components. A base component's implementation can be changed at
runtime without breaking the inheritance relationship.

Component inheritance includes the ability to execute cross-type
actions. Thus, if we have a base Employee component and
the sub-types Manager and WorkerBee, we
can ask the Employment "family":

[prettify]
//Give me all the employees in Department 2
Collection employees = 
   employeeHome.getEmployeesInDepartment( 
         new Integer( 2 ) 
);
[/prettify]

and expect to get back a collection of both Manager
and WorkerBee Employees.

Inclusion polymorphism works as it does for objects: components
can be treated in the same manner and handled in the same context
when treated as a base type. The implementation of the base
component's inherited interface can be either overridden or
utilized by the sub-type. Lastly, the base component has no
knowledge of the sub-types that have inherited from it. This does not
stop bounded inheritance, or the ability to restrict sub-types, but
leaves this control in the hands of the deployer.

What Are We Trying to Solve with Component-Oriented Programming
(COP) that Object-Oriented Programming (OOP) Cannot?

Why are EJBs components and not Plain Old Java Objects
(POJOs)?

Many "enterprise" projects require numerous complex technologies,
such as security architectures, transaction management, data
manipulation, messaging, and other services. Custom solutions could
be developed for these needs, but doing so would render most
projects too expensive and unviable. Similar functionality has
already been developed and tested by many groups. On the opposite
end of the spectrum, a project could be fulfilled by an
"off the shelf" application. However, this approach may not address
the need for customization or combining functionality with existing
software. Neither extreme is a perfect solution.

Component-oriented programming offers a balance between the
extremes. A component-model framework gives companies the ability
to combine custom software with tested, pre-developed software to
provide services outside of the scope of a project or skills of their
team. Components offer a great degree of customization on two
levels:

  1. Purchased components can be configured and customized during
    deployment.
  2. Custom components, as units of independent deployment, can be
    used alongside third-party components.

A component can be deployed into numerous contexts and
implementations of the model framework without code changes. This
is enabled by loose coupling of components with services through
adherence to interface contracts. An entity bean can use the same
code (ejbStore()) to prepare instance data for
persistence regardless of whether it will be stored in a database,
XML file, email, or any other source.

Some programmers may want POJOs to replace EJBs due to a
misconception about the purpose of EJBs, borne in part from the
business context in which EJBs came to the industry. Sun needed to
make sure the EJB architecture was adopted by vendors, and more
importantly, give the vendors a market. While Sun wanted a new
paradigm, they knew most companies would be unwilling to scrap
their existing systems. Most were built on top of RDBMSes, and if
Sun's new product had been incompatible, it would not have been a
good business decision. Unfortunately, this resulted in too close
of a relationship between EJBs and RDBMSes, both in design and in
the minds of developers.

As a result, most resources on EJB speak of entity beans as
though they were simply an object facade for database entries.
Container-managed persistence is frequently regarded as
synchronizing an entity bean with a database record (one example of
this can be found in "http://www.amazon.com/gp/product/0596002262/">Enterprise
JavaBeans, 3rd Edition
, by Richard Monson-Haefel). While all of
the popular EJB containers work only with databases, that is an
implementation choice, not a specification requirement.
Entity beans could represent emails persisted into email servers,
files, XML, or some other entities we have yet to imagine. In fact,
Sun's definition of EJBs makes no mention of data or
databases. "The EJB architecture is a component architecture for
the development and deployment of component-based distributed
business applications."

Part of the problem also stems from shifting from OOP's
development/compile-time focus to COP's deployment-oriented focus.
Perhaps that is why components in general have not caught on in the
world of Java like they did in Microsoft's world.







Our Implementation of Component Inheritance in EJB 2.0

Our implementation of inheritance in EJBs can be used for any
CMP entity bean components. In our sample application, the Music
Store, the store sells CDs and magazines--both music-related
products. These products have common and type-specific attributes
such as a music genre category. Customers can search for all "Rock"
products regardless of whether they are CDs or magazines.

First we need to write the code to implement inheritance. In a
component model that explicitly enables inheritance, this would be
unnecessary. However, the specification does not explicitly
incorporate inheritance, so we need to write the "plumbing"
ourselves.

The first part of inheritance mapping is our entity bean
FamilyMemberBean, which registers each component with
its proper family. It also handles getting the bound object via
JNDI for each sub-type. Each instance EJBObject of
FamilyMemberBean represents a sub-type component. In
our sample, the Product family has the sub-types
CD and Magazine.

[prettify]//Home interface
public interface FamilyMemberLocalFactory 
extends EJBLocalHome
{
    /*
     * MemberName is a compound primary key with 
     * the familyName and component's schemaName
     *
     * The CD entity bean could be added as:
     *
     *  familyHome.create( 
     *          new MemberName( "Product", "CD" ), 
     *          CDRemoteHome.class.getName(),
     *          CDLocalHome.class.getName()
     *  );
     */
    create(
        MemberName name, 
        String factoryClass, 
        String localFactoryClass
    )
    {...}
}

//EJB Object interface
public interface FamilyMemberLocal 
extends EJBLocalObject
{
    //Retrieves local/remote interface of our sub-type.
    public String getTargetClass(boolean local) 
    throws ClassNotFoundException;

    //Returns the actual bound interface from JNDI
    public Object getBoundInterface(
        InitialContext env, 
        String factoryClass
    ) throws InheritanceException;
}
[/prettify]

Although the FamilyMemberBean handles the mapping,
no component family ever need know of its existence. The base bean of each component
family extends InheritableComponent. This
class is our "plumbing." It handles all component inheritance
mapping and actions, and is abstract in order to force the family's base
class and sub-types to implement required pieces of
information.

[prettify]
public abstract class InheritableComponent
{
   /**
    * This is the sub-type's name. E.g. in our 
    * sample we would have "CD" and "Magazine". 
    * We call it schema name to match "Abstract 
    * Schema Name" from the EJB specifications 
    * for ejb-jar.xml.
    */
   public abstract String getSchemaName();
   
   /**
    * The name of the component family. E.g. our
    * sample has the "Product" family.
    */
   public abstract String getFamilyName();
   ...elided...
}
[/prettify]

In component inheritance, there are two manners of cross-type
actions: family-scope and single-scope. A family-scope action is
called across some or all sub-types of the family; for example,
finding all jazz-related Products. A single-scope
action acts only against one particular sub-type, for example
finding a CD that has primaryKey == 13142.
InheritableComponent makes these scopes available to
the component family's base bean via two methods:

[prettify]
/*
 * Family-scope methods return as collections.
 * If a sub-type's Home method returns a 
 * collection, that collection's items, not 
 * the collection itself are added to this 
 * collection.
 * 
 * The local parameter exists because there is no
 * way in the current specification to know if
 * the CMP bean was called from a local or remote 
 * context. So each method must explicitly state 
 * that choice.
 */
protected Collection executeFamilyScopeMethod(
    String methodName,  //Sub-Type's Home Method
    Class[] parameters, //Method's Param Types
    Object[] arguments, //Arguments for Method
    boolean local       //Is it from local/remote?
) throws FinderException;
   
/*
 * All Single-scope methods are returned as Objects.
 * A Home method could return a Collection, or 
 * some other Object. It is up to the base or
 * sub-type bean to properly cast this (or leave 
 * it as Object).
 */
protected Object executeSingleScopeMethod(
    String requestedSchema, //Schema Name (e.g. CD)
    String methodName, 
    Class[] parameters, 
    Object[] arguments, 
    boolean local
) throws FinderException;
[/prettify]

You might notice the methods seem similar to those used in Java
reflection. That's because we make use of reflection in our
implementation. This was done for a few reasons:

  1. To avoid introducing a new API, such as byte-code
    engineering.
  2. Saving Class instances to cast the bound Object
    leads to ClassCastExceptions when the sub-type is
    redeployed.
  3. This article aims to explore developmental and functional
    benefits of EJB inheritance, not produce amazingly performant
    code.
  4. The inheritance code is usable by any family, not just
    Product.

The scoped methods can be called by the family's base bean in a
"Home Method." For instance, in our Product family, a
Magazine entity, Jazz World Magazine, and a jazz CD, Satch Plays
Fats
, can both be returned via:

[prettify]
//Part of our Home interface 
public interface ProductRemoteFactory 
extends EJBHome
{
    //Find products by category, e.g. Jazz.
    public Collection getProductByCategory(
        Integer categoryID
    ) 
    throws FinderException, RemoteException;
    ...elided...
}

//Our base bean's corresponding Home Method
public abstract class ProductBean 
extends InheritableComponent 
implements EntityBean
{
    public Collection ejbHomeGetProductByCategory(
        Integer categoryId
    ) 
    throws FinderException
    {
        //Calls findByCategory( categoryId ) on 
        //sub-type Homes
        return executeFamilyScopeMethod( 
            "findByCategory",
            PARAMETERS_CATEGORY,
            new Object[] { categoryId },
            true 
        );
    }
}
[/prettify]

There are three other methods in
InheritableComponent that are important for our
discussion: addToFamily(), getFamily(),
and removeFromFamily().

[prettify]
public abstract class InheritableComponent
{
    /*
     * This method checks with FamilyMemberBean
     * for this registration. If it exists,
     * it changes it to this new info,
     * treating it as a redeployment. If not,
     * it creates the registration.
     */
    final protected static void addToFamily(
        MemberName name, 
        String factoryClass, 
        String localFactoryClass
    );
    
    /*
     * Removes/unregisters a sub-type component
     * from the family.
     */
    final protected static void removeFromFamily(
        MemberName name
    ) throws InheritanceException;
    
    /* 
     * Gets the complete registration of sub-types
     * for a family. Used for cross-type
     * actions within a component family.
     *
     */
    final protected static Collection getFamily(
        InitialContext env, 
        String familyName
    ) throws NamingException, FinderException;
    ...elided...
}
[/prettify]

Sub-type components are added to a family when its bean class is
loaded into the JVM via a static block. Static blocks
and variables are allowed by the EJB specification as long as they
do not imply or require the ability to change any variables via
instances. While InheritableComponent manages the
inheritance hierarchy and actions, each sub-type must register
itself. This is essential to ensure the base component requires no
knowledge of the sub-types.

[prettify]
/* Our Example CD bean, a sub-type of Product. */   
public abstract class CDBean extends ProductBean
{
    final public static String SCHEMA_NAME = "CD";
    
    static
    {
        /*
         * This static block registers the 
         * CD component for the Product family.
         * Since we use class names, it works
         * across VMs and redeploys of the bean.
         */
        ProductBean.addToFamily( 
            new MemberName( //Primary Key
                ProductBean.FAMILY_NAME, 
                SCHEMA_NAME 
            ), 
            CDRemoteFactory.class.getName(), 
            CDLocalFactory.class.getName() 
        );
    }
    
    public String getSchemaName() 
    { 
        return SCHEMA_NAME; 
    }
    
    public Integer ejbCreate(
        Integer id, 
        Date dateCreated, 
        String songList,
        String name,
        Integer category,
        double price
    ) 
    throws CreateException, RemoteException
    {
        //Defer to base for common fields
        super.ejbCreate( 
                id, 
                name, 
                category, 
                price 
        );
        
        //Set CD fields
        setDateCreated( dateCreated );
        setSongList( songList );
        return null;
    }
    
    public void ejbPostCreate(
        Integer id, 
        Date dateCreated, 
        String songList, 
        String name, 
        Integer category, 
        double price
    )
    throws CreateException
    {
        //Call base component post create
        super.ejbPostCreate( 
                id, 
                name,
                category, 
                price 
        );
    }
    
    /*
    * CMP Fields. Notice some fields are missing,
    * such as the primary Key (Id), Name, etc.
    * They're common to all Products, so they're
    * in our base component.
    */
    public abstract void setDateCreated(Date dateCreated);
    public abstract Date getDateCreated();
    public abstract void setSongList(String songList);
    public abstract String getSongList();
}
[/prettify]

Notice how little code there is in our sub-type bean, which is
one of the benefits of component inheritance.

Making the sub-type's interfaces extend the base interfaces
enables polymorphism.

[prettify]
/*
 * Our base interface for both remote and local interfaces.
 * Notice it has RemoteException on each method. That's
 * because you can hide exceptions, but not add them in
 * overriding. This enables dealing with all Products, local
 * or remote in the same way - something that should have
 * been a part of the EJB spec.
 */
public interface Product
{
    /*
     * This has to be implemented in the base bean class
     * even though EJBObject has the method as component 
     * inheritance is not an explicit part of the 2.0 
     * specification. In this example, it would
     * simply return getId().
     */
    public Object getPrimaryKey() throws
        RemoteException, EJBException;
    
    //This is a facade for getSchemaName()
    public String getProductType() throws
        RemoteException, EJBException;
    
    public Integer getCategory() throws
        RemoteException, EJBException;
    public double getPrice()  throws
        RemoteException, EJBException;
    public String getName() throws
        RemoteException, EJBException;
}

/*
 * Base local interface. (Base remote is almost the same).
 */
public interface ProductLocal extends EJBLocalObject, 
Product
{
    //You must override this in remote and local
    //or you'll end up with two getPrimaryKey()'s
    public Object getPrimaryKey() throws EJBException;
    
    public String getProductType(); 
    public Integer getCategory();
    public double getPrice();
    public String getName();
}

/*
 * CD's local interface. Notice how it inherits the 
 * methods from ProductLocal and adds the CD-specific
 * methods.
 *
 * Now CDLocal can be either a Product, a ProductLocal,
 * an EJBLocalObject or a CDLocal. 
 */
public interface CDLocal extends ProductLocal
{
    //Our CD-specific methods
    public Date getDateCreated() throws EJBException;
    public String getSongList() throws EJBException;
}
[/prettify]

Inheritance can be useful to client code. Imagine we have a
shopping cart that can add and list products. We will keep the
cart's code simple.

[prettify]
//An Internal Class to describe the items
public class CartItem implements java.io.Serializable
{
    ...elided...
    //Has type and primary key, and  
    //Product-common fields
    public CartItem(
        String type, 
        Integer id, 
        String name, 
        Integer category, 
        double price)
    {...}    
    public void addToQuantity(int amnt){...}    
    public int getQuantity(){...}
}

public class ShoppingCartImpl implements SessionBean
{
    ...elided...
    
    /Notice it handles both local and remote beans
    public void addToCart(Product product)
    {
        CartItem item = getItem( 
            product.getProductType(), 
            product.getPrimaryKey() 
        );
        
        //Already in cart, add to quantity
        if ( item != null ) item.addToQuantity( 1 );
        else //Not in cart, create it
        {
            addItem( new CartItem( 
                product.getProductType(), 
                product.getPrimaryKey(), 
                product.getName(),
                product.getCategory(),
                product.getPrice()
            );
        }
    }
}
[/prettify]

The method addToCart() is able to add any product,
regardless of whether it is a CD,
Magazine, or any other Product sub-type we
create. This frees our code from implementation restrictions. If
the store were to add DVDs or clothes, the same code could be used
without changes. That eliminates sub-type specific code and
minimizes the chance for errors. If the types were instead
hard-coded into a large application and one was undeployed or
added, there's a good possibility a developer could forget to
update it in a section of code.

With cross-type actions, in the music store, the customers want
to be able to find all "Rock" products regardless of product
type.

[prettify]
/*
 * A session bean lists the products which fit 
 * the criteria from a search form. Normally, we'd
 * return value objects, but we're interested
 * only in component inheritance.
 */
public class Catalog implements SessionBean
{
    ...elided...
    
    //Searches for matching products
    public String[] findProductsByCategory(Integer categoryId)
    {
        //Get generic product factory
        ProductFactory factory = ( ProductFactory ) 
            new InitialContext().lookup( 
                "java:comp/env/ejb/ProductFactoryLocal" 
            );
        
        //Find Products by category
        Collection products = factory.getProductsByCategory( 
        categoryId 
        );
        
        //Save as String links for website
        int size = products.size();
        String[] foundProducts = new String[ size ];
        Iterator prodsIterator = products.iterator();
        
        for ( int i = 0; i < size; i++ )
            foundProducts[ i ] = getAsString( 
                ( Product ) prodsIterator.next() 
            );
    }
    
    //e.g. <a href="proddescr?prodId=1&prodType=CD">Satch Plays 
    //Fats</a> [ 2 items ] $12.95
    private String getAsString(Product p)
    {
        return "<a href="proddescr?prodId=" + p.getPrimaryKey() +
            "&prodType=" + p.getProductType() + "">" + 
            p.getName() + "</a> [ " + p.getQuantity() + 
            " items ] $" + p.getPrice();
            
    }
}
[/prettify]

There are many areas where component inheritance can greatly
improve your project's design and minimize required effort. Take a
look at this article's "/today/2005/10/25/component-inherit_10-19-2005.zip">source code for a deeper
look into the implementation and an example of session bean
inheritance.







What Is Missing from Our Solution?

One important point to remember about our implementation is that it
is not meant to be a performant, production-ready implementation of
component inheritance. Rather, it is an illustrative example to
show the functional possibilities of EJB component inheritance and
reveal that EJBs have the potential to be a powerful, reusable, and
easy-to-use technology. However, our solution does not, and cannot,
enable independently redeploying and changing the base component
implementation. Without a change to the component frameworks'
implementations, we are unable to implement this piece of component
inheritance.

There are two reasons for this:

First, when an entity bean has a relationship with another bean,
or is mentioned in an EJB-QL statement, both beans must be in the
same descriptor. This means to redeploy the base component, or add
or remove sub-types, you must also redeploy the entire family;
they're all in the same descriptor. So we could not implement
inheritance using a container-managed relationship (CMR).

Secondly, we are restricted by how the application server
constitutes components. Since the specification does not explicitly
incorporate inheritance, we are reliant on object inheritance to
enable deferring to the base's implementation of a method. For
example, inheritance should allow the sub-type's create method to
defer to or override the Product component's implementation.

[prettify]
public Integer ejbCreate(...) 
{
    //Defer to base component for Product fields
    super.ejbCreate( id, name, category, price );
    ...
}
[/prettify]

Without changing the component framework, we had to use OO
inheritance to defer to the base component's
ejbCreate(). Because we used OO inheritance, we
eliminated the possibility of a redeployable base component; i.e., a
base with an implementation separated from its type. It is
possible with EJBs, however, when explicitly incorporated into the
specification.

Looking Forward: Incorporating It into the Specification

The following suggestions come from experiments with component
inheritance and a terse examination on how it might fit into the
specification. They are not final. With more thought and testing, we
could likely discover numerous improvements.

There are three areas where the specification requires a change,
each fairly small and non-intrusive on the current architecture.
The first is a slight modification on applications defined by
application.xml. Secondly, we'll add a few nodes in the
ejb-jar.xml descriptor for entity beans. Lastly,
EntityContext and EJBHome/LocalHome will
gain a few methods, and we'll create a new empty interface named
javax.ejb.CompoundPrimaryKey.

Defining Applications

Currently beans can only have relationships with beans declared
in the same descriptor. This is too restrictive in an inheritance
context, but we do need a way to enclose the scope of components.
An application is the way to accomplish that, and the concept
already exists within J2EE. We'll simply add the ability to deploy
and redeploy components and modules into an already loaded
application. This is achieved with one extra node in
application.xml and a new XML descriptor:
application-part.xml.

[prettify]
<!--
    There's only one change to application.xml: 
    the "name" node.
-->
<application>
    <!-- 
        This is required/unique within an app. 
        server 
    -->
    <name>MusicStore</name>
    ...The rest is the same as currently...
</application>

<!-- 
    This enables deploying components into 
    an existing application so components
    can make reference to and interact with 
    components declared outside their 
    descriptor files and so we can reload
    base bean implementations.
-->
<application-part>
    <!-- 
        The name of an application within 
        the App Server to which these 
        modules will be deployed
    -->
    <application>MusicStore</application>
    
    <!-- 
        The rest is the same...For example,
        Our new Product type (DVD):
    -->
    <module>
        <ejb>product-dvd.jar</ejb>
    </module>
</application-part>
[/prettify]

This enables reloading component implementations independently
of other components, even if they participate in inheritance or
CMR. Beans will need to be uniquely named within an application--a minor task for a deployer. We'll also need a mechanism to
distinguish between reloading a component's implementation and
redefining a component. Currently this is impossible, but it could
be implemented quite simply: if a component is redeployed without
its interfaces, it is changing the implementation. If the
redeployment includes its interfaces, it is a redefinition and
breaks the component contracts. This should exist irrespective of
component inheritance.

Changes to ejb-jar.xml

None of the current XML nodes need to change, except by adding
two nodes under : these are
for base components, and
for sub-types to declare where
they inherit from. While a bean can inherit and be inherited, we'll
look at them separately.

[prettify]
<!-- This bean example allows inheriting -->
<entity>
    <ejb-name>ProductEntity</ejb-name>
    
    <!-- 
        Including this means it can be inherited.
        Not including it is equivalent to 
        declaring a class final.
    -->
    <inheritance>
    
        <!-- 
            This node exists only if the spec. 
            recognizes the idea of versions.
        -->
        <inheritance-version>
            <!-- 
                The version of this deployment of 
                the Product component 
            -->
            <version>1.3.2</version>
            
            <!--
                Declares what versions
                this deployment will replace, via
                the rules:
                0 == No versions
                0+ == Any version
                1.3- == 1.3 and earlier versions
                1.3.2 == Only 1.3.2 versions
                1.3+ == 1.3 and later
                1.0+1.3 == 1.0 to 1.3 inclusive
            -->
            <replace>1.3.2-</replace>
        </inheritance-version>
           
        <!--
            These are not the local and remote 
            interfaces. Rather, they are the base 
            types of the entity and home.
            For example, ProductLocal/Remote, 
            ProductFactoryLocal/Remote would 
            extend these interfaces to give 
            greater flexibility to client code. 
            There are three interfaces: instance, 
            home and bean.
        -->
        <inheritance-interfaces>
            <instance>
                com.mycompany.ejb.Product
            </instance>
            <home>
                com.mycompany.ejb.ProductHome
            </home>
            
            <!--
                This is a new interface and quite 
                important for component
                inheritance. It gives sub-types 
                the ability to defer to the base 
                component for methods similarly
                to how Objects do with their super 
                classes.
                
                The interface includes any public 
                methods in the base bean even if not 
                declared in the base bean's client 
                interfaces. The interface will be 
                retrieved from "EntityContext."
            -->
            <bean>
                com.mycompany.ejb.ProductEJB
            </bean>
        </inheritance-interfaces>
           
        <!--
            Note: these are not called queries.
            They're cross-type actions. They 
            allow both queries and actions. 
            They can act on any sub-type of 
            this base entity type, but only 
            through inherited methods that are 
            declared on this base type. We saw 
            some examples in our sample code.
        -->
        <inheritance-method>
            <!-- 
                This must be exist in the Home 
                interface. Per this XML, it 
                would be: 
                
                public void 
                updateAllVacationTimes(
                    Integer department
                )
                
                The method signature is 
                determined by a combination of
                the parameters and whether it 
                is a family or single scope
                method and must match to the 
                parameters of the
                individual-type-method. 
            -->
            <method-name>
                updateAllVacationTimes
            </method-name>
               
            <!--
                Is it a cross-type scope, or 
                just single-scope? i.e.
                do we want to call it on 
                the Home of all sub-types
                or just one sub-type?
                "Family" or "Single"
            -->
            <scope>Family</scope>
            
            <!-- 
                The method on each EJBHome in
                component family. Here, 
                updateAllVacationTimes(...)
                is equivalent to calling 
                updateVacation(...) on
                the Home of each Product sub-type.
            -->
            <individual-type-method>
                <method-name>
                    updateVacation
                </method-name>
                
                <!-- 
                    These must match the 
                    parameters of 
                    inheritance-method's
                    method-name except if 
                    single-scoped these must 
                    match parameters[ 1 ] and 
                    later (or have zero 
                    parameters if only the type 
                    is used) and arg[ 0 ] is 
                    Schema Name. 
                    For example, if we had:
                    
                          getProductByTypeAndId(
                              String schemaName, 
                              Integer id
                          )
                        
                    The first parameter is the 
                    type's "schema name"
                    The individual-type-method 
                    would be:
                    
                          findByPrimaryKey(
                              Integer id
                          )
                        
                    If we passed it "CD", it 
                    would search only in CD 
                    products for a bean with 
                    that id.
                -->
                <method-params>
                    <method-param>
                        java.lang.Integer
                    </method-param>
                </method-params>
            </individual-type-method>
        </inheritance-method>
    </inheritance>
    ...The rest the same as currently...
</entity>
[/prettify]

Now we'll look at an inheriting sub-type entity bean.

[prettify]
<!--
    This bean inherits from ProductEntity. 
    
    You do not declare base fields in the XML,  
    they're mapped by the app server.
    
    If deploying all beans, and the base isn't
    deployed, this bean will be postponed 
    until the base is deployed or if the base 
    isn't found, an exception is thrown.
-->
<entity>
    <ejb-name>CDEntity</ejb-name>
    
    <!-- This declares it inherits -->
    <base-entity>
    
        <!-- 
            The base entity (must exist in 
            same application) 
        -->
        <ejb-name>ProductEntity</ejb-name>
        
        <!-- 
            Which versions of the base will it
            accept? Rules same as <inheritance>.
        -->
        <version>1.3.2+</version>
        
        <!-- 
            Does it use the persistence mapping 
            of the base, or its own mapping for 
            those fields? — the mapping 
            is vendor-based.
        -->
        <inherit-persistence-mapping/>
    </base-entity>
    ...the rest the same as currently...
</entity>
[/prettify]

The final specification changes are in the API. Beans need a way
to defer to a base's implementation without requiring it be
compiled against it--which would be a violation of component
inheritance. We accomplish this with a slight addition to
EntityContext and EJBHome/LocalHome, and
the new interface javax.ejb.CompoundPrimaryKey.

[prettify]
<!--
    This enables retrieving the base component to
    do the equivalent of: super.ejbCreate().
-->
public interface EntityContext extends EJBContext
{
    /*
     * You cast this to the proper interface, it
     * will be an object tied to the EntityBean
     * that relates to this context. It will also 
     * be mapped to the same backing data source 
     * as that entity bean (not the base bean).
     */
    public Object getBase() throws 
    InheritanceNotSupportedException;
    
    /*
     * Returns currently registered schema names 
     * of sub-types. Types can be deployed and 
     * undeployed so this should be treated 
     * as "transient."
     */
    public String[] getFamilySchemas() throws 
    InheritanceNotSupportedException;
    ...the rest the same as currently...
}
   
/*
 * NOTE: Just as an EJB 2.x EJBHome must
 * define a findByPrimaryKey() method, the home
 * interface of an inheritable entity bean must
 * define:
 *     findByPrimaryKey(
 *          String schemaName, 
 *          <PK_Base_Type> primaryKey
 *     )
 * The bean class does not implement this,
 * the application server will.
 */
public interface EJBHome/EJBLocalHome
{
    //The Schema name defined in the XML 
    //descriptor (throw RemoteException 
    //in EJBHome)
    public String getSchemaName(); 
    
    //The application in which it is deployed
    //(throw RemoteException in EJBHome)
    public String getApplicationName(); 
    
    ...the rest the same as currently...
}
[/prettify]

Let's look at how you might use this in a bean.

[prettify]
<!-- Set the base for use in methods -->
public void setEntityContext(EntityContext ctx)
{
    base = ( ProductBase ) ctx.getBase();
}

<!-- Our create method -->
public Integer ejbCreate(
    Integer id, 
    String name,
    String dept
)
{
    //Calling the "super" constructor
    base.ejbCreate( id, name );
    
    setDepartment( dept );
    return null;
}
[/prettify]

The sub-type's primary key must be either the same primitive
wrapper type as that of the base component, or implement a new
interface: javax.ejb.CompoundPrimaryKey. This allows
the sub-type bean's primary key to be used in the base bean's
required method: "#findByPrimaryKeyNew">

findByPrimaryKey(String type,
primaryKey)
, where
is either one of the primitive
wrappers, Object, or CompoundPrimaryKey.
Whatever choice the base component uses, the sub-type components
must do the same. Just as in EJB 2.x, the key fields must be
public variables in the compound primary key.

[prettify]
/*
 * Requirement remains the same: all
 * primary keys must make CMP fields
 * public.
 */
public interface javax.ejb.CompoundPrimaryKey 
extends Serializable
{
    //No Methods
}
[/prettify]

The following two scenarios show how using
CompoundPrimaryKey might work.

Scenario One

Figure 1 shows a foreign-key reference scenario.

<br "Using foreign-key reference" />
Figure 1. Using foreign-key reference

[prettify]
public class ProductPK 
implements CompoundPrimaryKey
{
    //The key field, marked public
    public Integer id;
    
    public ProductPK() {}       
    public ProductPK(Integer id) {...}
    ...elided...
}

public class MagazinePK extends ProductPK
{
    //...inherited "id" field
    //Second key field, "name"
    public String name;
    
    public MagazinePK() 
    {
        super();
    }   
    public MagazinePK(String name, Integer id)
    {
        super( id );
        this.name = name;
    }
    ...elided...
}
[/prettify]

Scenario Two

Every sub-type is in its own table and has different primary key
field types. Here, Magazine uses a String or varchar, while CD might
use Integer to represent the primary key's persisted field. Each is implemented as a CompoundPrimaryKey. Figure 2 shows such a
table description.

<br "A separate table for each sub-type" />
Figure 2. A separate table for each sub-type

[prettify]
/*
 * Doesn't extend base type, but can still be
 * used in findByPrimaryKey() search because
 * they're both of type CompoundPrimaryKey.
 * This offers more flexibility.
 */
public class MagazinePK 
implements CompoundPrimaryKey
{
    //Key field, marked public
    public String name;
    
    public MagazinePK() {...}
    ...elided...
}
[/prettify]

Conclusion

Component-based architectures eliminate a number of the
limitations of object-oriented development. EJB components offer a
greater base for reuse and integration for Java developers than
POJOs. However, to make the most of what they offer requires a
shift in focus from development-oriented programming to the more
flexible realm of deployment-oriented programming. Recognizing the
component aspects of EJBs will improve your application design and
allow you to take advantage of their power. This will also prepare
you for the day when inheritance is finally added to the EJB
specification.

While this article concentrated only on using inheritance in
entity beans, it is also possible in session beans. Check the
source code for an
example of using inheritance in session beans, as well as a brief
discussion on the differences in inheritance for session and entity
beans.

Resources

  1. Article source
    code
    : Download the implementation code as well as "the Music
    Store," which uses component inheritance.
  2. EJB 2.0
    specification.
  3. EJB 1.1
    specification.
  4. " "http://www.onjava.com/pub/a/onjava/2002/09/04/ejbinherit.html">EJB
    Inheritance
    :" An article on inheritance in EJB 2.0.
  5. "http://www.amazon.com/exec/obidos/tg/detail/-/0201745720/qid=1127165772/sr=8-1/ref=pd_bbs_1/002-6698120-6091237?v=glance&s=books&n=507846">
    Component Software: Beyond Object-Oriented Programming
    , by
    Clemens Szyperski: A fantastic book on component
    inheritance in general, not just with EJBs.
  6. "http://www.amazon.com/exec/obidos/tg/detail/-/0201753065/qid=1127165844/sr=1-1/ref=sr_1_1/002-6698120-6091237?v=glance&s=books">
    Component Development for the Java Platform
    , by Stuart
    Halloway: A book more on deployment-based programming in Java than
    on components.
  7. Enterprise JavaBeans, 3rd
    Edition
    , by Richard Monson-Haefel: A book about programming
    with EJB 2.0.

width="1" height="1" border="0" alt=" " />
David Musicant is currently an application architect and consultant at Spiral Generation.
Related Topics >> EJB   |