Previous     Contents     Index     DocHome     Next     
iPlanet Application Server Programmer's Guide (Java)



Chapter 6   Building Business Entity EJBs


This chapter explains what a business entity EJB is and what entity beans must contain. It also provides additional guidelines for creating entity beans, and for determining the entity beans needs of your applications.

This chapter contains the following sections:

All specifications are accessible from installdir/ias/docs/index.htm, where installdir is the location in which you installed iAS.



Introducing Business Entity EJBs



Often, the heart of a distributed, multi-user application involves interactions with data sources, such as a database, or even with an existing legacy application. Such interactions are often transactional. In most of these cases, the external data source or business object is transparent to the user, or is shielded or buffered from direct user interaction. These protected, transactional, and persistent interactions with databases, document files, and other business objects, are candidates for encapsulation in entity EJBs.

Business EJBs are self-contained, reusable components—with data members, properties, and methods—that represent instances of generic, transactionally aware, persistent data objects that can be shared among clients. Persistence refers to the creation and maintenance of a bean throughout the lifetime of the application.

There are two types of persistence management and iAS supports both types as listed below.

  • container-managed persistence - This is when the container is responsible for the persistence of the beans.

  • bean-managed persistence - This is when the beans are responsible for their own persistence.

As a developer, you code a bean-managed entity bean by providing database access calls—via JDBC or SQL—directly in the methods of the bean class. Database access calls must be placed in the code for the ejbCreate(...), ejbRemove(), ejbFindXXX(), ejbLoad(), and ejbStore() methods. The advantage of using bean-managed persistence is that such beans can be installed into any container without requiring the container to generate database calls.

Entity beans rely on the container to manage security, concurrency, transactions, and other, container-specific services for the entity objects it manages. Multiple clients can access an entity object at the same time, and the container transparently handles simultaneous access through transactions.

As an iAS application developer, you cannot access the container's entity bean services directly, nor do you ever need to. Instead, know that the container is there to take care of low-level implementation details so that you can focus on the larger role the entity bean plays in your application picture.

Clients access an entity bean through the bean's remote interface. The object that implements the remote interface is called the EJB object. Usually, an entity EJB is shared among multiple clients, and represents a single point of entry to a data resource or business object, such as a database. Regardless of which client access an entity object at a given time, each client's view of the object is both location independent, and transparent to other clients.

Finally, any number of entity beans can be installed in a container. The container implements a home interface for each entity bean. The home interface enables a client to create, look up, and remove entity objects. A client can look up an entity bean's home interface through the Java Naming and Directory Interface (JNDI).

An entity bean:

  • represents data in a database.

  • supports transactions.

  • executes for multiple clients.

  • persists for as long as needed by all clients.

  • transparently survives server crashes.

Generally, an entity bean represents shared data in a database and is transaction aware. Its operations always take place in the context of transactions managed by the bean's container.


How an Entity Bean Is Accessed

A client, such as a browser or servlet, accesses an entity bean through the bean's remote interface, EJBObject. An EJB object is a remote Java programming language object accessible from the client through standard Java APIs for remote object calls. The EJB lives in the container from its creation to its destruction, and the container manages the EJB's life cycle and support services.

A client never accesses instances of an entity bean directly. Instead, a client uses the entity bean's remote interface to access a bean instance. The EJB object class that implements an entity bean's remote interface is provided by the container. At a minimum, an EJB object supports all of the methods of the java.ejb.EJBObject interface. This includes methods to obtain the entity bean's home interface, to get the object's handle, to retrieve the entity's primary key, to test if the object is identical to another object, and to remove the object. These methods are stipulated by the EJB Specification. All specifications are accessible from installdir/ias/docs/index.htm, where installdir is the location in which you installed iAS.

In addition, the remote interface for most EJB objects also support specific business logic methods. These are the methods at the heart of your specific applications.



Entity Bean Components



When you code an entity bean you must provide the following class files:

  • Enterprise Bean class

  • Enterprise Bean home interface, javax.ejb.EJBHome

  • Enterprise Bean remote interface, javax.ejb.EJBObject


Creating the Class Definition

For an entity bean, the Bean class must be defined as public, and cannot be abstract. The Bean class must implement the javax.ejb.EntityBean interface. For example:

