Developing Your First Enterprise Beans, Part 1
by Bill Burke, Richard Monson-Haefel, and Sacha LaboureyOne of the most important features of EJB is that enterprise beans have the ability to work with containers from different vendors. However, that doesn't mean that selecting a server and installing your enterprise beans on that server are trivial processes.
|
Related Reading
Enterprise JavaBeans |
Choosing and Setting Up an EJB Server
The EJB server you choose should provide a utility for deploying enterprise beans. It doesn't matter whether the utility is command-line oriented or graphical, as long as it does the job. The deployment utility should allow you to work with prepackaged enterprise beans, i.e., enterprise beans that have already been developed and archived in a JAR file. Finally, the EJB server must support an SQL-standard relational database that is accessible using JDBC. For the database, you should have privileges sufficient for creating and modifying a few simple tables in addition to normal read, update, and delete capabilities. If you have chosen an EJB server that does not support a SQL-standard relational database, you may need to modify the examples to work with the product you are using.
This book does not say very much about how to install and deploy enterprise beans. That task is largely server-dependent. We'll provide some general ideas about how to organize JAR files and create deployment descriptors, but for a complete description of the deployment process, you'll have to refer to your vendor's documentation.
Setting Up Your Java IDE
To get the most from this chapter, it helps to have anIDE that has a debugger and allows you to add Java files to its environment. Several Java IDEs - such as BEA's Weblogic Workshop, IBM'sEclipse, Borland's JBuilder, and Sun'sForte - fulfill this requirement. Some EJB products, such as IBM'sWebSphere and BEA's Weblogic, are tightly coupled with an IDE that makes life a lot easier when it comes to writing, deploying, and debugging your applications.
Once you have an IDE set up, you need to include the Enterprise JavaBeans and other J2EE packages which will be provided by your application server vendor - usually in a single JAR file (e.g., j2ee.jar).
Developing an Entity Bean
There's no better place to start than the Cabin EJB, which we have been examining throughout the previous chapters. The Cabin EJB is an entity bean that encapsulates the data and behavior associated with a cruise ship cabin in Titan's business domain.
Cabin: The Remote Interface
When developing an entity bean, we first want to define its remote interface. The remote interface defines the bean's business purpose; the methods of this interface must capture the concept of the entity. We defined the remote interface for the Cabin EJB in Chapter 2; here, we add two new methods for setting and getting the ship ID and the bed count. The ship ID identifies the ship to which the cabin belongs, and the bed count tells how many people the cabin can accommodate:
package com.titan.cabin;
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;
public int getShipId( ) throws RemoteException;
public void setShipId(int sp) throws RemoteException;
public int getBedCount( ) throws RemoteException;
public void setBedCount(int bc) throws RemoteException;
}
The CabinRemote interface defines four properties:
name, deckLevel,
shipId, and bedCount.
Properties
are attributes of an enterprise bean that can be accessed by public
set and get methods.
Notice that we have made the CabinRemote interface
a part of a new package named com.titan.cabin.
Place all the classes and interfaces associated with each type of
bean in a package specific to the bean. Because our beans are for the
use of the Titan cruise line, we placed these packages in the
com.titan package hierarchy. We also created
directory structures that match package structures. If you are using
an IDE that works directly with Java files, create a new directory
called dev (for development) and create the
directory structure shown in
Figure 4-1. Copy the CabinRemote
interface into your IDE and save its definition to the
cabin directory. Compile the
CabinRemote interface to ensure that its
definition is correct. The CabinRemote.class
file, generated by the IDE's compiler, should be
written to the cabin directory, the same
directory as the CabinRemote.java file. The rest
of the Cabin bean's classes will be placed in this
same directory.

Figure 4-1. Directory structure for the Cabin bean
CabinHome: The Remote Home Interface
Once we have defined the remote
interface of the Cabin EJB, we have defined the remote view of this
simple entity bean. Next, we need to define the Cabin
EJB's remote home interface, which specifies how the
enterprise bean can be created, located, and destroyed by remote
clients; in other words, the Cabin EJB's life-cycle
behavior. Here is a complete definition of the
CabinHomeRemote home interface:
package com.titan.cabin;
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 CabinHomeRemote interface extends
javax.ejb.EJBHome and defines two life-cycle
methods: create( ) and findByPrimaryKey(
). These methods create and locate remote
references to Cabin EJBs. Remove methods (for deleting enterprise
beans) are defined in the javax.ejb.EJBHome
interface, so the CabinHomeRemote interface
inherits them.
CabinBean: The Bean Class
We have now defined the remote
client-side API for creating, locating, using, and removing the Cabin
EJB. Now we need to define CabinBean, the class
that provides the implementation on the server for the Cabin EJB. The
CabinBean class is an entity bean that uses
container-managed persistence, so its definition will be fairly
simple.
In addition to the callback methods discussed in Chapter 2 and Chapter 3, we
must also define accessor
methods for the CabinRemote interface and an
implementation of the create method defined in the
CabinHomeRemote interface. Here is the complete
definition of the
CabinBean class:
package com.titan.cabin;
import javax.ejb.EntityContext;
public abstract class CabinBean implements javax.ejb.EntityBean {
public Integer ejbCreate(Integer id){
this.setId(id);
return null;
}
public void ejbPostCreate(Integer id){
}
public abstract void setId(Integer id);
public abstract Integer getId( );
public abstract void setShipId(int ship);
public abstract int getShipId( );
public abstract void setName(String name);
public abstract String getName( );
public abstract void setBedCount(int count);
public abstract int getBedCount( );
public abstract void setDeckLevel(int level);
public abstract int getDeckLevel( );
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.
}
}
The CabinBean class can be divided into two
sections for discussion: declarations for the
container-managed fields and the
callback methods.
Container-managed fields
The CabinBean defines several pairs of abstract
accessor methods. For example, setName( ) and
getName( ) are a pair of abstract accessor
methods. These methods are responsible for setting and getting the
entity bean's name field. When
the bean is deployed, the EJB container automatically implements all
the abstract accessor methods so that the bean state can be
synchronized with the database. These implementations map the
abstract accessor methods to fields in the database. Although all the
abstract accessor methods have corresponding methods in the remote
interface, CabinRemote, it's not
necessary that they do so. Some accessor methods are for the entity
bean's use only and are never exposed to the client
through the remote or local interfaces. Note that, unlike the
matching methods in the remote interface, the abstract accessor
methods do not throw RemoteExceptions.
It's customary to consider the abstract accessor
methods as providing access to virtual fields and to refer to those
fields by their method names, less the get or
set prefix. For example, the getName(
)/setName( ) abstract accessor methods
define a virtual container-managed
persistence (CMP) field called
name (the first letter is always changed to
lowercase). The getDeckLevel(
)/setDeckLevel( ) abstract accessor
methods define a virtual CMP field called
deckLevel, and so on.
The name, deckLevel,
shipId, and bedCount fields
represent the Cabin EJB's persistent state. They
will be mapped to the database at deployment time. These fields are
also publicly available through the entity bean's
remote interface. Invoking the getBedCount( )
method on a CabinRemote EJB object causes the
container to delegate that call to the corresponding
getBedCount( ) method on the
CabinBean instance.
There is no requirement that CMP fields must be exposed. The
id field is another container-managed field, but
its abstract accessor methods are not exposed to the client through
the CabinRemote interface. This field is the
primary key of the Cabin EJB; it's the entity
bean's index to its data in the database.
It's bad practice to expose the primary key of an
entity bean - you don't want client applications
to be able to change that key.