In order to use Enterprise JavaBeans effectively, you need to understand the EJB architecture. This chapter explores the core of the EJB architecture: how enterprise beans are distributed as business objects. Chapter 3 explores the services and resource-management techniques supported by EJB.
In order to be truly versatile, the EJB component design had to be smart. For application developers, assembling enterprise beans requires little or no expertise in the complex system-level issues that often plague three-tier development efforts. While EJB makes the process easier for application developers, it also provides EJB server developers with a great deal of flexibility in how they support the EJB specification.
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. Message-driven beans process messages from non-RMI systems like Java Message Service, legacy systems, and web services. All EJB servers must at least support a JMS-based message driven bean, but they may also support other types of message-driven bean.
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 extensions of the client application that manage 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 entity 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 coordinate tasks involving other session and entity beans. Message-driven beans and session beans differ primarily in how they are accessed. While a session bean provides a remote interface that defines which methods can be invoked, a message-driven bean subscribes to or listens for messages. It responds by processing the message and managing the actions that other beans take. 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 that 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.
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,[7] a bean class, and a primary key:
The remote
interface defines the
bean’s
business
methods which can be accessed from applications outside the EJB
container: the business methods a bean presents to the outside world
to do its work. 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.
The home interface defines the
bean’s
life-cycle
methods which can be accessed from applications outside the EJB
container: the life-cycle methods for creating new beans, removing
beans, and finding beans. 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.
The
local interface for an enterprise bean
defines business methods that can be used by other beans in the same
EJB container: the business methods a bean presents to other beans
running in the same JVM. 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.
The local home interface defines
life-cycle methods that can be used by other beans in the same EJB
container; that is, the life-cycle methods a bean presents to other
beans running in the same JVM. 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.
The endpoint interface defines business
methods
that can be accessed from applications outside the EJB container via
SOAP. The endpoint interface is based on JAX-RPC (Java API for
XML-RPC) and is designed to adhere to the SOAP and WSDL standards.
The endpoint interface extends java.rmi.Remote
. It
can be used only by stateless session beans. There is no home
interface associated with the endpoint interface.
Message-driven beans implement the message interface, which defines the methods by which messaging systems, such as Java Message Service, can deliver messages to the bean.
The session and entity bean classes implement the
bean’s business and life-cycle methods. Note that
the bean class usually does not implement the remote or local
component interfaces, but it may implement the endpoint interface.
However, the bean class must have methods matching the signatures of
the methods defined in the remote, local, and endpoint 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. In addition, an entity bean must implement
javax.ejb.EntityBean
; a session bean must
implement javax.ejb.SessionBean
. The
EntityBean
and SessionBean
extend javax.ejb.EnterpriseBean
.
A message-driven bean implements one or more message delivery methods
(e.g., onMessage( )
) defined in a message
interface. The container calls these methods when a new messages
arrives. The message-driven bean class must also implement
javax.ejb.MessageDrivenBean
. EJB 2.1 and 2.0
containers must support JMS-based message-driven beans, which
implement the javax.jms.MessageListener
interface.
EJB 2.1 also supports message-driven beans that process messages from
other types of messaging systems with their own message interfaces.
The MessageDrivenBean
, like the
EntityBean
and the SessionBean
,
extends the javax.ejb.EnterpriseBean
interface.
The primary key
is a class that provides a pointer into the database. Only entity
beans need a primary key. The principal requirement for this class is
that it implements java.io.Serializable
.
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 or Web service clients.
Likewise, an enterprise bean doesn’t need to provide
a remote or an endpoint interface if you know it will be called only
by enterprise beans in the same container. You can provide any
combination of local, remote, and endpoint interfaces.
The complexity 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 and local home interfaces do not involve RMI, they still represent a stub or a proxy to the bean class. While there is no network, the stubs allow the container to monitor the interactions between beans and to apply security and transactions as appropriate.
It’s important to note that message-driven beans don’t support remote, local, or endpoint component interfaces, but they 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 interact may be located in the same container, in which case the message-driven bean uses their local component interfaces, or they may be located in a different address space and EJB container, in which case the remote or endpoint component interfaces are used.
There are also many interactions between an enterprise bean and its container (Many people use the terms “container” and "server” interchangeably, which is understandable because the difference between the terms 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.
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 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,” 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. The acronym MDB is frequently used in place of the term “message-driven bean.”
We also use suffixes to distinguish between local, remote, and
endpoint 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. The local interface of the
Cabin EJB would be the CabinLocal
interface. The
endpoint interface for the Cabin EJB-based web service would be
CabinWS
(WS stands for Web Service). The home
interfaces add the word Home to the mix. The
remote and local home interfaces for the Cabin EJB would be
CabinHomeRemote
and
CabinHomeLocal
, respectively.[8] 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
.
These naming conventions are used for clarity; they are not prescriptive or even recommended for use in production. Once you understand the differences between the component interfaces and the different types of beans, you can use any naming strategy you wish.
Having introduced the machinery, let’s look at how to build an entity bean with remote component interfaces. In this section, we 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 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. 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.
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 pk) throws CreateException, RemoteException; public CabinRemote findByPrimaryKey(Integer id) 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. For
example, you could provide a create( )
method that
initializes the cabin’s deck and name.
The
findByPrimaryKey( )
method, with a single argument, is required, and allows you to look
up a particular Cabin given its primary key. 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 or
message-driven beans.
Now
let’s look at an actual entity bean.
Here’s the code for the
CabinBean
; it’s a sparse
implementation, but it shows how the pieces fit together:
import javax.ejb.EntityContext; public abstract class CabinBean implements javax.ejb.EntityBean { public Integer ejbCreate(Integer pk){ setId(id); return null; } public void ejbPostCreate(Integer pk){ // 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 pk); public void setEntityContext(EntityContext ctx){ // empty implementation } public void unsetEntityContext( ){ // empty implementation } public void ejbActivate( ){ // empty implementation } public void ejbPassivate( ){ // empty implementation } public void ejbLoad( ){ // empty implementation } public void ejbStore( ){ // empty implementation } public void ejbRemove( ){ // empty implementation } }
Notice that the CabinBean
class is abstract, as
are several of the methods that access or update the
bean’s persistent state. Also notice that there are
no instance fields to hold the state information these methods
access. The abstract methods (and the missing fields) are implemented
by the container system automatically. 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 in session or message-driven beans.
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 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( )
and
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 ready 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( )
enables the EJB container to give the bean 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
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 lifecycle, when it is about to be loaded,
removed, deactivated, and so on. 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 10.
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.
It’s 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 10.
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 ejbLoad( )
or ejbStore( )
methods, because session beans are not persistent. While
session beans have a setSessionContext( )
method,
they do not have an unsetSessionContext( )
method.
Session beans have ejbActivate( )
and
ejbPassivate( )
methods, which are used by
stateful session beans to manage conversational state. Finally,
session beans provide an ejbRemove( )
method,
which notifies the bean that the client no longer needs it. However,
this method doesn’t tell the bean that its data is
about to be removed from the database, because a session bean
doesn’t represent data.
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 11.
Message-driven beans (MDBs) implement a message interface;
they don’t implement remote, local, endpoint, or
home interfaces. The message-driven bean defines a few callback
methods and one or more message delivery 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); and the
setMessageDrivenContext( )
method. The kind of
message delivery methods implemented by the MDB depend on the type of
messaging service it supports. For example, a JMS-based MDB, which
all EJB containers must support, must implement the
onMessage( )
method, which is called every time a
new asynchronous JMS message is delivered. The message-driven bean
doesn’t define the ejbPassivate( )
, ejbActivate( )
, ejbLoad( )
, or 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 12.
The interfaces and classes we have discussed don’t address how beans are managed at runtime. We didn’t talk about how beans interact with security, transactions, naming, and other services common to distributed object systems. These types of primary services are handled automatically by the EJB container, but that prompts the question, “How does the EJB container know how to handle security, transactions, and so on?” The EJB container gets this kind of runtime information from deployment descriptors.
Deployment descriptors allow us to customize an EJB’s runtime behavior without having to change the software itself. Deployment descriptors are also similar to the 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.).
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. Integrated development environments ( IDEs) that support development of Enterprise JavaBeans often allow developers to set up the deployment descriptors they need using visual utilities like property sheets. After the developer has set all of the bean’s properties, 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 package Java classes and other resources 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 location is given to the container’s deployment tools.
When the container opens the JAR file, it reads 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 information. The person deploying the bean can alter some of these settings, such as transactional and security access attributes, to customize the bean for a particular application. Most container tools provide user-friendly property sheets for reading and altering the deployment descriptor when the bean is deployed.
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 schema (XML Schema in EJB 2.1 and Document Type Definition (DTD) in EJB 2.0) that can be extended so the type of deployment information stored evolves as the specification evolves. Chapter 17 provides a detailed description of XML deployment descriptors. The following sections provide a brief overview of XML deployment descriptors.
The following descriptor might be used to describe the Cabin bean in EJB 2.1:
<?xml version="1.0" encoding="UTF-8"?> <ejb-jar xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd" version="2.1"> <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>
The first element in an EJB 2.1 deployment descriptor declares the document to be an XML document conformant with XML Version 1.0, and the character encoding, normally UTF-8.
The root
element is the ejb-jar
element. It declares the namespace of the EJB 2.1 XML Schema as well
as the schema’s location. In addition, the
ejb-jar
element declares the version of EJB
supported, which in the case of EJB 2.1 is version
“2.1”.
The following 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> <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>
The first element in an EJB 2.0 deployment descriptor is
<!DOCTYPE>
. This element describes the
organization that defined the DTD for the XML document, supplies the
DTD’s version, and provides a URL for the DTD. The
DTD describes how a particular XML document is structured.
Now, let’s look more closely at the information in the deployment descriptor. Note that 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 a deployment descriptor. Here’s what the individual 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. While this deployment descriptor describes a
single entity bean, 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. It is the name used 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-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
<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 that have not been covered yet, so don’t worry about knowing all of the elements you might find in a deployment descriptor.
The entity and session beans both declare the component interfaces that their clients use to access them. (Message-driven beans are a very different kind of animal). In EJB 2.0, clients outside the container system always use the enterprise bean’s remote component interfaces. In EJB 2.1, clients outside the container system have the option of accessing stateless session beans as Web services. For both EJB 2.1 and 2.0, clients within the same J2EE system (i.e., enterprise beans, Servlets, and JSPs) can use local component interfaces to interact. This section explains how the component interfaces are connected to instances of the bean class at runtime.
Now that you have a basic understanding of some of an enterprise bean’s parts (component interfaces, bean class, and deployment descriptor), it’s time to talk 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 practice is useful 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 the missing pieces in the big picture are understandable.
This chapter has said a lot about a bean’s remote
and local interfaces, which extend the EJBObject
and 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 interfaces of the enterprise bean. The EJB object
is generated by your EJB container and 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. This
object works with the container to apply transactions, security, and
other system-level operations to the bean at runtime.
We’re forced to use “and/or” a lot when talking about which interface the EJB object implements. That’s because enterprise beans in EJB can declare the local interface, the remote interface, or both! In EJB 2.1, stateless session beans can also implement an endpoint interface, which turns it into a Web service. (The endpoint interface and Web services are addressed separately in Chapter 14.) 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 how the vendor chooses to implement it. But that distinction, while it matters to EJB vendors, isn’t visible to EJB developers.
A vendor can use a number of strategies to implement the EJB object.
Figure 2-1 illustrates two possibilities using the
CabinRemote
interface. The same implementation
strategies apply to the CabinLocal
and
javax.ejb.EJBLocalObject
interfaces.
In Figure 2-1(a), the EJB object class actually extends the bean class, adding functionality specific to the EJB container. In Figure 2-1(b), 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. But 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. But everything a client (including other enterprise beans) needs to know about an enterprise bean is described by the remote and home interfaces.
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. The EJB home is responsible for locating, creating, and removing enterprise beans. These tasks may involve working with the EJB server’s resource managers, 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 work with the EJB object by
invoking business methods. The stub relays the methods to the EJB
object; in turn, the EJB object delegates those method calls to the
bean instance.
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 returns a remote interface
reference. If the client is working with a local home interface, the
EJB home returns a reference implementing the local interface. EJB
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 pk) throws CreateException, RemoteException; public CabinRemote findByPrimaryKey(Integer id) throws FinderException, RemoteException; } // The Cabin EJB's local home interface public interface CabinHomeLocal extends javax.ejb.EJBLocalHome { public CabinLocal create(Integer pk) throws CreateException; public CabinLocal findByPrimaryKey(Integer id) 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. The bean class is wrapped by the EJB object. Remember, though, that this is only an illustration. “EJB object” and “EJB home” are simply terms to describe the EJB container’s responsibilities for supporting the 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.
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.
[7] There are basically three kinds of component interfaces: remote, local, and endpoint. The remote and local interfaces are supported by both EJB 2.1 and 2.0, while the endpoint component interface is new in EJB 2.1 and is not supported by EJB 2.0.
[8] The endpoint interface does not have a corresponding home interface.