Implementing a Stateless Session Bean

Implementing a Session bean involves defining a class that implements javax.ejb.SessionBean and provides an implementation for the lifecycle methods of the SessionBean, implementations of each method in the home interface, and an implementation of each business method in the remote interface. Before looking in detail at how to do all this, you need to be comfortable with the details of the stateless Session bean lifecycle.

Stateless Session Bean Lifecycle

Stateless beans hold no state for any particular client, but they do have a lifecycle—and thus different states—imposed on them by the EJB architecture. Specifically, these are the interactions between the bean and the container in which it has been deployed.

This is a recurrent theme throughout the EJB architecture, so it is important to fully understand it. The methods you define in your bean will be invoked either by the client or by the EJB container itself. Specifically, the methods invoked by the client will be those defined in the remote interface and the create methods in the home interface, whereas the methods invoked by the container are those defined by the javax.ejb.SessionBean interface.

Figure 5.2 shows the SessionBean interface and its super-interfaces.

Figure 5.2. The javax.ejb.SessionBean interface defines certain lifecycle methods that must be implemented by Session beans.


In the case study, the AgencyBean class indicates that it is a Session bean by implementing this interface:

package agency;

import javax.ejb.*;
// some import statements omitted

public class AgencyBean implements SessionBean
{
    // code omitted
}

The lifecycle for Session beans, as perceived by the Session bean and as likely to be enacted by the EJB container, is as shown in the UML state chart diagram in Figure 5.3.

Figure 5.3. Stateless Session beans have a lifecycle managed by the EJB container.


The lifecycle is as follows:

  • When the EJB container requires an instance of the stateless Session bean (for example, because the pool of instances is too small), it instantiates the bean and then calls the lifecycle method setSessionContext(). This provides the bean with a reference to a SessionContext object, providing access to its security and transaction context.

  • Immediately after the context has been set, the container will call ejbCreate(). This means that the bean is now ready to have methods invoked. Note that the ejbCreate() method is not part of the SessionBean interface, but nevertheless must be declared in the bean.

  • When a client invokes a business method, it is delegated by the bean's EJBObject proxy to the bean itself. During this time, the bean is temporarily bound to the client. When the method completes, the bean is available to be called again.

The binding of the bean to the client lasts only as long as the method takes to execute, so it will typically be just a few milliseconds. The EJB specification specifies this approach so that the bean developer does not need to worry about making the bean thread-safe.

To support the case where two (or more) clients need to invoke the service of some stateless Session bean at the same time, most EJB containers hold a pool of Session beans. In general, the pool will never be larger than the maximum number of concurrent clients. If a container decides that the pool is too large or that some Session bean instances are surplus, it will call the bean's ejbRemove() method.

If the client calls create() or remove(), the bean itself is not necessarily affected. However, the client's reference to the bean will be initialized or destroyed. The client is not aware of the complexities of this lifecycle, so the client's perception of a stateless bean is somewhat simpler, as shown in Figure 5.4.

Figure 5.4. The client's perception of the bean's lifecycle is simple.


From the client's perspective, the bean is simply instantiated when the client callscreate() on the bean's home interface and is removed when the client calls remove() on the bean itself.

Figures 5.3 and 5.4 show how the methods of the SessionBean interface are invoked through the bean's lifecycle. You will have noticed that the ejbActivate() and ejbPassivate() methods are not mentioned; this is because these methods are only called for stateful Session beans, a topic covered later today. However, given that these methods are in the SessionBean interface, they do require an implementation. For stateless Session beans, this implementation will be empty.

Implementing javax.ejb.SessionBean

The implementation of the methods of the SessionBean interface is often boilerplate. The setSessionContext() method will typically just save the supplied SessionContext object:

private SessionContext ctx;
public void setSessionContext(SessionContext ctx) {
    this.ctx = ctx;
}

As already noted, for a stateless Session bean, the ejbActivate() and ejbPassivate() methods should have a null implementation:

public void ejbActivate() { }
public void ejbPassivate() { }

Although ejbRemove() method is part of the SessionBean interface, you'll learn about its implementation in the next section.

Implementing the Home Interface Methods

The home interface has a single method create(). The ejbCreate() method in the bean corresponds to this method. It makes sense to look up JNDI references in the ejbCreate() method and store them in instance variables. This is shown in Listing 5.2.

Listing 5.2. AgencyBean.ejbCreate() Method
private DataSource dataSource;
private String name = "";
public void ejbCreate () throws CreateException {
    InitialContext ic = null;
    try {
        ic = new InitialContext();
        dataSource = (DataSource)ic.lookup("java:comp/env/jdbc/Agency");
    }
    catch (NamingException ex) {
        error("Error connecting to java:comp/env/jdbc/Agency:",ex);
        return;
    }
    try {
        name = (String)ic.lookup("java:comp/env/AgencyName");
    }
    catch (NamingException ex) {
        error("Error looking up java:comp/env/AgencyName:",ex);
    }
}

In this case, the ejbCreate() method makes two lookups from JNDI; the first is to obtain a DataSource (DataSource objects were covered on Day 3, “Naming and Directory Services”), and the other is to obtain the Agency name environment configuration information.

