Implementing a Session Bean

   

Now that you've seen how to declare the component and home interfaces for a session bean, the next step is to look at implementing the bean class itself. What's important here is to understand the methods you're required to implement, how the container manages the life cycle of a session bean, and how to access other EJBs and resources, such as a database, from a session bean's methods.

The SessionBean Interface

All session beans must implement the SessionBean interface, which extends EnterpriseBean just like the EntityBean interface does. Remember that EnterpriseBean is simply a marker interface, so it doesn't add any methods to the set that your session beans have to implement. The container uses the methods declared by SessionBean to notify a bean instance of life-cycle events. Table 9.2 describes the methods of the SessionBean interface. The ejb callback methods declared by this interface should look very familiar to you from the discussion of entity beans back in Chapter 5.

Table 9.2. Methods of the SessionBean Interface

Return Type

Method Name

Description

void

ejbActivate()

Called by the container just after an instance is activated from its "passive" state.

void

ejbPassivate()

Called by the container just before an instance is passivated.

void

ejbRemove()

Called by the container just before an instance is removed.

void

setSessionContext (SessionContext ctx)

Called by the container to associate a runtime session context with an instance.

You'll see more about how these methods fit into a session bean's life cycle a little later.

The SessionContext Interface

The SessionContext passed to a session bean instance in the setSessionContext method provides the bean with access to the runtime session context that the container manages for the life cycle of the bean instance. The session bean will normally store the SessionContext object in an instance variable within the session bean to hold it as part of its conversational state. As shown in Table 9.3, the SessionContext interface only declares the methods needed to obtain a reference to the component interface.

Table 9.3. The SessionContext Interface

Return Type

Method Name

Description

void

getEJBObject()

Get the EJBObject (the remote interface) currently associated with this instance.

void

getEJBLocalObject()

Get the EJBLocalObject (the local interface) currently associated with this instance.

The SessionContext interface extends the EJBContext interfaces and therefore has access to the methods defined there. Table 9.4 summarizes the methods of EJBContext .

Table 9.4. 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.

Other than providing access to the home interface, EJBContext is concerned with security and transaction management. Principals and roles and how the getCallerPrincipal and isCallerInRole methods are used are covered in Chapter 14, "Security Design and Management." The getRollbackOnly and setRollbackOnly methods of EJBContext are valid only if you're using container-managed transaction demarcation. You can call getUserTransaction only if you're using bean-managed demarcation. You'll learn more about what these methods do in Chapter 12.

Caution

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


Session Bean's Life Cycle

The container manages a pool of session bean instances to efficiently serve its clients. Based on the pooling configuration and the requests received from clients , the container determines when an instance is created, assigned to a client, passivated, activated, and destroyed . Part of this control consists of invoking methods on the instance that include the methods declared by SessionBean and the ejbCreate method (or methods) you implement. The container also delegates the execution of business method requests to your bean implementation. The stages in the life cycle of a session bean are different for stateless and stateful beans. As you might expect, the life cycle for a stateless session bean is much simpler than that for a stateful one, so it's best to start there.

