The Callback Methods

All entity beans (container- and bean-managed) must implement the javax.ejb.EntityBean interface. The EntityBean interface contains a number of callback methods that the container uses to alert the bean instance of various runtime events:

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

Each callback method is invoked on an entity bean instance at a specific time during its life cycle.

As described in Chapter 9, BMP beans must implement most of these callback methods to synchronize the bean’s state with the database. The ejbLoad( ) method tells the BMP bean when to read its state from the database; ejbStore( ) tells it when to write to the database; and ejbRemove( ) tells the bean when to delete itself from the database.

While BMP beans take full advantage of callback methods, CMP entity beans may not need to use all of them. The persistence of CMP entity beans is managed automatically, so in most cases the resources and logic that might be managed by these methods is already handled by the container. However, a CMP entity bean can take advantage of these callback methods if it needs to perform actions that are not automatically supported by the container.

You may have noticed that each method in the EntityBean interface throws both a javax.ejb.EJBException and a java.rmi.RemoteException. EJB 1.0 required that a RemoteException be thrown if a system exception occurred while a bean was executing a callback method. However, since EJB 1.1 the use of RemoteException in these methods has been deprecated in favor of the javax.ejb.EJBException. EJB 2.0 and EJB 2.1 suggest that the EJBException be thrown if the bean encounters a system error, such as a SQLException, while executing a method. The EJBException is a subclass of RuntimeException, so you don’t have to declare it in the method signature. Since the use of the RemoteException is deprecated, you also don’t have to declare it when implementing the callback methods either; in fact, it’s recommended that you don’t.

setEntityContext( ) and unsetEntityContext( )

The first method called after a bean instance is instantiated is setEntityContext( ). As the method signature indicates, this method passes the bean instance a reference to a javax.ejb.EntityContext, which is the bean instance’s interface to the container. The purpose and functionality of the EntityContext is covered later in this chapter.

The setEntityContext( ) method is called prior to the bean instance’s entry into the instance pool. In Chapter 3, we discussed the instance pool that EJB containers maintain, where instances of entity and stateless session beans are kept ready to use. EntityBean instances in the instance pool are not associated with any data in the database; their state is not unique. When a client requests a specific entity, an instance from the pool is chosen, populated with data from the database, and assigned to service the client. Any nonmanaged resources needed for the life of the instance should be obtained when this method is called. This ensures that such resources are obtained only once in the life of a bean instance. A nonmanaged resource is one that is not automatically managed by the container (e.g., references to CORBA objects). Only resources that are not specific to the entity bean’s identity should be obtained in the setEntityContext( ) method. Other managed resources (e.g., Java Message Service factories) and entity bean references are obtained as needed from the JNDI ENC. Bean references and managed resources obtained through the JNDI ENC are not available from setEntityContext( ). The JNDI ENC is discussed later in this chapter.

At the end of the entity bean instance’s life, after it is removed permanently from the instance pool and before it is garbage collected, the unsetEntityContext( ) method is called, indicating that the bean instance is about to be evicted from memory by the container. This is a good time to free up any resources obtained in the setEntityContext( ) method.

ejbCreate( )

In a CMP bean, the ejbCreate( ) method is called before the bean’s state is written to the database. Values passed in to the ejbCreate( ) method should be used to initialize the CMP fields of the bean instance. Once the ejbCreate( ) method completes, a new record, based on the CMP fields, is written to the database.

In bean-managed persistence, the ejbCreate( ) method is called when it’s time for the bean to add itself to the database. Inside the ejbCreate( ) method, a BMP bean must use some kind of API to insert its data into the database.

Each ejbCreate( ) method must have parameters that match a create( ) method in the home interface. If you look at the ShipBean class definition and compare it to the Ship EJB’s home interfaces (see Chapter 7 and Chapter 9), you can see how the parameters for the create methods match exactly in type and sequence. This enables the container to delegate each create( ) method invoked on an EJB home to the proper ejbCreate( ) method in the bean instance.

In addition, the ejbCreate( ) method can take the form ejbCreate< SUFFIX >( ), which allows for easier method overloading when parameters are the same but the methods act differently. For example, ejbCreateByName(String name) and ejbCreateByRegistration(String registration) would have corresponding create( ) methods defined in the local or home interface, in the form createByName(String name) and createByRegistration(String registration).