TIP

Many developers prefer to use the setSessionContext() method to initialize a stateless Session bean, as this mirrors the use of the setEntityContext() method used for initializing Entity beans (see Day 6, “Entity EJBs”).


Incidentally, this is a good place to note that a stateless Session bean does not mean that the bean has no state; just that it has no state that is specific to any given client. In the case of the Agency bean, it caches a DataSource and its name in instance variables.

The bean's remote interface inherits a remove() method from EJBObject and the bean's home interface inherits a remove() method from EJBHome. Calls to either of these methods are delegated to the ejbRemove() method of the bean. The implementation is usually very simple; it should just reset state:

public void ejbRemove(){
    dataSource = null;
    name = null;
}

Implementing the Remote Interface Methods

The remaining methods in a Session bean correspond to the business methods defined in the remote interface. The Agency Session bean manipulates the data in the Applicant, Customer, Location, and Skill tables, providing methods to return all the data in a table, to insert a new item, or to delete an existing item. When deleting rows, dependent rows in other tables are also removed (a cascade delete strategy).

The methods that manipulate the database all require a java.sql.Connection to submit SQL to the database. In regular “fat client” applications, the idiom is to create a database connection at application startup and to close the connection only when the user quits the application. This idiom exists because making database connections is expensive in performance terms. When writing EJBs, however, the idiom is the precise opposite. You should obtain the database connection just before it is needed, and close it as soon as your processing is complete. In other words, “acquire late, release early.” This is because the EJB container has already made the database connections and holds them in a pool. When your bean obtains its connection, it is simply being “leased” one from the pool for a period of time. When the connection is “closed,” in reality it is simply returned back to the pool to be used again.

The getLocations() method shows this principle clearly, as shown in Listing 5.3.

Listing 5.3. AgencyBean.getLocations() Method
public Collection getLocations() {
    Connection con = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;
    try {
        con = dataSource.getConnection();
        stmt = con.prepareStatement("SELECT name FROM Location");
        rs = stmt.executeQuery();

        Collection col = new TreeSet();
        while (rs.next()) {
            col.add(rs.getString(1));
        }

        return col;
    }
    catch (SQLException e) {
        error("Error getting Location list",e);
    }
    finally {
        closeConnection(con, stmt, rs);
    }
    return null;
}
private void closeConnection (Connection con,
             PreparedStatement stmt, ResultSet rslt) {
   if (rslt != null) {
      try {
          rslt.close();
      }
      catch (SQLException e) {}
  }
  if (stmt != null) {
      try {
          stmt.close();
      }
      catch (SQLException e) {}
  }
  if (con != null) {
      try {
          con.close();
      }
      catch (SQLException e) {}
  }
}

In this method, you can see the DataSource object obtained in the ejbCreate() method in use. The Connection object is obtained from this DataSource object. Another advantage of this approach is that the user and password information does not need to be embedded within the code; rather, it is set up by the deployer who configures the DataSource using vendor-specific tools. As you remember from Day 2, “The J2EE Platform and Roles,” in the J2EE RI, the DataSource object is configured using the asadmin command-line tool or the Web-based Admin Console.

The other business methods all access the database in a similar manner and can be examined on the code supplied under the examples directory of Day 5 on the accompanying Web site.

Exceptions

Your bean needs to be able to indicate when it hits a problem. Because the client does not call your bean directly, the EJB specification lays out certain rules as to the types of exceptions your bean can throw. For remote clients, there is also the possibility of network problems.

The EJB specification categorizes exceptions as either application exceptions or system exceptions. These correspond quite closely to the regular Java categories of checked exceptions and runtime exceptions.

Figure 5.5 shows the exceptions in the javax.ejb package, indicating which are application and which are system exceptions.

Figure 5.5. Exceptions are either system exceptions or application exceptions.


So, what do these categorizations mean? If a bean throws an application exception, the EJB container will propagate this back to the application client. As you shall see on Day 8, “Transactions and Persistence,” any ongoing transaction is not terminated by an application exception. In other words, the semantics of an application exception are pretty similar to a checked exception; generally, the client can recover if desired.

However, if a bean throws a system exception, that indicates a severe problem that will not be recoverable by the client. For example, if the database connection fails, there is very little that the client can do about it. In such a case, the EJB container will take steps to terminate any ongoing transaction because it is unlikely to complete. Moreover, the EJB container will discard the bean instance that threw the exception. In other words, there is no need to code any clean up logic in your bean after a system exception is thrown.

Although all runtime exceptions are classified as EJB system exceptions and the javax.ejb.EJBException is a RuntimeException provided for your use, this class allows the underlying cause to be wrapped through one of its constructors. The error() helper method in AgencyBean does precisely this:

private void error (String msg, Exception ex) {
    String s = "AgencyBean: "+msg + "
" + ex;
    System.out.println(s);
    throw new EJBException(s,ex);
}

In Figure 5.5, you can see that there is one checked exception, namely java.rmi.RemoteException, that is classified as an EJB system exception rather than as an EJB application exception. Your bean code should never throw this exception; instead, it is reserved for the EJB container itself. If your bean has hit a system exception, it should throw an EJBException rather than RemoteException.

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

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