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
 

Discussion groups have recently been abuzz with talk of "coarse-grained entity beans" - a slight misnomer deriving, I suspect, from the addition of mandatory entity beans in EJB 1.1. This month I'll examine the finer points of the Enterprise JavaBeans specification regarding coarse-grained entities, as well as my own, and provide an example for you - with plenty of comments to provide food for thought when you tackle the challenge yourself.

For those of you who finally came out of your Y2K shelter, EJB is a specification for building server-side business components on the Java platform. Its initial release required only enterprise beans called session beans, which are a logical extension of a client's business process. The specification mentioned entity beans, which represent persistent business components, but didn't mark them as mandatory for EJB 1.0 compliance.

However, industry demand for details about entity beans forced Sun and its partners to include them in the EJB 1.1 release. The updated specification provides some structure around entity bean development, but it created a whole new set of questions about EJB design patterns. The main EJB design pattern I'll cover this month revolves around coarse-grained entity beans and dependent objects.

Granularity of a Bean
I was recently on "The Hill," near the campus of the University of Colorado at Boulder, and noticed that the sidewalk had numerous quotes from philosophers and great thinkers - they must be there to provide inspiration to students who look at their feet when they walk or something! I'm going to start off with a few quotes myself, by way of helping to describe the granularity of an entity bean.

Section 9.1.2 of the EJB specification 1.1 reads: "In general, an entity bean should represent an independent business object that has independent identity and lifecycle, and is referenced by multiple enterprise beans and/or clients. A dependent object should not be implemented as an entity bean. Instead, a dependent object is better implemented as a Java class...and included as part of the entity bean on which it depends."

Great, but now you're asking what in the heck "dependent objects" are. Richard Monson-Haefel sums up the notion of dependent objects well in this quote from his book Enterprise JavaBeans (O'Reilly and Associates, 1999): "Dependent objects are objects that only have meaning within the context of another business object. They typically represent fairly fine-grained business concepts, like an address, phone number or order item. For example, an address has little meaning when it is not associated with a business object like Person or Organization. It depends on the context of the business object to give it meaning. Such an object can be thought of as a wrapper for related data. The fields that make up an address (street, city, state, and zip) should be packaged together in a single object called Address. In turn, the Address object is usually an attribute or property of another business object; in EJB, we would typically see an Address or some other dependent object as a property of an entity bean."

If I could say it better, I would.

Why Coarse-Grained?
The three core reasons for building coarse-grained entity beans are:

  1. Reusable business value
  2. Less work to develop and deploy
  3. Scalability, manageability of EJB Server

First, let me reiterate: Enterprise JavaBeans is a specification for server-side business components for the Java platform. EJB strives for reusable, configurable business components. With EJB you're no longer working with fine-grained objects. Small business objects by themselves provide little value. Most likely they'll rely on helper objects to provide real value, and together they make a component. Coarse-grained components provide a reusable piece of business functionality that can be applied to many different solutions as a single unit. Although I'm happy to see people get excited about innovative EJB design patterns, coarse-grained components are really nothing new; the design pattern has been around for years. I used to call it a "constellation," a collection of interrelated objects, all fetched from a root business object and dependent on that parent for management of their life cycle.

Second, enterprise beans aren't easy to develop, so the fewer beans you need to develop and deploy, the faster your applications are going to get to market. For instance, when developing a typical entity bean, you must code and test a remote interface, home interface, bean class, primary key class (unless you're using the J2EE reference implementation - has anybody figured that thing out yet?) and deployment descriptor.

You need to walk through various steps to package these classes in the correct format and deploy them in a nice little JAR file along with a manifest file describing each entry. Couple your EJB classes with the classes your container generates to manage your component, and you have a major class explosion on your hands. Sounds like a lot of work, and it is. However, building coarse-grained components eliminates tedious steps and class explosion by encapsulating the functionality of dependent classes within their parent entity bean.

Third, fewer entity beans means fewer resources for your server to manage and greater performance for object interactions within your components. Entity beans are remote objects, which can be costly for a server to manage when their numbers are high. For instance, a bean's home is registered in a naming service for remote lookups. As the naming service becomes full of entries, lookups may take longer. Have you ever done a file search on a directory with 10 files, then the same search on a directory with 100, 200 or 1000+ files? You get the picture.

Your EJB Server should provide a scalable, secure execution environment for your EJBs; however, you should work with it, not against it, to promote this scalability in your bean design. Because beans are remote objects, the server is required to manage remote connections and garbage collection, and usually maintains caches for your bean instances. Fine-grained objects usually interact closely with one another to perform their business functions. Fine-grained interactions aren't suitable for remote objects, which must marshal calls to each other, repeatedly creating copies of parameters and return values that must be garbage-collected at some point. These interactions are more conducive to a coarse-grained business component that encapsulates all logic behind its umbrella of locality.

While your application logic may be scalable, sometimes the pipeline to it or the server itself gets taxed. Keeping the number of remote connections to your server, remote method invocations and bean instances to a minimum is thus advisable. A coarse-grained business component encourages these best practices.