The EntityContext maintained by the bean instance does not provide an entity bean with the proper identity until ejbCreate( ) has completed. This means that while the ejbCreate( ) method is executing, the bean instance doesn’t have access to its primary key or EJB object. The EntityContext does, however, provide the bean with information about the caller’s identity and access to its EJB home object (local and remote) and properties. The bean can also use the JNDI naming context to access other beans and resource managers such as javax.sql.DataSource.

However, the CMP entity bean developer must ensure that ejbCreate( ) sets the CMP fields that correspond to the fields of the primary key. When a new CMP entity bean is created, the container will use the CMP fields in the bean class to instantiate and populate a primary key automatically. If the primary key is undefined, the container and database will work together to generate the primary key for the entity bean.

Once the bean’s state has been populated and its ejbCreate( ) method has executed, the ejbPostCreate( ) method is invoked. This method gives the bean an opportunity to perform any postprocessing prior to servicing client requests. In CMP entity beans, ejbPostCreate( ) is used to manipulate container-managed relationship (CMR) fields. These CMR fields must not be modified by ejbCreate( ). The reason for this restriction has to do with referential integrity. The primary key for the entity bean may not be available until after ejbCreate( ) executes. The primary key is needed if the mapping for the relationship uses it as a foreign key, so assignment of relationships is postponed until ejbCreate( ) completes and the primary key becomes available. This is also true with autogenerated primary keys, which usually require that the insert be done before a primary key can be generated. In addition, referential integrity may specify non-null foreign keys in referencing tables, so the insert must take place first. In reality, the transaction does not complete until both ejbCreate( ) and ejbPostCreate( ) have executed, so the vendors are free to choose the best time for database inserts and linking of relationships.

The bean identity is not available during the call to ejbCreate( ), but it is available in ejbPostCreate( ). This means that the bean can access its own primary key and EJB object (local or remote) inside of ejbPostCreate( ). This can be useful for performing postprocessing prior to servicing business-method invocations.

Each ejbPostCreate( ) method must have the same parameters as the corresponding ejbCreate( ) method, as well as the same method name. For example, if the ShipBean class defines an ejbCreateByName(String name) method, it must also define a matching ejbPostCreateByName(String name) method. The ejbPostCreate( ) method returns void.

Matching the name and parameter lists of ejbCreate( ) and ejbPostCreate( ) methods is important for two reasons. First, it indicates which ejbPostCreate( ) method is associated with which ejbCreate( ) method. This ensures that the container calls the correct ejbPostCreate( ) method after ejbCreate( ) is done. Second, in CMP, it is possible that one of the parameters passed is not assigned to a CMP field. In this case, you would need to duplicate the parameters of the ejbCreate( ) method to have that information available in the ejbPostCreate( ) method. CMR fields are the primary reason for utilizing the ejbPostCreate( ) method in CMP, because of referential integrity.

ejbCreate( ) and ejbPostCreate( ) Sequence of Events

To understand how an entity bean instance gets up and running, we have to think of a entity bean in the context of its life cycle. Figure 10-1 shows the sequence of events during a portion of a CMP bean’s life cycle, as defined by the EJB specification. Every EJB vendor must support this sequence of events.

Event sequence for bean-instance creation

Figure 10-1. Event sequence for bean-instance creation

The process begins when the client invokes one of the create( ) methods on the bean’s EJB home. A create( ) method is invoked on the EJB home stub (step 1), which communicates the method to the EJB home across the network (step 2). The EJB home plucks a ShipBean instance from the pool and invokes its corresponding ejbCreate( ) method (step 3).

The create( ) and ejbCreate( ) methods are responsible for initializing the bean instance so that the container (CMP) or bean class (BMP) can insert a record into the database. In the case of the ShipBean, the minimal information required to add a new ship to the system is the ship’s name and unique id. These persistent fields are set during the ejbCreate( ) method invocation (step 4).

