Executing
the transaction management and security policies

Figure 5.3 Enterprise
beans and containers.
There can be
any number of different enterprise beans installed in a container.
Besides the installation and execution of an enterprise bean, a container
also provides tools for the deployment of an enterprise bean, which
are explained in the section,"EJB Deployment," later in the chapter.
The interfaces to a container are currently not standardized in the
EJB specification.
Identifying an Enterprise Bean
A client always
invokes methods on an enterprise bean’s remote interface. This interface
is referenced by a portable remote
object reference. Additionally,
an enterprise bean can be referenced by a handle,
which is a more abstract identifier.
Portable Remote Object Reference
The portable
remote object reference identifies remote interfaces and the home
interfaces of enterprise beans. It is an opaque data type that contains
all the necessary information to enable a client to make invocation
on the interfaces. Similar to a CORBA Interoperable Object Reference
(IOR), it comprises address information such as IP addresses, port
numbers, and information about the type and the instance of the referenced
object.
Handle
A handle is
a more abstract way of referencing a remote object to an enterprise
bean. It is defined by the following interface that must be implemented
by all enterprise bean handles.
public interface javax.ejb.Handle
{
public abstract EJBObject getEJBObject()
throws java.rmi.RemoteException;
}
It can be used
to persistently store a reference to an EJB object by serializing
an instance of the class that implements the handle interface, which
is similar to a stringified CORBA IOR. Therefore, implementations
of the interface must also implement the interface java.io.Serializeable.
Handle implementations are typically provided by a container.
Container Implementation
The EJB specification
neither defines the interface to a container nor does it prescribe
the technologies used to implement a container. In this section we
give an example of an EJB container based on CORBA technology and
discuss the concept of a specialized container. The main purpose of
this is to illustrate how the magic functionality provided by the
container can be implemented.
CORBA Container Implementation
A CORBA-based
implementation of an EJB container is typically based on the following
components (summarized in Table 5.1): CORBA core and IIOP as the communication
infrastructure, OTS for the transaction management, a JNDI wrapper
of the CORBA Naming Service, and the CORBA Security Service. Figure
5.4 shows how these CORBA components fit into the EJB container picture.
Table 5.1 EJB
Server with CORBA Technology
issue CORBA Technology
Communication protocol RMI over
IIOP (including OTS transaction id in the service context)
JNDI JNDI over CORBA Naming Service
Transactions CORBA Object Transaction
Service
EJB activation and deactivation POA
Security IIOP over SSL

Figure 5.4 CORBA
container.
A CORBA-based
container implementation is a native Java implementation using OMG’s
Java-to-IDL mapping. Besides the Java platform features, availability,
and portability, CORBA provides one major additional advantage. The
interoperability between enterprise beans is guaranteed by OMG’s industry
standards: IIOP and standardized IDL interfaces for underlying services,
specifically OTS.
Furthermore,
CORBA has been designed and proven to be a very efficient mechanism
for integrating legacy systems into new applications. Object Transaction
Service’s compliance with the X/A allows for seamlessly accessing
heterogeneous resources and integration with legacy systems.
CORBA implementations
have also proven the scalability and robustness expected for enterprise
systems. CORBA is deployed today in critical business applications
in intranet, extranet, and Internet environments as well as backend
systems. These deployments are across all major industry sectors,
including finance, telecommunications, and healthcare.
The examples
of enterprise beans we present in this book have been deployed in
a CORBA-based container, the Inprise EJB Server. This EJB server is
based on Visibroker for Java, Visibroker Naming, Visibroker ITS, and
Visibroker SSL. JBuilder integrates tool supports for development,
deployment, and debugging aspects. Inprise AppCenter provides the
management of an EJB application deployed in the Inprise container.
Specialized Container Implementation
A specialized
container is an EJB wrapper around a legacy system—the container and
the enterprise beans installed become a single entity. Arbitrary enterprise
beans may not necessarily be installable into such a container. The
container would, however, expose the functionality of the legacy system
through EJB home and remote interfaces.
An enterprise
bean is a proxy for the program, which is executed by a legacy system.
For example, the only enterprise beans installed in this container
can be proxies, which communicate, for example, via CICS to a mainframe
program. Instead of having independent enterprise bean providers,
the vendor, who provides the container, would also provide tools to
automatically generate enterprise beans, which interface with the
backend system. Specialized containers require a standard protocol
for communication and transaction control. Otherwise the use would
be quite limited.
Figure 5.5
shows a possible scenario of a specialized container. It depicts the
different components used by a container front-ending CICS. The remote
access to the enterprise bean would be via RMI (over IIOP). The bean
is registered with JNDI, using any JNDI implementation. The security
mechanisms and the transaction management are tightly integrated with
the backend systems. Mainframes have sophisticated access control
mechanisms, which would be wrapped with Java Security APIs. Transactions
could be managed by CICS, or via integration with JTS.

Figure 5.5 Specialized
container implementation.
EJB Development
The enterprise
bean provider is concerned with a number of tasks:
Defining
the Remote Interface
Defining
the Home Interface
Defining
the Primary Key class (for entity beans)
Implementing
the enterprise bean; that is, the functionality defined in the remote
and home interface
Figure 5.6
illustrates the relationships among the remote interface, the home
interface, the implementation of the enterprise bean, and other components
supplied by the infrastructure.

