DekGenius.com
[ Team LiB ] Previous Section Next Section

9.5 Enterprise JavaBeans

RMI is a distributed object API. It specifies how to write objects so that they can talk to one another no matter where on the network they are found. I could write dozens of business objects that can, in principal, talk to your business objects using RMI. At its core, however, RMI is nothing more than an API to which your distributed objects must conform. RMI says nothing about other characteristics normally required of an enterprise-class distributed environment. For example, it says nothing about how a client might perform a search for RMI objects matching some criteria. It also says nothing about how those objects might work together to construct a single transaction.

What is missing from the picture is a distributed component model. A component model is a standard that defines how components are written so that systems can be built from components by different authors with little or no customization. You may be familiar with the JavaBeans component model. It is a component model that defines how you write user interface components so that they may be plugged into third-party applications. The magic thing about JavaBeans is that there is very little API behind the specification—you neither implement nor extend any special classes and you need call no special methods. The force of JavaBeans is largely in conformance with an established naming convention.

Enterprise JavaBeans is a more complex extension of this concept. While there are API elements behind Enterprise JavaBeans, it is much more than just an API. It is a standard way of writing distributed components so that the components I write can be used with the components you write in someone else's system. RMI does not support this ability for several reasons. Consider all of the following issues RMI does not address:


Security

RMI says nothing about security. RMI alone basically leaves your system wide open. Anybody who has access to your RMI interfaces can forge access to the underlying components. Unless you write some complex security checks to authenticate clients and verify access, you will have no security. Your components are therefore unlikely to interoperate with my components unless we agree to share some sort of security model.


Searching

RMI provides the ability to do a lookup for only a specific, registry-bound object. It says nothing about how you find unbound objects or perform searches for a group of objects meeting certain requirements. Writing a banking application, you might want to support the ability to find all accounts with negative balances. In order to do this in an RMI environment, you would have to write your own search methods in bound objects. Again, your custom approach to handling searches simply won't work with someone else's custom approach to searching without forcing clients to deal with both search models.


Transactions

Perhaps the most important piece to a distributed component model is support for transactions. RMI says absolutely nothing about transactions. When you build an RMI-based application, you will need to address how you will support transactions. In other words, you will need to keep track of when a client begins a transaction, what RMI objects that client changes, and committing and rolling back those changes when the client is done. This problem is compounded by the fact that most distributed object systems are supporting more than one client at a time. Different transaction models are even more incompatible than different search or security models. While client coders can get around differences in search and security models by being aware of those differences, transaction models can almost never be made to work together.


Persistence

RMI says nothing about how RMI objects persist across time. Over the course of this book, I will introduce several different persistence models. EJB is behind three of these persistence models.

Enterprise JavaBeans addresses all of these points and more so that you can literally pick and choose the best designed business components from different vendors and make them work and play well with one another in the same environment. EJB is now the standard component model for capturing distributed business components. It hides from you the details you might have to worry about yourself if you were writing an RMI application.

9.5.1 EJB Roles

One of the benefits of the EJB approach is that it separates different application development roles into distinct parts so that everything one role does is usable by any possible player of any of the other roles. EJB specifically defines the following roles:[5]

[5] Any given role may be played by multiple players on a project. Similarly, one person may play multiple roles.


The EJB provider

The EJB provider is an expert in the problem domain in question and develops Java objects that capture the business concepts that make up the problem domain. The EJB provider worries about nothing other than business logic programming.


The application assembler

The application assembler is an expert in the processes that make up a business and in building user interfaces that employ the EJB provider's business components.


The deployer

The deployer is an expert in a specific operating environment. The deployer takes an assembled application and configures it for deployment in the runtime environment.


The EJB server provider

A server provider supports one or more services, such as a JDBC driver supporting database access.


The EJB container provider

The container is where EJB components live. It is the runtime environment in which the beans operate. The container provider is a vendor that builds the EJB container.


The system administrator

The system administrator manages the runtime environment in which EJB components operate.

An EJB provider captures each of the business components that model a business in Java code. The EJB specification breaks down each of these business components into three pieces: the home interface, the remote interface, and the bean implementation. Your job as the EJB provider is thus to write these three classes for each business component in your system.

9.5.2 Kinds of Beans