In container-managed persistence, the container uses two of the bean’s CMP fields (id and name), to insert a record into the database. Only the fields described as CMP fields in the deployment descriptor are accessed. Once the container has read the CMP fields from the bean instance (step 5), it will automatically insert a new record into the database using those fields (step 6).[26] How the data is written to the database is defined when the bean’s fields are mapped at deployment time. In our example, a new record is inserted into the SHIP table.

Tip

In bean-managed persistence, the bean class itself reads the fields and performs a database insert to add the bean’s data to the database. This would take place in steps 5 and 6.

Once the record has been inserted into the database, the bean instance is ready to be assigned to an EJB object (step 7). Once the bean is assigned to an EJB object, the bean’s identity is available. This is when ejbPostCreate( ) is invoked (step 8).

In CMP entity beans, ejbPostCreate( ) is used to manage the beans’ container-managed relationship fields. This might involve setting the Cruise in the Ship EJB’s cruise CMR field or some other relationship (step 9).

Finally, when the ejbPostCreate( ) processing is complete, the bean is ready to service client requests. The EJB object stub is created and returned to the client application, which will use it to invoke business methods on the bean (step 10).

Using ejbLoad( ) and ejbStore( ) in Container-Managed Persistence

The process of ensuring that the database record and the entity bean instance are equivalent is called synchronization. In container-managed persistence, the bean’s CMP fields are automatically synchronized with the database. Persistence in container-managed beans is fairly straightforward, so in most cases we will not need the ejbLoad( ) and ejbStore( ) methods.

Leveraging the ejbLoad( ) and ejbStore( ) callback methods in container-managed beans, however, can be useful if custom logic is needed when synchronizing CMP fields. Data intended for the database can be reformatted or compressed to conserve space; data just retrieved from the database can be used to calculate derived values for nonpersistent fields.

Imagine a hypothetical bean class that includes some binary value you want to store in the database. The binary value may be very large (an image, for example), so you may need to compress it before storing it away. Using the ejbLoad( ) and ejbStore( ) methods in a container-managed bean allows the bean instance to reformat the data as appropriate for the state of the bean and the structure of the database. Here’s how this might work:

import java.util.zip.Inflater;
import java.util.zip.Deflater;

public abstract class HypotheticalBean implements javax.ejb.EntityBean {
    // Instance variable
    public byte [] inflatedImage;
    
    // CMP field methods
    public abstract void setImage(byte [] image);
    public abstract byte [] getImage( );
  
    // Business methods. Used by client.
    public byte [] getImageFile( ) {
        if(inflatedImage == null) {
            Inflater unzipper = new Inflater( );
            byte [] temp = getImage( );
            unzipper.setInput(temp);
            unzipper.inflate(inflatedImage);
        }
        return inflatedImage;
    }
    public void setImageFile(byte [] image) {
        inflatedImage = image;
    }
    
    // callback methods
                  public void ejbLoad( ) {
                  inflatedImage = null;
                  }   
                  public void ejbStore( ) {
                  if(inflatedImage != null) {
                  Deflater zipper = new Deflater( );
                  zipper.setInput(inflatedImage);
                  byte [] temp = new byte[inflatedImage.length];
                  int size = zipper.deflate(temp);
                  byte [] temp2 = new byte[size];
                  System.arraycopy(temp, 0, temp2, 0, size);
                  setImage(temp2);
                  }
                  }
}

Just before the container synchronizes the state of entity bean with the database, it calls the ejbStore( ) method. This method uses the java.util.zip package to compress the image file, if it has been modified, before writing it to the database.

Just after the container updates the fields of the HypotheticalBean with fresh data from the database, it calls ejbLoad( ), which reinitializes the inflatedImage instance variable to null. Decompression is preformed lazily, so it’s done only when it is needed. Compression is performed by ejbStore( ) only if the image was accessed; otherwise, the image field is not modified.

Using ejbLoad( ) and ejbStore( ) in Bean-Managed Persistence

In bean-managed persistence, the ejbLoad( ) and ejbStore( ) methods are called by the container when it’s time to read from or write to the database. The ejbLoad( ) method is invoked after the start of a transaction, but before the entity bean can service a method call. The ejbStore( ) method is usually called after the business method is called, but it must be called before the end of the transaction.

