Enterprise JavaBeans
Enterprise JavaBeans
Suppose you need to develop a multi-tiered application
to view and update records in a database through a Web interface. You can write
a database application using JDBC, a Web interface using JSP/servlets, and a
distributed system using CORBA/RMI. But what extra considerations must you make
when developing a distributed object system rather than just knowing
API’s? Here are the issues:
Performance: The distributed objects you
create must perform well, as they could potentially service many clients at a
time. You’ll need to use optimization techniques such as caching as well
as pooling resources like database connections. You’ll also have to manage
the lifecycle of your distributed objects.
Scalability: The distributed objects
must also be scalable. Scalability in a distributed application means that the
number of instances of your distributed objects can be increased and moved onto
additional machines without modifying any code.
Security: A distributed object must
often manage the authorization of the clients that access it. Ideally, you can
add new users and roles to it without recompilation.
Distributed Transactions: A distributed
object should be able to reference distributed transactions transparently. For
example, if you are working with two separated databases, you should be able to
update them simultaneously within the same transaction and roll them both back
if a certain criteria is not met.
Reusability: The ideal distributed
object can be effortlessly moved onto another vendors’ application server.
It would be nice if you could resell a distributed object component without
making special modifications, or buy someone else’s component and use it
without having to recompile or rewrite it.
Availability: If one of the machines in
the system goes down, clients should automatically fail-over to backup copies of
the objects running on other machines.
These considerations, in addition the business problem
that you set out to solve, can make for a daunting development project. However,
all the issues except for your business problem are
redundant—solutions must be reinvented for every distributed business
application.
Sun, along with other leading distributed object
vendors, realized that sooner or later every development team would be
reinventing these particular solutions, so they created the Enterprise JavaBeans
specification (EJB). EJB describes a server-side component model that tackles
all of the considerations mentioned above using a standard approach that allows
developers to create business components called EJBs that are isolated from
low-level “plumbing” code and that focus solely on providing
business logic. Because EJB’s are defined in a standard way, they can
vendor independent.
JavaBeans vs. EJBs
Because of the similarity in names, there is much
confusion about the relationship between the JavaBeans component model and the
Enterprise JavaBeans specification. While both the JavaBeans and Enterprise
JavaBeans specifications share the same objectives in promoting reuse and
portability of Java code between development and deployment tools with the use
of standard design patterns, the motives behind each specification are geared to
solve different problems.
The standards defined in the JavaBeans component model
are designed for creating reusable components that are typically used in IDE
development tools and are commonly, although not exclusively, visual components.
The Enterprise JavaBeans specification defines a
component model for developing server side java code. Because EJBs can
potentially run on many different server-side platforms—including
mainframes that do not have visual displays—An EJB cannot make use of
graphical libraries such as AWT or Swin
The EJB specification
The Enterprise JavaBeans specification describes a
server-side component model. It defines six roles that are used to perform the
tasks in development and deployment as well as defining the components of the
system. These roles are used in the development, deployment and running of a
distributed system. Vendors, administrators and developers play the various
roles, to allow the partitioning of technical and domain knowledge. The vendor
provides a technically sound framework and the developers create domain-specific
components; for example, an “accounting” component. The same party
can perform one or many roles. The roles defined in the EJB specification are
summarized in the following table:
|
Role
|
Responsibility
|
|
Enterprise Bean Provider
|
The developer responsible for creating reusable EJB
components. These components are packaged into a special jar file (ejb-jar
file).
|
|
Application Assembler
|
Creates and assembles applications from a collection
of ejb-jar files. This includes writing applications that utilize the collection
of EJBs (e.g., servlets, JSP, Swing etc. etc.).
|
|
Deployer
|
Takes the collection of ejb-jar files from the
Assembler and/or Bean Provider and deploys them into a run-time environment: one
or more EJB Containers.
|
|
EJB Container/Server Provider
|
Provides a run-time environment and tools that are
used to deploy, administer, and run EJB components.
|
|
System Administrator
|
Manages the different components and services so that
they are configured and they interact correctly, as well as ensuring that the
system is up and running.
|
EJB components
EJB components are elements of reusable business logic
that adhere to strict standards and design patterns as defined in the EJB
specification. This allows the components to be portable. It also allows other
services—such as security, caching and distributed transactions—to
be performed on behalf of the components. An Enterprise Bean Provider is
responsible for developing EJB components.
EJB Container & Server
The EJB Container is a run-time environment
that contains and runs EJB components and provides a set of standard services to
those components. The EJB Container’s responsibilities are tightly defined
by the specification to allow for vendor neutrality. The EJB container provides
the low-level “plumbing” of EJB, including distributed transactions,
security, lifecycle management of beans, caching, threading and session
management. The EJB Container Provider is responsible for providing an EJB
Container.
An EJB Server is defined as an Application
Server that contains and runs one or more EJB Containers. The EJB Server
Provider is responsible for providing an EJB Server. You can generally assume
that the EJB Container and EJB Server are the same.
Java Naming and Directory Interface
(JNDI)
Java Naming and Directory Interface (JNDI) is used in
Enterprise JavaBeans as the naming service for EJB Components on the network and
other container services such as transactions. JNDI maps very closely to other
naming and directory standards such as CORBA CosNaming and can actually be
implemeted as a wrapper on top of it.
Java Transaction API/Java Transaction Service
(JTA/JTS)
JTA/JTS is used in Enterprise JavaBeans as the
transactional API. An Enterprise Bean Provider can use the JTS to create
transaction code, although the EJB Container commonly implements transactions in
EJB on the EJB components’ behalf. The deployer can define the
transactional attributes of an EJB component at deployment time. The EJB
Container is responsible for handling the transaction whether it is local or
distributed. The JTS specification is the Java mapping to the CORBA OTS (Object
Transaction Service)
CORBA and RMI/IIOP
The EJB specification defines interoperability with
CORBA through compatibility with CORBA protocols. This is achieved by mapping
EJB services such as JTS and JNDI to corresponding CORBA services and the
implementation of RMI on top of the CORBA protocol IIOP.
Use of CORBA and RMI/IIOP in Enterprise JavaBeans is
implemented in the EJB Container and is the responsibility of the EJB Container
provider. Use of CORBA and RMI/IIOP in the EJB Container is hidden from the EJB
Component itself. This means that the Enterprise Bean Provider can write their
EJB Component and deploy it into any EJB Container without any regard of which
communication protocol is being used.
The pieces of an EJB component
An EJB consists of a number of pieces, including the
Bean itself, the implementation of some interfaces, and an information file.
Everything is packaged together into a special jar file.
Enterprise Bean
The Enterprise Bean is a Java class that the
Enterprise Bean Provider develops. It implements an Enterprise Bean interface
and provides the implementation of the business methods that the component is to
perform. The class does not implement any authorization, authentication,
multithreading, or transactional code.
Home interface
Every Enterprise Bean that is created must have an
associated Home interface. The Home interface is used as a factory for your EJB.
Clients use the Home interface to find an instance of your EJB or create a new
instance of your EJB.
Remote interface
The Remote interface is a Java Interface that reflects
the methods of your Enterprise Bean that you wish to expose to the outside
world. The Remote interface plays a similar role to a CORBA IDL
interface.
Deployment descriptor
The deployment descriptor is an XML file that contains
information about your EJB. Using XML allows the deployer to easily change
attributes about your EJB. The configurable attributes defined in the deployment
descriptor include:
- The Home and Remote interface names that are required
by your EJB
- The name to publish into JNDI for your EJBs Home
interface
- Transactional attributes for each method of your
EJB
- Access Control Lists for authentication
EJB-Jar file
The EJB-Jar file is a normal java jar file that
contains your EJB, Home and Remote interfaces, as well as the deployment
descriptor.
EJB operation
Once you have an EJB-Jar file containing the Bean, the
Home and Remote interfaces, and the deployment descriptor, you can fit all of
the pieces together and in the process understand why the Home and Remote
interfaces are needed and how the EJB Container uses them.
The EJB Container implements the Home and Remote
interfaces that are in the EJB-Jar file. As mentioned earlier, the Home
interface provides methods to create and find your EJB. This means that the EJB
Container is responsible for the lifecycle management of your EJB. This level of
indirection allows for optimizations to occur. For example, 5 clients might
simultaneously request the creation of an EJB through a Home Interface, and the
EJB Container would respond by creating only one EJB and sharing it between all
5 clients. This is achieved through the Remote Interface, which is also
implemented by the EJB Container. The implemented Remote object plays the role
of a proxy object to the EJB.
All calls to the EJB are ‘proxied’ through
the EJB Container via the Home and Remote interfaces. This indirection is the
reason why the EJB container can control security and transactional behavior.
Types of EJBs
The Enterprise JavaBeans specification defines
different types of EJBs that have different characteristics and behaviors. Two
categories of EJBs have been defined in the specification: Session Beans
and Entity Beans, and each categoriy has variations.
Session Beans
Session Beans are used to represent Use-Cases or
Workflow on behalf of a client. They represent operations on persistent data,
but not the persistent data itself. There are two types of Session Beans,
Stateless and Stateful. All Session Beans must implement the
javax.ejb.SessionBean interface. The EJB Container governs the life of a
Session Bean.
Stateless Session Beans are the simplest type
of EJB component to implement. They do not maintain any conversational state
with clients between method invocations so they are easily reusable on the
server side and because they can be cached, they scale well on demand. When
using Stateless Session Beans, all state must be stored outside of the
EJB.
Stateful Session Beans maintain state between
invocations. They have a one-to-one logical mapping to a client and can maintain
state within themselves. The EJB Container is responsible for pooling and
caching of Stateful Session Beans, which is achieved through Passivation
and Activation. If the EJB Container crashes, data for all Stateful
Session Beans could be lost. Some high-end EJB Containers provide recovery for
Stateful Session Beans.
Entity Beans
Entity Beans are components that represent persistent
data and behavior of this data. Entity Beans can be shared among multiple
clients, the same way that data in a database can be shared. The EJB Container
is responsible for caching Entity Beans and for maintaining the integrity of the
Entity Beans. The life of an Entity Bean outlives the EJB Container, so if an
EJB Container crashes, the Entity Bean is still expected to be available when
the EJB Container again becomes available.
There are two types of Entity Beans: those with
Container Managed persistence and those with Bean-Managed persistence.
Container Managed Persistence (CMP). A CMP
Entity Bean has its persistence implemented on its behalf by the EJB Container.
Through attributes specified in the deployment descriptor, the EJB Container
will map the Entity Bean’s attributes to some persistent store
(usually—but not always—a database). CMP reduces the development
time for the EJB, as well as dramatically reducing the amount of code
required.
Bean Managed Persistence (BMP). A BMP Entity
Bean has its persistence implemented by the Enterprise Bean Provider. The
Enterprise Bean Provider is responsible for implementing the logic required to
create a new EJB, update some attributes of the EJBS, delete an EJB and find an
EJB from persistent store. This usually involves writing JDBC code to interact
with a database or other persistent store. With BMP, the developer is in full
control of how the Entity Bean persistence is managed.
BMP also gives flexibility where a CMP implementation
may not be available. For example, if you wanted to create an EJB that wrapped
some code on an existing mainframe system, you could write your persistence
using CORBA.
Developing an EJB
As an example, the “Perfect Time” example
from the previous RMI section will be implemented as an EJB component. The
example will be a simple Stateless Session Bean.
As mentioned earlier, EJB components consist of at
least one class (the EJB) and two interfaces: the Remote and Home interfaces.
When you create a Remote interface for an EJB , you must follow these
guidelines:
- The remote interface must be public.
- The remote interface must extend the interface
javax.ejb.EJBObject.
- Each method in the remote interface must declare
java.rmi.RemoteException in its throws clause in addition to any
application-specific exceptions.
- Any object passed as an argument or return value
(either directly or embedded within a local object) must be a valid RMI-IIOP
data type (this includes other EJB
objects).
Here
is the simple remote
interface
for
the PerfectTime EJB:
//:
c15:ejb:PerfectTime.java
//#
You must install the J2EE Java Enterprise
//#
Edition from java.sun.com and add j2ee.jar
//#
to your CLASSPATH in order to compile
//#
this file. See details at java.sun.com.
//
Remote Interface of PerfectTimeBean
import
java.rmi.*;
import
javax.ejb.*;
public
interface
PerfectTime
extends
EJBObject {
public
long
getPerfectTime()
throws
RemoteException;
}
///:~
The Home interface is the factory where the component
will be created. It can define create methods, to create instances of
EJBs, or finder methods, which locate existing EJBs and are used for
Entity Beans only. When you create a Home interface for an EJB , you must follow
these guidelines:
- The Home interface must be public.
- The Home interface must extend the interface
javax.ejb.EJBHome.
- Each create method in the Home interface must
declare java.rmi.RemoteException in its throws clause as well as a
javax.ejb.CreateException.
- The return value of a create method must be a
Remote Interface.
- The return value of a finder method (Entity
Beans only) must be a Remote Interface or java.util.Enumeration or
java.util.Collection.
- Any object passed as an argument (either directly or
embedded within a local object) must be a valid RMI-IIOP data type (this
includes other EJB objects)
The
standard naming convention for Home interfaces is to take the Remote interface
name and append “Home” to the end. Here is the Home interface for
the PerfectTime EJB:
//:
c15:ejb:PerfectTimeHome.java
//
Home Interface of PerfectTimeBean.
import
java.rmi.*;
import
javax.ejb.*;
public
interface
PerfectTimeHome
extends
EJBHome {
public
PerfectTime create()
throws
CreateException, RemoteException;
}
///:~
You can now implement the business logic. When you
create your EJB implementation class, you must follow these guidelines, (note
that you should consult the EJB specification for a complete list of guidelines
when developing Enterprise JavaBeans):
- The class must be public.
- The class must implement an EJB interface (either
javax.ejb.SessionBean or javax.ejb.EntityBean).
- The class should define methods that map directly to
the methods in the Remote interface. Note that the class does not implement the
Remote interface; it mirrors the methods in the Remote interface but does
not throw java.rmi.RemoteException.
- Define one or more ejbCreate( ) methods to
initialize your EJB.
- The return value and arguments of all methods must be
valid RMI-IIOP data types.
//:
c15:ejb:PerfectTimeBean.java
//
Simple Stateless Session Bean
//
that returns current system time.
import
java.rmi.*;
import
javax.ejb.*;
public
class
PerfectTimeBean
implements
SessionBean {
private
SessionContext sessionContext;
//return current
time
public
long
getPerfectTime() {
return
System.currentTimeMillis();
}
// EJB
methods
public
void
ejbCreate()
throws
CreateException {}
public
void
ejbRemove() {}
public
void
ejbActivate() {}
public
void
ejbPassivate() {}
public
void
setSessionContext(SessionContext ctx) {
sessionContext = ctx;
}
}///:~
Because this is a simple example, the EJB methods
(ejbCreate( ), ejbRemove( ), ejbActivate( ),
ejbPassivate( ) ) are all empty. These methods are invoked by the
EJB Container and are used to control the state of the component. The
setSessionContext( ) method passes a javax.ejb.SessionContext
object which contains information about the component’s context, such as
the current transaction and security information.
After we have created the Enterprise JavaBean, we then
need to create a deployment descriptor. The deployment descriptor is an XML file
that describes the EJB component. The deployment descriptor should be stored in
a file called ejb-jar.xml.
//:!
c15:ejb:ejb-jar.xml
<?xml
version="1.0"
encoding="Cp1252"?>
<!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>
<description>Example
for
Chapter 15</description>
<display-name></display-name>
<small-icon></small-icon>
<large-icon></large-icon>
<enterprise-beans>
<session>
<ejb-name>PerfectTime</ejb-name>
<home>PerfectTimeHome</home>
<remote>PerfectTime</remote>
<ejb-class>PerfectTimeBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
<ejb-client-jar></ejb-client-jar>
</ejb-jar>
///:~
You can see the Component, the Remote interface and
the Home interface defined inside the <session> tag of this
deployment descriptor. Deployment descriptors may be automatically generated
using EJB development tools.
Along with the standard ejb-jar.xml deployment
descriptor, the EJB specification states that any vendor specific tags should be
stored in a separate file. This is to achieve high portability between
components and different brands of EJB containers.
The files must be archived inside a standard Java
Archive (JAR) file. The deployment descriptors should be placed inside the
/META-INF sub-directory of the Jar file.
Once the EJB component is defined in the deployment
descriptor, the deployer should then deploy the EJB component into the EJB
Container. At the time of this writing, the deployment process was quite
“GUI intensive” and specific to each individual EJB Container, so
this overview does not document that process. Every EJB Container, however will
have a documented process for deploying an EJB.
Because an EJB component is a distributed object, the
deployment process should also create some client stubs for calling the EJB
component. These classes should be placed on the classpath of the client
application. Because EJB components can be implemented on top of RMI-IIOP
(CORBA) or RMI-JRMP, the stubs generated could vary between EJB Containers;
nevertheless they are generated classes.
When a client program wishes to invoke an EJB, it must
look up the EJB component inside JNDI and obtain a reference to the home
interface of the EJB component. The Home interface is used to create an instance
of the EJB.
In this example the client program is a simple Java
program, but you should remember that it could just as easily be a servlet, a
JSP or even a CORBA or RMI distributed object.
//:
c15:ejb:PerfectTimeClient.java
//
Client program for PerfectTimeBean
public
class
PerfectTimeClient {
public
static
void
main(String[] args)
throws
Exception {
// Get a JNDI context using
// the JNDI Naming
service:
javax.naming.Context context =
new
javax.naming.InitialContext();
// Look up the home interface in
the
// JNDI Naming
service:
Object ref =
context.lookup("perfectTime");
// Cast the remote object to the
home interface:
PerfectTimeHome home = (PerfectTimeHome)
javax.rmi.PortableRemoteObject.narrow(
ref,
PerfectTimeHome.class);
// Create a remote object from the
home interface:
PerfectTime pt = home.create();
// Invoke
getPerfectTime()
System.out.println(
"Perfect Time EJB invoked, time is:
" +
pt.getPerfectTime() );
}
}
///:~
The sequence of the example is explained in the
comments. Note the use of the narrow( ) method to perform a kind of
casting of the object before a Java cast is performed. This is very similar to
what happens in CORBA. Also note that the Home object becomes a factory for
PerfectTime objects
|