Accelerate EJB 2.0 development with EJBGen

Simplify development and maintenance in your EJB CMP 2.0 model

Developers who have worked with Enterprise JavaBeans (EJB) technology since its inception have experienced the hassle of the EJB development lifecycle. When I started to work with EJB 1.1 containers, I had to maintain classes and interfaces, usually using the Business Interface pattern to spot errors at compile time. I even had to manually create, edit, and maintain the XML deployment descriptors. In addition to being bug-prone, those steps usually delayed my development process.

Since then, the implementation of various projects has eased the EJB development process by automatically generating the deployment descriptors and remote, home, and local interfaces — based on information provided by the bean class (that which implements javax.ejb.SessionBean or javax.ejb.EntityBean).

In this article, I will present EJB expert Cedric Beust’s EJBGen tool. EJBGen limits your code editing to just one file, the bean class, which you annotate with javadoc tags. EJBGen, which is basically a doclet, parses your entity bean source file and generates the remote, home, and local interfaces for you.

EJBGen, doclet, and javadoc explained

As a Java developer, you certainly know javadoc, the JDK tool that lets you generate some HTML documentation based on comments you embed in your source code.

Javadoc uses the Doclet API to specify the content and format of its output. Sun Microsystems provides a default doclet that outputs HTML documentation. That doclet is composed of the classes in the com.sun.tools.doclets, com.sun.tools.doclets.standard, and com.sun.tools.doclets.standard.tags packages. The default doclet is used unless you select the javadoc’s -doclet option. However, you can write your own doclet for enhanced functionality.

EJBGen is a doclet that outputs Java source code (local, remote, home interfaces) and XML deployment descriptors (ejb-jar.xml). In addition, EJBGen can generate proprietary deployment descriptors, weblogic-cmp-rdbms-jar.xml and weblogic-ejb-jar.xml, for BEA WebLogic 6.1.

EJBGen is easy to use because:

  • You can quickly implement a large schema and entity bean relationships in just a few days
  • You maintain just one class, the bean class
  • You no longer manually maintain XML deployment descriptors

To show how to use EJBGen, I implemented an example from the EJB 2.0 specification, section 10.3.12: “An Order Entity Bean that Has Relationships with Line Items and Customers.”

If you’re new to EJB, I strongly suggest you read Chapter 10 of the EJB 2.0 specification: “Entity Bean Component Contract for Container-Managed Persistence.”

Entity bean CMP 2.0

I don’t intend to give you an in-depth presentation on entity beans; that would exceed the scope of this article. However, I would like to outline some of the many features and advantages that container-managed persistence (CMP) 2.0 can bring to your projects:

  • An entity bean CMP class is small and maintainable
  • The container generates the SQL code for persistence and finder queries, based on your portable EJBQL queries
  • With a 2.0-compliant container, you can define relationships between entity beans (one-to-one, one-to-many, and many-to-many) and let the container manage these relations
  • The container implements lazy loading techniques and other performance tricks
  • You can focus on the business code, rather than the technical infrastructure
  • You are not restricted to one vendor

Obviously, the EJB specification is not perfect. Some people were turned away by entity bean CMP in the EJB 1.1 specification, so they disregard it in EJB 2.0. However, if you’re one of those people, you should reconsider your position and see how much this technology has improved.

Moreover, the move to the EJB 2.0 CMP model will protect your investment. By adopting CMP, you minimize the move to next-generation, specification-compliant containers; you also allow the container to regenerate the code behind the scenes, and EJBGen to automatically produce specification-compliant XML deployment descriptors. You therefore automatically take advantage of container improvements. Exciting updates, especially on the security side, are currently being defined for the EJB 2.1 and J2EE (Java 2 Platform, Enterprise Edition) 1.4 specifications. (See Java Specification Requests JSR 151 and JSR 153.)

Okay, I’ll stop preaching; now let me prove it.

The example

EJB 2.0 provides some sample code to illustrate the relationships and local interface concepts in the EJB 2.0 CMP model. The section 10.3.12 describes this example as an:

…Order entity bean with relationships to line items and customers, which are other entity beans within the same local scope. Product is indirectly related to Order by means of the relationship between LineItem and Product.

We will simply implement this example. I’ve tested the source code for this article on the Win2K platform with the following tools:

  • BEA WebLogic Server 6.1
  • Microsoft MS SQL Server
  • Inprise JBuilder Personal
  • Sun Microsystems JDK 1.3.1