While the entity bean is responsible for reading and writing its state from and to the database, the container is responsible for managing the scope of the transaction. This means that the entity bean developer need not worry about committing operations on database-access APIs, provided the resource is managed by the container. The container will take care of committing the transaction and persisting the changes at the appropriate times.

If a BMP entity bean uses a resource that is not managed by the container system, the entity bean must manage the scope of the transaction manually, using operations specific to the API. Examples of how to use the ejbLoad( ) and ejbStore( ) methods in BMP are shown in detail in Chapter 9.

ejbPassivate( ) and ejbActivate( )

The ejbPassivate( ) method notifies the bean developer that the entity bean instance is about to be pooled or otherwise disassociated from the entity bean identity. This gives the entity bean developer an opportunity to do some last-minute cleanup before the bean is placed in the pool, where it will be reused by some other EJB object. In real-world implementations, the ejbPassivate( ) method is rarely used, because most resources are obtained from the JNDI ENC and are managed automatically by the container.

The ejbActivate( ) method notifies the bean developer that the entity bean instance has just returned from the pool and is now associated with an EJB object and has been assigned an identity. This gives the entity bean developer an opportunity to prepare the entity bean for service, for example by obtaining some kind of resource connection.

As with the ejbPassivate( ) method, it’s difficult to see why this method would be used in practice. It is best to secure resources lazily (i.e., as needed). The ejbActivate( ) method suggests that some kind of eager preparation can be accomplished, but this is rarely done in practice.

Tip

Even in EJB containers that do not pool entity bean instances, the value of ejbActivate( ) and ejbPassivate( ) is questionable. It’s possible that an EJB container may choose to evict instances from memory between client invocations and create a new instance for each new transaction. While this may appear to hurt performance, it’s a reasonable design, provided that the container system’s Java Virtual Machine has an extremely efficient garbage collection and memory allocation strategy. Hotspot is an example of a JVM that has made some important advances in this area. Even in this case, however, ejbActivate( ) and ejbPassivate( ) provide little value because the setEntityContext( ) and unsetEntityContext( ) methods can accomplish the same thing.

One of the few practical reasons for using ejbActivate( ) is to reinitialize nonpersistent instance fields of the bean class that may have become “dirty” while the instance serviced another client.

Regardless of their general usefulness, these callback methods are at your disposal if you need them. In most cases, you are better off using setEntityContext( ) and unsetEntityContext( ), since these methods will execute only once in the life cycle of a bean instance.

ejbRemove( )

The component interfaces (remote, local, remote home, and local home) define remove methods that can be used to delete an entity from the system. When a client invokes one of the remove methods, as shown in the following code, the container must delete the entity’s data from the database:

CustomerHomeRemote customerHome;
CustomerRemote customer;

customer.remove( );
// or
customerHome.remove(customer.getPrimaryKey( ));

The data deleted from the database includes all the CMP fields. So, for example, when you invoke a remove method on a Customer EJB, the corresponding record in the CUSTOMER table is deleted.

In CMP, the remove method also removes the link between the CUSTOMER record and the ADDRESS record. However, the ADDRESS record associated with the CUSTOMER record will not be automatically deleted. The address data will be deleted along with the customer data only if a cascade delete is specified. A cascade delete must be declared explicitly in the XML deployment descriptor, as explained in Chapter 7.

The ejbRemove( ) method in container-managed persistence notifies the entity bean that it’s about to be removed and its data is about to be deleted. This notification occurs after the client invokes one of the remove methods defined in a component interface but before the container actually deletes the data. It gives the bean developer a chance to do some last-minute cleanup before the entity is removed. Any cleanup operations that might ordinarily be done in the ejbPassivate( ) method should also be done in the ejbRemove( ) method, because the bean will be pooled after the ejbRemove( ) method completes without having its ejbPassivate( ) method invoked.

In bean-managed persistence, the bean developer is responsible for implementing the logic that deletes the entity bean’s data from the database.



[26] The specification does not actually require that the record be inserted into the database immediately after the ejbCreate( ) method is called (step 6). As an alternative, the record insert may be deferred until after the ejbPostCreate( ) method executes or even until the end of the transaction.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.36.194