import java.rmi.*;
import java.util.*;
import javax.ejb.*;
public class MyEntityBean implements EntityBean {

// Entity Bean implementation. These methods must always included.

public void ejbActivate() throws RemoteException {
}

public void ejbLoad() throws Remote Exception {
}

public void ejbPassivate() throws RemoteException {
}

public void ejbRemove() throws RemoteException{
}

public void ejbStore() throws RemoteException{
}

public void setEntityContext(EntityContext ctx) throws RemoteException {
}

public void unsetEntityContext() throuws RemoteException {
}

// other code omitted here....
}

In addition to these methods, the entity bean class must also define one or more ejbCreate() methods and the ejbFindByPrimaryKey() finder method. Optionally, it may also define one ejbPostCreate() method for each ejbCreate() method. It may also optionally provide additional, developer-defined finder methods that take the form ejbFindXXX, where XXX represents a unique continuation of a method name (e.g., ejbFindApplesAndOranges) that does not duplicate any other methods names.

Finally, most useful entity beans also implement one or more business methods. These methods are usually unique to each bean and represent its particular functionality. Business method names can be anything you want, but must not conflict with the names of methods used in the EJB architecture. Business methods must be declared as public. Method arguments and return value types must be legal for Java RMI. The throws clause may define application-specific exceptions, and may include java.rmi.RemoteException.

There are really two types of business methods you can implement in an entity bean: internal ones, that are used by other business methods in the bean, but are never accessed outside the bean itself; and external ones, that are referenced by the entity bean's remote interface.

The following sections describe the various methods in an entity bean's class definition in more detail.

The examples in this section assume the following member variable definitions:

private transient javax.ejb.EntityContext m_ctx = null;

// These define the state of our bean
private int m_quantity;
private int m_totalSold;


Using ejbActivate and ejbPassivate

When an instance of an entity bean is needed by a server application, the bean's container invokes ejbActivate() to ready an instance of the bean for use. Similarly, when an instance is no longer needed by the application, the bean's container invokes ejbPassivate() to disassociate the bean from the application.

If there are specific application tasks that need to be performed when a bean is first made ready for an application, or that need to be performed when a bean is no longer needed by the application, code those operations within these methods.

Activation is not the same as creating a bean. You can only activate a bean that has already been created. Similarly, passivation is not the same as removing a bean. Passivation merely returns a bean instance to the container pool for later use. ejbRemove() is required to actually terminate a bean instance's existence.

The container passivates session beans after they are inactive for a specified (or default) time. This timeout value is set in the bean's property file. For more information, see"EJB XML DTD" in Chapter 11, "Packaging for Deployment".

For more information about ejbActivate() and ejbPassivate(), see the EJB Specification. All specifications are accessible from installdir/ias/docs/index.htm, where installdir is the location in which you installed iAS.

For more information about ejbCreate() and ejbRemove(), see Using ejbCreate Methods.


Using ejbLoad and ejbStore

An entity bean should permit its container to store the bean's state information in a database for synchronization purposes. Use your implementation of ejbStore() to store state information in the database, and use your implementation of ejbLoad() to retrieve state information from the database. When the container calls ejbLoad(), it synchronizes the bean state by loading state information from the database.

The following example shows ejbLoad() and ejbStore() definitions that methods store and retrieve active data.

public void ejbLoad()
            throws java.rmi.RemoteException
{
    String itemId;
    DatabaseConnection dc = null;
    java.sql.Statement stmt = null;
    java.sql.ResultSet rs = null;

    itemId = (String) m_ctx.getPrimaryKey();

    System.out.println("myBean: Loading state for item " + itemId);

    String query =
        "SELECT s.totalSold, s.quantity " +
        " FROM Item s " +
        " WHERE s.item_id = " + itemId;

    dc = new DatabaseConnection();
    dc.createConnection(DatabaseConnection.GLOBALTX);
    stmt = dc.createStatement();
    rs = stmt.executeQuery(query);

    if (rs != null) {
        rs.next();
        m_totalSold = rs.getInt(1);
        m_quantity = rs.getInt(2);
    }
}

