EJB Inheritance, Part 3
by Emmanuel Proulx
11/13/2002
Editor's Note: This is Part 3 of a multi-part series on EJB inheritance. Part 1 deals with inheritance and entity beans and Part 2 focuses on table mapping.
Inheritance is not only for entity beans. Session beans may also take advantage of inheritance.
A session bean's life revolves around pure business logic. Implementing session
bean inheritance is nowhere near as hard as it is with entity beans. Home interfaces
are plain, containing no tricky business logic. The problems we had with entity
beans were regarding access or lifecycle of the bean, not the actual bean invocation.
There were issues also regarding the mapping of in-memory objects to database
tables. These problems are gone in the case of session beans.
Still, I feel it is important to show you how to do session bean inheritance.
I want to make sure people have the right technique, even though this technique
may seem obvious. Secondly, I would like to mention the importance of factories
when dealing with inheritance. Message-driven bean inheritance will be the topic of a future article.
Business Logic Inheritance
Why would one want to use session bean inheritance? For the same reasons
as stated in my previous articles: abstraction, reuse, and maintainability.
But in this case, we don't want to project data onto a hierarchy;
we want to do that with pure business logic. When is this useful? Here are
a few examples:
- Whenever there are business rules that are slightly different from
one type of user to another.
- When data is being processed differently depending on factors like
geographic location, time of day, etc.
- When code is repeated in many related objects.
I'm sure you can think of many more examples. Let's now define a business
story for our session bean examples.
New Requirements for RTM
|
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 4
This series has demonstrated all sorts of ways to handle inheritance in beans. With web and message services, though, how do you handle inheritance with remotely-invoked beans? The EJB 2.0 specification allows it; Emmanuel Proulx demonstrates how.
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 the previous article, points could be redeemed for air flights using simplistic
rules. A trip from New York to Chicago would cost the same number of points
as a trip from New York to San Diego. But now the requirements have changed.
A business decision was made that the number of points needed to
buy a trip would be proportional to the distance traveled. An approximation
of this distance can be calculated based on the zip codes of the departure and
the destination airports. This only applies in the USA. For flights to Canada,
there's a flat rate. For overseas flights, time zones are taken into consideration,
instead.
On top of that, there is an administrative charge of 1,000 points, which applies to all regions
except overseas, where the charge is 1,500 points. In some countries a tourism
tax applies, also charged as points. Regular customers pay the full price. Gold
customers get 15 percent off (before fees). Platinum customers get 30 percent off (before
fees). The following table contains a summary of the rules used to calculate
point redemption.
Table 1. New business logic requirements
| Region |
Rules |
Administrative fees |
| USA |
Subtract the departure airport's zip code from the arrival airport's. Keep the absolute value of this value. This new value represents the number of points needed for this flight. |
1000 points |
| Canada |
The flat fee is 10,000 points. |
1000 points |
| Overseas |
Count the number of time slots between the departure airport and the arrival airport. Multiply this value by 3,000. Add the country's tourism tax fees. |
1500 points |
As with our previous examples, these are completely bogus rules and calculations,
and should not be used for any serious business logic; they will serve, however, to illustrate the question at hand: how do you implement session bean inheritance?
You may have guessed from looking at the code of our previous example application
(look for a big if else if ... block) that objects are
well suited for this calculation.
The design of this system is straightforward: the base class is PointCalculator,
with subclasses CanadaPointCalculator and OverseasPointCalculator.
Note that the points themselves are not objects in this system, but the point calculators are.
They are, of course, session beans. Here's the class diagram:

Figure 1. Point calculator classes diagram
Overview of the Steps
Here's a quick overview of the various steps required
for my technique. We will then cover each step in more detail.
- Create a base bean class implementing
SessionBean. Write bean subclasses
extending base bean class.
- Create a base business interface, extending
EJBObject or EJBLocalObject.
Create sub-interfaces extending base business interface.
- Write a home interface for base class, extending
EJBHome or EJBLocalHome.
Write home interfaces for subclass, extending EJBHome or EJBLocalHome as usual.
Don't extend home interface of base class.
- Write the classes'
setSessionContext(), ejbCreate(), ejbPassivate(), ejbActivate(), and ejbRemove() methods. These can be inherited. Overridden methods should call the corresponding super method first.
Note: In my previous articles, I forgot to mention the other methods of the bean lifecycle: set...Context(), ejbRemove(), ejbPassivate(), ejbActivate(), ejbLoad(), and ejbStore(). These should be treated similarly to ejbCreate methods. They can be inherited, but if they're overridden, they must call the corresponding methods in super first. Also, pay close attention to the scope of class members. It would be a good idea to make EntityContext objects protected instead of private.
Step 1. Bean Classes
First let's implement the bean classes. The base class is PointCalculatorBean,
implementing SessionBean. The subclasses are CanadaPointCalculatorBean and OverseesPointCalculatorBean, extending PointCalculatorBean. We will leave the implementation of create and remove methods for later. Here's a code snippet that will help you understand what this step involves:
Example 1. PointCalculatorBean
//Removed for readability: package, imports.
public class PointCalculatorBean implements SessionBean {
// Removed for readability: constructor, session
// context, activate, passivate, create, remove.
public int getPoints(String departureAirport, String destinationAirport) {
try {
Connection c = ds.getConnection();
Statement s = c.createStatement();
s.execute("SELECT ZIP FROM AIRPORT WHERE ID = '" + departureAirport + "'" );
ResultSet rs = s.getResultSet();
if(!rs.next()) throw new Exception("Airport code not found for departure.");
int zipDeparture = rs.getInt(1);
s.execute("SELECT ZIP FROM AIRPORT WHERE ID = '" + destinationAirport + "'" );
rs = s.getResultSet();
if(!rs.next()) throw new Exception("Airport code not found for destination.");
int zipDestination = rs.getInt(1);
rs.close();
s.close();
c.close();
return Math.abs(zipDeparture-zipDestination);
} catch (Exception e) {
System.out.println("ERROR! Can't get zip codes. " + e);
e.printStackTrace();
}
return 0;
}
public int getAdministrativeFees() {
return 1000;
}
}
That piece of code requires some time to digest. But how about this one?
Example 2. PointCalculatorBean
//Removed for readability: package, import
public class CanadaPointCalculatorBean extends
basepointcalculator.PointCalculatorBean {
public int getPoints(String departureAirport, String destinationAirport) {
return 10000;
}
}
A lot smaller don't you think? This is reuse at its best!