The first method called by the
container after a bean instance is created is
setEntityContext( )
.
This method passes the bean instance a reference to its
javax.ejb.EntityContext
, which is really the
instance’s interface to the container.
The
setEntityContext( )
method should be implemented
by the entity bean developer so that it places the
EntityContext
reference in an instance field of
the bean where it will be kept for the life of the instance. The
definition of EntityContext
is as follows:
public interface javax.ejb.EntityContext extends javax.ejb.EJBContext { public EJBLocalObject getEJBLocalObject( ) throws IllegalStateException public abstract EJBObject getEJBObject( ) throws IllegalStateException; public abstract Object getPrimaryKey( ) throws IllegalStateException; }
As the bean instance is swapped from one EJB object to the next, the
information obtainable from the EntityContext
changes to reflect the EJB object to which the instance is assigned.
This change is possible because the EntityContext
is an interface, not a static class definition, so the container can
implement the EntityContext
with a concrete class
that it controls. As the entity bean instance is swapped from one EJB
object to another, the information made available through the
EntityContext
will also change.
The EntityContext.getEJBObject( )
method returns a
remote reference to the bean instance’s EJB object.
The EntityContext.getEJBLocalObject( )
method, on
the other hand, returns a local reference to the bean
instance’s EJB object.
Session beans also define the getEJBObject( )
and
getEJBLocalObject( )
methods in the
SessionContext
interface; their behavior is
exactly the same.
The EJB objects obtained from the EntityContext
are the same kinds of references that might be used by an application
client, in the case of a remote reference, or another co-located
bean, in the case of a local reference. The getEJBObject( )
and getEJBLocalObject( )
methods allow
the bean instance to get its own EJB object reference, which it can
then pass to other beans. Here is an example:
public class A_Bean extends EntityBean {
public EntityContext context;
public void someMethod( ) {
B_BeanRemote b = ... // Get a remote reference to B_Bean.
EJBObject obj = context.getEJBObject( );
A_BeanRemote mySelf = (A_BeanRemote)
PortableRemoteObject.narrow(obj,A_BeanRemote.class);
b.aMethod( mySelf );
}
...
}
It is illegal for a bean instance to pass a this
reference to another bean; instead, it passes its remote or local EJB
object reference, which the bean instance gets from its
EntityContext
.
The ability of a bean to obtain an EJB object reference to itself is also useful when establishing relationships with other beans in container-managed persistence. For example, the Customer EJB might implement a business method that allows it to assign itself a Reservation:
public abstract class CustomerBean implements javax.ejb.EntityBean { public EntityContext context; public void assignToReservation(ReservationLocal reservation) { EJBLocalObject localRef = context.getEJBLocalObject( ); Collection customers = reservation.getCustomers( ); customers.add(localRef); } ... }
The EntityContext.getPrimaryKey( )
method allows a bean instance to get a
copy of the primary key to which it is currently assigned. Use of
this method outside of the ejbLoad( )
and
ejbStore( )
methods of BMP entity beans is
unusual, but the EntityContext
makes the primary
key available for those unusual circumstances when it is needed.
As the context in which the bean instance operates changes, some of
the information made available through the
EntityContext
reference will be changed by the
container. This is why the methods in the
EntityContext
throw the
java.lang.IllegalStateException
. The
EntityContext
is always available to the bean
instance, but the instance is not always assigned to an EJB object.
When the bean is between EJB objects (i.e., when
it’s in the pool), it has no EJB object or primary
key to return. If the getEJBObject( )
,
getEJBLocalObject( )
, or getPrimaryKey( )
methods are invoked when the bean is in the pool, they
will throw an IllegalStateException
. Appendix B
provides tables of allowed operations for each bean type describing
which EJBContext
methods can be invoked at what
times.
The EntityContext
extends the javax.ejb.EJBContext
class, which is
also the base class for the SessionContext
session
beans use. EJBContext
defines several methods that
provide useful information to a bean at runtime.
Here is the definition of the EJBContext
interface:
package javax.ejb; public interface EJBContext { // EJB 2.1 only: TimerService public TimerService getTimerService( ) throws java.lang.IllegalStateException; // EJB home methods public EJBHome getEJBHome( ) java.lang.IllegalStateException; public EJBLocalHome getEJBLocalHome( ) java.lang.IllegalStateException; // security methods public java.security.Principal getCallerPrincipal( ); public boolean isCallerInRole(java.lang.String roleName); // transaction methods public javax.transaction.UserTransaction getUserTransaction( ) throws java.lang.IllegalStateException; public boolean getRollbackOnly( ) throws java.lang.IllegalStateException; public void setRollbackOnly( ) throws java.lang.IllegalStateException; // deprecated methods public java.security.Identity getCallerIdentity( ); public boolean isCallerInRole(java.security.Identity role); public java.util.Properties getEnvironment( ); }
The EJBContext.getTimerService( )
method (EJB 2.1
only) returns a reference to the container’s Time
Service, which allows the entity to set up notifications for itself
of timed events. In other words, an entity can set alarms so that the
container will call it when a specific date arrives, or some interval
of time has passed. For example, an entity bean might set a timer as
follows:
public class CustomerBean implements EntityBean, TimedObject {
EntityContext ejbContext;
...
public void scheduleAppointment(Date date, String description){
TimerService timerService = ejbContext.getTimerServcie( );
timerService.createTimer( date, description );
}
public void ejbTimeout(Timer timer){
// do something when the timer goes off
}
...
}
The scheduleAppointment( )
method is a business
method that’s made available to remote clients via
the remote interface. A client can call this method passing in a date
and description of an event, which are in turn used to create a
timer; to register the entity for a timed event. In order for an
entity to be notified of a timed event it must implement the
javax.ejb.TimedObject
interface, which defines one
method: ejbTimeout( )
. When the date of a timed
event arrives, the Timer Service, which is part of the EJB container,
will call the ejbTimeout( )
method. The entity can
get details of the timed event from the
javax.ejb.Timer
object, including the description.
The Timer Service is covered in detail in Chapter 13.
The EJBContext.getEJBHome( )
and EJBContext.getEJBLocalHome( )
methods return a reference to the
bean’s EJB home. This is useful if the bean needs to
create or find entity beans of its own type. Access to the EJB home
may be more useful in BMP entity beans than in CMP entity beans,
which have select methods and CMR fields.
As an example, if all of the employees in Titan’s
system (including managers) are represented by BMP Employee beans, a
manager who needs access to subordinate employees can use the
getEJBLocalHome( )
method to get beans
representing the appropriate employees:
public class EmployeeBean implements EntityBean { EntityContext ejbContext; ... public EmployeeLocal createSubordinate( ) { EmployeeLocal mySelf = (EmployeeLocal)ejbContext.getEJBLocalObject( ); EmployeeHomeLocal home = (EmployeeHomeLocal)ejbContext.getEJBLocalHome( ); EmployeeLocal subordinate = home.createSubordinateTo( mySelf ); return subordinate; } ... }
The EJBContext.getCallerPrincipal( )
method is used to obtain the
java.security.Principal
object representing the
client that is currently accessing the bean. The
Principal
object can,
for example, be used by the BMP Ship EJB to track the identities of
clients making updates:
public class ShipBean implements EntityBean { String lastModifiedBy; EntityContext context; ... public void setTonnage(double tons) { tonnage = tons; Principal principal = context.getCallerPrincipal( ); String modifiedBy = principal.getName( ); ... } ... }
The
EJBContext.isCallerInRole( )
method tells you whether the client
accessing the bean is a member of a specific role, identified by a
role name. This method is useful when more access control is needed
than simple method-based access control can provide. In a banking
system, for example, you might allow the Teller
role to make most withdrawals but only the Manager
role to make withdrawals of over $10,000. This kind of fine-grained
access control cannot be addressed through EJB’s
security attributes because it involves a business logic problem.
Therefore, we can use the isCallerInRole( )
method
to augment the automatic access control provided by EJB. First,
let’s assume that all managers are also tellers. The
business logic in the withdraw( )
method uses
isCallerInRole( )
to make sure that only the
Manager
role can withdraw sums over $10,000.00:
public class AccountBean implements EntityBean {
int id;
double balance;
EntityContext context;
public void withdraw(Double withdraw) throws AccessDeniedException {
if (withdraw.doubleValue( ) > 10000) {
boolean isManager = context.isCallerInRole("Manager");
if (!isManager) {
// Only Managers can withdraw more than 10k.
throw new AccessDeniedException( );
}
}
balance = balance - withdraw.doubleValue( );
}
...
}
The EJBContext
contains some methods that were
used in EJB 1.0 but were deprecated in EJB 1.1 and have been
abandoned in EJB 2.0 and EJB 2.1. Support for these deprecated
methods were optional for EJB 2.0 containers, they are not supported
by EJB 2.1. EJB containers that do not support the deprecated
security methods will throw a RuntimeException
.
The deprecated security methods are based on EJB
1.0’s use of the Identity
object
instead of the Principal
object. The semantics of
the deprecated methods are basically the same but, because
Identity
is an abstract class, it has proven to be
too difficult to use. Even if your EJB 2.0 vendor supports these
deprecated methods, you should never use them for new entity beans.
They should only be used if you are continuing to support legacy EJB
1.0 entities—a fairly rare occurence.
The
getEnvironment( )
method has been replaced by the JNDI environment naming context, which is
discussed later in this book. EJB 2.0 containers may optionally
support this method for backward compatibly with legacy EJB 1.0
components, but EJB 2.1 containers do not support this method at all
and will throw a RuntimeException
if its called.
The transactional methods—getUserTransaction( )
, setRollbackOnly( )
, and
getRollbackOnly( )
—are described in detail
in Chapter 15.
The material on the EJBContext
covered in this
section applies equally well to session and message-driven beans.
There are some exceptions, however, and these differences are covered
in Chapter 11 and Chapter 12.
Starting with EJB
1.1, the bean-container contract for entity and stateful beans was
expanded beyond the EJBContext
using the Java
Naming and Directory Interface ( JNDI). A special JNDI name space,
which is referred to as the environment naming
context (ENC), was added to allow any enterprise bean to
access environment entries, other beans, and resources (such as JDBC
DataSource
objects) specific to that enterprise
bean.
The JNDI ENC continues to be an extremely important part of the bean-container contract. Although we used the JNDI ENC to access JDBC in the bean-managed persistence chapter (Chapter 9), it’s not specific to entity beans. The JNDI ENC is used by session, entity, and message-driven beans alike. To avoid unnecessary duplication, a detailed discussion of this important facility is left for Chapter 11. What you learn about using the JNDI ENC in Chapter 11 applies equally well to session, entity, and message-driven beans.