Figure 5.6 Components
of an enterprise bean.
The EJB provider
defines the remote and the home interface and implements the enterprise
bean. Every enterprise bean must have a home interface, which always
provides methods to locate or to create instances of the remote interface.
The remote interface specifies methods according to the application
logic encapsulated by the enterprise bean.
An EJB object
is an implementation of functionality declared in the enterprise bean’s
home and remote interface. The remote and home interface and the bean
implementation are in no formal (inheritance or implementation) relationship;
however, their methods must match according to a number of rules and
naming conventions defined by the EJB spec. Application-specific methods
must have the same signatures. The container provides the glue between
the home and remote interface and the enterprise bean implementation
class where the bean provider defines the semantics of the enterprise
bean. The container ensures, at compile time or runtime, that the
remote interface and the enterprise bean implementation match.
Figure 5.6
only shows the relationships between interfaces and classes. The glue
between the home and remote interface and the enterprise bean implementation
is not specified by the EJB specification, and its realization is
left to the container provider.
Remote Interface
An enterprise
bean has a remote interface that defines the application-specific
operations. Clients to the enterprise bean access it through its remote
interface.
An enterprise
bean’s remote interface is a public Java interface extending the interface
javax.ejb.EJBObject,
which is the base interface of all remote interfaces.
package javax.ejb;
public interface EJBObject extends
java.rmi.Remote {
EJBHome getEJBHome() throws java.rmi.RemoteException;
Object getPrimaryKey() throws java.rmi.RemoteException;
void remove() throws java.rmi.RemoteException,
RemoveException;
Handle getHandle() throws java.rmi.RemoteException;
boolean isIdentical (EJBObject p0)
throws java.rmi.RemoteException;
}
The method getEJBHome()
allows you to get the associated home interface. For entity beans,
you can get the primary key of the entity bean using the method getPrimaryKey().
The remove()
method deletes the enterprise bean; we explain details in the context
of the life cycle of the various types of enterprise beans. The method
getHandle
returns the handle, as explained earlier, of the enterprise bean,
and the isIdentical()
allows you to compare enterprise beans.
The remote interface
defines public methods, which can be invoked by clients. All methods
must throw the exception java.rmi.RemoteException.
We already
defined a remote interface for the ATM session bean in Chapter 1,
"Quick Start to EJB, JTS, and OTS." The parts of the following
interface definition that are in bold type are required by the EJB
specification.
package vogel.transaction.chapter1.ejb;
public interface
Atm extends javax.ejb.EJBObject
{
public
void transfer(
String source, String target, float
amount )
throws
java.rmi.RemoteException, InsufficientFundsException;
}
The definition
of the remote interface of session and entity beans follows the same
rules.
Home Interface
An enterprise
bean’s home interface controls the life cycle of a bean. It provides
operations to create, find, and remove an EJB object; that is, an
instance of enterprise bean. Session and entity beans have different
life cycles due to their respective design patterns. Consequently,
the types of their home interfaces must define different methods.
In either case,
the home interface defines the interface and the implementation is
provided by the container and by the enterprise bean implementation
class.
Home Base Interface
Every home interface
extends the interface javax.ejb.EJBHome.
This interface is defined as follows:
package javax.ejb;
public interface EJBHome extends
java.rmi.Remote {
void remove(Handle handle)
throws java.rmi.RemoteException, RemoveException;
void remove(Object primaryKey)
throws java.rmi.RemoteException,
RemoveException;
EJBMetaData getEJBMetaData()
throws RemoteException;
}
There are two
remove()
methods provided to remove EJB object instances. The first remove()
method removes an instance identified by a handle, the second one
by a primary key. A handle is a unique EJB object identifier, which
we introduced earlier. A handle has the same lifetime as the EJB object
it is referencing. In the case of an entity object, a handle can be
valid for multiple instantiations of an EJB object; for example, it
is valid even if a server crashed that hosted the EJB object, or when
the EJB object moves between different servers and machines. A serialized
handle is a concept very similar to a stringified CORBA object reference.
The other remove()
operation on the EJBHome
interface uses a primary key to determine the object to be removed.
A primary key can be of any Java type extending the Java Object
class and must implement the Java Serializable
interface. Primary keys are the main means of identification for entity
beans. A primary key is typically a key in a database table which
uniquely defines the data, represented by the entity object.
The method getEJBMetaData()
returns metadata of the EJB object. The metadata type is defined by
a Java interface, which provides methods to obtain the EJBHome
interface, the type (class) of the home and the remote interface,
as well as the type of the primary key. It also provides a method
to find out if the object hosted by this home interface is a session
or entity bean.
package javax.ejb;
public interface EJBMetaData {
EJBHome getEJBHome();
Class getHomeInterfaceClass();
Class getRemoteInterfaceClass();
Class getPrimaryKeyClass();
boolean isSession();
}
Session Bean Home
The session bean
pattern mandates that each client has an individual session bean instance.
The home interface becomes a session bean factory by defining one
or more create()
methods. The EJB specification defines a naming convention for these
create()
methods:
Return
type is the remote interface type
Method
name is always"create", parameters can vary
Throws
the exception java.rmi.RemoteException
Throws
javax.ejb.CreateException
Parameters
of the method are used to initialize the new EJB object
The following
example shows different create methods at a session home interface.
Required parts are shown in bold.
package vogel.transaction.chapter1.ejb;
public interface
AtmHome extends javax.ejb.EJBHome
{
Atm create()
throws
java.rmi.RemoteException, javax.ejb.CreateException;
Atm create(
Profile preferredProfile )
throws
java.rmi.RemoteException, javax.ejb.CreateException;
}
Note that session
home interface must not define finder methods to locate objects, as
stateful session beans are intended to be used only by their creator.
Entity Bean Home
Similar to
the session’s home interface, the entity home interface can provide
create methods following the same pattern. However, the primary means
of locating an entity bean is by using finder operations, since entity
beans are long lived and typically already exist and a client just
has to find the one it wants to invoke.
An entity home
interface has to provide the default finder method, which returns
an object of the bean’s remote interface type and takes primary key
as an argument. The type of the primary key can be of any Java type,
which extends the Java Object class. As part of the deployment description,
you tell the container the type of the primary key.
<entity
bean’s remote interface> findByPrimaryKey(
<primary
key type> key )
throws java.rmi.RemoteException,
FinderException;
The home interface
can define further find methods according to the following conventions:
Return
type is the remote interface type or a collection type, which has
the remote interface type as the content type
Method
always starts with the prefix"find"
Throws
the exception java.rmi.RemoteException
Typically
throws the exception javax.ejb.FinderException
Parameters
determine one or more EJB objects
Additionally,
an entity bean’s home interface can provide one or more create()
methods, which returns an object reference of the bean’s remote interface
type. The arguments are application specific.
The create
methods must comply with the following conventions:
Return
type is the remote interface type
Method
name is always"create", parameters can vary
Throws
the exception java.rmi.RemoteException
Throws
the exception javax.ejb.CreateException
The following
example shows different types of finder and create methods. Required
parts are shown in bold.
package vogel.transaction.chapter1.ejb;
public interface
AccountHome extends javax.ejb.EJBHome
{
Account create(
String accountId )
throws
java.rmi.RemoteException, javax.ejb.CreateException;
Account create(
String accountId, float initialBalance )
throws
java.rmi.RemoteException, javax.ejb.CreateException;
Account findByPrimaryKey(
String key )
throws
java.rmi.RemoteException, javax.ejb.FinderException;
Enumeration findBySocialSecurityNumber(
String socialSecurityNumber )
throws
java.rmi.RemoteException, javax.ejb.FinderException;
}
Bean Implementation
The bean implementation
is the principal work of a bean provider as it contains all of the
application-specific semantics. A bean class implements the SessionBean
or the EntityBean
interface depending on its type. These interfaces extend the enterprise
bean base interface EnterpriseBean.
Enterprise Bean Interface
The enterprise
bean interface just defines a common base interface; there are no
methods defined.
package javax.ejb;
public interface EnterpriseBean extends
Serializable {}
Session Bean
A session bean
implementation must implement the session bean interface and comply
with a number of rules and naming conventions that define the relationship
of this class to the remote and the home interface.
Session Bean Interface
The session bean
interface SessionBean
extends the interface EnterpriseBean.
The methods of the session bean interface are closely associated with
the life cycle of a session bean. To fully understand the semantics
of the methods, we first have to take a look at the life cycle of
session beans. During this process we introduce the methods of the
session bean interface. Note that a session implementation must also
implement methods corresponding to the home interface and to the remote
interface. The home interface’s create()
methods have corresponding ejbCreate()
methods in the implementation class.
The EJB specification
defines stateless
and stateful
session beans.
Stateless session
bean. The
bean does not contain conversational state between method invocations.
As a consequence, any session bean instance can be used for any
client.
Stateful session bean. The
bean contains conversational state that is kept across method invocations
and transactions. Once a client has obtained a specific session
bean, it must use this instance for the lifetime of the session.
A client can, however, have multiple, distinguished sessions and
hence multiple session bean instances.
Life Cycle of a Stateless Session
Bean
The life cycle
of a stateless session is pretty simple, as shown in Figure 5.7. A
new instance is created and the methods setSessionContext()
and ejbCreate()
are invoked on the session object. Now the new instance is in a pool
of instances, which are ready to be invoked. Since the stateless session
objects do not keep state, any of the instance can be used for any
of the incoming method invocations. When an instance is removed from
that pool, the method ejbRemove()
is invoked on the session object.