After a short introduction to relationships, we will create the tables in the database that represent Order, LineItem, Customer, Product, and Address. Steps 1 and 2 describe how to implement your entity beans without relationships using EJBGen. Step 3 defines those relationships. As a bonus, I will provide an EJB 2.0 business method. This business method adds a LineItem to an Order and demonstrates the use of value objects and local interfaces.

Step 1: Create the tables in your database

To understand how to create your database tables, you must first understand how the EJB 2.0 model creates relationships and the implication of those relationships on the tables. For beginners, I recommend you develop the entity beans without relationships, and then add the relationships once the beans have been unit tested. Obviously, the more you gain experience with the 2.0 model, the more easily you can develop the beans with their relationships from the start.

The following list describes the relationship types and their physical mappings:

  • One-to-one relationship: This relationship involves a physical mapping from a foreign key in one bean to the primary key in another bean. A primary key uniquely identifies a piece of data. The foreign key creates an association between the tables (a one-to-one, one-to-many, or many-to-many association), and therefore, an association between objects through container-managed relationship (CMR) fields.

  • One-to-many relationship: This relationship involves a physical mapping from a foreign key in one bean to the primary key in another bean (like the one-to-one relationship). The constraint: the foreign key is always contained in the table that occupies the relationship’s many sides. An Order can have multiple LineItems. The foreign key is in the LineItem table and references the primary key in the Order table.

  • Many-to-many relationship: This relationship involves a physical mapping to a join table used in the RDBMS (relational database management system). Each row in this join table contains two foreign keys that map to the primary keys of the relationship entities.
Note
You often need to define attributes in the join table. The EJB 2.0 specification does not solve the problem of accessing, adding, and removing these attributes from the join table because there is no corresponding entity representation. To solve this, you have to abstract the join table using an entity CMP bean that has two one-to-many relationships with the two other tables.

The data physical view

The following diagram represents the Order, Customer, LineItem, Product, and Address tables, plus the relationships between them.

The proceeding tables describe for each column its key type (primary or not; auto means the database increases the integer value — the key increments automatically when a record is inserted), its name, its data type (String, Integer, and so on), and whether or not it allows the null type. A blank cell (-) means that the key type is neither primary nor foreign. Each table has its own corresponding bean class implementation (for the Order table, there is a corresponding OrderBean entity CMP bean, for example).

Tables and relationships

An Order can have only one Customer, but a Customer can have multiple Orders. An Order can have multiple LineItems, with a minimum of one LineItem per Order. The Order table defines the columns shown in Table 1.

Table 1. Order table

Key type Column name Data type Allows null
Primary, auto orderid String No
status String No
creditApproved Boolean Yes
Foreign key to Customer customerfk String Yes

A Customer can have zero or more Orders. The Customer table defines the columns shown in Table 2.

Table 2. Customer table

Key type Column name Data type Allows null
Primary email String No
firstname String No
lastname String No

A Product can have multiple LineItems. The Product table defines the columns shown in Table 3.

Table 3. Product table

Key type Column name Data type Allows null
Primary, auto productid String No
Primary productname String No
price Double No

A LineItem can have only one Order and one Product. The LineItem table defines the columns shown in Table 4.

Table 4. LineItem table

Key type Column name Data type Allows null
Primary key, foreign key to Order orderfk String No
Primary key, foreign key to Product productfk String No
Foreign key to Address addressfk String No
quantity Integer No
status String No
tax Double No
shipmentdate Date No

The Address table defines the columns shown in Table 5.

Table 5. Address table

Key type Column name Data type Allows null
Primary key addressid String No
street1 String No
street2 String Yes
postcode String No
city String No
country String No

You can download the DDL (data description language) script from the Resources section to create the schema in your database.

Step 2: Write the CMP entity beans without relationships

This step is important, but not mandatory. Developing the beans without the relationships is a simple pragmatic approach I usually take in the development process. Testing the basic getters and setters is easier without the relationships. This would be the first step in testing your beans, prior to testing the relationships. As you get used to the EJB 2.0 model, you can begin to merge Steps 2 and 3.

I will focus on the Order table that has a one-to-many relationship with LineItemCustomer. I will introduce a few simple EJBGen tags.

Again, remember EJBGen’s main advantage here: we only work on the bean class; EJBGen generates the rest. The bean class without the relationships looks like this:

