home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


CONTENTS

Chapter 2. Architectural Overview

As you learned in Chapter 1, Enterprise JavaBeans is a component model for component transaction monitors, which are the most advanced type of business application servers available today. To effectively use Enterprise JavaBeans, you need to understand the EJB architecture, so this book includes two chapters on the subject. This chapter explores the core of EJB: how enterprise beans are distributed as business objects. Chapter 3 explores the services and resource-management techniques supported by EJB.

To be truly versatile, the EJB component design had to be smart. For application developers, assembling enterprise beans is simple, requiring little or no expertise in the complex system-level issues that often plague three-tier development efforts. While EJB makes the process easy for application developers, it also provides system developers (the people who write EJB servers) with a great deal of flexibility in how they support the EJB specification.

The similarities among different CTMs allow the EJB abstraction to be a standard component model for all of them. Each vendor's CTM is implemented differently, but they all support the same primary services and similar resource-management techniques. These services and techniques are covered in more detail in Chapter 3, but some of the infrastructure for supporting them is addressed in this chapter.

2.1 The Enterprise Bean Component

Enterprise JavaBeans server-side components come in three fundamentally different types: entity, session, and message-driven beans. Both session and entity beans are RMI-based server-side components that are accessed using distributed object protocols. The message-driven bean, which is new to EJB 2.0, is an asynchronous server-side component that responds to JMS asynchronous messages.

A good rule of thumb is that entity beans model business concepts that can be expressed as nouns. For example, an entity bean might represent a customer, a piece of equipment, an item in inventory, or even a place. In other words, entity beans model real-world objects; these objects are usually persistent records in some kind of database. Our hypothetical cruise line will need entity beans that represent cabins, customers, ships, etc.

Session beans are an extension of the client application and are responsible for managing processes or tasks. A Ship bean provides methods for doing things directly to a ship but doesn't say anything about the context under which those actions are taken. Booking passengers on the ship requires that we use a Ship bean, but it also requires a lot of things that have nothing to do with the ship itself: we'll need to know about passengers, ticket rates, schedules, and so on. A session bean is responsible for this kind of coordination. Session beans tend to manage particular kinds of activities, such as the act of making a reservation. They have a lot to do with the relationships between different enterprise beans. A TravelAgent session bean, for example, might make use of a Cruise, a Cabin, and a Customer—all entity beans—to make a reservation.

Similarly, message-driven beans in EJB 2.0 are responsible for coordinating tasks involving other session and entity beans. The major difference between a message-driven bean and a session bean is how they are accessed. While a session bean provides a remote interface that defines which methods can be invoked, a message-driven bean does not. Instead, the message-driven bean subscribes to or listens for specific asynchronous messages to which it responds by processing the message and managing the actions other beans take in response to those messages. For example, a ReservationProcessor message-driven bean would receive asynchronous messages—perhaps from a legacy reservation system—from which it would coordinate the interactions of the Cruise, Cabin, and Customer beans to make a reservation.

The activity a session or message-driven bean represents is fundamentally transient: you start making a reservation, you do a bunch of work, and then it's finished. The session and message-driven beans do not represent things in the database. Obviously, session and message-driven beans have lots of side effects on the database: in the process of making a reservation, you might create a new Reservation by assigning a Customer to a particular Cabin on a particular Ship. All of these changes would be reflected in the database by actions on the respective entity beans. Session and message-driven beans like TravelAgent and ReservationProcessor, which are responsible for making a reservation on a cruise, can even access a database directly and perform reads, updates, and deletes to data. But there's no TravelAgent or ReservationProcessor record in the database—once the bean has made the reservation, it waits to process another.

What makes the distinction between the different types of beans difficult to understand is that it's extremely flexible. The relevant distinction for Enterprise JavaBeans is that an entity bean has persistent state; session and message-driven beans model interactions but do not have persistent state.

2.1.1 Classes and Interfaces

A good way to understand the design of enterprise beans is to look at how you'd go about implementing one. To implement entity and session enterprise beans, you need to define the component interfaces,[1] a bean class, and a primary key:

Remote interface

The remote interface defines the bean's business methods that can be accessed from applications outside the EJB container: the business methods a bean presents to the outside world to do its work. It enforces conventions and idioms that are well suited for distributed object protocols. The remote interface extends javax.ejb.EJBObject, which in turn extends java.rmi.Remote. It is used by session and entity beans in conjunction with the remote home interface.

Remote home interface

The home interface defines the bean's life-cycle methods that can be accessed from applications outside the EJB container: the life-cycle methods for creating new beans, removing beans, and finding beans. It enforces conventions and idioms that are well suited for distributed object protocols. The home interface extends javax.ejb.EJBHome, which in turn extends java.rmi.Remote. It is used by session and entity beans in conjunction with the remote interface.

EJB 2.0: Local interface

The local interface for an enterprise bean defines the bean's business methods that can be used by other beans co-located in the same EJB container: the business methods a bean presents to other beans in the same address space. It allows beans to interact without the overhead of a distributed object protocol, which improves their performance. The local interface extends javax.ejb.EJBLocalObject. It is used by session and entity beans in conjunction with the local home interface.

EJB 2.0: Local home interface

The local home interface defines the bean's life-cycle methods that can be used by other beans co-located in the same EJB container: that is, the life-cycle methods a bean presents to other beans in the same address space. It allows beans to interact without the overhead of a distributed object protocol, which improves their performance. The local home interface extends javax.ejb.EJBLocalHome. It is used by session and entity beans in conjunction with the local interface.

Bean class

The session and entity bean classes actually implement the bean's business and life-cycle methods. Note that the bean class for session and entity beans usually does not implement any of the bean's component interfaces directly. However, it must have methods matching the signatures of the methods defined in the remote and local interfaces and must have methods corresponding to some of the methods in both the remote and local home interfaces. If this sounds perfectly confusing, it is. The book will clarify this as we go along. An entity bean must implement javax.ejb.EntityBean; a session bean must implement javax.ejb.SessionBean. The EntityBean and SessionBean extend javax.ejb.EnterpriseBean.

The message-driven bean in EJB 2.0 does not use any of the component interfaces, because it is never accessed by method calls from other applications or beans. Instead, the message-driven bean contains a single method, onMessage(), which is called by the container when a new message arrives. The message-driven bean does not have a component interface as does the session and entity beans; it only needs only the bean class to operate. The message-driven bean class implements the javax.ejb.MessageDrivenBean and javax.jms.MessageListener interfaces. The JMS MessageListener interface is what makes a message-driven bean specific to JMS, instead of some other protocol. EJB 2.0 requires the use of JMS, but future versions may allow other messaging systems. The MessageDrivenBean, like the EntityBean and the SessionBean, extends the javax.ejb.EnterpriseBean interface.

Primary key

The primary key is a very simple class that provides a pointer into the database. Only entity beans need a primary key. The only requirement for this class is that it implements java.io.Serializable.

EJB 2.0 adds the crucial distinction between remote and local interfaces. Local interfaces provide a way for beans in the same container to interact efficiently; calls to methods in the local interface don't involve RMI; the methods in the local interfaces don't need to declare that they throw RemoteException, and so on. An enterprise bean isn't required to provide a local interface if you know when you're developing the bean that it will interact only with remote clients. Likewise, an enterprise bean doesn't need to provide a remote interface if you know it will be called only by enterprise beans in the same container. You can provide either a local or a remote component interface, or both.