Figure 5.7 Life
cycle of a stateless session bean.
Note that the
creation and removal of session bean instances are not related to
the create()
and remove()
methods on the home/remote interface. The life cycle of stateless
session beans is governed by container policies.
Life Cycle of a Stateful Session Bean
Typically, the
life of a session bean starts when a client invokes a create()
method on the session bean’s home interface (see Figure 5.8). The
implementation of the home interface is provided by the container,
based on the session bean implementation class. The container creates
a new instance of the session bean, initializes it, and returns an
object reference back to the client. During this process, first the
method setSessionContext()
and then the method ejbCreate…()
are invoked on the session bean implementation (which implements the
session bean interface). The bean provider can use these methods to
initialize the session bean. Details of these methods are given later
in the chapter. The state of the session bean is now method
ready.

Figure 5.8 Life
cycle of a stateful session bean.
Once a client
invokes a remove()
method on the remote or home interface, the corresponding ejbRemove()
method is invoked on the EJB object, to allow the bean provider to
add application-specific cleanup code. Once the invocation is completed,
the object is in a nonexistent state again. When a client tries to
invoke a method on a session object in this state, the exception java.rmi.NoSuchObjectException
is thrown by the container.
The container
can deactivate the session bean instance, typically for resource management
reasons. For example, when a session object has not been used by a
client for a certain time, the container stores reference and state
of the session object on disk and frees the memory allocated by the
bean. However, files or other operating system controlled resources
that may have been opened by the session bean must be explicitly handled
in the implementation. This can be done in the ejbActivate()
and ejbPassivate()
methods, which are triggered by the container. The instance is typically
reactivated when the client makes the next call on the session bean’s
reference it holds. Then the container recreates the session object
in memory. CORBA’s object adapter provides similar activation and
deactivation mechanisms.
When a method
is invoked on the object in a transactional context, there are again
a number of interception points for the bean provider. They are defined
in the interface SessionSynchronization,
which is explained below. The use of this interface is optional. The
method afterBegin()
is invoked after a method is invoked on the remote interface. That
puts the EJB object in the state method
ready in TX. Once the object
is in this state it will accept the invocation of the method in this
transaction context and only this. The commit of the transaction triggers
the invocation of two methods, beforeCompletion()
and afterCompletion( true ),
on the EJB object. In the case of a rollback, the method afterCompletion(
false ) is invoked on the
session object. As a result of the commit or rollback, the session
object will be back in the method
ready state.
A session bean’s
lifetime is bound to the lifetime of the container in which it is
installed. Once the process, or JVM, in which the container is executed
terminates, the session bean’s reference usually becomes invalid.
Once a session bean was passivated and its state persisted, a smart
EJB server could re-activate the session bean in a different container.
SessionBean Interface
The session
bean interface defines the methods all session beans must implement.
package javax.ejb;
public interface SessionBean extends
EnterpriseBean {
void setSessionContext(SessionContext
sessionContext)
throws RemoteException;
void ejbRemove() throws RemoteException;
void ejbActivate() throws RemoteException;
void ejbPassivate() throws RemoteException;
}
The methods
have the following semantics.
setSessionContext()
sets a session context. The session context interface provides methods
to access runtime properties of the context in which a session runs.
Details about the session and EJB contexts are given later in the
chapter when we explain the deployment of an enterprise bean. This
method is typically implemented by storing the session context in
a private variable of the session bean implementation class. The
variable must be declared not-transient to retain its value of de-activation/re-activation
cycles.
ejbRemove()
notifies a session object
that it is about to be removed. Whenever the container removes a
session bean (for example, because a client invoked a remove operation
on the remote or home interface), it calls this operation on the
bean implementation.
ejbActivate()
notifies a session object that it has been activated.
ejbPassivate()
notifies a session object that it is about to be deactivated.
The activation
and deactivation notifications allow a sophisticated bean implementation
to manage resources, which they may control.
Optional Session Synchronization Interface
The interface
SessionSynchronization
provides methods for session beans to get notified in the event of
transaction synchronization. For example, implementing this interface
allows a bean to synchronize cached data with the database within
an ongoing transaction.
package javax.ejb;
public interface SessionSynchronization
{
void afterBegin() throws RemoteException;
void beforeCompletion() throws RemoteException;
void afterCompletion(boolean completionStatus)
throws RemoteException;
}
The three methods
are notifications from the container whenever a specific step in a
transaction is completed.
afterBegin()
notifies the session bean that a new transaction has begun. The
session bean is already in a transaction and any work that is executed
is within the scope of this transaction.
beforeCompletion()
notifies the session bean that a client has completed work on the
current transaction, but the resources involved have not yet been
committed. The bean should now update the database with its cached
values if there are any. If necessary, the session bean can force
a rollback of the current transaction by invoking the method setRollBackOnly()
on its session context.
afterCompletion()
notifies the session bean that the current transaction has been
completed. The parameter completionStatus
indicates the outcome of the transaction. The value true
means committed; false
means rollback.
Session Bean Implementation
A session bean
implementation must implement:
The
SessionBean
interface
Optionally,
the SessionSynchronization
interface
Methods
that correspond to the ones defined in the home interface
Methods
defined in the remote interface
We have already
explained the typical implementations of the methods of the SessionBean
interface. See Figure 5.9.

