Implementing an Entity Bean

   

A goal of this chapter has been to cover how to declare the interfaces for an entity bean that are exposed to clients , but not to get into the implementation details (that's saved for the two chapters that follow). However, some implementation topics relate to both BMP and CMP, so it's better to get to them now. No matter which persistence mechanism you choose, you need to understand the interfaces specific to an entity bean implementation class, and you need to know how to implement a bean's business and home methods . It's also important to cover how the container interacts with your bean classes.

The EntityBean Interface

Your bean classes must always either directly or indirectly implement the javax.ejb.EntityBean interface. This interface is an extension of the EnterpriseBean marker interface and it defines several of the callback methods used by the container to interact with your bean classes. The interface consists of the following declarations:

 public interface EntityBean extends EnterpriseBean {   public void ejbActivate() throws EJBException, RemoteException;    public void ejbPassivate() throws EJBException, RemoteException;    public void ejbLoad() throws EJBException, RemoteException;    public void ejbStore() throws EJBException, RemoteException;    public void ejbRemove() throws RemoveException, EJBException,      RemoteException;    public void setEntityContext(EntityContext ctx) throws EJBException,      RemoteException;    public void unsetEntityContext() throws EJBException, RemoteException;  } 

Note

A marker interface such as EnterpriseBean doesn't declare any methods or fields. Its purpose is simply to identify a class as belonging to a particular category. In this case, you identify an entity bean as being an enterprise bean by (indirectly) implementing the EnterpriseBean interface.


You'll see the use of the ejb callback methods declared by EntityBean discussed later in this section. Two of the EntityBean methods, setEntityContext and getEntityContext , are used to associate a runtime context with an entity instance. This context is an object that implements the EntityContext interface. It is through this interface that an entity bean instance is able to make calls to the container.

An important point to note about the declarations found in EntityBean relates to the references to RemoteException . This exception is used by methods declared in a remote interface or remote home interface to report system errors. RemoteException cannot be used in local interface or local home declarations, which should makes its use here seem out of place to you. The only reason RemoteException is included in these declarations is for backward compatibility with EJB 1.0. When using EJB 2.0, you must throw EJBException instead of RemoteException to report system errors. So, your implementations of the EntityBean methods will never actually include RemoteException in their throws clauses. This is fine because a method implementation can always throw fewer exceptions than what its declaration in an interface indicates. You should take this same approach when using EJB 1.1, but the container will let you get away with throwing RemoteException if you don't.

To learn more about EJBException and RemoteException , see "System Exceptions," p. 372 .

The EntityContext Interface

EntityContext extends EJBContext to define an interface to an entity bean's runtime context that is provided by the container. EJBContext allows a bean instance to obtain a reference to its home, obtain security information about its caller (see Chapter 14, "Security Design and Management"), or work with the current transaction (see Chapter 12). Table 5.2 summarizes the purposes of the methods declared by EJBContext . You'll see more on these methods in later chapters.

Table 5.2. Methods of the EJBContext Interface

Return Type

Method Name

Description

Principal

getCallerPrincipal()

Get the security Principal that identifies the caller.

boolean

getRollbackOnly()

Test whether the current transaction has been marked for rollback.

void

setRollbackOnly()

Mark the current transaction for rollback.

EJBHome

getEJBHome()

Get the bean's remote home interface.

EJBLocalHome

getEJBLocalHome()

Get the bean's local home interface.

UserTransaction

getUserTransaction()

Get the transaction demarcation interface.

boolean

isCallerInRole (String role)

Test to see whether the caller has a given security role.

EntityContext adds the following three methods to those declared by EJBContext :

 public EJBObject getEJBObject() throws IllegalStateException;  public EJBLocalObject getEJBLocalObject() throws IllegalStateException;  public Object getPrimaryKey() throws IllegalStateException; 

These methods can be called by an instance from within a business method or certain callback methods. Some callback methods, such as ejbCreate , are invoked at a point where the EntityContext methods aren't accessible. You'll learn more about ejbCreate shortly, but in this case, there isn't an EJB object identity associated with the instance when the method is called. If you try to call getEJBObject , getEJBLocalObject , or getPrimaryKey in this situation, an IllegalStateException is thrown.

The purpose of the getPrimaryKey method should be fairly obvious. You can call this method to get a reference to the primary key associated with an entity bean instance. The meaning of the getEJBObject and getEJBLocalObject methods might not be quite so apparent to you at first glance. Remember that EJBObject is the interface that must be extended by any enterprise bean's remote interface, and EJBLocalObject must be extended by any local interface. The getEJBObject and getEJBLocalObject methods allow you to obtain a reference to the component interface associated with an instance. This is useful if an entity needs to pass a reference to itself as a method parameter or return value. You'll see more about this in Chapter 16.

Caution

Because an entity bean isn't required to have both a local and a remote interface, calls to getEJBLocalObject and getEJBObject aren't always valid. If you call a method for which a corresponding interface doesn't exist, an IllegalStateException is thrown. The same is true for invalid calls to getEJBHome or getEJBLocalHome .


Business Methods

Every business method you define in a bean's component interface must have a corresponding implementation in your bean class. Just as with any interface method implementation, a method in the bean class must have the same name, parameter list, and return type as the declaration in the interface. If the method implementation is declared to throw any exceptions, you must include those exceptions in the interface declaration as well. This is typically the case when you define application-specific exceptions to report business logic errors. Even though your remote interface declarations must always include RemoteException in their throws clauses, your bean class implementations should never list this exception. If an entity bean method calls a remote method that might throw a RemoteException , it needs to catch that exception and throw EJBException to its client instead.

You'll see examples of business method implementations in the next two chapters. These implementations are independent of your choice of BMP or CMP. This is because your business methods access the persistent fields within an entity bean instead of accessing the database directly. All database access for an entity bean is encapsulated in methods such as ejbLoad and ejbStore .

Home Methods

You must provide an implementation for every home method declared in an entity bean's home interface. The requirements for these method implementations are, for the most part, the same as those for component interface business methods. A home method implementation must be declared with the same parameter list and return type as its declaration in the interface, but there's a difference when it comes to the method name. Instead of naming a home method implementation to match the home interface, you must name it using that name (with its first character uppercased) with ejbHome in front of it. For example, ejbHomeGetItemsBeingAuctioned would correspond to a home interface declaration for getItemsBeingAuctioned .

The same rules for declaring exceptions that apply to component interface methods apply to home methods as well. Any exceptions that you include in the throws clause of a home method declaration must appear in the home interface declaration too.

An interesting aspect of home methods relates to how the container executes one. While reading the description that's been provided of them so far, you've likely considered them to be similar to static methods in regular Java classes. They do share the fact that both of these method types are used by a class to do work that doesn't depend on the instance variables of the class. You never even have to create an instance of a class to execute one of its static methods. In this respect, home methods are handled differently than static methods. The good news is that the mechanism for executing home methods, which makes use of the bean pool, is handled transparently by the container.

You were introduced to the concept of bean pooling back in Chapter 3. There you saw that the container optimizes resources by maintaining a pool of objects that can be associated with a particular bean instance as needed. When a client calls a home method, the container pulls an object from the pool without associating it with a particular entity object. It then invokes the home method that was called on that object and returns the result to the client. A home method isn't allowed to access the attributes of a particular entity instance, so there's no need for the object to be associated with one. When the method call completes, the object is returned to the pool.

Callback Methods and an Entity Bean's Life Cycle

The container manages an entity bean's existence using a set of callback methods that it invokes on a bean instance. These methods include those defined by the EntityBean interface and the methods that are required to support the operations declared by the home interface. You're responsible as a bean provider for implementing these methods, so it's important to understand when they're called and what they're expected to do. This chapter is mostly concerned with when they're called by the container because what you do within them is determined by whether you're using BMP or CMP.

Callback methods are invoked based on where an entity bean is within its life cycle. The EJB specification defines three states that an entity bean instance can be in: does not exist , pooled state , and ready state . Transitions between these states are, for the most part, associated with a call to one or more callback methods.

The existence of an entity bean instance begins when the container creates an instance of the bean implementation class using the Class.newInstance method. The container then calls setEntityContext to assign a runtime context to the instance. It's important to understand that the instance isn't yet associated with any particular entity object identity. Its attributes hold nothing but their default values because no data has been retrieved from the database and assigned to the instance. The instance has only moved into the pooled state where it can eventually be associated with a particular entity when needed. The container places a number of instances into the pooled state to help with resource management and to satisfy client requests that don't depend on a particular entity's state. Instances in the pooled state are all equivalent, and any one of them can be used by the container to execute a finder or home method without ever having to have an entity object identity assigned to it. Remember from the preceding discussion of home methods, that ejbHome methods are the callbacks used by the container to execute them.

Note

All instances of an enterprise bean are created by the container using Class. newInstance , which requires a no-argument constructor to be available for each bean class. You'll never use a bean's constructor yourself, so the simplest way to support this requirement is to never declare any constructors for your bean classes. This way, the default constructor will always be present for the container's use.


An entity bean instance moves into the ready state when it's assigned an entity object identity. This happens either when a new entity is created or an existing entity is activated. The container creates an entity in response to a client call of a create method. You don't implement the create method in your bean class ”you implement corresponding ejbCreate and ejbPostCreate methods instead. As an example, the earlier createWithData method defined in EnglishAuctionHome would have a corresponding declaration of

 public Integer ejbCreateWithData(String name, String description)    throws CreateException {   // do the required work and return the primary key if doing BMP    // or null if doing CMP  } 

After the container creates an entity object, it selects an instance from the pool and calls its ejbCreate method. This method must have the same parameter list as the create method and it must be declared to throw CreateException . Instead of returning a component interface reference, an ejbCreate method must be declared to return the primary key type. After completion of the ejbCreate method, the instance has a primary key value that corresponds to its entity object identity. Its ejbPostCreate method is then called. This method is declared like the following:

 public void ejbPostCreateWithData(String name, String description) {   // do any initialization required after creation  } 

After the ejbPostCreate method completes, the entity instance moves into the ready state and the container returns a corresponding local or remote interface reference to the client, as appropriate.

An entity instance can also move into the ready state in response to a call to its ejbActivate method. This is a result of the object pooling used by the container to manage its resources. Entity objects referenced by a client can be passivated and returned to the pool if the resources used by the instance need to be applied elsewhere. In this case, an entity object identity exists but it's not currently assigned to a bean instance. The same is true when a finder method is executed. For each finder method you declare, either you (BMP) or the container (CMP) must supply a corresponding ejbFind method like the following:

 public Collection ejbFindAllAuctions() throws FinderException {   // determine the primary keys of all the existing auctions    // and return a collection of them  } 

An ejbFind method must accept the same parameter list as its corresponding finder declaration and it must be declared to throw FinderException . Instead of returning one or more component interface references, an ejbFind method must return primary key values. The object identities are known for the results of a finder method, but no operations have yet been performed on them that require knowledge of their state from the database. This means that these entity objects are in effect in a passivated state. The same is true after the invocation of CMP select methods that you'll learn about in Chapter 7.

No matter how an entity object enters a passivated state, it becomes active only when the container selects an entity instance to associate with it and invokes the ejbActivate method of that instance to move it into the ready state. An instance in the ready state has an assigned object identity, but it doesn't necessarily know the attribute values for that object yet. At the point a business method is first executed on an instance that hasn't loaded its object's state, that state must be loaded from the database. If you're using BMP, the container calls the ejbLoad method you're responsible for implementing to retrieve the object's state. For CMP, the container loads the object's state and then calls ejbLoad in case there's anything you need to do after the object's state has been synchronized. While in the ready state, updates to the entity are transferred to the database by calls to your ejbStore method (BMP) or by the container. It is within the ready state that an entity instance services business method calls from clients.

An instance is returned to the pooled state if it's passivated or removed. If the container decides to passivate an instance, it calls its ejbStore method to make certain that the object's state is correctly synchronized with the database, and then it calls ejbPassivate . The purpose of ejbPassivate isn't to write an object's state to the database, it's just to give you the chance to do anything that might be necessary (such as releasing some resource being used by the instance) before the instance is returned to the pool.

When a client invokes a remove method on either the home or component interface, the container calls ejbRemove on the corresponding bean instance. If you're using BMP, ejbRemove is where you delete the object from the database and release any resources associated with it. For CMP, the container calls your ejbRemove method to allow you to release its resources (if necessary) before the object is deleted. After a call to ejbRemove , the container returns the instance to the pooled state.

The container might allow an instance in the pooled state to be garbage collected. If so, the instance's unsetEntityContext method is called and it is removed from the pool. As you'll see later in Chapter 13, an instance throwing a system exception is one reason the container will discard it in this manner.

Figure 5.1 summarizes the method calls and state transitions that define an entity bean's life cycle.

Figure 5.1. An entity bean instance can be described by one of three states during its life cycle.

graphics/05fig01.gif

Accessing the Environment

Entity bean instances are obviously highly configurable because most of their attribute values are read from a database. It's also possible that some of your bean method implementations could benefit from configuration information that applies to all instances of an entity bean. As you saw back in Chapter 4, "Java Naming and Directory Interface," the correct way to do this is to use entries in the bean's environment. For example, you might use a JNDI lookup from a create method to set the default minimum bid increment for an auction:

 InitialContext ctx = new InitialContext();  Double minIncrement = (Double)ctx.lookup("java:comp/env/DefaultBidIncrement"); 

To learn more about environment entries, see "Accessing an EJB's Environment," p. 98 .



Special Edition Using Enterprise JavaBeans 2.0
Special Edition Using Enterprise JavaBeans 2.0
ISBN: 0789725673
EAN: 2147483647
Year: 2000
Pages: 223

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net