public void ejbStore()
            throws java.rmi.RemoteException
{
    String itemId;
    itemId = (String) m_ctx.getPrimaryKey();
    DatabaseConnection dc = null;
    java.sql.Statement stmt1 = null;
    java.sql.Statement stmt2 = null;

    System.out.println("myBean: Saving state for item = " + itemId);

    String upd1 =
        "UPDATE Item " +
        " SET quantity = " + m_quantity +
        " WHERE item_id = " + itemId;

    String upd2 =
        "UPDATE Item " +
        " SET totalSold = " + m_totalSold +
        " WHERE item_id = " + itemId;

    dc = new DatabaseConnection();
    dc.createConnection(DatabaseConnection.GLOBALTX);
    stmt1 = dc.createStatement();
    stmt1.executeUpdate(upd1);
    stmt1.close();
    stmt2 = dc.createStatement();

    stmt2.executeUpdate(upd2);
    stmt2.close();
}

Note For related information about isolation levels in beans that access transactions concurrently with other beans, see Handling Concurrent Access.


Using setEntityContext and unsetEntityContext

A container calls setEntityContext() after it creates an instance of an entity bean in order to provide the bean with an interface to the container itself. When you implement this method, use it to store the reference to the container in an instance variable.

public void setEntityContext(javax.ejb.EntityContext ctx)
{
m_ctx = ctx;
}

Similarly, a container calls unsetEntityContext() to remove the container reference from the instance. This is the last bean class method a container calls. After this call, the Java garbage collection mechanism will eventually call finalize() on the instance to clean it up and dispose of it.

public void unsetEntityContext()
{
m_ctx = null;
}


Using ejbCreate Methods

The entity bean must also implement one or more ejbCreate(...) methods. There should be one such method for each way a client is allowed to invoke the bean. For example:

public int ejbCreate() {
   string[] userinfo = {"User Name", "Encrypted Password"};
}

Each ejbCreate(...) method must be declared as public, return either the entity's primary key type or a collection, and be named ejbCreate. The return type can be any legal Java RMI type that can be converted to a number for key purposes. Any arguments must be legal types for Java RMI. The throws clause, may define application-specific exceptions, may include java.rmi.RemoteException, or java.ejb.CreateException.

For each ejbCreate() method, the entity bean class may optionally define an ejbPostCreate() method to handle entity services immediately following creation Each ejbPostCreate() method must be declared as public, must return void, and be named ejbPostCreate. The method arguments, if any, must match in number and type the arguments of its corresponding ejbCreate method. The throws clause, may define application-specific exceptions, may include java.rmi.RemoteException, or may include java.ejb.CreateException.

Finally, an entity bean also implements one or more ejbRemove() methods to free a bean when it is no longer needed.


Using Finder Methods

Because entity beans are persistent, are shared among clients, and may have more than once instance instantiated at the same time, an entity bean must implement at least one method, FindByPrimaryKey(), that enables the client and the bean's container to locate a specific bean instance. All entity beans must provide a unique primary key as an identifying signature. You can implement the FindByPrimaryKey() method in the bean's class to enable a bean to return its primary key to the container.

The following example shows a definition for FindByPrimaryKey():

public String ejbFindByPrimaryKey(String key)
              throws java.rmi.RemoteException,
                     javax.ejb.FinderException
{
    //System.out.println("@@@ myBean.ejbFindByPrimaryKey key = " + key);
    return key;
}

In some cases, you may want to find a specific instance of an entity bean based on what the bean does, based on certain values the instance is working with, or based on still other criteria. These names of these implementation-specific finder methods take the form ejbFindXXX, where XXX represents a unique continuation of a method name (e.g., ejbFindApplesAndOranges) that does not duplicate any other methods names.

Finder methods must be declared as public, and their arguments and return values must be legal types for Java RMI. The return type of each finder method must be the entity bean's primary key type or a collection of objects of the same primary key type. If the return type is a collection, the return type must be java.util.Enumeration.

The throws clause of a finder method can be an application specific exception, may include java.rmi.RemoteException and/or java.ejb.FinderException.


Declaring vs. Implementing the Remote Interface

