edit P.Komisar latest
revision Nov / 2007
reference:http://developer.java.sun.com/developer/onlineTraining/EJBIntro
Enterprise JavaBeans provide a programming model to build
and deploy
server-side
distributed, portable, transactional
software components called
enterprise
Javabeans.
The enterprise java beans are hosted in a program called a
container from which the
bean's remote services are provided
to network clients. The model is
designed to make
enterprise
beans portable between EJB containers provided by different
vendors.
// EJB 2.x adds 'local' services which
makes beans available
// locally to components running on
the same JVM
The EJB Container
The container hosts an enterprise bean in a manner similar how
serlets
are managed in a web
server application or applets are
hosted in a HTML browser. The EJB
container also manages:
Containers manage many beans simultaneously using pooling to
reduce memory consumption. An unused bean may be pooled
for
reuse by another client or removed from memory and brought back
when needed. This is
transparent to the
client whose reference has
been kept alive and reassociated with a new identical instance.
The bean depends on the container for all resource access. The
container
provides three
channels
Callback Methods
Every bean implements a subtype of the EnterpriseBean interface
which
defines several methods,
called callback methods. Each
callback method alerts the bean to a
different event in its lifecycle.
The container will invoke these methods to notify the bean when it's
about to activate the bean,
persist its state to the database, end a
transaction, remove the bean
from memory, etc.
The callback methods allow the bean to do housework immediately
before or after some
event.
EJBContext
Every bean obtains an EJBContext object, which is a reference
to the
container.The EJBContext interface provides methods for
interacting with the container so a
bean can request information
about its environment like the identity of its client, the status of
a
transaction, or to obtain remote
references to itself.
Java Naming and Directory Interface
Java Naming and Directory Interface (JNDI) is a standard extension
to
the Java platform for
accessing naming systems like LDAP, NetWare,
file systems, etc. Every
bean automatically
has access to a special
naming system called the Environment
Naming
Context (ENC). The
ENC is managed by the container and accessed by beans using
JNDI. The
JNDI ENC allows
a bean to access resources like JDBC
connections, other enterprise
beans, and properties specific to that
bean.
Enterprise Beans
A bean is created to represent a business concept, like a
customer
or
a clerk To create
an EJB, a developer provides:
The bean implementation is
instantiated
at runtime in
the
container to become a distributed object. The client accesses
this object over the network through the remote
and home
interfaces. It is the
clients
actions that invoke a beans life
cycle, to create, manipulate, and remove beans from the
EJB
server.
// EJB 2.x adds the local call vs the more
expensive remote call
Remote and Home Interfaces
The home interface represents the life-cycle methods of the
component.
The remote interface represents the business method of the bean.
The remote and home
interfaces extend the javax.ejb.EJBObject
and
javax.ejb.EJBHome
interfaces
respectively.
These EJB interface
types define a standard set of utility methods
and provide common base
types for all remote and home interfaces.
The Remote interface
hierarchy
Used with EJBs
java.rmi.Remote
// rmi interface level
|
|
javax.ejb.EJBObject
javax.ejb.EJBHome //
EJB specific interface level
|
|
Bean's_Remote
Bean's_Home
// developer's defined extensions
Entity & Session Beans
// data stored in a
database
and returned by get-set type methods are typically wrapped
// in entity beans while
business
operations are wrapped in session beans.
The remote interface defines accessor and mutator methods to
read
and
update information
about a business concept. This is
typical of a an entity bean, representing
a persistent business
object whose data is stored in a
database.
Entity beans wrap
business data with behavior specific to that data.
Although entity beans often
have task-oriented methods, tasks
are more typical of session beans.
Session beans do not represent
data. Instead they represent business processes or services.
A Session Bean Example
//
modified from EJB Fundamentals by Richard Monson-Haefel
import
javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface HotelClerk extends
EJBObject {
public void reserveRoom(
Customer
cust, RoomInfo ri ) throws RemoteException;
public RoomInfo
availableRooms( Location
loc, DateInfo di) throws RemoteException;
}
Entity Beans
Entity beans implement the
javax.ejb.EntityBean
which
extends
javax. ejb.EnterpriseBean.
Entitiy Bean Example
// from EJB Fundamentals by Richard Monson-Haefel
import
javax.ejb.EntityBean;
// note the implementation of javax.ejb.EntityBean
public class
CustomerBean implements
EntityBean {
Address myAddress;
Name myName;
CreditCard myCreditCard;
public Name getName( ) {
return myName;
}
public void setName(Name name) {
myName = name;
}
public Address getAddress() {
return myAddress;
}
public void setAddress(Address address) {
myAddress = address;
} // other contents
}
Session Beans
Session beans represent a set of processes or tasks,
typically calling on other beans or accessing a database
directly. Both behaviours
Session Bean
Example
// from EJB Fundamentals by Richard
Monson-Haefel
import javax.ejb.SessionBean;
//
note the implementation of javax.ejb.SessionBean
public class HotelClerkBean implements SessionBean {
public void
reserveRoom(Customer
cust, RoomInfo ri, Date from, Date to) {
CreditCard card = cust.getCreditCard( );
RoomHome roomHome = // ... get home
reference
Room room = roomHome.findByPrimaryKey(ri.getID());
double amount = room.getPrice(from,to);
CreditServiceHome creditHome = // ...
get
home reference
CreditService creditAgent = creditHome.create();
creditAgent.verify(card, amount);
ReservationHome resHome = // ... get
home
reference
Reservation reservation = resHome.create(cust,room,from,to);
}
public RoomInfo[]
availableRooms(Location
loc, Date from, Date to) {
// Make an SQL call to find available rooms
Connection con = // ... get database
connection
Statement stmt = con.createStatement( );
ResultSet results = stmt.executeQuery("SELECT ...");
// ...
return roomInfoArray;
}
}
Bean Classes Don't Implement the Remote or
Home Interfaces
The bean classes do not implement the remote or home
interfaces.
EJB doesn't require
that the bean class implement these interfaces
and in fact it is
discouraged. This is because the base types of
the
remote and home interfaces (EJBObject and EJBHome) define a
lot
of other methods that
are implemented by the container
automatically.
The bean class does
provide implementations
for all the business
methods defined in the remote as well as callback
methods defined
in the
home interface.
The Home Interface //
life cycle create, find & destroy
The home interface provides life cycle methods for creating,
destroying,
and locating beans.
These methods are separated
out of the remote interface
because they are general behaviours
not specific to a single bean instance.
Home Interface Example
// from EJB Fundamentals by Richard Monson-Haefel
import javax.ejb.EJBHome;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
import
java.rmi.RemoteException; //
notice the javax.ejb.EJBHome extension
public interface CustomerHome extends EJBHome {
public
Customer create(Integer customerNumber)
throws RemoteException, CreateException;
public
Customer findByPrimaryKey(Integer customerNumber)
throws RemoteException, FinderException;
public
Enumeration findByZipCode(int zipCode)
throws RemoteException, FinderException;
}
Home Interface Methods
Following is a code sample showing the home interface methods
being
used to
find and create beans
Example
// from EJB
Fundamentals
by Richard Monson-Haefel
CustomerHome home
= //
Get a reference to the CustomerHome object
Customer customer
= home.create(new
Integer(33));
Name name = new
Name("Richard",
"Wayne", "Monson-Haefel");
customer.setName(name);
Enumeration
enumOfCustomers
= home.findByZip(55410);
Customer customer2 =
home.findByPrimaryKey(new
Integer(33));
Name name2 =
customer2.getName(
);
//
output is "Richard Wayne Monson-Haefel"
System.out.println(name);
Enterprise Beans as Distributed Objects
The remote and home interfaces are types of Java RMI
Remote interfaces.
An enterprise bean
is an RMI-style
distributed object, accessible from applications in other
address spaces.
In EJB, the
RMI 'skeleton' for the remote and home interfaces
are implemented by the
container, not the bean
class.
Distributed object protocols define the format of network
messages
sent
between address
spaces. Most EJB servers
support either the Java
Remote Method Protocol (JRMP)
or
CORBA's Internet
Inter-ORB Protocol (IIOP),
details of which
are hidden from the users of the EJB system.
Container and Bean
Managed
Persistence
The entity bean provides an object-oriented view of data
that will frequently be mapped to a relational database.
There are two types of entity bean:
Container-Managed Persistence
Container-managed persistence beans is the simplest for the bean
developer
to create
and the most difficult for the EJB server to support.
All the logic for
synchronizing the bean's state with the database is
handled
automatically
by the container.
The bean developer doesn't
write any data
access
logic. Most EJB
vendors support automatic
persistence to a relational database, but
the level of support
varies
greatly. // ~observation circa 2000
Details of CMP
Notice in the
following CMP bean example there is
no database access code. EJB server vendors provide
tools to map bean values to databases.
Bean
Class Example
// from EJB Fundamentals by Richard Monson-Haefel
import javax.ejb.EntityBean;
public class CustomerBean implements EntityBean {
int
customerID;
Address
myAddress;
Name
myName;
CreditCard myCreditCard;
//
CREATION METHODS
public Integer
ejbCreate(Integer id)
{
customerID
= id.intValue( );
return
null;
}
public Customer
ejbCreate(Integer
id, Name name) {
myName
= name;
return
ejbCreate(id);
}
public void
ejbPostCreate(Integer
id) { }
public void
ejbPostCreate(Integer
id, Name name) { }
//
BUSINESS METHODS
public Name getName( ) {
return
myName; }
public void setName(Name name)
{ myName
= name; }
public Address getAddress(
) {
return myAddress; }
public void setAddress(Address
address)
{ myAddress = address; }
public CreditCard
getCreditCard( )
{ return myCreditCard; }
public void
setCreditCard(CreditCard
card) { myCreditCard = card; }
//
CALLBACK
METHODS defined in EntityBean interface
public void
setEntityContext(EntityContext
cntx) { }
public void
unsetEntityContext() {
}
public void ejbLoad( )
{
}
public void ejbStore( )
{
}
public void ejbActivate( )
{
}
public void ejbPassivate( )
{
}
public void ejbRemove( )
{
}
}
Following are the objects referenced in the above beans
which ultimately use mostly String values which may in
turn represent other primitive Java types.
// The Name class
public class Name implements Serializable {
public String lastName, firstName, middleName;
public Name(String
lastName, String
firstName, String middleName) {
this.lastName
= lastName;
this.firstName
= firstName;
this.middleName
= middleName;
}
public Name( ) { } //
also includes a no-args empty default constructor
}
// The Address
class
public class Address implements
Serializable {
public String street, city, state, zip;
public Address( String
street, String
city, String state, String zip) {
this.street
= street;
this.city
= city;
this.state
= state;
this.zip
= zip;
}
public Address( ) {
}
// no-args empty default constructor
}
// The
CreditCard class
public class CreditCard implements
Serializable
{
public String number, type, name;
public Date expDate;
public CreditCard ( String number,
String type,
String name, Date expDate) {
this.number
= number;
this.type
= type;
this.name
= name;
this.expDate
= expDate;
}
public CreditCard( )
{}
// no-args empty default constructor
}
Container Managed Fields
These fields are called container-managed fields
because
the container synchronizes their state with the database.
Container-managed
fields can be
any primitive data types
or
serializable
type.
Not all fields in a bean are automatically
container-managed
fields, such as plain instance fields for transient use within the
bean. The container-managed
fields are differentiated from
plain instance fields in the
deployment descriptor.
The container-managed fields are mapped to corresponding
types such as columns in an RDBMS or through Object-Relational
mapping to an Object-Oriented Database.
The CustomerBean / Container Mapping
{
id
INTEGER PRIMARY KEY,
last_name
CHAR(30),
first_name
CHAR(20),
middle_name
CHAR(20),
street
CHAR(50),
city
CHAR(20),
state
CHAR(2),
zip
CHAR(9),
credit_number CHAR(20),
credit_date DATE,
credit_name
CHAR(20),
credit_type CHAR(10)
}
The Primary Key
A subset of the container-managed fields will server as the
bean's
primary key, the
pointer to a unique record in a database.
In CustomerBean,
the id field is the
primary key field. Primitive
single field primary keys are represented
as by their corresponding
object wrappers, for example
a primitive int
in the bean class
manifests itself to
a bean's client as a java.lang.Integer type.
A primary key made up of several fields are called compound
primary keys is represented
by a special
class defined by the
bean developer.
The Home Interface of the CMP
Entity
Beans
// from EJB
Fundamentals
by R. Monson-Haefel
public interface CustomerHome extends javax.ejb.EJBHome {
public Customer create(
Integer customerNumber)
throws RemoteException,CreateException;
public Customer
create(Integer customerNumber,
Name name)
throws RemoteException,CreateException;
public Customer
findByPrimaryKey(Integer
customerNumber)
throws RemoteException, FinderException;
Example //
How the Home interface is used by a client
// from EJB
Fundamentals
by R. Monson-Haefel
CustomerHome home = //
Get a reference to the CustomerHome object
Name name = new Name("John", "W", "Smith");
Customer customer = home.create( new
Integer(33),
name);
Creation of a New CMP Instance Entity Bean
Invoking the
create method on the bean's home interface,
creates a new instance of a CMP entity
bean and
inserts
data into the
database.
A bean's home interface may declare zero or more create( )
methods. Each must
have corresponding ejbCreate( ) and
ejbPostCreate( ) methods in the
bean class.
These
creation methods are linked at runtime, so that when
a
create( ) method is invoked on
the home
interface, the container
delegates
the invocation to the corresponding
ejbCreate( )
and
ejbPostCreate() methods on
the bean class.
The ejbCreate( )
methods
initialize the instance
state before a
record is inserted into the
database. When the ejbCreate( )
method is finished (returning null in CMP)
the container reads
the container-managed
fields and inserts a new record into the
CUSTOMER table indexed by
the primary key.
// customerID maps to the CUSOTMER.ID column.
In EJB, an entity bean doesn't technically exist until after it's
data
has been inserted into
the database. Once inserted, the entity
bean exists and can
access
its own primary key
and remote
references. A bean needing to access its own primary key
or
remote reference
after its created, but before it services any
business methods, may use it's ejbPostCreate( ) method which
allows the bean to
do any post-create
processing
before it
begins serving client
requests.
Find Methods
Methods in the home interface that begins with "find" are called
the find methods, and are used to query the EJB server
for
specific entity beans, based on the name of the method and
arguments passed in. In CMP
entity beans, the find
methods
do not have
matches in the bean class as the container provides
them. The deployer
will use vendor-specific tools to utilize the
container provided find methods.
Types of Find Methods
Remote Interface
Beside the home interface, an entity bean must define a
remote
interface
defining the entity's
business methods.
Customer
Remote Interface Example
// from EJB
Fundamentals
by R. Monson-Haefel
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface Customer extends EJBObject {
public Name getName( )
throws RemoteException;
public void setName(Name name)
throws
RemoteException;
public Address getAddress(
) throws
RemoteException;
public void setAddress(Address
address)
throws RemoteException;
public CreditCard
getCreditCard( )
throws RemoteException;
public void
setCreditCard(CreditCard
card) throws RemoteException;
}
How Used
Customer customer = //
... obtain
a remote reference to the bean
// get the customer's
address
Address addr = customer.getAddress( );
// change the zip code
addr.zip = "56777";
// update the
customer's address
customer.setAddress(addr);
Callback Methods
Aside from implement Remote Interface business methods
and Home Interface create methods, the bean class
also
implements a set of
callback methods that allow the container
to notify the bean of events
in its lifecycle.
The
callback methods are defined in the javax.ejb.EntityBean
interface implemented by all
entity beans. The EntityBean
interface
has the following
definition. Notice that the bean
class implements these methods.
public interface javax.ejb.EntityBean {
public void setEntityContext();
public void unsetEntityContext();
public void ejbLoad();
public void ejbStore();
public void ejbActivate();
public void ejbPassivate();
public void ejbRemove();
}
EntityBean Methods
ejbLoad( ) & ejbStore
Example
// from EJB
Fundamentals
by R. Monson-Haefel
// going to and from object field identifiers and table names
public void ejbLoad( ) {
if (myName
== null)
myName = new Name( );
myName.lastName = lastName;
// other fields
}
public void ejbStore( ) {
lastName
= myName.lastName;
//
other fields
}
Passivation - The disassociation of an bean instance with
its
remote
reference so the container
can evict it from memory or
reuse it. A
resource
conservation measure to reduce the number
of instances in
memory.
The ejbPassivate() and ejbActivate() methods notify the bean
when it is about to
be passivated or activated.
Bean-Managed Persistence
BMP beans manage
synchronizing
their state with
a database.
The bean usually uses JDBC to
read and write its fields to the
database. The container tells it when
to do each synchronization
operation and manages the transactions for the bean automatically.
Bean-managed persistence
is useful when persistence
operations
are too complicated for
the container or to use a data source that
is not supported by the
container such as custom or
legacy databases.
Monson-Haefel's CustomerBean can be modified to be a
BMP
Persistence bean without impacting the remote or home interfaces
at
all. In fact, the original CustomerBean is not modified directly.
Instead, extension and overriding the appropriate methods is used.
(In most
cases, you would not extend a bean to make it BMP, instead
just
implementing
as a BMP
bean directly.) This strategy does allow
the bean to be act as either a CMP
or BMP bean.
Customer Bean Class Extension
public class CustomerBean_BMP extends
CustomerBean
{
public void ejbLoad( ) { // override
implementation
}
public void ejbStore() { // override
implementation
}
public void ejbCreate() {// override
implementation}
public void ejbRemove() {// override
implementation}
private Connection getConnection() { //
override
implementation}
}
How
Does It Do It?
A BMP bean manages its own persistence by including database
access
logic in it's versions of
the ejbLoad( ) and ejbStore( ) methods.
These
methods are called on the
bean when the EJB server 'thinks'
it is appropriate to read or write data.
The ejbLoad() method is usually
invoked by the container at the
beginning
of a transaction, just
before
the container delegates a business method to the bean. The code
below shows how to
implement the ejbLoad( ) method using JDBC.
ejbLoad(
) method overridden to provide Bean Managed Persistence via JDBC
// condensed from an example
in EJB Fundamentals
by
R. Monson-Haefel
public void ejbLoad( ) {
Connection con;
try {
Integer primaryKey = (Integer)ejbContext.getPrimaryKey();
con = this.getConnection();
Statement sqlStmt = con.createStatement("SELECT * FROM Customer " +
" WHERE customerID = " + primaryKey.intValue());
ResultSet results = sqlStmt.executeQuery();
if (results.next( )) {
// get the name information from
the customer
table
myName = new Name();
myName.first = results.getString("FIRST_NAME");
myName.last = results.getString("LAST_NAME");
myName.middle = results.getString("MIDDLE_NAME");
// the address and credit card info are left out for the sake of
brevity
}
}
catch (SQLException sqle) {throw new EJBException(sqle); }
finally { if (con!=null) con.close( );}
}
}
In the ejbLoad() method, use the ejbContext( ) reference to the
bean's
EntityContext to obtain
the instance's primary key. This ensures that you
use the correct index
to the database. Tthe CustomerBean_BMP will
need to use the inherited setEntityContext()
and unsetEntityContext( )
methods as illustrated earlier.
ejbStore( ) overridden to
provide
Bean
Managed Persistence with JDBC
// condensed from an
example
in EJB Fundamentals by R. Monson-Haefel
public void ejbStore() {
Connection con;
try { Integer primaryKey = (Integer)ejbContext.getPrimaryKey( );
con = this.getConnection();
PreparedStatement sqlPrep = con.prepareStatement("UPDATE customer set "
+
"last_name = ?, first_name = ?, middle_name = ?, " +
"street = ?, city = ?, state = ?, zip = ?, " +
"card_number = ?, card_date = ?, " +
"card_name = ?, card_name = ?, " +
"WHERE id = ?"
);
sqlPrep.setString(1,myName.last);
sqlPrep.setString(2,myName.first);
sqlPrep.setString(3,myName.middle);
sqlPrep.setString(4,myAddress.street);
sqlPrep.setString(5,myAddress.city);
sqlPrep.setString(6,myAddress.state);
sqlPrep.setString(7,myAddress.zip);
sqlPrep.setInt(8, myCreditCard.number);
sqlPrep.setString(9, myCreditCard.expDate);
sqlPrep.setString(10, myCreditCard.type);
sqlPrep.setString(11, myCreditCard.name);
sqlPrep.setInt(12,primaryKey.intValue());
sqlPrep.executeUpdate();
}
catch (SQLException sqle) {throw new EJBException(sqle); }
finally { if (con!=null) con.close( ); }
}
}
The getConnection( ) Method
The getConnection( )
method is not a standard EJB method.
It is a private helper method
implemented to conceal
the mechanics
of obtaining a database connection. Below is the
definition
of the
getConnection( )
method.
The getConnection( ) Method
Code
private Connection getConnection( ) throws
SQLException
{
InitialContext
jndiContext
= new InitialContext( );
DataSource source =
(DataSource)
jndiContext.lookup("java:comp/env/jdbc/myDatabase");
return
source.getConnection();
}
}
Note this uses the JNDI lookup syntax rather than the typical
JDBC equivalent code. Database connections are obtained
from the container using a default
JNDI context called
the JNDI
Environment Naming Context (ENC). The ENC provides access
to transactional,
pooled JDBC connections through the standard
connection factory, the
javax.sql.DataSource type.
Inserting and Removing Entities in the
Database
The ejbCreate( ) and ejbRemove( ) methods
are implemented with
similar database access logic. The ejbCreate(
) methods inserts
a new record into the database and the ejbRemove( )
methods
delete the entities data from the database, using SQL INSERT
and DELETE commands.
ejbCreate( ) & ejbRemove
( ) BMP Examples
// from EJB
Fundamentals
by R. Monson-Haefel
public void ejbCreate(Integer id)
{
this.customerID = id.intValue();
Connection con;
try {
con = this.getConnection();
Statement sqlStmt =
con.createStatement("INSERT INTO customer id VALUES (" + customerID +
")");
sqlStmt.executeUpdate();
return id;
}
catch(SQLException sqle) {throw new
EJBException(sqle);
}
finally { if (con!=null) con.close(); }
}
public void
ejbRemove(
) {
Integer primaryKey = (Integer)ejbContext.getPrimaryKey( );
Connection con;
try {
con = this.getConnection();
Statement sqlStmt = con.createStatement
("DELETE FROM customer WHERE id = " primaryKey.intValue( ));
sqlStmt.executeUpdate();
}
catch(SQLException sqle) {throw new EJBException(sqle); }
finally { if (con!=null) con.close( ); }
}
In BMP, the bean class is responsible for implementing the find
methods
defined in the home
interface. For each find method
defined in the home interface there
must be corresponding
ejbFind( ) method in the bean class. The ejbFind( ) methods
locate
the appropriate bean
records in the database and return
their primary keys to the container.
The container converts
the primary keys into bean references and returns them to the client.
BMP Find Method Example
// from
EJB
Fundamentals
by R. Monson-Haefel
public Integer ejbFindByPrimaryKey(
Integer
primaryKey)
throws ObjectNotFoundException {
Connection con;
try {
con = this.getConnection();
Statement sqlStmt =con.createStatement("SELECT * FROM Customer " +
" WHERE customerID = " + primaryKey.intValue( ));
ResultSet results = sqlStmt.executeQuery();
if (results.next())
return primaryKey;
else
throw ObjectNotFoundException();
}
catch (SQLException sqle) { throw new EJBException(sqle); }
finally { if (con!=null) con.close( ); }
}
// Single-entity find methods return a single primary key or throw the ObjectNotFoundException
public Enumeration
ejbFindByZipCode(
int zipCode) {
Connection con;
try {
con = this.getConnection();
Statement sqlStmt =
con.createStatement("SELECT id FROM Customer " +
" WHERE zip = " +zipCode);
ResultSet results = sqlStmt.executeQuery( );
Vector keys = new Vector();
while(results.next()){
int id = results.getInt("id");
keys.addElement(new Integer(id));
}
return keys.elements( );
}
catch (SQLException sqle) { throw new EJBException(sqle); }
finally { if (con!=null) con.close(); }
}
// If no matching bean records are found, an empty collection is returned
Session Type Enterprise Beans
Session beans manage the interactions of entity and
other
session
beans,
access resources, and generally perform tasks on behalf of
the client.
Session beans
are not persistent business objects and
do not
represent data in
the database. Session beans correspond
to the controller in a
model-view-controller
architecture because they
encapsulate the business logic of a
three-tier
architecture.
Two kinds of session bean:
Stateless Session Beans
Stateless session beans represent business
processes
or tasks that are performed on behalf of the client using
them.
Stateless Session
Bean Interfaces
// from EJB
Fundamentals by R. Monson-Haefel
// remote interface
public interface
CreditService
extends javax.ejb.EJBObject {
public void verify(CreditCard card, double amount)
throws RemoteException, CreditServiceException;
public void charge(CreditCard card, double amount)
throws RemoteException, CreditServiceException;
}
// home interface
public interface
CreditServiceHome
extends java.ejb.EJBHome {
public CreditService create( )
throws RemoteException, CreateException;
}
Stateless
Session Beans Required No-args Create( ) Method
All home
interfaces for stateless session
beans will
define
just one method, a
no-argument
create( ) method. Session
beans
do not have find methods and they cannot be initiated
with any
arguments
when they are created.
Every client
that uses the same
type of session bean gets the
same
service.
CreditService
Stateless Session Bean Sample
// from
EJB
Fundamentals by R. Monson-Haefel
import javax.ejb.SessionBean;
public class
CreditServiceBean implements
SessionBean {
URL acmeURL;
HttpURLConnection acmeCon;
public void ejbCreate( ) {
try {
InitialContext jndiContext = new InitialContext( );
URL acmeURL = (URL) jndiContext.lookup("java:comp/ejb/env/url/acme");
acmeCon = acmeURL.openConnection( );
}
catch (Exception e) { throws new EJBException(e); }
}
public void
verify(CreditCard card,
double amount)
throws CreditCardException {
String response = post("verify:" + card.postString( ) + ":" + amount);
if (response.substring("approved") == -1 )
throw new CreditServiceException("denied");
}
public void
charge(CreditCard card,
double amount)
throws CreditCardException {
String response = post("charge:" + card.postString( ) + ":" + amount);
if (response.substring("approved")== -1)
throw new CreditServiceException("denied");
}
private String post(String
request)
{
try {
acmeCon.connect();
acmeCon.setRequestMethod("POST "+request);
String response = ameCon.getResponseMessage( );
}
catch (IOException ioe) {
throw new EJBException(ioe);
}
}
public void ejbRemove( ) {
acmeCon.disconnect( );
}
public void
setSessionContext(SessionContext
cntx) {}
public void ejbActivate() {}
public void ejbPassivate() {}
}
This bean encapsulates accesses the Acme secure web
server and posts requests to validate
or charge the
customer's credit card. This example demonstrates the
stateless bean
can represent a collection
of independent
but related services.
Benefits of Connecting Through the Bean
By
having the client connecting
to the CreditService bean
instead of a connecting directly to
the service the EJB
container is
able to pool connections, manage transactions
and provide security
automatically for the client.
In the example above,
the CreditServiceBean
uses the
ejbCreate( ) method to obtain a reference to the
HttpURLConnection
factory,
which it will use throughout
its lifetime to obtain connections to
the Acme web server.
The CreditServiceBean uses the JNDI ENC to obtain a
URL connection
factory
in the
same way that the
CustomerBean used the JNDI ENC to obtain a
DataSource
resource
factory for JDBC connections.
Stateful
Session Beans
Stateful session beans are dedicated to one client
and maintain 'conversational-state'
between method
invocations. Unlike stateless session beans, clients
do
not share
stateful beans.
A stateful bean, is dedicated
to service only one client allowing business
state to be
shared by methods in the same stateful bean.
As an example, the HotelClerk bean can be modified
to be a stateful
bean which can
maintain conversational
state between method invocations. This would
be useful,
for
example, to allow the HotelClerk bean to take many
reservations,
but
then process them together under one
credit card.
HotelClerkBean
Modified to a Stateful Session Bean
// from EJB
Fundamentals by R. Monson-Haefel
import javax.ejb.SessionBean;
import javax.naming.InitialContext;
public class HotelClerkBean implements SessionBean {
InitialContext
jndiContext;
//conversational-state
Customer cust;
Vector
resVector
= new Vector( );
public
void ejbCreate(Customer
customer) { }
cust
= customer;
}
public void addReservation(Name
name, RoomInfo
ri, Date from, Date to) {
ReservationInfo
resInfo = new ReservationInfo(name, ri , from, to);
resVector.addElement(resInfo);
}
public void reserveRooms() {
CreditCard
card = cust.getCreditCard( );
Enumeration
resEnum = resVector.elements( );
while (resEnum.hasMoreElements( )) {
ReservationInfo resInfo = (ReservationInfo) resEnum.nextElement( );
RoomHome roomHome = (RoomHome)
getHome("java:comp/env/ejb/RoomEJB", RoomHome.class);
Room room = roomHome.findByPrimaryKey(resInfo.roomInfo.getID( ) );
double amount = room.getPrice(resInfo.from,restInfo.to);
CreditServiceHome creditHome = (CreditServiceHome)getHome
("java:comp/env/ejb/CreditServiceEJB", CreditServiceHome.class);
CreditService creditAgent = creditHome.create( );
creditAgent.verify(card, amount);
ReservationHome resHome = (ReservationHome)getHome
("java:comp/env/ejb/ReservationEJB", ReservationHome.class);
Reservation reservation = resHome.create
(cresInfo.getName( ), resInfo.roomInfo,resInfo.from,resInfo.to);
}
public RoomInfo[] availableRooms(Location loc, Date from, Date to){
// Make an SQL call to find available rooms
}
private Object getHome(String path, Class type) {
Object ref = jndiContext.lookup(path);
return PortableRemoteObject.narrow(ref,type);
}
}
In the stateful version of the HotelClerkBean class,
the
conversational
state is
maintained in the
Customer
reference which
is obtained
when the bean is created and
also in the Vector of
ReservationInfo
objects. The bean keeps track of the
reservations
and processes
them in a
batch when the serverRooms( ) method is invoked.
Stateful
Session Beans Usually Are Passivated Using Serialization
Stateful session
beans
may be passivated when they
are not
in use.
In stateful beans,
passivation means the
bean's
conversational-state
is written to
a secondary
storage (often disk)
and the instance is
removed from memory. The client's
reference to the bean is not affected
by passivation, it remains alive
and usable while the
bean is passivated.
When the client invokes a method on a passivated
bean , the
container
reactivates the bean by
instantiating
a new instance and restores
it's
conversational-state
with data that had been
written to secondary
storage. This
passivation/activation process is often accomplished using
simple Java
serialization.
Stateful session beans, unlike stateless beans, do use the
ejbActivate(
)
and ejbPassivate( ). Developers should use these method to close open
resources and to do
other clean-up
before the instance's state is written
to secondary storage and evicted
from memory.
// The ejbRemove( ) method should do the
same kind of clean-up
// performed in the ejbPassivate()
method.
Deploying
Enterprise JavaBeans
The EJB specification describes a declarative mechanism for
how the
container handles
persistence, transactions, concurrency,
and access control enterprise
beans called the
XML deployment
descriptor.
When
a bean is deployed into a container, the container
reads
the deployment descriptor to find out how these functions should
be handled.
The person deploying the bean will use this information
and specify additional
information to hook the bean up to these
facilities at runtime.
A deployment descriptor has a predefined format that all EJB
compliant
beans must use and
all EJB compliant servers must
know how to read it. This format is
specified
in an XML
Document
Type Definition, or DTD.
The deployment descriptor describes the type of
bean (session or
entity) and the classes used for the remote, home,
and bean class.
It also
specifies the transactional attributes of every method in the
bean,
which security roles can
access each method (access control),
and whether persistence in the
entity beans is handled
automatically
or is performed by the bean.
Example of an
XML Deployment
Descriptor for
the Customer Bean
// from EJB
Fundamentals by R. Monson-Haefel
<?xml version="1.0"?>
<!DOCTYPE ejb-jar
PUBLIC "- // Sun Microsystems,
Inc.
DTD Enterprise
JavaBeans 1.1//EN"
"http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd">
<ejb-jar>
<enterprise-beans>
<entity>
<description> This bean represents
a customer </description>
<ejb-name> CustomerBean </ejb-name>
<home> CustomerHome </home>
<remote> Customer </remote>
<ejb-class> CustomerBean </ejb-class>
<persistence-type> Container </persistence-type>
<prim-key-class> Integer </prim-key-class>
<reentrant> False </reentrant>
<cmp-field><field-name> myAddress
</field-name></cmp-field>
<cmp-field><field-name> myName
</field-name></cmp-field>
<cmp-field><field-name> myCreditCard
</field-name></cmp-field>
</entity>
</enterprise-beans>
<assembly-descriptor>
<security-role>
<description>
The role of who is allowed full
access to
the Customer bean.
</description>
<role-name> everyone </role-name>
</security-role>
<method-permission>
<role-name> everyone
</role-name>
<method>
<ejb-name> CustomerBean </ejb-name>
<method-name> * </method-name>
</method>
</method-permission>
<container-transaction>
<description> All methods require a
transaction
</description>
<method>
<ejb-name>CustomerBean</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
EJB-capable application servers usually provide tools to build
deployment
descriptors,
greatly simplifying the process. When a
bean
is deployed, its
remote,
home, and
bean
class files and
the
XML
deployment descriptor must
be packaged into a JAR file.
META-INF/ejb-jar.xml
The
deployment
descriptor must be
stored
under the META-INF
directory with the name, ejb-jar.xml.
This JAR file, called an ejb-jar,
is vendor
neutral and can be deployed in any EJB compliant
container. // EAR archive file
When a bean is deployed
in an EJB container
it's XML deployment
descriptor is read from the JAR to determine how
to manage the
bean
at runtime. Deployment descriptor attributes are mapped to
the container's environment, including security aspects,
adding
the bean to the EJB naming system etc. Successful deployment
makes the bean available to clients.
Enterprise JavaBeans clients may be standalone applications,
servlets,
applets, or even
other enterprise beans. For instance,
the HotelClerk session bean shown
above was a
client of the
Room entity beans.
All clients use the server bean's
home interface to obtain
references
to the server bean. This reference has the server bean's
remote
interface
datatype, therefore the client interacts with the server
bean solely
through the methods
defined in the remote interface.
The remote interface defines the business methods, such as
accessor
and mutator methods
for changing a customer's name,
or business methods that perform tasks
like using the
HotelClerk
bean to reserve a room at a hotel.
Below is an example of
how a Customer
bean might be accessed
from a client application. In this case the
home interface is
CustomerHome and the remote interface is Customer.
Example
//
from EJB
Fundamentals by R. Monson-Haefel
CustomerHome home;
Object ref;
//
Obtain a reference to the CustomerHome
ref =
jndiContext.lookup("java:comp/env/ejb/Customer");
// Cast object returned by the JNDI lookup to the appropriate datatype
home =
PortableRemoteObject.narrow(ref,
CustomerHome.class);
// Use the home interface to create a new instance of the Customer bean.
Customer customer =
home.create(customerID);
// Use a business method on the Customer.
customer.setName(someName);
A client first obtains a reference to the home interface by using
JNDI
ENC to lookup the server
beans. In EJB 1.1*, Java
RMI-IIOP
is the specified programming model.
As a consequence, all
CORBA references types must be supported. CORBA references
cannot be
cast using Java
native casting.
Instead the PortableRemoteObject.narrow( ) method must
be used
to explicitly
narrow a reference from one type to its subtype. Since
JNDI always
returns an Object type, all
bean references should be
explicitly narrowed to support portability
between containers.
After the home interface is obtained, the client uses the methods
defined
on the home interface
to create, find, or remove the server
bean. Invoking one of the
create()
methods on the home
interface
returns the client a remote reference to the server bean,
which the
client uses to complete it's tasks.
// * need an update for EJB 2.x
Assignment