EJB specifies two kinds of beans: entity beans and session beans. The distinction between the two is that entity beans are persistent and session beans are transient. In other words, entity beans save their states across time while session beans do not. Most business concepts will work best as entity beans—they are the entities that make up your business. Entity beans are shared by all clients.[6]

[6] This is not necessarily true of all environments. Specifically, EJB allows for a clustered environment in which multiple application servers work together to serve up beans. In such an environment, the same entity may appear on different servers and serve different clients. The containers are responsible in those situations for making the system appear as if the clients share the same entity reference.

Session beans are unique to each client. They come to life only when requested by a client. When that client is done with them, they go away. An example of a session bean might be a Registration class that represents the registration of a person for some event. The Registration exists for a specific client to associate a person with an event. It manages the business logic associated with a registration, but it goes away once the registration is complete. The persistent data is in the Person and Event classes.

The word bean is a heavily overloaded term in Java. Even within the EJB specification, the word bean has different meanings in different contexts. It can mean one of the three classes called the bean implementation or it can mean the business concept as a whole. I take the approach of using the term bean alone to mean the business component represented by the three EJB classes and the term bean implementation to mean the one class that implements the business logic.


The home and remote interfaces for both kinds of beans are RMI remote interfaces. That is, they are indirectly derived from the java.rmi.Remote interface and are exported for remote access. The class extended by a remote interface is EJBObject. If, for example, you wanted to turn the Ball object from earlier in the chapter into an entity bean, you would create a BallHome interface, a Ball interface, and a BallBean implementation. The Ball interface would extend javax.ejb.EJBObject, which in turn extends java.rmi.Remote. The result might be a class that looks like this:

public interface Ball extends javax.ejb.EJBObject {
    void hit( ) throws java.rmi.RemoteException;
   
    int getPosition( ) throws RemoteException;
}

This interface looks a lot like the RMI example from earlier in the chapter. In fact, the only difference is that this one extends Remote indirectly via EJBObject. The interface specifies only those methods that should be made available to the rest of the world.

The home interface is where you go to find or create instances of the bean. It specifies different versions of create( ) or findXXX( )[7] methods that enable a client to create new instances of the bean or find existing instances. If you think about the problem of a banking system, they might have account beans, customer beans, and teller beans. When the bank attracts a new customer, its enterprise banking system needs to create a Customer bean to represent that customer. The bank manager's Windows application that enables the registration of new customers might have the following code for creating a new Customer bean:

[7] Only entity beans have finder methods.

InitialContext ctx = new InitialContext( );
CustomerHome custhome;
Customer cust;
   
custhome = (CustomerHome)ctx.lookup("CustomerHome");
cust = custhome.create(ssn);

This code provides you with your first look at JNDI support in EJB. Using a JNDI initial context, you look up an implementation of the customer bean's home interface. That home interface, CustomerHome, provides a create( ) method that enables you to create a new Customer bean. In the preceding case, the create( ) method accepts a String representing the customer's Social Security number.[8] The EJB specification requires that a home interface specify create( ) signatures for each way to create an implementation of that bean. The CustomerHome interface might look like this:

[8] A Social Security number is a U.S. federal tax identifier.

public interface CustomerHome extends EJBHome {
    Customer create( ) throws CreateException, RemoteException;
   
    Customer create(String ssn)
    throws CreateException, RemoteException;
   
    Customer findByPrimaryKey(CustomerKey pk)
    throws FinderException, RemoteException;
   
    Customer findBySocialSecurityNumber(String ssn)
    throws FinderException, RemoteException;
}

The finder methods provide ways to look up Customer objects. All except the findByPrimaryKey( ) method can be named however you wish to name them, and they should return either a remote reference to the bean in question or a Collection.

The findByPrimaryKey( ) method is a special finder for EJBs. Each entity bean instance has a primary key that uniquely identifies it. The primary key can be any serializable Java class you write. The only requirement is that the class must implement the equals( ) and hashCode( ) in an appropriate fashion. For example, if your beans have a unique numeric identifier, you might create your own CustomerKey class that stores the identifier as a long. If you do the latter, your CustomerKey class should look something like the following:

public class CustomerKey implements Serializable {
    private long objectID = -1L;
   
    public CustomerKey(long l) {
        objectID = l;
    }
   
    public boolean equals(Object other) {
        if( other instanceof CustomerKey ) {
            return ((CustomerKey)other).objectID =  = objectID;
        }
        return false;
    }
   