Figure 5.9 Relationships
of a session bean implementation.
The home interface
defines one or more create methods. For each of these methods we have
to declare and implement a corresponding create method. The naming
convention for the session bean’s create methods corresponds to the
one for the home interface. The create methods are called ejbCreate().
The parameters must match those defined in the home interface’s corresponding
create methods. This includes the exceptions. The return value is,
however, void
(and not remote interface specified in the home interface). The pattern
for ejbCreate()
method is
public void ejbCreate( <zero or
more parameters> )
throws RemoteException {
// implementation
}
Finally, we
have to implement methods corresponding to the ones defined in the
remote interface. Their signatures must exactly match those of the
methods defined in the remote interface. The type safety is ensured
by the container. There is no explicit"implements" relationship between
the remote interface and the EJB implementation class. The container
usually checks type safety at install time. Appropriate development
tools for EJB should ensure the type safety at development time.
The constructor
of the bean should not do anything; in fact, you can omit the constructor.
All of the initialization of the bean is done in the ejbCreate()
methods.
Finally, we
revisit the example from Chapter 1.
package vogel.transaction.chapter1.ejb;
import java.rmi.RemoteException;
public class AtmBean implements SessionBean,
Serializable{
private SessionContext sessionContext;
// application specific methods
public void transfer(
String sourceAccountId, String targetAccountId,
float amount )
throws RemoteException, InsufficientFundsException
{
//get the accounts
...
// now that I got the accounts I
make the transfer
sourceAccount.debit( amount );
targetAccount.credit( amount );
return;
}
ATMBean
has no constructor and we have omitted some parts of the implementation
of the transfer method. In the remainder of the code we provide a
trivial implementation of an ebjCreate()
method and the methods defined in the SessionBean
interface.
// implement methods defined in
SessionBean
public void ejbCreate() throws RemoteException
{
System.out.println("ATM: create");
}
public void setSessionContext( SessionContext
sessionContext ) {
System.out.println("ATM: set session
context");
this.sessionContext = sessionContext;
}
public void ejbRemove() {
System.out.println("ATM: remove");
}
public void ejbActivate() {
System.out.println("ATM: activate");
}
public void ejbPassivate() {
System.out.println("ATM: passivate");
}
Entity Beans
A entity bean
implementation must implement the entity bean interface and comply
with a number of rules and naming conventions that define the relationship
of this class to the remote and the home interface.
Life Cycle of an Entity Bean
The entity
bean interface defines methods, similar to the ones in the session
bean interface, which an entity bean must implement. To understand
the meaning of the methods we must look at the life cycle of an entity
bean, as shown in Figure 5.10.

Figure 5.10 Life
cycle of an entity bean.
An instance
of an entity bean is in one of the following three states:
Nonexistent. The
instance does not exist.
Pooled. The
instance exists but it is not associated with a particular EJB object
entity; that is, there is no specific data associated with the instance.
Ready. The
instance is associated with a particular EJB object entity; that
is, there is specific data associated with the instance.
An instance
of an entity bean is created by the container. The container sets
the context of the instance by invoking setEntityContext()
on the entity bean. The instance is now in the pooled state. All the
instances in the pool are equal since they are not associated with
any data. When and how many instances in a pool are created is up
to the container implementation.
When a client
invokes a finder method, the corresponding ejbFind()
method is executed on an arbitrary object in the pooled state.
An instance
moves from the pooled to the ready state when the container selects
this instance to service a client’s request on an entity object with
no such instance in the ready state and in the proper transaction
context. Then the container initializes the instance with the appropriate
identity by invoking ejbCreate()
and ejbPostCreate()
on the instance. This is caused by a client invoking a create method
on the home interface. An alternative way to move an instance from
the pooled to the ready state is by ejbActivate(),
which happens when an already existing object gets activated.
An entity object
is moved back to the pooled state by deactivating it. This means that
the instance is decoupled from the data represented by the entity.
This happens when the transaction is completed or when the entity
bean is removed; the method ejbPassivate()
or ejbRemove()
is invoked on the entity bean, respectively. When an unassociated
instance is removed from the pool the method unsetEntityContext()
is invoked on it.
When the instance
is in the ready state, it is associated with a specific entity object.
Clients can invoke the application-specific methods on the entity
bean and the bean loads and stores its data as appropriate using the
ejbLoad()
and ejbStore()
methods.
Entity Bean Interface
The session
bean interface is defined as shown next.
package javax.ejb;
public interface EntityBean extends
EnterpriseBean {
void setEntityContext(EntityContext
entityContext) throws RemoteException;
void unsetEntityContext() throws
RemoteException;
void ejbRemove() throws RemoteException,
RemoveException;
void ejbActivate() throws RemoteException;
void ejbPassivate() throws RemoteException;
void ejbLoad() throws RemoteException;
void ejbStore() throws RemoteException;
}
The methods
have the following semantics.
setEntityContext()
sets an entity context. The entity context interface provides methods
to access properties of the runtime context in which an entity bean
runs. Details about the entity and EJB contexts are given later
in the chapter when we explain the deployment of an enterprise bean.
This method is typically implemented by storing the session context
in a private variable of the session bean implementation class.
unsetEntityContext()
is called by the container before it terminates the life of the
current instance of the entity bean. The bean can free resources
that have been allocated during the setEntityContext()
call.
ejbRemove()
removes the database entry or entries associated with this particular
entity bean.
ejbActivate()
notifies an entity bean that it has been activated.
ejbPassivate()
notifies an entity bean that it is about to be deactivated.
ejbLoad()refreshes
the data the entity object represents from the database.
ejbStore()stores
the data the entity object represents in the database.
Entity Bean Implementation
An entity bean
implementation must implement:
The
EntityBean interface
Methods
that correspond to the ones defined in the home interface
Methods
that correspond to the ones defined in the remote interface
The amount
of programming an enterprise bean provider has to do depends on the
persistence policy, which is defined in the deployment descriptor.
Persistence is the protocol or mechanism used to transfer the state
of an entity bean to and from an underlying database. Note that the
entity bean and the database may be connected via a legacy application.
The specification
identifies two approaches to implement persistence:
Bean-managed persistence. The
entity bean implementation is responsible for implementing the persistence.
The bean provider writes the code to access the underlying database
or application. These calls are placed in the methods ejbCreate(),
ejbFind…(),
ejbRemove(),
ejbLoad(),
and ejbStore().
Container-managed
persistence. The
container is responsible for implementing the persistence. Instead
of the bean provider implementing the database access code, the
container is responsible for generating the appropriate code and
its execution. The fields of the entity bean, which are managed
by the container, must be specified in the deployment descriptor.
The obvious
advantage of container-managed persistence is that the bean provider
is freed from the task of writing database access code. That, however,
requires sophisticated tools provided by the container. In many cases
these tools imply an object relational mapping.
The example
from Chapter 1 uses bean-managed persistence. We point out throughout
the example which parts could have been omitted when using container-managed
persistence.
The class AccountBean
implements the interface EntityBean.
We declare two variables for the entity context and the account balance;
that is, the state of the bean. Note that you have to declare state
variables, which are container-managed, as public.
We don’t declare a constructor. We also have a convenience function
to obtain the primary key, the account identifier, from the entity
context.
package vogel.transaction.chapter1.ejb;
import javax.ejb.*;
import java.sql.*;
import java.rmi.RemoteException;
public class AccountBean
implements EntityBean {
private EntityContext entityContext;
public float balance;
private String accountId() {
return (String) entityContext.getPrimaryKey();
}
The implementation
of the application-specific methods, credit()
and debit(),
is straightforward: We just change the value of the state variable
balance.
public void credit( float amount
)
throws RemoteException {
balance = balance + amount;
return;
}
public void debit( float amount
)
throws RemoteException, InsufficientFundsException
{
if( balance >= amount )
balance = balance - amount;
else
throw new InsufficientFundsException();
return;
}
Now we implement
the create and find methods corresponding to the ones defined in the
home interface. For a container-managed implementation we would have
trivial implementation, we only have to initial the state variables.
For the bean-managed
implementation we have to supply the database access code. We use
JDBC in this example. But other APIs, for example, to object databases
or to legacy applications, can be used. The ejbCreate()
method we implement with an SQL insert, the ejbFindByPrimaryKey()
method with an SQL select.
public String ejbCreate( String accountId
)
throws RemoteException, CreateException
{
balance = 0;
try {
Statement statement = getStatement();
ResultSet resultSet = statement.executeQuery(
"insert into accounts values( '"
+ accountId + "', 0.0 )" );
statement.close();
}
catch( Exception e ) {
throw new RemoteException( e.toString()
);
}
return accountId;
}
public void ejbPostCreate( String
accountId ) {
return ;
}
public String ejbFindByPrimaryKey(
String accountId )
throws RemoteException, FinderException
{
try {
Statement statement = getStatement();
ResultSet resultSet = statement.executeQuery(
"select balance from accounts where
id = '" + accountId + "'" );
if( resultSet.next() ) {
balance = resultSet.getFloat(1);
...
return accountId;
}
Finally, we implement
the methods defined in EntityBean
interface. Setting and unsetting the entity context is straightforward.
The implementation of the ejbRemove()
method is provided by an SQL delete. We don’t add code to the activation
and passivation of the entity bean.
public void setEntityContext( EntityContext
entityContext )
throws RemoteException {
this.entityContext = entityContext;
}
public void unsetEntityContext()
throws RemoteException {
entityContext = null;
}
public void ejbRemove() throws RemoteException
{
try {
Statement statement = getStatement();
ResultSet resultSet = statement.executeQuery(
"delete from accounts where id =
'" + accountId() + "'" );
statement.close();
}
catch( Exception e ) {
throw new RemoteException( e.toString()
);
}
return;
}
public void ejbActivate() throws
RemoteException {}
public void ejbPassivate() throws
RemoteException {}
The ejbCreate(),
ejbRemove(),
ejbFind(),
ejbLoad(),
and ejbStore()
methods are the biggest difference between container-managed and bean-managed
implementation. For the container-managed case, we don’t have to do
anything; in the bean-managed case, we have to make the database calls.
For example, we implement the ejbLoad()
method with a SQL select and the ejbStore()
method with a SQL update. We have also implemented a little helper
method to obtain a SQL statement.
public void ejbLoad()
throws RemoteException {
try {
Statement statement = getStatement();
ResultSet resultSet = statement.executeQuery(
"select balance from accounts where
id = '" +
accountId() + "'" );
if( resultSet.next() ) {
balance = resultSet.getFloat(1);
...
}
}
public void ejbStore()
throws RemoteException {
try {
Statement statement = getStatement();
ResultSet resultSet = statement.executeQuery(
"update accounts set balance = "
+ balance +
" where id = '" + accountId() +
"'");
statement.close();
}
catch( Exception e ) {
throw new RemoteException( e.toString()
);
}
}
private Statement getStatement()
throws RemoteException {
...
}
}
Furthermore,
there are a number of rules and conventions to be observed:
Concurrent access
to entity beans. The
synchronization between concurrent invocations is managed by the
container and is not the concern of the entity bean provider. The
container typically uses one of the following strategies: database
synchronization, or container synchronization. In the first case,
the container provides multiple instances of the same entity bean
and leaves the synchronization up to the database access calls in
the ejbCreate(),
ejbRemove(),
ejbLoad(),
and ejbStore()
methods. In the second case, the container creates only one instance
of the entity bean, which acquires an exclusive lock on the database.
The concurrent invocations are serialized by the container.
Reentrant entity beans. By
default, entity beans are not reentrant. When a call within the
same transaction context arrives at the entity bean, the exception
java.rmi.RemoteException
is thrown. An entity bean can be declared reentrant in the deployment
descriptor; however, special care needs to be taken in this case.
The critical issue is that a container can generally not distinguish
between a (loopback) call within the same transaction and a concurrent
invocation (in the same transaction context) on that same entity
bean. The second one is illegal and when the entity bean is marked
reentrant it becomes the programmer’s responsibility to ensure this
rule.
EJB Deployment
EJB deployer
is responsible for taking an enterprise bean and deploying it in a
certain context. The context determines the policies for executing
the bean. These policies determine, for example, the transactional
behavior or the access control of an enterprise bean.
EJB deployer
would typically use wizard-like tools, provided by the container,
to set the deployment descriptor for enterprise beans. The EJB specification
version 1.0 defines serialized Java objects of the type javax.ejb.deployment.SessionDescriptor
or javax.ejb
.deployment.EntityDescriptor,
depending on the type of the enterprise bean. The subsequent versions
of the EJB specification prescribe XML for the definition of deployment
descriptors. For both reasons, the change of the specification and
the fact that a deployer uses tools, we omit the syntactical representation
of the deployment descriptor. Instead, we focus on the content of
the deployment information.
A deployment
descriptor keeps information about the following:
Transaction
policies govern the transactional behavior of a bean.
Access
control policies govern the access to an enterprise bean.
JNDI
names set the name under which the home interface of the enterprise
is registered.
Type
information defines the types, that is the name of the classes,
for the home and remote interfaces, the primary key class (entity
beans only), and the implementation class.
Properties
for enterprise beans with container-managed persistence provide
the information required by the container to manage the persistence
of a bean.
Transaction Polices
Transaction
policies cover two aspects: the transaction scope and the isolation
levels.
Transaction Scope
The EJB specification
defines the values for the transactional scope:
TX_NOT_SUPPORTED. The
enterprise bean does not support a global transaction.
TX_SUPPORTS. The
enterprise bean supports a global transaction. If a caller makes
an invocation in a transactional context, the container executes
the invocation in the caller’s transactional. If a caller has no
association with a transaction, the container executes the invocation
without a transaction.
TX_MANDATORY. An
invocation on the enterprise bean requires a transactional context.
If a caller makes an invocation in a transactional context, the
container executes the invocation in the caller’s transactional.
If a caller has no association with a transaction, the container
throws the exception javax.jta.TransactionRequiredException.
TX_REQUIRED. The
enterprise bean requires a global transaction for the execution
of an invocation. If a caller makes an invocation in a transactional
context, the container executes the invocation in the caller’s transactional.
If a caller has no association with a transaction, the container
creates a new transaction and executes the invocation in with this
transaction.
TX_REQUIRED_NEW. The
enterprise bean requires a new global transaction and executes the
invocation with this transaction.
TX_BEAN_MANAGED. The
transactional behavior is managed by the enterprise bean implementation
using the javax.transaction.CurrentTransaction.
Transaction
isolation levels are originally defined in the ANSI SQL specification.
The concept has been adopted by the ODBC and JDBC standards and found
its way in the EJB specification. Isolation level refers to the degree
to which multiple interleaved transactions are prevented from interfering
with each other in a multi-user database system. Ideally, one would
like to have serializable transactions. That means that the interleaved
execution of any set of concurrent transactions produces the same
effect as a serial execution of the same transactions. There are three
specific ways in which the serializability of a transaction may be
violated.
Dirty Read. Transaction
t1 modifies a row. Transaction t2 then reads the row. Now t1 performs
a rollback and t2 has seen a row that never really existed.
Non-repeatable Read. Transaction
t1 retrieves a row. Then transaction t2 updates this row and t1
retrieves the same row again. Transaction t1 has now retrieved the
same row twice and has seen two different values for it.
Phantoms. Transaction
t1 reads a set of rows that satisfy certain search conditions. Then
transaction t2 inserts one or more rows that satisfy the same search
condition. If transaction t1 repeats the read, it will see rows
that did not exist previously. These rows are called phantoms.
The transaction
isolation levels are defined based on the above defined permitted
violations.
TRANSACTION_READ_COMMITTED. Does
not allow dirty reads but allows the other two violations.
TRANSACTION_READ_UNCOMMITTED. Allows
all three violations.
TRANSACTION_REPEATABLE_READ. Allows
phantoms but not the other two violations.
TRANSACTION_SERIALIZABLE. Does
not allow any of the three violations.
Access Control
Access control
is based on identities. There are three values you can set for enterprise
bean defining with which identity it runs. These are:
CLIENT_IDENTITY. Runs
the enterprise with the identity provided by the caller.
SYSTEM_IDENTITY. Runs
the enterprise with a predefined system identity.
SPECIFIED_IDENTITY. Runs
the enterprise with the identity specified in the deployment descriptor.
The deployment
descriptor allows you to specify an access control entry. It can be
defined for individual methods or for all methods of the enterprise
bean. An access control entry associates a set of identities or roles
with a set of methods, meaning that only the specified clients with
the specified identities or roles can invoke those methods.
Note that the
access control is type-based and not instance-based. That means you
cannot formulate and enforce access control policies such as"only
the creator of a session can access the session," or"only the portfolio
owner and the financial consultant can access the portfolio."
Naming
For each enterprise
bean you must define a name under which its home interface is registered
with JNDI. JNDI is a way to locate enterprise beans. The name is specified
using the syntax for JNDI names.
Typing
For each enterprise
bean you must define a name of the classes for the home and the remote
interface and for the bean implementation class. For entity beans
you provide the name of the primary key class.
Container-Managed Persistence
To enable container-managed
persistence, you must specify the fields, which are managed by the
container.
Application Assembling
The task of
an application assembler is to combine one or more enterprise beans
with some additional components (servlets, applets, scripts, etc.).
The result of the assembly process can be a complete application or
another, more complex enterprise bean consisting of several enterprise
beans. The EJB specification does not define much detail on the bean
assembling. Generally, the client, which can be an application-level
client or just another enterprise bean, has to do the following things:
Locate the bean’s
home interface. The
EJB specification defines the JNDI as the API to locate home interfaces.
Obtain an EJB object. Create
a session bean, or create or find an entity bean.
Invoke the EJB object. Invoking
methods on the EJB object’s remote interface.
Locating the Home Interface
The EJB specification
defines JNDI as the API for locating home interfaces. JNDI is intended
to be implemented on top of different services. Examples include the
CORBA’s Naming Service, LDAP/X.500, flat file, and proprietary directory
services. Figure 5.11 illustrates the different implementation choices.
Typically, the EJB server provider selects a particular implementation
of JNDI.

Figure 5.11 JNDI
and sample implementation.
From the client’s
point of view, the selected technology doesn’t matter. Client just
needs to obtain an initial context and resolve the name to a home
interface as shown in the following sample code. The initialization
of the initial naming context factory is EJB container/server specific.
AtmHome atmHome = null;
try {
Hashtable env = new Hashtable();
env.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY,
"com.inprise.ejb.jndi.Context");
javax.naming.Context root =
new javax.naming.InitialContext(env);
atmHome = (AtmHome) root.lookup("Atm");
}
catch( Exception ex ) {
System.err.println(
"Could not get home interface for
ATM EJBs" + ex );
}
The context’s
lookup()
method returns an object of type java.lang.Object,
which we have to cast to the expected type (for example, to the AtmHome
interface).
Obtaining the Remote Interface
Now that we
have obtained the home interface of an enterprise bean we can get
a reference to the enterprise bean’s remote interface. We use the
home’s create or finder methods. Exactly which method we call depends
on the type of the enterprise bean and the methods the enterprise
bean provider has defined in the home interface.
Session Beans
A client obtains
a session bean by calling one of the create methods on the home interface.
The default create method has no parameters; for example:
Atm atm = atmHome.create();
Stateless session
beans have only create methods with no arguments. Stateful session
beans can have a number of create methods. The additional parameters
of these methods are used to initialize the session.
Entity Beans
A client obtains
an entity typically through a find operation. The default find method
is
findByPrimaryKey( <key type>
primaryKey )
The key type
can be any Java class, which implements Serializable.
The primary key class is set in the deployment description. For example,
we can obtain an account entity bean by using an account identifier
of the type AccountPK.class,
which contains a string or integer which is the account number.
AccountPK accountPK = new AccountPK("1234-56-789");
Account source = accountHome.findByPrimaryKey(
accountPK );
Again, the
bean provider can define additional find methods to be used by a client.
A client can also create entity beans using the home interface. However,
note the different life spans of session and entity beans. While a
session bean is typically only valid for a client’s session, an entity
bean exists as long as its data is present in a database.
Invoking Methods
The client
can now invoke methods on the EJB as defined in the remote interface.
For example, we invoke the transfer method on the ATM bean.
// transfer some money from savings
to checking
atm.transfer( "savings", "checking",
100f );
// cleaning up
atm.remove();
}
catch( Exception ex ) {
...
}
The invocation
of the remove()
method is specific to session beans. A well-behaved client should
always call the remove()
method once it is finished with the session object. If the client
doesn’t do so, due to badly written code or due to a crash of the
client program, the container will remove the object after a certain
time. The timeout value is a deployment property. However, a client
can also keep a handle to the session for future reference.
Clients of
entity beans don’t have to deal with this problem as entity beans
are only associated with a client for the duration of a transaction
and the container is in charge of their life cycles, including the
activation and deactivation.
CORBA and Enterprise JavaBeans
There are a
number of aspects to the relationship between CORBA and Enterprise
JavaBeans. Three important ones are the implementation of an EJB container/server
with an ORB, the integration of legacy systems into an EJB middle
tier, and the access of enterprise beans from non-Java components,
specifically clients. The EJB specification is currently only concerned
with the third aspect.
CORBA is a
very suitable and natural platform on which to implement an EJB infrastructure.
CORBA addresses all of the concerns of the EJB specification with
the CORBA Core specification or the CORBA Services:
Support for distribution. CORBA
Core and CORBA Naming Service
Support for transactions. CORBA
Object Transaction Service
Support for security. CORBA
Security Specification, including IIOP-over-SSL
Additionally,
the CORBA allows the integration of non-Java components into an application.
These components can be legacy systems and applications and different
kinds of clients. Back-ends can be easily integrated using OTS and
any programming language for which an IDL mapping exists. That, however,
requires an EJB container, which provides OTS and IIOP APIs as we
explained earlier in the chapter.
The EJB specification
is concerned with the accessibility of enterprise beans from non-Java
clients and provides an Enterprise JavaBean to CORBA mapping. The
goals of the EJB/CORBA mapping are
Interoperability
between clients written in any CORBA-supported programming language
and enterprise beans running on a CORBA-based EJB server.
Enabling
client programs to mix and match calls to CORBA objects and enterprise
beans within the same transaction.
Supporting
distributed transactions involving multiple enterprise beans running
on CORBA-based EJB servers provided by different vendors.
The mapping
is based on the Java-to-IDL mapping. The specification has the following
parts: mapping of distribution-related aspects, the mapping of naming
conventions, the mapping of transactions, and the mapping of security.
We explain each of these aspects in the following sections. Since
the mapping uses new IDL features introduced by the OMG’s Object-by-Value
specification, interoperability with other programming languages requires
CORBA 2.3-compliant ORBs.
Mapping for Distribution
An enterprise
bean has two interfaces that are remotely accessible: the remote interface
and the home interface. Applying the Java/IDL mapping to these interfaces
results in corresponding IDL specifications. The base classes defined
in the EJB specification are mapped to IDL in the same manner.
We explain
generation and use of IDL interfaces to enterprise with the ATM session
bean introduced in Chapter 1. By applying the Java/IDL mapping to
the home and the remote interface, we get the following IDL interface.
module vogel {
module transaction {
module chapter1 {
module ejb {
valuetype InsufficientFundsException
: ::java::lang::Exception {};
exception InsufficientFundsEx
{
::vogel::transaction::chapter1::ejb::InsufficientFundsException
value;
};
interface Atm : ::javax::ejb::EJBObject{
void transfer (in string arg0,
in string arg1, in float arg2)
raises (::vogel::transaction::chapter1::ejb::InsufficientFundsEx);
};
interface AtmHome : ::javax::ejb::EJBHome
{
::vogel::transaction::chapter1::ejb::Atm
create ()
raises (::javax::ejb::CreateEx);
};
};};};};
Mapping for Naming
A CORBA-based
EJB runtime environment that wants to enable any CORBA clients to
access enterprise beans must use the CORBA Naming Service for publishing
and resolving the home interfaces of the enterprise beans. The runtime
can use the CORBA Naming Service directly or indirectly via JNDI and
its standard mapping to the CORBA Naming Service.
JNDI names
have a string representation of the following form"directory1/directory2/…/directoryN/objectName".
The CORBA Naming Service defines names as a sequence of name components.
typedef string Istring;
struct NameComponent {
Istring id;
Istring kind;
};
typedef sequence<NameComponent>
Name;
Each "/" separated
name of a JNDI string name is mapped to a name component; the leftmost
component is the first entry in the CORBA Naming Service name
A JNDI string
name is relative to some naming context, which we call the JNDI
root context.
The JNDI root context corresponds to a CORBA Naming Service initial
context. CORBA Naming Service names are relative to the CORBA initial
context.
A CORBA program
obtains an initial CORBA Naming Service naming context by calling
resolve_initial_references("NameService")
on the ORB (pseudo) object. The CORBA Naming Service does not prescribe
a rooted graph for organizing naming context and, hence, the notion
of a root context does not apply. Which context is returned by resolve_initial_references()
depends on the initialization
of the ORB.
For example,
a C++ Client could locate the home interface to our ATMSession
bean, which has been registered with a JNDI string name "vogel/transaction/chapter1/corbaEjb/atm".
First we obtain initial naming context.
Object_ptr obj = orb->resolve_initial_refernces("NameService");
NamingContext initialNamingContext=
NamingContext.narrow( obj );
if( current == NULL ) {
cerr << "Couldn’t initial
naming context" << endl;
exit( 1 );
}
Then we create
a CORBA Naming Service name and initialize it according to the mapping
explained previously.
Name name = new Name( 1 );
name[0].id = "atm";
name[0].kind = "";
Now we resolve
the name on the initial naming context. We assume that we have initialized
so that we have context of the naming domain of the enterprise bean.
We narrow the resulting CORBA object to the expected type and make
sure that the narrow was successful.
Object_ptr obj = initialNamingContext->resolve(
name );
ATMSessionHome_ptr atmSessionHome
= ATMSessionHome.narrow( obj );
if( atmSessionHome == NULL ) {
cerr << "Couldn’t narrow to
ATMSessionHome" << endl;
exit( 1 );
}
Mapping for Transaction
A CORBA-based
EJB runtime environment that wants to enable any CORBA client to participate
in a transaction involving enterprise beans must use the CORBA Object
Transaction Service for transaction control.
When an enterprise
bean is deployed it can be installed with different transaction policies.
The policy is defined in the enterprise bean’s control descriptor.
If the policy is TX_NOT_SUPPORTED, the enterprise does not support
transactions, and we don’t have to worry about the mapping of transactions
to CORBA.
If the transaction
policy is set to a different value, the enterprise bean must be instructed
to interoperate with the CORBA Object Transaction Service. The following
rules have been defined for transactional enterprise beans: A CORBA
client invokes an enterprise through stubs generated from the IDL
interfaces for the enterprise bean’s remote and home interface. If
the client is involved in a transaction, it uses the interfaces provided
by CORBA Object Transaction Service. Details on the explicit transaction
management are explained later in the chapter. For example, a C++
Client could invoke the ATMSession
bean defined and located in the previous example.
try {
...
// obtain transaction current
Object_ptr obj = orb->resolve_initial_refernces("Current");
Current current = Current.narrow(
obj );
if( current == NULL ) {
cerr << "Couldn’t resolve
current" << endl;
exit( 1 );
}
// execute transaction
try {
current->begin();
atmSession->transfer("checking",
"saving", 100.00 );
current->commit( 0 );
} catch( ... ) {
current->rollback();
}
}
catch( ... ) {
...
}
Mapping for Security
Security aspects
of the EJB specification are mainly focused on controlling the access
to enterprise beans, as explained earlier in the chapter. CORBA defines
a number of ways to define the identities, including the following
cases:
Plain IIOP. CORBA’s
principal interface was deprecated in early 1998. The principal
interface was intended for determining the identity of a client.
However, the authors of the CORBA security services decided to choose
a different approach.
The GIOP specification
contains a component called service
context,
which is an array of
an/value pair. The identifier is a CORBA long and the value is a
sequence of octet. Among other purposes, entries in the service
context can be used to identify a caller.
Secure IIOP. The
CORBA security specification defines an opaque data type for the
identity. The real type of the identity is determined by the chosen
security mechanism; for example, GSS Kerberos, SPKM, or CSI-ECMA.
IIOP over SSL. SSL
uses X.509 certificates to identify servers and, optionally, clients.
When a server requests a client certificate, the server can use
the certificate as a client identity.