A bean class definition must include one matching method definition, including matching method names, arguments, and return types, for each method defined in the bean's remote interface. The EJB specification also permits the bean class to implement the remote interface's methods, but recommends against this practice to avoid inadvertently passing a direct reference (via this) to a client in violation of the client-container-EJB protocol intended by the specification.


Creating the Home Interface

The home interface defines the methods that enable a client accessing your application to create and remove entity objects. A home interface always extends javax.ejb.EJBHome. For example:

import javax.ejb.*;
import java.rmi.*;

public interface MyEntityBeanHome extends EJBHome {
   MyEntityBean create() throws CreateException, RemoteException;
}

As this example illustrates, an entity bean's home interface defines one or more create methods. Usually the home interface also defines one or more find methods corresponding to the finder methods in the bean class.


Defining Create Methods

Each such method must be named create, and must correspond in number and type of arguments to an ejbCreate method defined in the entity bean class. The return type for each create method, however, does not match the return type of its corresponding ejbCreate method. Instead, it must return the entity bean's remote interface type.

All exceptions defined in the throws clause of an ejbCreate method must be defined in the throws clause of the matching create method in the home interface. In addition, the throws clause in the home interface must always include javax.ejb.CreateException.


Defining Find Methods

A home interface can define one or more find methods. Each such method must be named findXXX (e.g., findApplesAndOranges), where XXX is a unique continuation of the method name. Each finder method must correspond to one of the finder methods defined in the entity bean class definition. The number and type of arguments must also correspond to the finder method definitions in the bean class. The return type, however, may be different. The return type for a finder method in the home interface must be the entity bean's remote interface type or a collection of interfaces.

Finally, all home interfaces automatically define two remove methods for destroying an EJB when it is no longer needed. Do not override these methods.


Creating the Remote Interface

An entity bean's remote interface defines a user's access to a bean's methods. All remote interfaces extend javax.ejb.EJBObject. For example:

import javax.ejb.*;
import java.rmi.*;
public interface MyEntityBean extends EJBObject {
// define business method methods here....
}

The remote interface defines the entity bean's business methods that a client calls. The business methods defined in the remote interface are implemented by the bean's container at runtime. For each method you define in the remote interface, you must supply a corresponding method in the bean class itself. The corresponding method in the bean class must have the same signature.

Besides the business methods you define in the remote interface, the EJBObject interface defines several abstract methods that enable you to retrieve the home interface for the bean, to retrieve the bean's handle, to retrieve the bean's primary key which uniquely identifies the bean's instance, to compare the bean to another bean to see if it is identical, and to remove the bean when it is no longer needed.

For more information about these built-in methods and how they are to be used, see the EJB Specification. All specifications are accessible from installdir/ias/docs/index.htm, where installdir is the location in which you installed iAS.



Additional Entity Bean Guidelines



Before you decide what parts of your application you can represent as entity beans, you should know a few more things about entity beans. A couple of these things are related to the EJB specification for entity beans, and a couple are specific to iAS and its support for entity beans.


Accessing iAS Functionality

You can develop entity beans that adhere strictly to the EJB Specification, you can develop entity beans that take advantage both of the specification and additional, value-added iAS features, and you can develop entity beans that adhere to the specification in non-iAS environments, but that take advantage of iAS features if they are available. Make the choice that is best for your intended deployment scenario.

iAS offers several features through the iAS container and iAS APIs that enable your applications to take programmatic advantage of specific features of the iAS environment. You can embed API calls in your entity beans if you plan on using those beans only in the iAS environment.


Serializing Handles and References

The EJB Specification indicates that to guarantee serializable bean references, you should use handles rather than direct references to EJBs.

iAS direct references are also serializable. You may wish to take advantage of this extension, but you should be aware that not all vendors support it.


Managing Transactions

Most entity beans interact with databases. You can control transactions in beans using settings in the bean's property file. This permits you to specify transaction attributes at bean deployment time. By having a bean handle transaction management you are freed from having explicitly to start, roll back, or commit transactions in the bean's database access methods.

By moving transaction management to the bean level, you gain the ability to place all of a bean's activities—even those not directly tied to database access—under the same transaction control as your database calls. This guarantees that all parts of your application controlled by a entity bean run as part of the same transaction, and either everything the bean undertakes is committed, or is rolled back in the case of failure. In effect, bean-managed transactional state permits you to synchronize your application without having to code any synchronization routines.