    public int hashCode( ) {
        return (new Long(objectID)).hashCode( );
    }
}

You can even use primitive wrapper classes instead of custom primary key classes. The following example, for instance, could just as easily have used the Long class for its primary keys.

You do not actually write the class that implements the Customer or CustomerHome interfaces—that is the task of the EJB container. Generally, the EJB container will have tools that enable a deployer to automatically create and compile implementation classes for the home and remote interfaces. These automatically generated classes handle issues such as security and then delegate to your bean implementation class. The bean is where you write your business logic.

The bean class must implement the following methods:

  • It must implement every method in the class it implements: EntityBean for entity beans and SessionBean for session beans.

  • It must implement every method in the remote interface using the exact same signatures found in the remote interface.

  • It must implement a variation of the methods in the home interface.[9] For create( ) methods, it must implement counterparts called ejbCreate( ) that each takes the same arguments but returns a primary key object. Similarly, the findXXX( ) counterparts for entities are ejbFindXXX( ) methods that each takes the same arguments and returns either a primary key object or a collection of primary keys.

    [9] This applies only to beans using bean-managed persistence. For container-managed beans, the creates and finds are implemented by the container.

Consider that the Customer remote interface for the previous home interface looks like this:

public interface Customer extends EJBObject {
    String getSocialSecurityNumber( ) throws RemoteException;
}

A skeleton of the bean implementation might look something like this (minus the method bodies):

public class CustomerBean implements EntityBean {
    private transient EntityContext context = null;
    private           String        ssn     = null;
   
    public void ejbActivate( ) throws RemoteException {
        // you will mostly leave this method empty
        // activation of resources required by
        // an object of this type independent of the
        // customer it represents belong here
        // an example might be opening a file handle
        // for logging
    }
   
    public CustomerKey ejbCreate( ) throws CreateException {
        // this method creates a primary key for the
        // customer and inserts the customer into the 
        // database
    }
   
    public CustomerKey ejbCreate(String ssn) 
    throws CreateException {
        // this method works the same as ejbCreate( )
    }
   
    public CustomerKey ejbFindByPrimaryKey(CustomerKey pk) 
    throws FinderException, RemoteException {
        // this method goes to the database and performs
        // a SELECT and returns the PK if it is in the
        // database
    }
   
     public CustomerKey ejbFindBySocialSecurityNumber(String ssn)
    throws FinderException {
        // this method goes to the database and performs
        // a SELECT and returns the PK of the row
        // with a matching SSN
    }
   
    public void ejbLoad( ) throws RemoteException {
        // this method goes to the database and selects
        // the row that has this object's primary key
        // and then populates this object's fields
    }
   
    public void ejbPassivate( ) throws RemoteException {
        // this method is generally empty
        // you should release any system resources held
        // by this object here
    }
   
    public void ejbPostCreate( ) {
        // this is called to let you do any initialization
        // for this object after ejbCreate( ) is called and
        // a primary key is assigned to the object
    }
   
    public void ejbRemove( ) throws RemoteException {
        // this method goes to the database and deletes
        // the record with a primary key matching this
        // object's primary key
    }
   
    public void ejbStore( ) throws RemoteException {
        // this method goes to the database and saves
        // the state of this bean
    }
   
    public String getSocialSecurityNumber( ) {
        // this method is from the Customer remote interface
        return ssn;
    }
   
    public void setEntityContext(EntityContext ctx)
    throws RemoteException {
        // this method assigns an EntityContext to the
        // bean
        context = ctx;
    }
   
    public void unsetEntityContext( )
    throws RemoteException {
        // this method removes the EntityContext assignment
        context = null;
    }
}

JDBC comes into play under the bean-managed persistence model in the ejbCreate( ), ejbFindXXX( ), ejbLoad( ), ejbStore( ), and ejbRemove( ) methods. Chapter 6 describes the details of bean-managed persistence. Under container-managed persistence, you do not worry about any persistence issues. EJB supports two distinct container-managed persistence models. The old model, EJB 1.x persistence, did not work well at all. The newer model, EJB 2.x persistence, is very promising though not yet widely implemented in production systems. Chapter 5 covers container-managed persistence.

The book Enterprise JavaBeans (O'Reilly) by Richard Monson-Haefel contains a more complete discussion of EJB development.

    [ Team LiB ] Previous Section Next Section