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
in EJB 2.0 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; }
EJBLocalObject
is new to EJB 2.0 and is not
supported by EJB 1.1. The definition of the
EntityContext
in EJB 1.1 is as follows:
public interface javax.ejb.EntityContext extends javax.ejb.EJBContext { 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, and it means that 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, some of the information made
available through the EntityContext
will also
change.
The getEJBObject()
method returns a remote
reference to the bean instance’s EJB object. The
getEJBLocalObject()
method (EJB 2.0), on the other
hand, returns a local reference to the bean instance’s EJB
object.
Session beans also define the getEJBObject()
and
getEJBLocalObject()
(EJB 2.0) 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. These 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
.
In EJB 2.0, 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
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 probably rare, 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 home methods public EJBHome getEJBHome(); // EJB 2.0 only public EJBLocalHome getEJBLocalHome(); // 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
getEJBHome()
and
getEJBLocalHome()
(EJB
2.0) 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 proves more useful in bean-managed
entity beans and CMP 1.1 entity beans than in CMP 2.0 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 CMP 1.1 Employee beans, a
manager who needs access to subordinate employees can use the
getEJBHome()
method to get beans representing the
appropriate employees:
public class EmployeeBean implements EntityBean { int id; String firstName; ... public Enumeration getSubordinates() { Object ref = ejbContext.getEJBHome(); EmployeeHome home = (EmployeeHome) PortableRemoteObject.narrow(ref, EmployeeHome.class); Integer primKey = (Integer)context.getPrimaryKey(); Enumeration subordinates = home.findByManagerID(primKey); return subordinates; } ... }
The
getCallerPrincipal()
method
is used to obtain the Principal
object
representing the client that is currently accessing the bean. The
Principal
object can,
for example, be used by the Ship bean 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
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. Support for these deprecated methods is
optional for EJB 1.1 containers, which can host EJB 1.0 beans. 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.
The
getEnvironment()
method
has been replaced by the JNDI environment naming context, which is
discussed later in this book. Support for the deprecated
getEnvironment()
method in EJB 1.1 is discussed in
detail in Chapter 12. The transactional
methods—getUserTransaction()
,
setRollbackOnly()
, and
getRollbackOnly()
—are described in detail in
Chapter 14.
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 12 and Chapter 13.
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 in EJB 2.0. Although we used the JNDI ENC to access JDBC in the bean-managed persistence chapter (Chapter 10), 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 12. What you learn about using the JNDI ENC in Chapter 12 applies equally well to session, entity, and message-driven beans.