package org.jyperion.order;
import org.jyperion.framework.AbstractEntityBean;
import org.jyperion.framework.PrimaryKeyGenerator;
/**
 * OrderEntityBean
 *
 * @ejbgen:entity
 *  ejb-name            = OrderBean
 *  table-name          = OrderBean
 *  data-source-name    = jdbc/JavaWorldPool
 *  prim-key-class      = java.lang.String
 *  default-transaction = Required
 *
 * @ejbgen:jndi-name
 *   remote =  OrderBean
 *   local  =  OrderBeanLocal
 *
 * @ejbgen:finder
 *   signature             = "Collection findAllOrders()"
 *   ejb-ql                = "SELECT OBJECT(O) FROM OrderBean O"
 *   transaction-attribute = Required
 *
 * @ejbgen:finder
 *   signature             = "Collection findAllPendingOrders()"
 *   ejb-ql                = "SELECT OBJECT(O) FROM OrderBean O WHERE O.status="PENDING""
 *   transaction-attribute = Required
 *
 * @author Thierry Janaudy
 * @date 12 November 2001
 * @version 1.0
 */
public abstract class OrderBean extends AbstractEntityBean {
  ///////////////////////////////////////////////////////
  // Container Managed Persistent Fields               //
  ///////////////////////////////////////////////////////
  /**
   * @ejbgen:cmp-field column = orderid
   * @ejbgen:primkey-field
   */
  public abstract String getOrderId();
  public abstract void setOrderId(String orderid);
  /**
   * @ejbgen:cmp-field column = status
   * @ejbgen:remote-method
   * @ejbgen:local-method
   */
  public abstract String getStatus();
  public abstract void setStatus(String status);
  /**
   * @ejbgen:cmp-field column = creditApproved
   * @ejbgen:remote-method
   * @ejbgen:local-method
   */
  public abstract Boolean getCreditApproved();
  public abstract void setCreditApproved(Boolean creditApproved);
  ///////////////////////////////////////////////////////
  // Container Managed Relation Fields                 //
  ///////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////
  // Create methods                                    //
  ///////////////////////////////////////////////////////
  public String ejbCreate(String status) {
    this.setOrderId(PrimaryKeyGenerator.getUUID(this));
    return null;
  }
  public void ejbPostCreate(String status) {
    this.setStatus(status);
  }
  ///////////////////////////////////////////////////////
  // Overridden methods                                 //
  ///////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////
  // Business methods                                  //
  ///////////////////////////////////////////////////////
}

This bean class is part of the org.jyperion.order package that you can find in the source code.

As you can see, the Javadoc is pretty straightforward:

  • The @ejbgen:entity tag defines the table name, the entity bean name, the data source, the default transaction, and so on
  • The @ejbgen:jndi-name tag defines the JNDI (Java Naming and Directory Interface) name for lookup of the remote and local interfaces
  • The @ejbgen:finder tag lets you define finder queries for your entity bean
  • The @ejbgen:cmp-field tag defines a CMP field
  • The @ejbgen:primkey-field optional tag tells whether or not the CMP field is a primary key

You just have to declare the getters and setters for the CMP fields and the different ejbCreate() and ejbPostCreate() methods.

At this point, we’ve only implemented the bean class. Now we need to generate all the interfaces as well as the deployment descriptors. For this, you just need to run the EJBGen doclet on your source.

Run the doclet

You use the following command line to generate the interfaces and XML deployment descriptors:

javadoc -classpath %BEA_HOME%libweblogic.jar;..sources -docletpath
..libejbgen.jar;..sources -doclet EJBGen orgjyperionorderOrderBean.java

EJBGen generates:

  • LocalOrder.java
  • Order.java
  • OrderHome.java
  • OrderLocalHome.java
  • ejb-jar.xml
  • weblogic-ejb-jar.xml
  • weblogic-cmp-rdbms-jar.xml

Now you just package those files in a jar file, and then run ejbc on it.

In my development environment, I use Apache Ant for a multiplatform build tool. You can use EJBGen within your Ant tasks. You can refer to the EJBGen documentation for this, but here is a quick tip:

<target name="BuildEjbDescriptors" depends="prepare">
  <javadoc sourcefiles="${src}/*Bean.java" sourcepath="${src}" destdir="${src}" doclet="EJBGen" docletpath="../../ejbgen.jar" />