Committing a Transaction

When a commit occurs, it signals to the container that the entity bean has completed its useful work, and should synchronize its state with the underlying data store. The container permits the transaction to complete, and then returns the bean to the pool for later reuse. Result sets associated with a committed transaction are no longer valid. Subsequent requests for the same bean cause the container to issue a load to synchronize state with the underlying data store.

Note that transactions begun in the container are implicitly committed. Also, any participant can roll back a transaction. For more information about transactions, see Chapter 7 "Handling Transactions with EJBs".


Commit Options B and C

Commit options B and C are supported by iAS CMP.

  • Commit option B caches the entity bean instance between method invocations. The instance retains it's association with a particular primary key and is not yet returned to the pool of free, unassociated instances. When the instance's stay in the ready cache exceeds the ready cache timeout value, the container will transition the instance back to the free pool.

  • Commit option C gets an instance of a bean from the free pool at the start of a transaction and transitions the instance back to the free pool at the end of the transaction.

Note If the entity bean has the TX_NOT_SUPPORTED attribute set and the commit option B defined, the bean will perform read only data caching.

The rules of operation for commit option B are a little more complex than for commit option C. Here are some examples to explain the differences.


Example 1
This is the lifecyle for several business method invocations by a single transactional client.

On the first invocation (no ready instance has been cached):

ejbActivate -> ejbLoad -> business method-> ejbStore-> (instance is put in ready cache)

On subsequent invocations:

(retrieve ready instance)-> ejbLoad -> business method -> ejbStore-> (instance is returned to ready cache)

In comparison, the lifecycle for every business method invocation under commit option C looks like this:

ejbActivate-> ejbLoad -> business method -> ejbStore -> ejbPassivate

If there is more than one transactional client concurrently accessing the same entity EJBObject, the first client will get the ready instance and subsequent concurrent clients will get new instances from the pool. The lifecycle for these new instances will be the same as if they were operating under commit option C.


Example 2
This is the lifecyle for several business method invocations by a single non-transactional client.

On the first invocation (no ready instance has been cached):

ejbActivate -> ejbLoad -> (instance is put in ready cache)-> business method

On subsequent invocations:

(get reference to ready instance) -> business method

Multiple non-transactional clients can access the ready instance concurrently. The bean, therefore, must be declared as reentrant.

If a transactional client is using the ready instance, non-transactional clients will block until the transaction is completed.


Handling Concurrent Access

As an entity bean developer, you do not have to be concerned about concurrent access to an entity bean from multiple transactions. The bean's container automatically provides synchronization in these cases. In iAS, the iAS container activates one instance of an entity bean for each simultaneously-occurring transaction that uses the bean. Transaction synchronization is performed automatically by the underlying database during database access calls.

The iAS EJB container implementation does not provide its own synchronization mechanism when multiple transactions try to access an entity bean. It creates a new instance of the entity bean for every new transaction. The iAS container delegates the responsibility of synchronization to the application.

You typically perform this synchronization in conjunction with the underlying database or resource. One possible approach would be to acquire the corresponding database locks in the ejbLoad() method, for example by choosing an appropriate isolation level or by using a "select for update" clause. The specifics depend on the database backend that is being used. For more information, see the EJB specification as it relates to concurrent access.

The following example ejbLoad() snippet illustrates the use of "select for update" syntax to obtain database locks. This prevents other instances from being loaded at the same time.

public void ejbLoad() throws java.rmi.RemoteException
{
....
// Get the lock on the corresponding DB table
    try {
        java.sql.Connection dbConn = ds.getConnection();
        String query = "SELECT accountNum, balance FROM accounts "
                     + "WHERE customerId = ? FOR UPDATE";
        prepStmt = dbConn.prepareStatement(query);
        prepStmt.setString(1, m_customerId);
        resultSet = prepStmt.executeQuery();
        if ((resultSet != null) && resultSet.next()) {
            acctNum = resultSet.getInt(1);
            acctBalance = resultSet.getInt(2);
        } else {
            throw new RemoteException("Database error. "
                                    + "Couldn't find accout");
        }
    } catch (java.sql.SQLException e) {
        throw new RemoteException("Database error. "
                                + "Couldn't load account");
    } finally {
        try {
            if (resultSet != null)
                resultSet.close();
            if (prepStmt != null)
                prepStmt.close();
            if (dbConn != null)
                dbConn.disconnect();
        } catch (java.sql.SQLException e) {
            System.out.println("Unexpected exception while "
                             + "closing resources"); }
    }
}