An Example: PersonBean
It's time for an example of a coarse-grained entity bean that manages dependent objects according to the EJB specification. I've chosen the Person business component because it appears in various shapes and sizes across numerous industries. My example (see Listing 1) consists of a Person entity bean and an Address, a dependent object and a test class to see the bean in action. It's written on WebLogic 4.5.1, an EJB 1.0-compliant server; although 5.0, which is 1.1 compliant, isn't available at this writing, I've nevertheless adhered to the EJB 1.1 specification's guidelines for building coarse-grained entity beans.

The example demonstrates a coarse-grained, bean-managed persistence (BMP) entity bean that manages the whole life-cycle of its dependent objects from insert to delete. It is configurable via its deployment descriptor to perform either lazy-loading (fetching only when requested) or eager-loading (loading dependents in anticipation of their use) of its dependent objects, both of which are supported by the spec. It then performs smart writes to the database, updating only modified dependent objects in its cached data.

As you'll see from the code at the end of the article, the example is loaded with comments to help you think about the issues behind building your own BMP entity beans.

Person to Address Association
The basic rules governing the PersonBean is that a Person can exist in the system without an address; however, an Address is dependent on the Person - it can't be inserted without its parent (i.e., PersonBean) and must be removed when the Person is deleted. This logic is evident in the factory interface of the PersonBean. For instance, the PersonHome.create method takes only Person attributes, but requires no Address object(s).