The complexity—particularly all the confusion about classes implementing the methods of an interface but not implementing the interface itself—comes about because enterprise beans exist in the middle between some kind of client software and some kind of database. The client never interacts with a bean class directly; it always uses the methods of the entity or session bean's component interfaces to do its work, interacting with stubs that are generated automatically. (For that matter, a bean that needs the services of another bean is just another client: it uses the same stubs, rather than interacting with the bean class directly.)

Although the local component interfaces (local and local home) in EJB 2.0 represent session and entity beans in the same address space and do not use distributed object protocols, they still represent a stub or a proxy to the bean class. While there is no network between co-located beans, the stubs allow the container to monitor the interactions between the beans and apply security and transactions as appropriate.

It's important to note that EJB 2.0's message-driven bean doesn't have any component interfaces, but it may become the client of other session or entity beans and interact with those beans through their component interfaces. The entity and session beans with which the message-driven bean interacts may be co-located, in which case it uses their local component interfaces, or they may be located in a different address space and EJB container, in which case the remote component interfaces are used.

There are also many interactions between an enterprise bean and its server. These interactions are managed by a container, which is responsible for presenting a uniform interface between the bean and the server. (Many people use the terms "container" and "server" interchangeably, which is understandable because the difference between them isn't clearly defined.) The container is responsible for creating new instances of beans, making sure they are stored properly by the server, and so on. Tools provided by the container's vendor do a tremendous amount of work behind the scenes. At least one tool takes care of creating the mapping between entity beans and records in the database. Other tools generate code based on the component interfaces and the bean class itself. The code generated does things like create the bean, store it in the database, and so on. This code (in addition to the stubs) is what actually implements the component interfaces, and it is the reason the bean class doesn't have to do so.

2.1.1.1 Naming conventions

Before going on, let's establish some conventions. When we speak about an enterprise bean as a whole—its component interfaces, bean class, and so forth—we will call it by its common business name, followed by the acronym " EJB." For example, an enterprise bean that is developed to model a cabin on a ship will be called the "Cabin EJB." Notice that we don't use a constant-width font for "Cabin." This is because we are referring to all the parts of the bean (the component interfaces, bean class, etc.) as a whole, not just to one particular part, such as the remote interface or bean class. The term enterprise bean or bean denotes any kind of bean, including entity, session, and message-driven beans. Entity bean denotes an entity-type enterprise bean; session bean denotes a session-type enterprise bean; and message-driven bean denotes a message driven-type enterprise bean. It's popular to use the acronym EJB for enterprise bean, a style adopted in this book.

We will also use suffixes to distinguish between local component interfaces and remote component interfaces. When we are talking about the remote interface of the Cabin EJB we will combine the common business name with the word Remote. For example, the remote interface for the Cabin EJB is called the CabinRemote interface. In EJB 2.0, the local component interface of the Cabin EJB would be the CabinLocal interface. The home interfaces follow the convention by adding the word Home to the mix. The remote and local home interfaces for the Cabin EJB would be CabinHomeRemote and CabinHomeLocal, respectively. The bean class is always the common business name followed by the word Bean. For example, the Cabin EJB's bean class would be named CabinBean.

2.1.1.2 The remote interface

Having introduced the machinery, let's look at how to build an entity or stateful enterprise bean with remote component interfaces. In this section, we will examine the Cabin EJB, an entity bean that models a cabin on a cruise ship. Let's start with its remote interface.

We'll define the remote interface for a Cabin bean using the CabinRemote interface, which defines business methods for working with cabins. All remote-interface types extend the javax.ejb.EJBObject interface:

import java.rmi.RemoteException;

public interface CabinRemote extends javax.ejb.EJBObject {
    public String getName() throws RemoteException;
    public void setName(String str) throws RemoteException;
    public int getDeckLevel() throws RemoteException;
    public void setDeckLevel(int level) throws RemoteException;
}

These are methods for naming the cabin and methods for setting the cabin's deck level; you can probably imagine lots of other methods that you'd need, but this is enough to get started. All of these methods declare that they throw RemoteException, which is required of all methods on remote component interfaces, but not EJB 2.0's local component interfaces. EJB requires the use of Java RMI-IIOP conventions with remote component interfaces, although the underlying protocol can be CORBA IIOP, Java Remote Method Protocol ( JRMP), or some other protocol. Java RMI-IIOP will be discussed in more detail in the next chapter.

2.1.1.3 The remote home interface

The remote home interface defines life-cycle methods used by clients of entity and session beans for locating enterprise beans. The remote home interface extends javax.ejb.EJBHome. We'll call the home interface for the Cabin bean CabinHomeRemote and define it like this:

import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.FinderException;

public interface CabinHomeRemote extends javax.ejb.EJBHome {
    public CabinRemote create(Integer id) 
        throws CreateException, RemoteException;
    public CabinRemote findByPrimaryKey(Integer pk)
        throws FinderException, RemoteException;
}

The create() method is responsible for initializing an instance of our bean. If your application needs them, you can provide other create() methods with different arguments.

In addition to findByPrimaryKey(), you are free to define other methods that provide convenient ways to look up Cabin beans—for example, you might want to define a method called findByShip() that returns all the cabins on a particular ship. Find methods like these are used in entity beans but not in session beans and obviously not in message-driven beans.

2.1.1.4 EJB 2.0: The bean class

Now let's look at an actual entity bean. Here's the code for the CabinBean; it's a sparse implementation, but it will show you how the pieces fit together:

import javax.ejb.EntityContext;

public abstract class CabinBean implements javax.ejb.EntityBean {

    public Integer ejbCreate(Integer id){
        setId(id);
        return null;
    }
    public void ejbPostCreate(Integer id){
        // do nothing
    }

    public abstract String getName();
    public abstract void setName(String str);

    public abstract int getDeckLevel();
    public abstract void setDeckLevel(int level);

    public abstract Integer getId();
    public abstract void setId(Integer id);

    public void setEntityContext(EntityContext ctx){
        // not implemented
    }
    public void unsetEntityContext(){
        // not implemented
    }
    public void ejbActivate(){
        // not implemented
    }
    public void ejbPassivate(){
        // not implemented
    }
    public void ejbLoad(){
        // not implemented
    }
    public void ejbStore(){
        // not implemented
    }
    public void ejbRemove(){
        // not implemented
    }
}

Notice that the CabinBean class is declared as abstract, as are several of its methods that access or update the EJB's persistent state. Also notice that there are no instance fields to hold the state information these methods access. This is because we are working with a container-managed entity bean, which has its abstract methods implemented by the container system automatically—this will be explained in detail later in the book. EJB 2.0 container-managed entity beans are the only beans that are declared as abstract with abstract accessor methods. You won't see abstract classes and methods with other types of entity beans, session beans, or message-driven beans.

2.1.1.5 EJB 1.1: The bean class

Here's the code for the CabinBean in EJB 1.1:

import javax.ejb.EntityContext;

public class CabinBean implements javax.ejb.EntityBean {

    public Integer id;
    public String name;
    public int deckLevel;
 
    public Integer ejbCreate(Integer id){
        setId(id);
        return null;
    }
    public void ejbPostCreate(Integer id){
        // do nothing
    }

    public String getName(){
        return name;
    }    
    public void setName(String str){
        name = str;
    }

    public int getDeckLevel(){
        return deckLevel;
    }
    public void setDeckLevel(int level){
        deckLevel = level;
    }
    public Integer getId(){
        return id;
    }
    public void setId(Integer id){
        this.id = id;
    }
    public void setEntityContext(EntityContext ctx){
        // not implemented
    }
    public void unsetEntityContext(){
        // not implemented
    }
    public void ejbActivate(){
        // not implemented
    }
    public void ejbPassivate(){
        // not implemented
    }
    public void ejbLoad(){
        // not implemented
    }
    public void ejbStore(){
        // not implemented
    }
    public void ejbRemove(){
        // not implemented
    }
}
2.1.1.6 EJB 2.0 and 1.1: The bean class

The set and get methods for the cabin's name and deck level are the CabinBean's business methods; they match the business methods defined by the EJB's remote interface, CabinRemote. The CabinBean class has state and business behavior that models the concept of a cabin. The business methods are the only methods visible to the client application; the other methods are visible only to the EJB container or the bean class itself. For example, the setId()/ getId() methods are defined in the bean class but not in the remote interface, which means they cannot be called by the entity bean's client. The other methods are required by the EJB component model and are not part of the bean class's public business definition.

The ejbCreate() and ejbPostCreate() methods initialize the instance of the bean class when a new cabin record is to be added to the database. The last seven methods in the CabinBean are defined in the javax.ejb.EntityBean interface. These methods are life-cycle callback methods. The EJB container invokes these callback methods on the bean class when important life-cycle events occur. The ejbRemove() method, for example, notifies an entity bean that its data is about to be deleted from the database. The ejbLoad() and ejbStore() methods notify the bean instance that its state is being read or written to the database. The ejbActivate() and ejbPassivate() methods notify the bean instance that it is about to be activated or deactivated, a process that conserves memory and other resources. setEntityContext() provides the bean with an interface to the EJB container that allows the bean class to get information about itself and its surroundings. unsetEntityContext() is called by the EJB container to notify the bean instance that it is about to be dereferenced for garbage collection.

All these callback methods provide the bean class with notifications of when an action is about to be taken, or was just taken, on the bean's behalf by the EJB server. These notifications simply inform the bean of an event; the bean doesn't have to do anything about it. The callback notifications tell the bean where it is during its life cycle, when it is about to be loaded, removed, deactivated, and so on. Most of the callback methods pertain to persistence, which can be done automatically for the bean class by the EJB container. Because the callback methods are defined in the javax.ejb.EntityBean interface, the entity bean class must implement them, but it isn't required to do anything meaningful with the methods if it doesn't need to. Our bean, the CabinBean, won't need to do anything when these callback methods are invoked, so these methods are empty implementations. Details about these callback methods, when they are called, and how a bean should react to them are covered in Chapter 11.

2.1.1.7 The primary key

The primary key is a pointer that helps locate data that describes a unique record or entity in the database; it is used in the findByPrimaryKey() method of the home interface to locate a specific entity. Primary keys are defined by the bean developer and must be some type of serializable object. The Cabin EJB uses a simple java.lang.Integer type as its primary key. Its also possible to define custom primary keys, called compound primary keys, which represent complex primary keys consisting of several different fields. Primary keys are covered in detail in Chapter 11.

2.1.1.8 What about session beans?

CabinBean is an entity bean, but a session bean wouldn't be all that different. It would extend SessionBean instead of EntityBean and would have an ejbCreate() method that would initialize the bean's state, but no ejbPostCreate(). Session beans do not have an ejbLoad() or ejbStore() method, because session beans are not persistent. While session beans have a setSessionContext() method, they do not have an unsetSessionContext() method. Session beans do have ejbActivate() and ejbPassivate() methods, which are used by stateful session beans to manage conversational state. Finally, session beans would provide an ejbRemove() method, which would be called to notify the bean that the client no longer needs it. However, this method wouldn't tell the bean that its data was about to be removed from the database, because a session bean doesn't represent data in the database.

Session beans don't have a primary key. That's because session beans are not persistent themselves, so there is no need for a key that maps to the database. Session beans are covered in detail in Chapter 12.

2.1.1.9 EJB 2.0: What about message-driven beans?

Remote, local, and home interfaces would not be defined for a message-driven bean, because message-driven beans do not have component interfaces. Instead, the message-driven bean would define only a few callback methods and no business methods. The callback methods include the ejbCreate() method, which is called when the bean class is first created; the ejbRemove() method, called when the bean instance is about to be discarded from the system (usually when the container doesn't need it any longer); the setMessageDrivenBeanContext(); and the onMessage() method. The onMessage() method is called every time a new asynchronous message is delivered to the message-driven bean. The message-driven bean doesn't define the ejbPassivate()/ejbActivate() or ejbLoad()/ejbStore() methods because it doesn't need them.

Message-driven beans don't have a primary key, for the same reason that session beans don't. They are not persistent, so there is no need for a key to the database. Message-driven beans are covered in detail in Chapter 13.

2.1.2 Deployment Descriptors and JAR Files

Much of the information about how beans are managed at runtime is not addressed by the interfaces and classes discussed previously. You may have noticed, for example, that we didn't talk about how beans interact with security, transactions, naming, and other services common to distributed object systems. As you know from prior discussions, these types of primary services are handled automatically by the EJB container, but the EJB container still needs to know how to apply the primary services to each bean class at runtime. To do this, we use deployment descriptors.

Deployment descriptors serve a function very similar to property files. They allow us to customize the behavior of software (enterprise beans) at runtime without having to change the software itself. Property files are often used with applications, but deployment descriptors are specific to an enterprise bean. Deployment descriptors are also similar in purpose to property sheets used in Visual Basic and PowerBuilder. Where property sheets allow us to describe the runtime attributes of visual widgets (background color, font size, etc.), deployment descriptors allow us to describe runtime attributes of server-side components (security, transactional context, etc.). Deployment descriptors allow certain runtime behaviors of beans to be customized without altering the bean class or its interfaces.

When a bean class and its interfaces have been defined, a deployment descriptor for the bean is created and populated with data about the bean. Frequently, integrated development environments ( IDEs) that support development of Enterprise JavaBeans will allow developers to graphically set up the deployment descriptors using visual utilities like property sheets. After the developer has set all the properties for a bean, the deployment descriptor is saved to a file. Once the deployment descriptor is completed and saved to a file, the bean can be packaged in a JAR file for deployment.

JAR ( Java ARchive) files are ZIP files that are used specifically for packaging Java classes (and other resources such as images) that are ready to be used in some type of application. JARs are used for packaging applets, Java applications, JavaBeans, web applications (servlets and JSPs), and Enterprise JavaBeans. A JAR file containing one or more enterprise beans includes the bean classes, component interfaces, and supporting classes for each bean. It also contains one deployment descriptor, which is used for all the beans in the JAR file. When a bean is deployed, the JAR file's path is given to the container's deployment tools, which read the JAR file.

When the JAR file is read at deployment time, the container tools read the deployment descriptor to learn about the bean and how it should be managed at runtime. The deployment descriptor tells the deployment tools what kind of beans are in the JAR file (session, entity, or message-driven), how they should be managed in transactions, who has access to the beans at runtime, and other runtime attributes of the beans. The person who is deploying the bean can alter some of these settings, such as transactional and security access attributes, to customize the bean for a particular application. Many container tools provide property sheets for graphically reading and altering the deployment descriptor when the bean is deployed. These graphical property sheets are similar to those used by bean developers.

The deployment descriptors help the deployment tools add beans to the EJB container. Once the bean is deployed, the properties described in the deployment descriptors will continue to be used to tell the EJB container how to manage the bean at runtime.

When Enterprise JavaBeans 1.0 was released, serializable classes were used for the deployment descriptor. Starting with Enterprise JavaBeans 1.1, the serializable deployment descriptor classes used in EJB 1.0 were dropped in favor of a more flexible file format based on the eXtensible Markup Language (XML). The XML deployment descriptors are text files structured according to a standard EJB Document Type Definition (DTD) that can be extended so the type of deployment information stored can evolve as the specification evolves. Chapter 16 provides a detailed description of XML deployment descriptors. The following sections provide a brief overview of XML deployment descriptors.

2.1.2.1 EJB 2.0: Deployment descriptor

The following deployment descriptor might be used to describe the Cabin bean in EJB 2.0:

<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD EnterpriseJavaBeans 2.0//EN" 
"http://java.sun.com/dtd/ejb-jar_2_0.dtd">

<ejb-jar>
    <enterprise-beans>
        <entity>
            <ejb-name>CabinEJB</ejb-name>
            <home>com.titan.CabinHomeRemote</home>
            <remote>com.titan.CabinRemote</remote>
            <local-home>com.titan.CabinHomeLocal</local-home>
            <local>com.titan.CabinLocal</local>
            <ejb-class>com.titan.CabinBean </ejb-class>
            <persistence-type>Container</persistence-type>
            <prim-key-class>java.lang.Integer</prim-key-class>
            <reentrant>False</reentrant>
        </entity>
    </enterprise-beans>
</ejb-jar>
2.1.2.2 EJB 1.1: Deployment descriptor

The following deployment descriptor might be used to describe the Cabin bean in EJB 1.1:

<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD EnterpriseJavaBeans 1.1//EN" 
"http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd">

<ejb-jar>
    <enterprise-beans>
        <entity>
            <ejb-name>CabinEJB</ejb-name>
            <home>com.titan.CabinHomeRemote</home>
            <remote>com.titan.CabinRemote</remote>         
            <ejb-class>com.titan.CabinBean</ejb-class>            
            <persistence-type>Container</persistence-type>
            <prim-key-class>java.lang.Integer</prim-key-class>
            <reentrant>False</reentrant>
        </entity>
    </enterprise-beans>
</ejb-jar>
2.1.2.3 EJB 2.0 and 1.1: Elements of the XML deployment descriptor

The deployment descriptor for a real bean would have a lot more information. This example simply illustrates the type of information you'll find in an XML deployment descriptor.

The first element in any XML document is <!DOCTYPE>. This element describes the organization that defined the DTD for the XML document, the DTD's version, and a URL location of the DTD. The DTD describes how a particular XML document is structured.

All the other elements in the XML document are specific to EJB. They do not represent all the elements used in deployment descriptors, but they illustrate the types of elements that are used. Here's what the elements mean:

<ejb-jar>

The root of the XML deployment descriptor. All other elements must be nested below this one. It must contain one <enterprise-beans> element and may contain other optional elements.

<enterprise-beans>

Contains declarations for all the enterprise beans described by this XML document. It may contain <entity>, <session>, or <message-driven> (EJB 2.0) elements, which describe entity, session, and message-driven enterprise beans, respectively.

<entity>

Describes an entity bean and its deployment information. There must be one of these elements for every entity bean described by the XML deployment descriptor. The <session> element is used in the same way to describe a session bean. The <message-driven> element is different as it does not define any component interfaces.

<ejb-name>

The descriptive name of the enterprise bean. This is the name we use for the enterprise bean in conversation, when talking about the bean component as a whole.

<home>

The fully qualified class name of the remote home interface. This interface defines the life-cycle behaviors (create, find, remove) of the enterprise bean to its clients outside the container system.

<remote>

The fully qualified class name of the remote interface. This interface defines the enterprise bean's business methods to its clients outside the container system.

EJB 2.0: <local-home>

The fully qualified class name of the local home interface. This interface defines the life-cycle behaviors (create, find, remove) of the enterprise bean to other co-located enterprise beans.

EJB 2.0: <local>

The fully qualified class name of the local interface. This interface defines the enterprise bean's business methods to other co-located enterprise beans.

<ejb-class>

The fully qualified class name of the bean class. This class implements the business methods of the bean.

<prim-key-class>

The fully qualified class name of the enterprise bean's primary key. The primary key is used to find the bean data in the database.

The last two elements in the deployment descriptor, the <persistence-type> and <reentrant> elements, express the persistence strategy and concurrency policies of the entity bean. These elements are explained in more detail later in the book.

As you progress through this book, you will be introduced to the elements that describe concepts we have not covered yet, so don't worry about knowing all of the elements you might find in a deployment descriptor at this time.

2.1.3 EJB Objects and EJB Home

The entity and session beans both declare the component interfaces that their clients will use to access them. In EJB 2.0 and 1.1, clients outside the container system, like servlets or Java applications, will always use the enterprise bean's remote component interfaces. In EJB 2.0, clients that are other enterprise beans in the same container system will usually use local component interfaces to interact. This section explains in logical terms how the component interfaces are connected to instances of the bean class at runtime.

While this discussion will help you understand entity and session beans, it doesn't apply to EJB 2.0's message-driven beans at all, because they do not declare component interfaces. Message-driven beans are a very different kind of animal, and a full description of these beans is left to Chapter 13.

Now that you have a basic understanding of some of the enterprise beans parts (component interfaces, bean class, and deployment descriptor) it's time to talk a little more precisely about how these parts come together inside an EJB container system. Unfortunately, we can't talk as precisely as we'd like. There are a number of ways for an EJB container to implement these relationships; we'll show some of the possibilities. Specifically, we'll talk about how the container implements the component interface of entity and session beans, so that clients, either applications outside the container or other co-located enterprise beans can interact with and invoke methods on the bean class.

The two missing pieces are the EJB object itself and the EJB home. You will probably never see the EJB home and EJB object classes because their class definitions are proprietary to the vendor's EJB implementation and are generally not made public. This is good because it represents a separation of responsibilities along areas of expertise. As an application developer, you are intimately familiar with how your business environment works and needs to be modeled, so you will focus on creating the applications and beans that describe your business. System-level developers, the people who write EJB servers, don't understand your business, but they do understand how to develop CTMs and support distributed objects. It makes sense for system-level developers to apply their skills to the mechanics of managing distributed objects but leave the business logic to you, the application developer. Let's talk briefly about the EJB object and the EJB home so you understand the missing pieces in the big picture.

2.1.3.1 The EJB object

This chapter has said a lot about a bean's remote and local interfaces, which extend the EJBObject and, for EJB 2.0, the EJBLocalObject interfaces, respectively. Who implements these interfaces? Clearly, the stub does: we understand that much. But what about the server side?

On the server side, an EJB object is an object that implements the remote and/or local (EJB 2.0) interfaces of the enterprise bean. The EJB object wraps the enterprise bean instance—that is, an instance of the enterprise bean class you've created (in our example, the CabinBean)—on the server and expands its functionality to include javax.ejb.EJBObject and/or javax.ejb.EJBLocalObject behavior.

You will have noticed that "and/or" is used a lot when talking about which interface the EJB object implements. That's because enterprise beans in EJB 2.0 can declare either the local interface, remote interface, or both! Local interfaces don't apply to EJB 1.1, so if you are working with that version, ignore references to them; they are relevant only to EJB 2.0 container systems.

In EJB 2.0, regardless of which interfaces the bean implements, we can think of the EJB object as implementing both. In reality, there may be a special EJB object for the remote interface and another special EJB object for the local interface of each enterprise bean; that depends on the how the vendor chooses to implement it. For our purposes, the term EJB object will be used to talk about the implementation of either the local or remote interfaces, or both. The functionality of these interfaces is so similar from the EJB object's perspective that discussing separate EJB object implementations isn't necessary.

The EJB object is generated by the utilities provided by the vendor of your EJB container and is based on the bean classes and the information provided by the deployment descriptor. The EJB object wraps the bean instance and works with the container to apply transactions, security, and other system-level operations to the bean at runtime. Chapter 3 talks more about the EJB object's role with regard to system-level operations.

A vendor can use a number of strategies to implement the EJB object. Figure 2-1 illustrates three possibilities using the CabinRemote interface. The same implementation strategies apply to the CabinLocal and javax.ejb.EJBLocalObject interfaces.

Figure 2-1. Three ways to implement the EJB object

figs/ejb3_0201.gif

In Figure 2-1(a), the EJB object is a classic wrapper because it holds a reference to the bean class and delegates the requests to the bean. Figure 2-1(b) shows that the EJB object class actually extends the bean class, adding functionality specific to the EJB container. In Figure 2-1(c), the bean class is no longer included in the model. In this case, the EJB object has both the proprietary implementation required by the EJB container and bean class method implementations that were copied from the bean class's definition.

The EJB object design shown in Figure 2-1(a) is perhaps the most common. Throughout this book, particularly in the next chapter, we will explain how EJB works with the assumption that the EJB object wraps the bean class instance as depicted in Figure 2-1(a). But the other implementations are used; it shouldn't make a difference which one your vendor has chosen. The bottom line is that you never really know much about the EJB object: its implementation is up to the vendor. Knowing that the EJB object exists answers a lot of questions about how enterprise beans are structured. The information that a client (including other enterprise beans) needs to know about an enterprise bean is described by the remote and home interfaces.

2.1.3.2 The EJB home

The EJB home is a lot like the EJB object. It's another class that's generated automatically when you install an enterprise bean in a container. It implements all the methods defined by the home interfaces (local and/or remote) and is responsible for helping the container manage the bean's life cycle. Working closely with the EJB container, the EJB home is responsible for locating, creating, and removing enterprise beans. This may involve working with the EJB server's resource managers and instance pooling and persistence mechanisms, the details of which are hidden from the developer.

For example, when a create method is invoked on a home interface, the EJB home creates an instance of the EJB object that references a bean instance of the appropriate type. Once the bean instance is associated with the EJB object, the instance's matching ejbCreate() method is called. In the case of an entity bean, a new record is inserted into the database. With session beans, the instance is simply initialized. Once the ejbCreate() method has completed, the EJB home returns a remote or local reference (i.e., a stub) for the EJB object to the client. The client can then begin to work with the EJB object by invoking business methods using the stub. The stub relays the methods to the EJB object; in turn, the EJB object delegates those method calls to the bean instance.

In EJB 2.0, how does the EJB home know which type of EJB object reference (local or remote) to return? It depends on which home interface is being used. If the client invokes a create() method on the remote home interface, the EJB home will return a remote interface reference. If the client is working with a local home interface, the EJB home will return a reference implementing the local interface. EJB 2.0 requires that the return type of remote home interface methods be remote interfaces and that the return type of local home interface methods be local interfaces:

// The Cabin EJB's remote home interface
public interface CabinHomeRemote extends javax.ejb.EJBHome {
    public CabinRemote create(Integer id) 
        throws CreateException, RemoteException;
    public CabinRemote findByPrimaryKey(Integer pk)
        throws FinderException, RemoteException;
}


// The Cabin EJB's local home interface
public interface CabinHomeLocal extends javax.ejb.EJBHome {
    public CabinLocal create(Integer id) 
        throws CreateException;
    public CabinLocal findByPrimaryKey(Integer pk)
        throws FinderException;
}

Figure 2-2 illustrates the architecture of EJB with the EJB home and EJB object implementing the home interface and remote or local interface, respectively. The bean class is also shown as being wrapped by the EJB object.

Throughout this book, we will consider the EJB object and EJB home as constructs that support both the remote and local component interfaces. In reality, we have no idea how the vendor chose to implement the EJB object and EJB home, since they are only logical constructs and may not have equivalent software counterparts. It's important to remember that "EJB object" and "EJB home" are simply terms to describe the EJB container's responsibilities for supporting the component interfaces. We have chosen to give them a more concrete description in this book purely for instructional purposes; the EJB object and EJB home implementations discussed throughout this book are to be considered illustrative and not a true representation of how these terms may be implemented.

2.1.3.3 Deploying a bean

The EJB objects and EJB homes are automatically generated during the deployment process. After the files that define the bean (the component interfaces and the bean classes) have been packaged into a JAR file, the bean is ready to be deployed; that is, it can be added to an EJB container so it can be accessed as a distributed component. During the deployment process, tools provided by the EJB container vendor generate the EJB object and EJB home classes by examining the deployment descriptor and the other interfaces and classes in the JAR file.

Figure 2-2. EJB architecture

figs/ejb3_0202.gif

2.2 Using Enterprise Beans

Let's look at how a client would work with an enterprise bean to do something useful. We'll start with the Cabin EJB defined earlier. A cabin is a thing or place whose description is stored in a database. To make the example a little more real, imagine that there are other entity beans: Ship, Cruise, Ticket, Customer, Employee, and so on.

2.2.1 Getting Information from an Entity Bean

Imagine that a GUI client needs to display information about a particular cruise, including the cruise name, the ship name, and a list of cabins. Using the cruise ID obtained from a text field, we can use some of our beans to populate the GUI with data about the requested cruise. Here's what the code would look like:

CruiseHomeRemote cruiseHome = ... use JNDI to get the home
// Get the cruise ID from a text field.
String cruiseID = textFields1.getText();
// Create an EJB primary key from the cruise ID.
Integer pk = new Integer(cruiseID);
// Use the primary key to find the cruise.
CruiseRemote cruise = cruiseHome.findByPrimaryKey(pk);
// Set text field 2 to show the cruise name.
textField2.setText(cruise.getName());
// Get a remote reference to the ship that will be used
// for the cruise from the cruise bean.
ShipRemote ship = cruise.getShip();
// Set text field 3 to show the ship's name.
textField3.setText(ship.getName());

// Get all the cabins on the ship.
Collection cabins = ship.getCabins();
Iterator cabinItr = cabins.iterator();

// Iterate through the enumeration, adding the name of each cabin
// to a list box.
while( cabinItr.hasNext())
    CabinRemote cabin = (CabinRemote)cabinItr.next();
    listBox1.addItem(cabin.getName());
}

Let's start by getting a remote reference to the EJB home for an entity bean that represents a cruise. We are using a remote reference instead of a local one, because the client is a GUI Java application located outside the EJB container. In EJB 1.1, we don't have a choice because only remote component interfaces are supported. It's not shown in the example, but references to the EJB home are obtained using JNDI. JNDI is a powerful API for locating resources, such as remote objects, on networks. It's a little too complicated to talk about here, but it will be covered in subsequent chapters.

We read a cruise ID from a text field, use it to create a primary key, and use that primary key together with the EJB home to get a CruiseRemote reference, which implements the business methods of our bean. Once we have the appropriate Cruise EJB, we can ask the Cruise EJB to give us a remote reference to a Ship EJB that will be used for the cruise. We can then get a Collection of remote Cabin EJB references from the Ship EJB and display the names of the Cabin EJBs in the client.

Entity beans model data and behavior. They provide a reusable and consistent interface to data in the database. The behavior used in entity beans is usually focused on applying business rules that pertain directly to changing data. In addition, entity beans can model relationships with other entities. A ship, for example, has many cabins. We can get a list of cabins owned by the ship by invoking the ship.getCabins() method.

2.2.2 Modeling Workflow with Session Beans

Entity beans are useful for objectifying data and describing business concepts that can be expressed as nouns, but they're not very good at representing a process or a task. A Ship bean provides methods and behavior for doing things directly to a ship, but it does not define the context under which these actions are taken. The previous example retrieved data about cruises and ships; we could also have modified this data. With enough effort, we could have figured out how to book a passenger—perhaps by adding a Customer EJB to a Cruise EJB or adding a customer to a list of passengers maintained by the ship. We could try to shove methods for accepting payment and other tasks related to booking into our GUI client application, or even into the Ship or Cabin EJBs, but that's a contrived and inappropriate solution. We don't want business logic in the client application—that's why we went to a multitier architecture in the first place. Similarly, we don't want this kind of logic in our entity beans that represent ships and cabins. Booking passengers on a ship or scheduling a ship for a cruise are the types of activities or functions of the business, not the Ship or the Cabin bean, and are therefore expressed in terms of a process or task.

Session beans act as agents for the client managing business processes or tasks; they're the appropriate place for business logic. A session bean is not persistent like an entity bean; nothing in a session bean maps directly into a database or is stored between sessions. Session beans work with entity beans, data, and other resources to control workflow. Workflow is the essence of any business system, because it expresses how entities interact to model the actual business. Session beans control tasks and resources but do not themselves represent data.

Although the term "workflow" is frequently used to describe the management of business processes that may span several days with lots of human intervention, that is not the definition used in this book. The term workflow in this book describes the interactions of beans within a single transaction, which takes only a few seconds to execute.

The following code demonstrates how a session bean, designed to make cruise-line reservations, might control the workflow of other entity and session beans to accomplish this task. Imagine that a piece of client software, in this case a user interface, obtains a remote reference to a TravelAgent session bean. Using the information entered into text fields by the user, the client application books a passenger on a cruise:

// Get the credit card number from the text field.
String creditCard = textField1.getText();
int cabinID = Integer.parseInt(textField2.getText());
int cruiseID = Integer.parseInt(textField3.getText());

// Create a new Reservation session passing in a reference to a 
// customer entity bean.
TravelAgent travelAgent = travelAgentHome.create(customer);

// Set cabin and cruise IDs.
travelAgent.setCabinID(cabinID);
travelAgent.setCruiseID(cruiseID);

// Using the card number and price, book passage.
// This method returns a Ticket object.
TicketDO ticket = travelAgent.bookPassage(creditCard, price);

This is a fairly coarse-grained abstraction of the process of booking a passenger: most of the details are hidden from the client. Hiding the fine-grained details of workflow is important because it provides us with more flexibility in how the system evolves and how clients are allowed to interact with the EJB system.

The following listing shows some of the code included in the TravelAgentBean. The bookPassage() method actually works with three entity beans, the Customer, Cabin, and Cruise EJBs, and another session bean, the ProcessPayment EJB. The ProcessPayment EJB provides several methods for making a payment, including check, cash, and credit card. In this case, we are using the ProcessPayment session to make a credit card purchase of a cruise ticket. Once payment has been made, a serializable TicketDO object is created and returned to the client application.

public class TravelAgentBean implements javax.ejb.SessionBean {
    public CustomerRemote customer;
    public CruiseRemote cruise;
    public CabinRemote cabin;

    public void ejbCreate(CustomerRemote cust){
        customer =cust;
    }
    public TicketDO bookPassage(CreditCardDO card,double price)
        throws IncompleteConversationalState {
        if (customer == null ||cruise == null ||cabin == null){
            throw new IncompleteConversationalState();
        }
        try {
            ReservationHomeRemote resHome = (ReservationHomeRemote)
                getHome("ReservationHome",ReservationHomeRemote.class);
            ReservationRemote reservation =
                resHome.create(customer,cruise,cabin,price,new Date());
            ProcessPaymentHomeRemote ppHome = (ProcessPaymentHomeRemote)
                getHome("ProcessPaymentHome",ProcessPaymentHomeRemote.class);

            ProcessPaymentRemote process = ppHome.create();
            process.byCredit(customer,card,price);

            TicketDO ticket = new TicketDO(customer,cruise,cabin,price);
            return ticket;
        }catch(Exception e){
            throw new EJBException(e);
        }
    }

// More business methods and callback methods follow
}

This example leaves out some details, but it demonstrates the difference in purpose between a session bean and an entity bean. Entity beans represent the behavior and data of a business object, while session beans model the workflow of beans. The client application uses the TravelAgent EJB to perform a task using other beans. For example, the TravelAgent EJB uses a ProcessPayment EJB and a Reservation EJB in the process of booking passage. The ProcessPayment EJB processes the credit card, and the Reservation EJB records the actual reservation in the system. Session beans can also be used to read, update, and delete data that can't be adequately captured in an entity bean. Session beans don't represent records or data in the database like entity beans, but they can access data in the database.

All of the work performed by the TravelAgent session bean could have been coded in the client application. Having the client interact directly with entity beans is a common but troublesome design approach because it ties the client directly to the details of the business tasks. This is troublesome for two reasons: any changes in the entity beans and their interaction require changes to the client, and it's very difficult to reuse the code that models the workflow.

Session beans are coarse-grained components that allow clients to perform tasks without being concerned with the details that make up the task. This allows developers to update the session bean, possibly changing the workflow, without impacting the client code. In addition, if the session bean is properly defined, other clients that perform the same tasks can reuse it. The ProcessPayment session bean, for example, can be reused in many areas besides reservations, including retail and wholesale sales. For example, the ship's gift shop could use the ProcessPayment EJB to process purchases. As a client of the ProcessPayment EJB, the TravelAgent EJB doesn't care how ProcessPayment works; it's only interested in the ProcessPayment EJB's coarse-grained interface, which validates and records charges.

Moving workflow logic into a session bean also helps to thin down the client applications and reduce network traffic and connections. Excessive network traffic is actually one of the biggest problems in distributed object systems. Excessive traffic can overwhelm the server and clog the network, hurting response times and performance. Session beans, if used properly, can substantially reduce network traffic by limiting the number of requests needed to perform a task. In distributed objects, every method invocation produces network traffic. Distributed objects communicate requests using an RMI loop. This requires that data be streamed between the stub and skeleton with every method invocation. With session beans, the interaction of beans in a workflow is kept on the server. One method invocation on the client application results in many method invocations on the server, but the network sees only the traffic produced by one method call on the session bean. In the TravelAgent EJB, the client invokes bookPassage(), but on the server, the bookPassage() method produces several method invocations on the component interfaces of other enterprise beans. For the network cost of one method invocation, the client gets several method invocations.[2]

In addition, session beans reduce the number of network connections needed by the client. The cost of maintaining many network connections can be very high, so reducing the number of connections each client needs improves the performance of the system as a whole. When session beans are used to manage workflow, the number of connections that each client has to the server may be substantially reduced, which improves the EJB server's performance. Figure 2-3 compares the network traffic and connections used by a client that uses only entity beans to those used by a client that uses session beans.

Figure 2-3. Session beans reduce network traffic and thin down clients

figs/ejb3_0203.gif

Session beans also limit the number of stubs used on the client, which saves the client memory and processing cycles. This may not seem like a big deal, but without the use of session beans, a client might be expected to manage hundreds or even thousands of remote references at one time. In the TravelAgent EJB, for example, the bookPassage() method works with several remote references, but the client is exposed only to the remote reference of the TravelAgent EJB.

2.2.2.1 Stateless and stateful session beans

Session beans can be either stateful or stateless . Stateful session beans maintain conversational state when used by a client. Conversational state is not written to a database; it's state that is kept in memory while a client uses a session. Maintaining conversational state allows a client to carry on a conversation with an enterprise bean. As each method on the enterprise bean is invoked, the state of the session bean may change, and that change can affect subsequent method calls. The TravelAgent session bean, for example, may have many more methods than the bookPassage() method. The methods that set the cabin and cruise IDs are examples. These set methods are responsible for modifying conversational state. They convert the IDs into remote references to Cabin and Cruise EJBs that are later used in the bookPassage() method. Conversational state is kept for only as long as the client application is actively using the bean. Once the client shuts down or releases the TravelAgent EJB, the conversational state is lost forever. Stateful session beans are not shared among clients; they are dedicated to the same client for the life of the enterprise bean.

Stateless session beans do not maintain any conversational state. Each method is completely independent and uses only data passed in its parameters. The ProcessPayment EJB is a perfect example of a stateless session bean. The ProcessPayment EJB doesn't need to maintain any conversational state from one method invocation to the next. All the information needed to make a payment is passed into the byCreditCard() method. Stateless session beans provide higher performance in terms of throughput and resource consumption than entity and stateful session beans because only a few stateless session bean instances are needed to serve hundreds and possibly thousands of clients. Chapter 12 talks more about the use of stateless session beans.

2.2.3 EJB 2.0: Accessing EJB with Message-Driven Beans

Message-driven beans are integration points for other applications interested in working with EJB applications. Java applications or legacy systems that need to access EJB applications can send messages via JMS to message-driven beans. The message-driven beans can then process those messages and perform the required tasks using other entity and session beans.

In many ways, message-driven beans fulfill the same role as session beans by managing the workflow of entity and session beans to complete a given task. The task to be completed is initiated by an asynchronous message sent by an application using JMS. Unlike session beans, which respond to business methods invoked on their component interfaces, a message-driven bean responds to asynchronous messages delivered to the bean through its onMessage() method. Since the messages are asynchronous, the client that sends them doesn't expect or wait for a reply. The messaging client simply sends the message and forgets about it.

As an example, we can recast the TravelAgent EJB developed earlier as the ReservationProcessor EJB, a message-driven bean:

public class ReservationProcessorBean implements javax.ejb.MessageDrivenBean,
    javax.jms.MessageListener {

    public void onMessage(Message message) {
        try {
            MapMessage reservationMsg = (MapMessage)message;

            Integer customerPk = (Integer)reservationMsg.getObject("CustomerID");
            Integer cruisePk = (Integer)reservationMsg.getObject("CruiseID");
            Integer cabinPk = (Integer)reservationMsg.getObject("CabinID");
            double price = reservationMsg.getDouble("Price");

            CreditCardDO card = getCreditCard(reservationMsg);
            CustomerRemote customer = getCustomer(customerPk);
            CruiseLocal cruise = getCruise(cruisePk);
            CabinLocal cabin = getCabin(cabinPk);

            ReservationHomeLocal resHome = (ReservationHomeLocal)
                jndiContext.lookup("java:comp/env/ejb/ReservationHome");
            ReservationLocal reservation =
                resHome.create(customer,cruise,cabin,price,new Date());

            Object ref = jndiContext.lookup("java:comp/env/ejb/ProcessPaymentHome");
            ProcessPaymentHomeRemote ppHome = (ProcessPaymentHomeRemote)
                PortableRemoteObject.narrow(ref,ProcessPaymentHomeRemote.class);

            ProcessPaymentRemote process = ppHome.create();
            process.byCredit(customer,card,price);

            TicketDO ticket = new TicketDO(customer,cruise,cabin,price);
        } catch(Exception e) {
            throw new EJBException(e);
        }
    }
// More helper methods and callback methods follow
}

Notice that all the information about the reservation is obtained from the message delivered to the message-driven bean. JMS messages can take many forms, one of which is javax.jms.MapMessage form used in this example, which carries name-value pairs. Once the information is obtained from the message and the enterprise bean references are obtained, the reservation is processed as it was in the session bean. The only difference is that a TicketDO is not returned to the caller; message-driven beans don't have to respond to the caller because the process is asynchronous.

Message-driven beans, like stateless session beans, do not maintain any conversational state. The processing of each new message is independent from the previous messages. The message-driven bean is explained in detail in Chapter 13.

2.3 The Bean-Container Contract

The environment that surrounds the beans on the EJB server is often referred to as the container. The container is more a concept than a physical construct. Conceptually, the container acts as an intermediary between the bean and the EJB server. It manifests and manages the EJB objects and EJB homes for a particular type of bean and helps these constructs to manage bean resources and apply primary services such as transactions, security, concurrency, naming, and so forth at runtime. Conceptually, an EJB server may have many containers, each of which may contain one or more types of enterprise beans. As you will discover a little later, the container and the server are not clearly different constructs, but the EJB specification defines the component model in terms of the container's responsibilities, so we will follow that convention here.

Enterprise bean components interact with the EJB container through a well-defined component model. The EntityBean, SessionBean, and MessageDrivenBean (EJB 2.0) interfaces are the bases of this component model. As we learned earlier, these interfaces provide callback methods that notify the bean class of life-cycle events. At runtime, the container invokes the callback methods on the bean instance when appropriate life-cycle events occur. When the container is about to write an entity bean instance's state to the database, for example, it first calls the bean instance's ejbStore() method. This provides the bean instance with an opportunity to do some cleanup on its state just before it's written to the database. The ejbLoad() method is called just after the bean's state is populated from the database, providing the bean developer with an opportunity to manage the bean's state before the first business method is called.[3] Other callback methods can be used by the bean class in a similar fashion. EJB defines when these various callback methods are invoked and what can be done within their contexts. This provides the bean developer with a predictable runtime component model.

While all the callback methods are declared in bean interfaces, a meaningful implementation of the methods is not mandatory. In other words, the method body of any or all of the callback methods can be left empty in the bean class. Beans that implement one or more callback methods are usually more sophisticated and access resources that are not managed by the EJB system. Enterprise beans that wrap legacy systems often fall into this category. The only exception to this is the onMessage() method, which is defined in the MessageDrivenBean interface. This method must have a meaningful implementation if the message-driven bean is to do anything useful.

javax.ejb.EJBContext is an interface that is implemented by the container and is also part of the bean-container contract. Entity beans use a subclass of javax.ejb.EJBContext called javax.ejb.EntityContext. Session beans use a subclass called javax.ejb.SessionContext. Message-driven beans use the subclass javax.ejb.MessageDrivenContext. These EJBContext types provide the bean with information about its container, the client using the enterprise bean, and the bean itself. They also provide other functionality that is described in more detail in Chapter 11, Chapter 12, and Chapter 13. The important thing about the EJBContext types is that they provide the enterprise bean with information about the world around it, which the enterprise bean can use while processing requests from clients and callback methods from the container.

In addition to EJBContext, EJB 1.1 and 2.0 have expanded the enterprise bean's interface with the container to include a JNDI name space, called the environment naming context, which provides the bean with a more flexible and extensible bean-container interface. The JNDI environment naming context is discussed in detail in Chapter 11 and Chapter 12.

2.3.1 The Container-Server Contract

The container-server contract is not defined by the Enterprise JavaBeans specification. It was left undefined to facilitate maximum flexibility for vendors defining their EJB server technologies. Other than isolating the beans from the server, the container's responsibility in the EJB system is a little vague. The EJB specification defines only the bean-container contract, not the container-server contract. It is difficult to determine, for example, exactly where the container ends and the server begins when it comes to resource management and other services.

In the first few generations of EJB servers this ambiguity has not been a problem, because most EJB server vendors also provide EJB containers. Since the vendor provides both the container and the server, the interface between the two can remain proprietary. In future generations of the EJB specification, however, some work may be done to define the container-server interface and delimit the responsibilities of the container.

One advantage of defining a container-server interface is that it allows third-party vendors to produce containers that can plug into any EJB server. If the responsibilities of the container and server are clearly defined, vendors who specialize in the technologies that support these different responsibilities can focus on developing the containers or servers that best match their core competencies. The disadvantage of a clearly defined container-server interface is that the plug-and-play approach could impact performance. The high level of abstraction that would be required to clearly separate the container interface from the server would naturally lead to looser binding between these large components, which could result in lower performance. The following rule of thumb best describes the advantages and disadvantages associated with a container-server interface: the tighter the integration, the better the performance; the higher the abstraction, the greater the flexibility. The biggest deterrent to defining a container-server interface is that it would require the definition of low-level facilities, which was one of the problems established CTM vendors had with CORBA. The ability to implement low-level facilities such as transactions and security as they see fit is one of EJB's biggest attractions for vendors.[4]

Many EJB-compliant servers actually support several different kinds of middleware technologies. It's quite common, for example, for an EJB server to support the vendor's proprietary CTM model as well as EJB, servlets, web-server functionality, a JMS provider, and other server technologies. Defining an EJB container concept is useful for clearly distinguishing the part of the server that supports EJB from all the other services it provides.

This said, we could define the responsibilities of containers and servers based on current implementations of the EJB specification. In other words, we could examine how current vendors are defining the container in their servers and use this as a guide. Unfortunately, the responsibilities of the container in each EJB server largely depend on the core competency of the vendor in question. Database vendors, for example, implement containers differently from TP-monitor vendors. The strategies for assigning responsibilities to the container and server are so varied that discussing them separately won't really help you understand the overall architecture. Instead, this book addresses the architecture of the EJB system as though the container and server were one component and refers to them collectively as the EJB server or container.

2.4 Summary

This chapter covered a lot of ground, describing the basic architecture of an EJB system. At this point, you should understand that beans are business object components. The home interfaces define life-cycle methods for creating, finding, and destroying beans, and the remote and local interfaces define the public business methods of the bean. Message-driven beans do not have component interfaces. The bean class is where the state and behavior of the bean are implemented.

There are three basic kinds of beans: entity, session, and message-driven. Entity beans are persistent and represent a person, place, or thing. Session beans are extensions of the client and embody a process or a workflow that defines how other beans interact. Session beans are not persistent, receiving their state from the client, and they live only as long as the client needs them. Message-driven beans in EJB 2.0 are integration points that allow other applications to interact with EJB applications using JMS asynchronous messaging. Message-driven beans, like stateless session beans, are not persistent and do not maintain conversational state.

The EJB object and EJB home are conceptual constructs that delegate method invocations to session and entity beans from the client and help the container to manage the enterprise bean at runtime. The clients of entity and session beans do not interact with the instances of the bean class directly. Instead, the client software interacts with stubs, which are connected to the EJB object and EJB home. The EJB object implements the remote and/or local interface and expands the bean class's functionality. The EJB home implements the home interface and works closely with the container to create, locate, and remove beans.

Beans interact with their containers through the well-defined bean-container contract. This contract provides callback methods, the EJBContext, and the JNDI environment naming context. The callback methods notify the bean class that it is involved in a life-cycle event. The EJBContext and JNDI environment naming context provide the bean instance with information about its environment. The container-server contract is not well defined and remains proprietary at this time. Future versions of EJB may specify the container-server contract .

[1]  There are basically two kinds of component interfaces: remote and local. The remote interfaces are supported by both EJB 2.0 and 1.1, while the local component interfaces are new in EJB 2.0 and are not supported by EJB 1.1.

[2]  In EJB 2.0, the bookPassage() method might have used the local component interfaces of the other enterprise beans, because they are much more efficient.

[3]  The ejbLoad() and ejbStore() behavior illustrated here is for container-managed persistence. With bean-managed persistence, the behavior is slightly different. This distinction is examined in detail in Chapter 11.

[4]  Of all the commercial and open source EJB servers available today, only one, OpenEJB, has experimented with defining a container-server interface. OpenEJB is an open source EJB container system that I developed.

CONTENTS