Accessing Databases

Most entity beans work with databases. Always use the transaction and security management methods in javax.ejb.deployment.ControlDescriptor to manage transaction requirements. For more information about creating and managing transactions with beans, see Chapter 7 "Handling Transactions with EJBs".

To work with data in the context of a bean-managed transaction, use JDBC. For more information about using JDBC to work with data, see Chapter 8 "Using JDBC for Database Access".


Container Managed Persistence

If you want the container to manage the storage of entity bean state to an underlying resource manager then you can use container managed persistence (CMP) entity beans.

Important iAS CMP goals include:

  • Support for the J2EE v 1.2 specification CMP model.

  • Support of multiple pluggable persistence managers in the server.

  • Provide user with a deployment tool (iASDT) to perform the object relational (O/R) mapping and create the separate CMP deployment descriptor XML files.

  • Support for sophisticated custom finder methods


How Container Managed Persistence Works

The EJB container needs two things to support CMP entity beans. First, it needs information on how to map an entity bean into a resource such as a table in a relational database. Second, it needs a CMP runtime environment that will use the mapping information to perform persistence operations on each bean.

iAS supports multiple CMP solutions via an architecture known as pluggable CMP (PCMP). A CMP solution consists of a mapping tool and a runtime environment. The mapping tool is used to generate mapping information for CMP entity beans. Typically this information is stored in an XML file. The CMP runtime environment is responsible for performing persistence operations.

iAS ships with a mapping tool that is embedded in iASDT. Please see the deployment tool documentation for information on how to use iASDT to generate mapping information. iAS also ships with a reference implementation of a CMP runtime environment that utilizes the mapping information generated by the iASDT to perform persistence operations (see below for more information).


Pluggable Container Managed Persistence

iAS supports CMP entity beans via the pluggable container managed persistence architecture (PCMP). The primary benefit of this architecture is that it separates the EJB container from the CMP runtime environment (also known as the persistence manager). The EJB container communicates with the persistence manager through a well defined open API. Using this API, the container is able to ask the persistence manager to perform various persistence operations throughout the entity bean lifecycle. The persistence manager is responsible for performing the persistence operations and may use caching and other techniques to improve performance.

A number of vendors, including Thought, Inc. and Forte, are planning to adapt their CMP products to support PCMP. Please look at the product release notes for a URL link with a list of products that currently support PCMP.

PCMP is an open and well defined API. It is possible to write your own persistence manager to support legacy systems. Please look at the product release notes for a URL link with information on how to obtain the PCMP API specification.


How to Use Persistence Managers in iAS

Pluggable persistence managers allow users to set the persistence policies for a bean at deployment time. In order to use PCMP with a particular bean, you need to set a persistence manager factory class in the xml deployment descriptor. The EJB container will use this factory class to create persistence managers during runtime.

The iASDT should be used to set the persistence manager factory class for a particular bean. Please refer to the iASDT documentation for information on how to modify the deployment descriptor.

If you are using the iAS persistence manager reference implementation the factory class tag should have the value: com.netscape.server.ejb.SQLPersistenceManagerFactory


iAS Persistence Manager Reference Implementation

iAS ships with a simple persistence manager reference implementation. The reference implementation uses mapping information generated by the iASDT. Users may also hand modify the mapping information file produced by iASDT to access additional features. For example, custom finder methods are not supported by iASDT but may be added to the mapping information file by hand.

Please go to the following url to obtain the latest technical documentation on the iAS persistence manager reference implementation. Please look at the product release notes for a URL link with information on how to obtain the latest technical documentation on the iAS persistence manager reference implementation.


Previous     Contents     Index     DocHome     Next     
Copyright © 2000 Sun Microsystems, Inc. Some preexisting portions Copyright © 2000 Netscape Communications Corp. All rights reserved.

Last Updated June 25, 2000