public interface PersonHome extends EJBHome {
PersonEJB create(int personID, String title,
String firstName, String lastName)
throws CreateException, RemoteException;

This inherently implies that it may be inserted without addresses, though the interface is flexible enough for an application requiring a Person to have at least one address. Such an application could wrap the PersonBean in a session bean and call PersonEJB.addAddress (newAddress) in the same transaction, ensuring that an Address was always inserted along with the Person. Already, you've seen a reusable scenario for this coarse-grained business component!

Caching Dependent Objects
When developing coarse-grained entity beans, you'll have to understand the EJB 1.1 specification's demands around loading and storing cached data.

Loading Cached Data
The EJB specification lists three ways to load cached data in an entity bean. The first is an eager-load, in which your code loads all cached data at once in the ejbLoad method (see Listing 1). The second approach is to lazy-load entity data, loading it only when it's needed to satisfy a business method. The last method to caching entity bean data is not to cache at all, but to pass all access to entity bean state directly to the underlying database.

The PersonBean caches its own fields as well as dependent Address objects in a local java.util.List. The loading of the Address list is determined at deployment of the bean via its "lazyLoad" bean environment property. If it's true, the ejbLoad method will not load address data. Instead, it will clear the list to ensure proper management of the cached data according to the EJB spec, section 9.1.7 (see Listing 1). Cached data that's not loaded needs to be cleared here, since ejbLoad is called whenever a container needs to synchronize state with the database, such as in the event of a javax.transaction.TransactionRolledBackException.

If "lazyLoad" is false, it will perform a JDBC query to fill its list right away. The PersonBean uses environment properties for flexibility and reusability by allowing bean deployers to modify the behavior of the component declaratively without ever touching the code.

A copy of the PersonBean's Address list is publicly accessible via the component interface method getAddresses. When the container invokes this method on behalf of a client, the PersonBean will check to see if the address list is already available. If so, it returns the list without reading from the database. Otherwise the PersonBean will use a JDBC query to fetch all of its addresses, caching each in its local list before returning the list to the caller.

Updating and Removing Cached Data
The EJB specification states, "When the container invokes the ejbStore method on the instance, the instance must push all cached updates to the entity object's state to the underlying database" (EJB specification 1.1, sec. 9.1.7).

The key phrase in this quote is cached updates. In other words, you can apply "smart updates" to your bean's cached data by checking for modifications and updating only this information back to the database.

The PersonBean uses smart updates for its dependent Address objects (see Listing 2). For instance, when the client updates an Address, the PersonBean will mark the Address as "modified" and replace the old version with the new one in the local list. The PersonBean will wait until ejbStore is invoked before updating the modified Address object to the database. When the container calls ejbStore, the PersonBean will loop through each Address in its list, calling a JDBC Update only for modified objects.

Similar to the way ejbStore manages all cached state for the entity bean, ejbRemove also handles the referential integrity of the PersonBean component, ensuring that all Address objects are removed when the PersonBean is deleted (see Listing 3). There are a couple of ways to maintain component integrity upon deletion of a parent. One approach is to create a cascading delete in the database that will automatically delete all related rows in the Address table when its Person is removed. In this case there would be no need to code a JDBC Delete call from within ejbRemove. A second option, the one I chose, is to manually perform the JDBC Delete of child-dependent object rows. This ensures the component's integrity no matter what JDBC-compliant database platform it's deployed against. Both methods have advantages and disadvantages, so "pick your poison."

The management of cached data in an entity bean is confusing at first, but soon you'll find it flexible enough for you to develop tuned, configurable data access code. The PersonBean uses two tuned data access patterns: lazy-loading and smart updates.

Summary
I hope this discussion has dissipated the confusion that's been circulating within numerous discussion groups about how to develop these components. The example demonstrated how to build a BMP entity bean that controls the life cycle of a dependent object and focused on some gray areas of the EJB 1.1 specification, such as how to manage cached state when loading, storing and removing an entity bean that encapsulates dependent objects. Please refer to the full code listing on the Web (www.sys-con.com) and modify the test case to perform more in-depth exercises on the behavior of a coarse-grained component.

Author Bio
Jason Westra is CTO of Verge Technologies Group, Inc., (www.vergecorp.com). Verge is a Boulder, CO based firm specializing in e-business solutions with Enterprise JavaBeans.
[email protected]

	

Listing 1: PersonalBean.ejbLoad 
 
/** 
* Loads the entity bean from the persistent storage. 
* Checks whether or not to load dependent address objects 
* 
* @exception java.rmi.RemoteException 
* if there is a communications 
or systems failure 
*/ 
public void ejbLoad() throws RemoteException { 
System.out.println("ejbLoad ("+personID+")"); 
try { 
// SQL actually performed in the read() method 
read((PersonPK) ctx.getPrimaryKey()); 
// discard or reload any cached data, as per EJB 1.1 
// spec, sec 9.1.7 
if (lazyLoad.equals("true")) { 
System.out.println("ejbLoad ("+personID+") - lazy load 
addresses"); 
// ensures reset of address for this PersonBean! 
addresses.clear(); 
} 
else{ 
System.out.println("ejbLoad ("+personID+") - 
loading addresses..."); 
getAddresses(); // loads the address list now 
} 
} 
catch (FinderException fe) { 
throw new RemoteException (fe.getMessage()); 
} 
}


Listing 2: PersonBean.ejbStore 
 
/** 
* Stores the entity bean's PersonBean fields in the 
* persistent storage. 
* Also saves any modified Address objects. 
* 
* @exception java.rmi.RemoteException 
* if there is a communications 
or systems failure 
*/ 
public void ejbStore() throws RemoteException { 
System.out.println("ejbStore (" +personID+ ")"); 
Connection con = null; 
PreparedStatement ps = null; 
try { 
con = getConnection(); 
ps = con.prepareStatement("update Person set title = ?, "+ 
"firstname = ?, "+ 
"lastname = ? "+ 
"where personid = ?"); 
ps.setString(1, title); 
ps.setString(2, firstName); 
ps.setString(3, lastName); 
ps.setInt(4, personID); 
int i = ps.executeUpdate(); 
if (i == 0) { 
throw new RemoteException 
("ejbStore: PersonBean ("+personID+") failed to update"); 
} 
// update cached entity state (i.e. our addresses) 
// as per EJB spec 1.1, sec: 9.1.7 
Iterator list = addresses.iterator(); 
while (list.hasNext()) { 
Address addr = (Address)list.next(); 
// only update if it was modified 
if (addr.isModified()) { 
sqlUpdateAddress(addr); 
} 
} 
} 
catch (RemoteException re) { 
throw re; 
} 
catch (SQLException sqe) { 
throw new RemoteException (sqe.getMessage()); 
} 
finally { 
try { 
if (ps != null) ps.close(); 
if (con != null) con.close(); 
} 
catch (Exception e) { 
throw new RemoteException (e.getMessage()); 
} 
} 
}


Listing 3: PersonBean.ejbRemove 
 
/** 
* Deletes the entity bean and all addresses (dependent 
* objects)from the persistent storage. 
* 
* @exception javax.ejb.RemoveException 
* if the entity does not 
allow removing the bean 
* @exception java.rmi.RemoteException 
* if there is a communications 
or systems failure 
*/ 
public void ejbRemove() throws RemoveException, RemoteEx- 
ception { 
System.out.println("ejbRemove ("+personID+")"); 
addresses.clear(); // reset 
Connection con = null; 
PreparedStatement ps = null; 
try { 
con = getConnection(); 
// get the PK from the context. This is because ejbLoad 
// does not have to be called (see sequence diagrams in 
// EJB spec1.1) on an ejbRemove() and using the personID 
// at this point may give the personID of a different bean! 
PersonPK pk = (PersonPK) ctx.getPrimaryKey(); 
ps= con.prepareStatement("delete from person where person 
ID = ?"); 
ps.setInt(1, pk.personID); 
int i = ps.executeUpdate(); 
if (i == 0) { 
throw new RemoteException ("ejbRemove : PersonBean ("+pk.personID+ ") not found"); 
} 
ps.close(); 
// delete all addresses for the personBean 
ps = con.prepareStatement("delete from address where per- 
sonID = ?"); 
ps.setInt(1, pk.personID); 
ps.executeUpdate(); 
} 
catch (SQLException sqe) { 
throw new RemoteException (sqe.getMessage()); 
} 
finally { 
try { 
if (ps != null) ps.close(); 
if (con != null) con.close(); 
} 
catch (Exception e) { 
throw new RemoteException (e.getMessage()); 
} 
} 
}onBean.ejbRemove


 

Download the complete source code (in ZIP format: 9KB) for Jason Westra's article

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.