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.
public Integer ejbCreate(...)
{
//Defer to base component for Product fields
super.ejbCreate( id, name, category, price );
...
}
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.
<!--
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>
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 <entity>: these are
<inheritance> for base components, and
<base-entity> for sub-types to declare where
they inherit from. While a bean can inherit and be inherited, we'll
look at them separately.
<!-- 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>
Now we'll look at an inheriting sub-type entity bean.
<!--
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>
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.
<!--
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...
}
Let's look at how you might use this in a bean.
<!-- 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;
}
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: findByPrimaryKey(String type,
<Base_Key_Type> primaryKey), where
<Base_Key_Type> 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.
/*
* Requirement remains the same: all
* primary keys must make CMP fields
* public.
*/
public interface javax.ejb.CompoundPrimaryKey
extends Serializable
{
//No Methods
}
The following two scenarios show how using
CompoundPrimaryKey might work.
Scenario One
Figure 1 shows a foreign-key reference scenario.

Figure 1. Using foreign-key reference
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...
}
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.

Figure 2. A separate table for each sub-type
/*
* 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...
}
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
- Article source
code: Download the implementation code as well as "the Music
Store," which uses component inheritance.
- EJB 2.0
specification.
- EJB 1.1
specification.
- "EJB
Inheritance:" An article on inheritance in EJB 2.0.
-
Component Software: Beyond Object-Oriented Programming, by
Clemens Szyperski: A fantastic book on component
inheritance in general, not just with EJBs.
-
Component Development for the Java Platform, by Stuart
Halloway: A book more on deployment-based programming in Java than
on components.
- Enterprise JavaBeans, 3rd
Edition, by Richard Monson-Haefel: A book about programming
with EJB 2.0.
David Musicant is currently an application architect and consultant at Spiral Generation.