</target>

Step 3: Entity relationships

Let’s look at the OrderBean relationships. An OrderBean can have only one CustomerBean, but a CustomerBean can have multiple OrderBeans. This is a one-to-many relationship. Similarly, an OrderBean can have multiple LineItemBeans, but a LineItemBean can have only one OrderBean. (In the source code, you can see that LineItemBean defines three relationships: a many-to-one with OrderBean, a many-to-one with ProductBean, and a one-to-one with AddressBean.)

Defining these relationships by hand — for example, editing the XML deployment descriptors — would be a hassle. EJBGen lets you generate them by just adding a specific tag, the @ejbgen:relation tag.

The Javadoc for OrderBean becomes:

/**
 * OrderBean
 *
 * @ejbgen:entity
 *  ejb-name            = OrderBean
 *  table-name          = TOrder
 *  data-source-name    = jdbc/JavaWorldPool
 *  prim-key-class      = java.lang.String
 *  default-transaction = Required
 *
 * @ejbgen:jndi-name
 *   remote =  OrderBean
 *   local  =  OrderBeanLocal
 *
 * @ejbgen:finder
 *   signature             = "Collection findAllOrders()"
 *   ejb-ql                = "SELECT OBJECT(O) FROM OrderBean O"
 *   transaction-attribute = Required
 *
 * @ejbgen:finder
 *   signature             = "Collection findAllPendingOrders()"
 *   ejb-ql                = "SELECT OBJECT(O) FROM OrderBean O WHERE O.status="PENDING""
 *   transaction-attribute = Required
 *
 * @ejbgen:relation
 *   multiplicity = many
 *   name = CustomerCanHaveMultipleOrders
 *   target-ejb = OrderBean
 *   fk-column = customerfk
 *   cmr-field = customer
 *
 * @ejbgen:relation
 *   multiplicity = one
 *   name         = OrderCanHaveMultipleLineItems
 *   target-ejb   = OrderBean
 *   cmr-field    = lineItems
 *
 * @author Thierry Janaudy
 * @date 12 November 2001
 * @version 1.0
 */

In the code above, we added two new

@ejbgen:relation

tags. The first one says that a

Customer

can have multiple orders, which translates into the relation name

CustomerCanHaveMultipleOrders

. You must have a relation defined with the same relation name in the

Customer

class. This relation’s target is the

OrderBean

, which uses the foreign key

customerfk

defined in the

Order

table to manage this relation. (

customerfk

links to the

Customer

table’s primary key.) In the

OrderBean

, a CMR is defined for the bean developer to manipulate the

Customer

bean from his/her business code and to navigate through the relationships for finder queries. In the

Customer

bean, you will find:

 * @ejbgen:relation
 *   multiplicity = one
 *   name         = CustomerCanHaveMultipleOrders
 *   target-ejb   = CustomerBean
 *   cmr-field    = orders

To programmatically use these relationships, you have to define the CMR accessors in the Order class:

  /**
    * @ejbgen:cmr-field
    * @ejbgen:local-method
    */
  public abstract LocalCustomer getCustomer();
  /**
    * @ejbgen:local-method
    */
  public abstract void setCustomer(LocalCustomer customer);

And in the Customer class:

  /**
    * @ejbgen:cmr-field
    * @ejbgen:local-method
    */
  public abstract Collection getOrders();
  /**
    * @ejbgen:local-method
    */
  public abstract void setOrders(Collection orders);
Note
You work only with local interfaces with the CMR accessors. According to the EJB 2.0 specification, section 10.3.2: “The lack of local interface (for an Entity Bean) prevents other entity beans from having a relationship to it.”

The second @ejbgen:relation tag says that an Order can have multiple LineItems, which translates into the relation name OrderCanHaveMultipleLineItems.

LineItemBean defines:

 * @ejbgen:relation
 *   multiplicity = many
 *   name = OrderCanHaveMultipleLineItems
 *   target-ejb = LineItemBean
 *   fk-column = orderfk
 *   cmr-field = order

The CMR accessors for Order are:

  /**
    * @ejbgen:cmr-field
    * @ejbgen:local-method
    */
  public abstract Collection getLineItems();
  /**
    * @ejbgen:local-method
    */
  public abstract void setLineItems(Collection lineItems);

and for

LineItem