The EJB specification defines two states for a stateless session bean instance: it either does not exist or it's in a method-ready state. A session object comes into existence when the container calls newInstance on the associated Class object that represents your bean implementation class. This could be done in response to a client calling a create method, but usually the container works independently from its clients in this case and creates instances only when it populates its object pool. After an instance is created, the container calls the object's setSessionContext and ejbCreate methods. You can declare only a single create method for a stateless bean, so the only ejbCreate method you declare should look something like the following:

 public void ejbCreate() throws CreateException {   // do any required initialization that's common to all clients  } 

Just like entity beans, an ejbCreate method for a session bean must be public , have a name that begins with ejbCreate , and it can't be declared as static or final . It can throw any application exceptions, such as CreateException , to report a problem, but it doesn't have to be declared to throw any. Unlike entity beans, an ejbCreate method must be declared to return void (because there's no primary key). For a stateless session bean, the one ejbCreate method you implement must be named ejbCreate exactly and it must not take any arguments. Remember that you don't need any arguments because an instance can't be used to hold state specific to a client. Most stateless session beans you implement will have an ejbCreate method that does nothing. The exception is when you want to hold a reference to a common resource, such as a connection factory object.

When the ejbCreate method has completed, the instance moves into the method-ready state. It's within this state that a stateless session object services client requests by executing its business methods. When a client calls a business method on a reference to the bean's component interface, the container selects an instance from the pool of those in the method-ready state and delegates the call to it. When the call completes, the instance is returned to the pool and no longer is associated with a particular client. Subsequent calls are handled the same way, so there is little chance of a client being assigned the same instance again.

Just like a create call by a client doesn't necessarily cause an instance of a session bean to be created, a call to remove doesn't cause an instance to be destroyed. After a client calls a business method on a stateless session object, the instance is returned to the method-ready state for use by any client. A client can call remove , but the container doesn't need to do anything in response. Only when the container is shutting down or reducing the number of instances in the method ready pool does it remove any session objects. Just before an object is removed, the container calls the ejbRemove method on the instance. This is where you should release any resources that you obtained references to in ejbCreate .

Figure 9.1 illustrates the life cycle of a stateless session bean instance.

Figure 9.1. A stateless session bean has a relatively simple life cycle.

graphics/09fig01.gif

Like a stateless session bean, a stateful bean can be described using the states does not exist and method ready . A stateful session bean can also be classified as being in a passive or a method-ready in transaction state. The life cycle for a stateful bean is more complex because stateless beans are never passivated and a stateful bean instance must ensure that it's involved in only a single transaction at a time.

Unlike stateless beans, the client exercises some control over the life cycle of a stateful bean. To start with, instead of being pulled from a pool, an instance of a stateful session bean is created when a client calls a create method on the bean's home interface. The container then invokes the newInstance method on the class and calls setSessionContext and ejbCreate . Remember that you can have multiple create methods (and corresponding ejbCreate methods) for a stateful bean based on how you want to initialize an instance. When the appropriate ejbCreate method completes, the instance moves into the method-ready state. The client then receives a component interface reference back from the create method.

When a client calls a business method on a stateful session bean, the container always delegates the call to the instance created specifically for the client. If there isn't a transaction context associated with the call, the session object simply executes the method and remains in the method-ready state. The interaction of the container with a stateful session bean in the method-ready state changes if a business method is called within the context of a transaction. You'll see a more complete description of transactions in Chapter 12, but it's important to cover enough now for you to see all the states in a stateful bean's life cycle.

A stateful session bean can optionally implement the SessionSynchronization interface. The bean can use this interface to be notified when it first becomes involved in a transaction and when that transaction completes. A session bean doesn't have to implement this interface, so you should do so only if a bean needs to be notified about transaction events.

Caution

This will become more relevant when you get to Chapter 12, but it's important to note that, although session beans participate in transactions, their instance variables aren't controlled by them. If a session bean instance variable is modified during a transaction, it isn't automatically reset to its previous value if the transaction rolls back. This is true for both stateful and stateless session beans. You'll see later how the SessionSynchronization interface allows you to handle this for stateful session beans. For stateless beans, this point emphasizes the fact that instance variables in stateless session beans are really appropriate only for holding resource references that can be established and held throughout the lifetime of a bean instance.


For more information on the SessionSynchronization interface, see "Transaction Synchronization," p. 352 .

Table 9.5 describes the method of the SessionSynchronization interface so that you can see where they fit into a session object's life cycle.

Table 9.5. Methods of the SessionSynchronization Interface

Return Type

Method Name

Description

void

afterBegin()

Notifies the bean instance that a new transaction has started.

void

afterCompletion (boolean committed)

Notifies the bean instance whether a transaction was successful or rolled back.

void

beforeCompletion()

Notifies the bean instance that a transaction is about to be committed.

When a stateful session bean that implements SessionSynchronization is included in a transaction, the container calls its afterBegin method when the client makes its first call on a business method after the transaction has started. The instance then enters the method ready in transaction state and executes the business method. The instance remains in this state while business methods are invoked within the transaction. When the transaction is about to commit, the beforeCompletion method is invoked. After the transaction has completed, the afterCompletion method is invoked with an argument that informs the instance of the transaction's outcome. The instance then returns to the method-ready state where it can execute non-transactional methods or be enlisted in another transaction.

While a session object is in the method ready in transaction state, it's an error for it to be called with another transaction context. The container is also prohibited from passivating the object while it's in this state. If the container chooses to passivate an instance in the method-ready state, it calls the ejbPassivate method on the instance and moves it into the passive state. An instance should release any resources it holds within ejbPassivate . An instance leaves the passive state either by being activated or removed. If a passivated instance is activated, the container calls its ejbActivate method and returns it to the method-ready state. If a timeout occurs (based on a time limit determined by the deployer), the container can remove a passivated instance. A timeout can apply to an instance in either the passive or the method-ready state. The client can also cause an instance in the method-ready state to be destroyed by calling a remove method on it. Before an instance is removed, its ejbRemove method is called. This method should release its resources just like in ejbPassivate . After a client has called remove on either the component reference or the home interface for a session bean, the client can no longer reference the instance. If the client does attempt to reference the instance after the remove operation, a NoSuchObjectException or NoSuchObjectLocalException will be thrown.

Figure 9.2 summarizes the life cycle of a stateful session bean instance.

Figure 9.2. A stateful session bean moves between four states during its life cycle.

graphics/09fig02.gif

Maintaining Conversational State

In general, the conversational state of a stateful session bean is made up of all the bean's instance variables and the objects that can be reached by following the references held by its instance fields. Other conversational state might include open resources such as sockets or database connections. Because the container can passivate a stateful session bean, it's the responsibility of the bean provider to ensure that the conversational state is ready to be passivated at the proper time. The container gives you the chance to do this by invoking ejbPassivate right before a bean instance is passivated. The container must be able to serialize a session object after ejbPassivate is called. This means that after ejbPassivate completes, all non-transient fields must be one of the following:

  • null .

  • A serializable object.

  • A component interface reference to an enterprise bean.

  • A home interface reference to an enterprise bean.

  • A reference to a SessionContext object.

  • A reference to a resource manager connection factory.

  • A reference to a UserTransaction interface.

  • A reference to an environment naming context or any of its subcontexts.

  • An object that is not immediately serializable but becomes serializable during the serialization process. For example, storing a collection of remote interfaces in the conversational state.

Any reference held by a session bean that doesn't correspond to one of the choices just listed needs to be replaced so that it does. What this means in practical terms is that some references held by a stateful session bean need to be released within ejbPassivate . For example, an open database connection held in an instance field must be closed in ejbPassivate and the reference set to null . The connection can then be reopened in ejbActivate .

Caution

You should never count on the values of transient fields to be maintained when an instance is passivated and subsequently activated. You must assume that a container might use Java serialization to perform passivation, which doesn't save transient fields. You also can't assume that transient fields are reset to their default values when an instance is activated. Because of these issues, the EJB specification discourages the declaration of transient fields in session beans.


Even though a stateful session bean can be included in a transaction, the container doesn't roll its conversational state back to its initial state if the transaction rolls back. If you need to reset the conversational state back to what it was prior to the start of the transaction, you must use the afterCompletion method of SessionSynchronization . If this method is called with an indication of a rollback, you can reset the conversational state back to its original state.

Accessing the Environment

Session beans have the same capability as entity beans to access their environment. You can use a JNDI lookup within a session bean method to obtain the value of an environment entry or a reference to a connection factory. If you need to access the database directly from a session bean, you can obtain a connection using the same approach defined for a BMP entity bean in Chapter 6, "Bean-Managed Persistence."

For more information on obtaining a connection to a database, see "Configuring a Data Source," p. 152 .

Accessing Other EJBs

Many of the session beans you write will need to access one or more entity objects to do their work. The way to do this is to declare an EJB reference in the session bean's deployment descriptor for each entity bean it uses. You can then use this reference to look up the home interface for the entity. For example, the auction house session bean needs to pass a submit bid request on to the appropriate auction entity. This session bean's descriptor must include a reference like the following:

 <ejb-jar>    <enterprise-beans>      <session>        <ejb-name>AuctionHouse</ejb-name>        ...        <ejb-local-ref>          <description>This EJB reference is used to locate an auction          </description>          <ejb-ref-name>ejb/EnglishAuction</ejb-ref-name>          <ejb-ref-type>Entity</ejb-ref-type>          <local-home>com.que.ejb20.auction.model.EnglishAuctionHome</local-home>          <local>com.que.ejb20.auction.model.EnglishAuction</local>        </ejb-local-ref>        ...      </session>      ...    </enterprise-beans>    ...  </ejb-jar> 

The deployment information would need to include a similar entry for the bidder entity bean. With these two references available, the submitBid method of AuctionHouseBean could be implemented as shown in Listing 9.6.

Listing 9.6 submitBid “Using EJB References in a Session Bean
 package com.que.ejb20.auction.controller;  ...  public class AuctionHouseBean implements SessionBean {   ...    public String submitBid(double bidAmount, int auctionId, int bidderId)     throws InvalidBidException, InvalidAuctionStatusException {     try {       // Get the home interface for the english auction bean        EnglishAuctionHome auctionHome = getEnglishAuctionHome();        // Get the home interface for the bidder bean        BidderHome bidderHome = getBidderHome();        // locate the specified auction        EnglishAuction auction = auctionHome.findByPrimaryKey(         new Integer(auctionId) );        // locate the specified bidder        Bidder bidder = bidderHome.findByPrimaryKey( new Integer(bidderId) );        // submit the bid to the auction        return auction.submitBid(bidAmount, bidder);      }      catch (FinderException fe) {       throw new InvalidBidException("Auction/Bidder ID is invalid");      }    }    ...    private EnglishAuctionHome getEnglishAuctionHome() {     InitialContext initCtx = null;      try {       // Obtain the default initial JNDI context        initCtx = new InitialContext();        // Look up the home interface for the English Auction        // that is defined as an EJB reference in the deployment        // descriptor        Object obj = initCtx.lookup( "java:comp/env/ejb/EnglishAuction" );        return (EnglishAuctionHome)obj;      }      catch (NamingException ex) {       throw new EJBException(ex);      }      finally {       // close the InitialContext        try {         if (initCtx != null) {           initCtx.close();          }        }        catch (Exception ex) {         throw new EJBException(ex);        }      }    }    private BidderHome getBidderHome() {     InitialContext initCtx = null;      try {       // Obtain the default initial JNDI context        initCtx = new InitialContext();        // Look up the home interface for the Bidder        // that is defined as an EJB reference in the deployment        // descriptor        Object obj = initCtx.lookup( "java:comp/env/ejb/Bidder" );        return (BidderHome)obj;      }      catch (NamingException ex) {       throw new EJBException(ex);      }      finally {       // close the InitialContext        try {         if (initCtx != null) {           initCtx.close();          }        }        catch (Exception ex) {         throw new EJBException(ex);        }      }    }    ...  } 


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