:

  /**
    * @ejbgen:cmr-field
    * @ejbgen:local-method
    */
  public abstract LocalOrder getOrder();
  /**
    * @ejbgen:local-method
    */
  public abstract void setOrder(LocalOrder order);

OrderBean business method: addLineItem()

The EJB 2.0 specification provides a business method called addLineItem(). According to the specification, this method “creates a LineItem object and adds it to the persistent-managed relationship.” I slightly modified the method signature for simplicity’s sake:

The method signature is:

public void addLineItem(String productId, Integer quantity, AddressVO addressValueObject)

This method will:

  1. Create a new LineItem object
  2. Find the product using the productId and get its local interface
  3. Assign the relationship product to the LineItem (using a CMR field)
  4. Set the fields Tax, Status, Quantity, Shipment Date (using a CMP field)
  5. If Address is valid, create it and assign the relationship address to the LineItem (using a CMR field)
  6. To the Order‘s LineItem collection, add the created line item

This translates to:

  /**
   * Adds a LineItem to the Order
   * @param productId
   * @param quantity
   * @param addressValueObject
   *
   * @ejbgen:remote-method
   */
  public void addLineItem(String productId, Integer quantity, AddressVO addressValueObject)
    throws RemoteException {
    try {
      //1. Create a new LineItem object
      Context context = new InitialContext();
      LineItemLocalHome localItemHome = (LineItemLocalHome)context.lookup("LineItemBeanLocal");
      LocalLineItem localItem = localItemHome.create();
      //2. Find the product using the productId and get its local interface
      ProductLocalHome localProductHome = (ProductLocalHome)context.lookup("ProductBeanLocal");
      LocalProduct localProduct = localProductHome.findByPrimaryKey(productId);
      //3. Assign the relationship product to the LineItem (CMR)
      localItem.setProduct(localProduct);
      //4. Set the fields Tax, Status, Quantity, Shipment Date (CMP)
      localItem.setQuantity(quantity);
      localItem.setShipmentdate(new java.sql.Date(System.currentTimeMillis()));
      localItem.setStatus(OrderBean.UNSHIPPED);
      localItem.setTax(OrderUtil.calculateTax(localProduct.getPrice(), quantity, addressValueObject));
      //5. If Address is valid, create it and assign the relationship address to the LineItem (CMR)
      if (addressValueObject.isValid()) {
        context = new InitialContext();
        AddressLocalHome localAddressHome = (AddressLocalHome)context.lookup("AddressBeanLocal");
        LocalAddress localAddress = localAddressHome.create(addressValueObject.street1,
          addressValueObject.street2, addressValueObject.postcode, addressValueObject.city,
          addressValueObject.country);
        localItem.setAddress(localAddress);
      } else {
        throw new RemoteException("Address is not valid!");
      }
      //6. To the Order's LineItem collection, add the created line item
      this.getLineItems().add(localItem);
    } catch(Throwable t) {
      throw new RemoteException(t.getMessage(), t);
    }
  }

That’s it!

Quick start

In the

ejbgen.zip

, you will find:

  • The source code in the sources folder
  • The batches in the bin folder
  • The SQL script files to create the database in the db folder
  • EJBGen in the lib folder

To try EJBGen easily and quickly, just:

  • Create the schema in the database
  • Invoke JyperionClient in the package org.jyperion.client to create the Customers and Products
  • Invoke JyperionClient to test the finders and call the business method addLineItem()

The

DIY.txt

file in

ejbgen.zip

details the steps for using EJBGen.

Discover what you’re missing

In this article, you learned:

  • To easily accelerate your EJB 2.0 development using EJBGen
  • To simplify maintenance by maintaining just the bean class
  • EJBGen can generate all the interfaces and XML deployment descriptors for you
  • The advantages of the EJB CMP 2.0 model

EJB 2.0 is great technology. Tools such as EJBGen and XDoclet (an extended Javadoc doclet engine from SourceForge) can speed your development if you work with WebLogic Server 6.1 or JBoss with MVCSoft. I was able to complete the code above (plus client code, database creation, testing, deployment) in just two nights. EJBGen helps you focus on the business logic rather than the technical infrastructure. You don’t believe me? Try it!

Thierry Janaudy is a
London-based independent consultant currently working for Jyperion. Thierry hopes to put
genetic algorithms
in his coffee machine someday for better beans selection.

Source: www.infoworld.com