4.4 EnrollmentEJB Stateful Session Bean in Detail

Let's now look at how the Benefits Enrollment example application uses the EnrollmentEJB stateful session bean. EnrollmentEJB illustrates the design of a typical stateful session bean. We start with a description of the EnrollmentEJB session bean implementation. Then we discuss how the client that is, EnrollmentWeb in our example uses the EnrollmentEJB's client-view interfaces.

4.4.1 EnrollmentEJB Session Bean Parts

Our example follows the recommended naming convention for an enterprise bean and its interfaces. Because this is a session bean implementing the enrollment business process, we name the session bean class EnrollmentBean. The component interface for EnrollmentBean is Enrollment. The home interface is EnrollmentHome. We refer to the session bean as a whole by using the name EnrollmentEJB.

Because EnrollmentEJB has only local interfaces, it can be accessed only by clients in the same Java virtual machine as the bean itself. The Enrollment interface is a local interface that extends the javax.ejb.EJBLocalObject interface. The EnrollmentHome interface is a local home interface that extends the javax.ejb.EJBLocalHome interface.

Figure 4.10 illustrates the Java classes and interfaces that make up the EnrollmentEJB session bean. (Refer to Graphics, on page xvii, for an explanation of the UML symbols used in this diagram.) In addition to the local interface, local home interface, and the session bean class, EnrollmentEJB uses a number of helper classes.

Figure 4.10. Main Parts of the EnrollmentEJB Session Bean

graphics/04fig10.gif

The Options, Summary, EmployeeInfo, and EnrollmentException helper classes are visible to the client view because they are used by the Enrollment and EnrollmentHome interfaces to pass information between the client and the session bean. The EmployeeInfo helper class passes employee information; the Options class passes a list of available benefits options to the client. The Summary helper class passes a summary of the user's benefits selections; the EnrollmentException class is the application-defined exception.

The DBQueryEmployee, DBInsertSelection, DBQuerySelection, and DBUpdateSelection classes are internal helper classes that implement data access operations. These classes follow the command bean design pattern explained in the section Data Access Command Beans on page 100.

The Employee and Selection classes are used internally by the EnrollmentBean class. They hold the information about employee and selection benefits in their conversational state. The EnrollmentBean class also uses one or more classes that implement the HealthPlan interface and encapsulate the plan-specific information, such as the calculation of the plan premium.

Session Bean Component Interface

Session beans can have a local or remote component interface. Session beans may have a local interface that defines the business methods that a local client, residing in the same JVM as the bean instance, may invoke on the individual session objects. Session beans may also have a remote interface: an RMI interface that can be invoked by clients residing on any machine on the network. The local and remote interfaces are together referred to as the component interface. The bean developer defines the local interface and/or the remote interface, and the EJB container provides its implementation. The implementation delegates to the session bean class instances.

Code Example 4.1 shows the Enrollment local interface for the EnrollmentBean session bean:

Code Example 4.1 Enrollment Local Interface
 package com.star.benefits; import javax.ejb.*; public interface Enrollment extends EJBLocalObject {    EmployeeInfo getEmployeeInfo() throws EnrollmentException;    Options getCoverageOptions() throws EnrollmentException;    void setCoverageOption(int choice) throws EnrollmentException;    Options getMedicalOptions() throws EnrollmentException;    void setMedicalOption(int choice) throws EnrollmentException;    Options getDentalOptions() throws EnrollmentException;    void setDentalOption(int choice) throws EnrollmentException;    boolean getSmokerStatus() throws EnrollmentException;    void setSmokerStatus(boolean status) throws EnrollmentException;    Summary getSummary() throws EnrollmentException;    void commitSelections() throws EnrollmentException; } 

Note that our example Enrollment local interface follows the EJB rules for all enterprise bean local interfaces (see the section Enterprise Bean Local Interface on page 38).

The local interface methods can throw an arbitrary number of application-defined exceptions. We have declared the Enrollment local interface methods to throw the application-defined exception EnrollmentException, which defines the individual error codes thrown by the session bean to its clients.

Session Bean Home Interface

Session beans may have a local home interface that allows a local client to control the life cycle of the session objects. Session beans may also have a remote home interface that can be accessed by clients on any machine on the network. The bean developer defines the local home and/or remote home interface, and the EJB container provides its implementation.

Code Example 4.2 shows the EnrollmentHome local home interface for the EnrollmentBean session bean:

Code Example 4.2 EnrollmentHome Local Home Interface
 package com.star.benefits; import javax.ejb.*; public interface EnrollmentHome extends EJBLocalHome {    Enrollment create(int emplnum) throws CreateException,    EnrollmentException; } 

The EnrollmentHome local home interface follows the EJB rules for defining a session bean local home interface (see Section 2.3.2, Enterprise Bean Home Interfaces, on page 33 for these rules).

Unlike the local interface, a session bean's local home interface cannot define arbitrary methods. It can define only methods whose name starts with create. Although these create methods can have an arbitrary number of arguments and argument types, every create method must return the session bean's local interface. Every session bean's local home interface must define at least one create method. Further, the throws clause of every create method must define the javax.ejb.CreateException exception and may define additional application-specific exceptions.

Client-View Helper Classes

The client-view interfaces for the session bean that is, the local interface and the local home interface use several helper Java classes to pass information between the client and the session bean. The Enrollment bean's client-view interfaces use four helper classes:

  1. The EmployeeInfo class passes employee information, such as the employee's first name and last name.

  2. The Options class passes a list of available options to the client. An Options object contains the description and cost of each option and the currently selected option.

  3. The Summary class passes a summary of the user's selections, enabling the user to confirm the selections before they are committed to the corporate databases.

  4. The EnrollmentException class is an application-defined exception. The methods of EnrollmentBean throw this exception to the client to indicate various application-level errors, such as when invalid values are passed to the methods' input parameters.

Section A.1, Session Bean Helper Classes, on page 361 gives the definition of the four helper classes.

Session Bean Class

A session bean class is a Java class that defines the implementation of certain specific methods. The session bean class implements the session bean's business methods, which are defined in the component interface. This class also defines implementations of the ejbCreate methods that correspond to the create methods defined in the home interface and implementations of the methods defined in the javax.ejb.SessionBean interface. A session bean class may also implement additional helper methods invoked internally by the previous methods.

In our example, the EnrollmentBean class is the session bean class. Code Example 4.3 shows the skeleton of the EnrollmentBean class:

Code Example 4.3 EnrollmentBean Class
 public class EnrollmentBean implements SessionBean {    // public no-arg constructor    public EnrollmentBean() { }    // Implementation of business methods defined in the    // session bean's local interface.    public EmployeeInfo getEmployeeInfo() { ... }    public Options getCoverageOptions() { ... }    public void setCoverageOption(int choice) throws            EnrollmentException { ... }    public Options getMedicalOptions() { ... }    public void setMedicalOption(int choice) throws            EnrollmentException { ... }    public Options getDentalOptions() { ... }    public void setDentalOption(int choice) throws            EnrollmentException { ... }    public boolean getSmokerStatus() { ... }    public void setSmokerStatus(boolean status) { ... }    public Summary getSummary() { ... }    public void commitSelections() { ... }    // Implementation of the create(...) methods defined in    // the session bean's home interface.    public void ejbCreate(int emplNum) throws            EnrollmentException { ... }    // Implementation of the methods defined in the    // javax.ejb.SessionBean interface.    public void ejbRemove() { ... }    public void ejbPassivate() { ... }    public void ejbActivate() { ... }    public void setSessionContext(SessionContext sc) { ... }    // Various helper methods that are used internally by    // the session bean implementation.    private void calculateTotalCostAndPayrollDeduction() { ... }    private void readEnvironmentEntries() { ... }    private static String[] parseClassNames(String list) { ... }    private static void trace(String s) { ... } } 

The EnrollmentBean class is a typical example of a session bean class implementation. Note that we have defined the EnrollmentBean class as public; we cannot define it to be final or abstract. Note also that it has a public constructor that takes no arguments and that it has no finalize method. (In fact, according to the specification, a session bean class must define a public constructor with no arguments, and it cannot define a finalize method.)

The EnrollmentBean class implements four kinds of methods:

  1. The business methods that were declared in the bean's remote interface the getEmployeeInfo, getSummary, and commitSelections methods and the methods to get and set various benefits options.

  2. An ejbCreate method that matches the create method of the home interface (see the section Session Bean Create Methods on page 88 for more information on how these methods are related).

  3. Implementations of the javax.ejb.SessionBean interface methods.

  4. Helper methods that are invoked only internally by its business methods. Note that these methods are declared as private.

Session Bean Business Methods

The session bean class implements the business methods declared by its local interface. The method name, number and types of parameters, and return value type for these business methods must match those defined in the local interface. In addition, the throws clauses for the session bean class business methods must not include more checked exceptions than the throws clauses of the corresponding local interface methods. (Note that the methods in the session bean class can define fewer exceptions than the methods in the local interface.)

Note, too, that the business methods must be declared public. They must not be declared final or static. For example, the EnrollmentBean class defines a number of business methods, including the following:

 public EmployeeInfo getEmployeeInfo() { ... } public Options getCoverageOptions() { ... } public void setCoverageOption(int choice)        throws EnrollmentException { ... } 

These methods match the following methods of the Enrollment local interface:

 EmployeeInfo getEmployeeInfo() throws EnrollmentException; Options getCoverageOptions() throws EnrollmentException; void setCoverageOption(int choice) throws EnrollmentException; 
Session Bean Create Methods

The session bean class defines ejbCreate<Method> methods that correspond to the create<Method> methods defined in the home interface. Each create method in the local home or remote home interface of a stateful session bean must have a corresponding ejbCreate method in the session bean class. For example, if the local home interface has a method named createEnrollmentBean, a method named ejbCreateEnrollmentBean must be in the session bean class.

The ejbCreate method has the same number of parameters, and each parameter must be of the same type as those defined in the home interface's corresponding create method. However, unlike the create methods, the ejbCreate methods define void as the return value type. (The return value is void because the container does not need any information from the bean to create the object reference that will be returned to the client as the result of the create method.) The throws clause for each ejbCreate method must not include more checked exceptions than the throws clause of the corresponding create method. However, the throws clause for the ejbCreate method can have fewer exceptions than the corresponding create method.

Like the business methods, the ejbCreate methods must be declared public and must not be declared final or static. For example, the EnrollmentBean class declares the following ejbCreate method:

 public void ejbCreate(int emplnum) throws            EnrollmentException { ... } 

This method corresponds to the create method defined in the EnrollmentHome local home interface:

 Enrollment create(int emplnum) throws            CreateException, EnrollmentException; 
SessionBean Interface Methods

A session bean class is required to implement the four methods defined by the javax.ejb.SessionBean interface. The EJB container invokes these methods on the bean instance at specific points in a session bean instance's life cycle. Code Example 4.4 shows the definition of the SessionBean interface:

Code Example 4.4 SessionBean Interface
 public interface SessionBean extends EnterpriseBean {    void setSessionContext(SessionContext sessionContext) throws           EJBException, RemoteException;    void ejbRemove() throws EJBException, RemoteException;    void ejbActivate() throws EJBException, RemoteException;    void ejbPassivate() throws EJBException, RemoteException; } 

The container invokes the setSessionContext method before invoking any other methods on the bean instance. The container passes the instance a reference to the SessionContext object. The instance can save the reference and use it during its lifetime to invoke methods on the SessionContext object.

When it is about to remove the instance, the container invokes the ejbRemove method. This happens either in response to a client's invoking the remove method or when a session timeout expires (see the section Session Object Removal on page 99).

The container invokes the ejbPassivate method when passivating an instance and the ejbActivate method when activating the instance. The container can passivate an instance to reclaim the resources held by the instance. Passivation and activation are described in the section Session Object Passivation and Activation on page 102.

4.4.2 EnrollmentBean Session Bean Class Details

In this section, we focus in detail on the EnrollmentBean stateful session bean class to understand how a bean developer implements a session bean class. We start by discussing the individual methods that implement the session bean's functions. We also suggest that you first refer to the full listing of the source code for EnrollmentBean, which you can find in Section A.2, EnrollmentBean Source Code, on page 366.

Section 4.4.3, Client Developer's Perspective, on page 104 explains how a client uses the EnrollmentBean. The EnrollmentBean class uses several command beans for database access. The section Data Access Command Beans on page 100 describes these command beans.

Plugging in Multiple Insurance Providers by Using Environment Entries

The EnrollmentBean class does not hard-code into the bean class either the set of insurance providers or the calculation of the insurance premium. To facilitate changes to the set of insurance providers and to the algorithm to calculate the premium, the IT department of Star Enterprise defined an extensible mechanism for "plugging" the insurance information into the EnrollmentEJB bean. The IT department uses the EJB environment mechanism to configure the set of available plans without the need to recompile the EnrollmentEJB bean.

The IT department of Star Enterprise defined the HealthPlan interface to keep insurance-related logic separate from the session bean code. Developers can change the insurance portion of the business logic and not have to change the session bean code. By keeping the insurance premium logic encapsulated within this HealthPlan interface, developers not only can change the algorithm that calculates the insurance cost without having to change the session bean code but also can add insurance providers without having to change the session bean code.

Code Example 4.5 shows the definition of the HealthPlan interface:

Code Example 4.5 HealthPlan Interface
 public interface HealthPlan {    String getPlanId();    String getDescription();    double getCost(int coverage, int age, boolean smokerStatus); } 

Each insurance plan Star Enterprise offers its employees is represented by a class that implements the HealthPlan interface. Each such insurance plan class provides its own implementation for the HealthPlan interface's getCost method, which calculates the insurance cost or premium, based on the employee's selected coverage category, age, and smoker status. Each insurance plan class also implements the getPlanID and getDescription methods, which are used by the Enrollment session bean. Code Example 4.6 provides an example of a class that implements the HealthPlan interface:

Code Example 4.6 Insurance Plan Class Implementing the HealthPlan Interface
 package com.star.plans; import com.star.benefits.HeathPlan; public class PremiumHealthPPOPlan implements HealthPlan {    public PremiumHealthPPOPlan() { super(); }    public String getPlanId() { return "PHPPO"; }    public String getDescription() { return "PremiumHealth PPO"; }    public double getCost(int coverage, int age,       boolean smokerStatus) {          // Calculate the insurance premium based on the          // coverage category, age, and smoking status.          ...          return premium;       }    } } 

Premium Health Care, the insurance provider in our example, implements this class. By providing its own implementation, the insurance company has the flexibility to use Java code to describe the algorithm that determines the insurance premium, based on the employee's coverage category, age, and smoking status. Other insurance providers chosen by Star Enterprise would provide their own implementations of the HealthPlan interface.

The IT department at Star Enterprise uses the EnrollmentEJB bean's environment entries to configure the list of medical and dental plans available to employees. In our example, the deployer sets the medicalPlans and dentalPlans environment entries to a colon-separated list of Java classes that implement the HealthPlan interface (see Code Example 4.7):

Code Example 4.7 Deployment Descriptor Environment Entries
 ... <enterprise-beans>    <session>       <display-name>Enrollment Bean</display-name>       <ejb-name>EnrollmentEJB</ejb-name>       ...       <env-entry>          <env-entry-name>medicalPlans</env-entry-name>          <env-entry-type>java.lang.String</env-entry-type>          <env-entry-value>com.star.benefits.plans.PremiumHealth          PPOPlan:com.star.benefits.plans.PremiumHealthHMOPlan:          com.star.benefits.plans.Kooper</env-entry-value>       </env-entry>       <env-entry>          <env-entry-name>dentalPlans</env-entry-name>          <env-entry-type>java.lang.String</env-entry-type>          <env-entry-value>com.star.benefits.plans.MetroDental:          com.star.benefits.plans.ProvidenceDental</env-entry-value>       </env-entry>       ..    </session>    ... 

The EnrollmentEJB bean uses the readEnvironmentEntries method to construct a list of medical plans. The method looks up the value of the medicalPlans environment entry as shown in Code Example 4.8:

Code Example 4.8 Constructing a List of Medical Plans
 ... Context ictx = new InitialContext(); String medicalPlanList = (String) ictx.lookup("java:comp/env/medicalPlans"); String[] medicalPlanClassNames = parseClassNames(medicalPlanList); medicalPlans = new HealthPlan[medicalPlanClassNames.length]; for (int i = 0; i < medicalPlanClassNames.length; i++) {    medicalPlans[i] = (HealthPlan)Class.forName(       medicalPlanClassNames[i]).newInstance(); } ... 
Stateful Session Bean Conversational State

The EnrollmentEJB session object's conversational state consists of the contents of the EnrollmentBean instance variables, including the Java objects reachable from these variables. The ejbCreate method initializes the conversational state, the business methods update the state, and the ejbRemove method destroys the conversational state.

The conversational state of the Enrollment session object consists of a number of objects; some are primitives, such as int, boolean, and double types, and some are Java classes. The EnrollmentBean class declares these objects at the beginning, prior to defining the business methods. The following objects comprise the bean's conversational state: medicalPlans, dentalPlans, employeeNumber, employee, selection, age, medicalSelection, dentalSelection, totalCost, payrollDeduction, employeeDS, benefitsDS, and payroll.

If it passivates a session object during the object's lifetime, the container moves the conversational state to secondary storage. The container restores the state when the object is later activated. The section Session Object Passivation and Activation on page 102 describes how a bean developer deals with session object passivation and activation.

Session Object Creation

How is a stateful session object created? The container creates a session object and an instance of the session bean class when the client invokes one of the create methods defined in the bean's home interface. The container performs the following steps:

  1. Creates an instance of the session bean class, using the constructor that takes no arguments

  2. Invokes the setSessionContext method on the instance

  3. Invokes the ejbCreate method on the instance

For EnrollmentBean, the container uses the following constructor to create a session bean class instance:

 public EnrollmentBean() { } 

The container calls the setSessionContext method to associate the bean instance with its SessionContext interface, which provides methods to access runtime properties of the context in which a session bean instance runs. If, during its life cycle, the bean instance needs to invoke the methods of the SessionContext interface, the instance should retain the SessionContext reference in an instance variable as part of its conversational state.

In our example, the setSessionContext method implementation is empty:

 public void setSessionContext(SessionContext sc) { } 

The method implementation is empty because the EnrollmentBean instances do not need to access the SessionContext interface during their lifetime. If, however, it wants access to the SessionContext reference at a later time, an instance needs to save this reference, and it does so from within the setSessionContext method. Rather than implementing an empty method, the developer includes code in setSessionContext to save the SessionContext reference. Section 11.3.7, Programmatic Security API, on page 355 illustrates the use of the SessionContext interface.

Finally, the container invokes the ejbCreate method on the instance. If the session bean has multiple ejbCreate methods, the container invokes the one with the same name and arguments corresponding to the create method invoked by the client. The EnrollmentBean class has only a single create method:

 public void ejbCreate(int emplNum) throws EnrollmentException {    ... } 

The session bean instance uses the ejbCreate method to initialize its conversational state: the session class instance variables. The instance may retrieve the session bean environment entries, read information from the corporate databases, and initialize the instance variables for later use by the business methods.

In the ejbCreate method, the EnrollmentBean instance stores the employee number, reads the environment entries, reads and caches the employee record, reads the current benefits selections, and sets up instance variables, as follows:

  • Stores the employee number The instance stores the employee number passed from the client in the instance variable employeeNumber.

  • Reads the environment entries The instance reads the session bean environment entries, using the method readEnvironmentEntries. The environment entries are defined in the deployment descriptor. The deployer configured the values of the java:comp/env/jdbc/EmployeeDB and java:comp/env/jdbc/BenefitsDB environment entries so that the session bean used the appropriate corporate databases. The deployer also configured the value of the java:comp/env/ejb/PayrollEJB environment entry to the home interface of the dependent enterprise beans (the PayrollEJB bean, in our example). The instance also obtains from the environment entries the names of the classes for the configured medical and dental plans offered by Star Enterprise to its employees. With this information, the instance builds the medicalPlans and dentalPlans tables used later by the business methods.

  • Reads and caches the employee record Using the DBQueryEmployee command bean, the instance reads the employee record from the database and caches this data in the employee variable. The business methods use this data in their subsequent operations. If the employee record does not exist, the instance throws EJBException, a system-level exception indicating to the container that the instance ran into an unexpected error condition from which it cannot recover. When it catches an EJBException from an instance, the container invokes no other method on the instance and instead destroys the instance.

  • Reads current benefits selections The instance next uses the DBQuerySelection command bean to read the user's current benefits selection record from the database into the selection object. If the record does not exist, the instance initializes the selection object with some default values.

  • Sets up instance variables The instance sets up various other instance variables used later by the business methods. It calculates the user's age, stores the value in the age variable, and builds the medicalPlans and dentalPlans tables that are used to present benefit plan choices to the user.

(See the code listing for ejbCreate in Section A.2, EnrollmentBean Source Code, on page 366.)

It is important to note that the work the instance performs in the ejbCreate method cannot be moved to the constructor or to the setSessionContext method. The reason is that the container might not have the full execution context for the instance when invoking the constructor and setSessionContext methods. If a session bean developer incorrectly attempts to implement the functionality performed by the ejbCreate method in the constructor or the setSessionContext method, the execution of either of these latter methods would likely result in a thrown exception.

Session Bean Business Methods

The EnrollmentBean class implements all the business methods defined in the Enrollment remote interface. This section describes the implementation of the getMedicalOptions, setMedicalOption, and commitSelections business methods, which are illustrative implementations of a typical session bean's business methods.

Code Example 4.9 illustrates the implementation of the getMedicalOptions method:

Code Example 4.9 The getMedicalOptions Method Implementation
 public Options getMedicalOptions() {    Options opt = new Options(medicalPlans.length);    for (int i = 0; i < medicalPlans.length; i++) {       HealthPlan plan = medicalPlans[i];       opt.setOptionDescription(i, plan.getDescription());       opt.setOptionCost(i,          plan.getCost(selection.coverage,             age, selection.smokerStatus));    }    opt.setSelectedOption(medicalSelection);    return opt; } 

The getMedicalOptions method creates an Options object and initializes it with the descriptions and costs of the available medical plans. The method also sets an indicator to the current medical plan selection and finishes by returning the initialized Options object to the client. Note that the getMedicalOptions method uses the information stored in the instance's conversational state from the execution of the ejbCreate method. (For example, the ejbCreate method stored the table of the available medical plans in the medicalPlan variable.) This illustrates that the instance's conversational state holds the information across client-invoked methods.

Code Example 4.10 shows the implementation for the setMedicalOption method:

Code Example 4.10 The setMedicalOption Method Implementation
 public void setMedicalOption(int choice) throws EnrollmentException {    if (choice >= 0 && choice < medicalPlans.length) {       medicalSelection = choice;       selection.medicalPlanId = medicalPlans[choice].getPlanId();    } else {       throw new EnrollmentException(                EnrollmentException.INVAL_PARAM);    } } 

The setMedicalOption method takes a single input argument: an index number into the list of medical plans in the Options object. (This index number was returned by the previously invoked getMedicalPlans method.) The setMedicalPlan method first checks the validity of the input argument. If the argument is valid, setMedicalPlan updates the session bean conversational state to reflect the selected medical plan: It updates the selection object, the medicalSelection variable, and the totalCost and payrollDeduction variables.

If the argument is invalid, the method throws EnrollmentException to the client. Because EnrollmentException is an application-defined exception, it does not cause the container to remove the session object or to roll back the transaction, unlike the system-level exception EJBException. The client can continue the enrollment business process by invoking the setMedicalOption method with a valid choice.

The commitSelections method updates the corporate databases to reflect the user's selection of benefits. Code Example 4.11 shows its implementation:

Code Example 4.11 The commitSelections Method Implementation
 public void commitSelections() {    // Insert new or update existing benefits selection record.    if (createSelection) {       DBInsertSelection cmd1 = null;       try {          cmd1 = new DBInsertSelection(benefitsDS);          cmd1.setEmplNumber(employeeNumber);          cmd1.setCoverage(selection.coverage);          cmd1.setMedicalPlanId(selection.medicalPlanId);          cmd1.setDentalPlanId(selection.dentalPlanId);          cmd1.setSmokerStatus(selection.smokerStatus);          createSelection = false;       } catch (SQLException ex) {          throw new EJBException(ex);       } finally {          if (cmd1 != null)             cmd1.release();       }    } else {       DBUpdateSelection cmd2 = null;       try {            cmd2 = new DBUpdateSelection(benefitsDS);            cmd2.setEmplNumber(employeeNumber);            cmd2.setCoverage(selection.coverage);            cmd2.setMedicalPlanId(selection.medicalPlanId);            cmd2.setDentalPlanId(selection.dentalPlanId);            cmd2.setSmokerStatus(selection.smokerStatus);            cmd2.execute();       } catch (SQLException ex) {          throw new EJBException(ex);       } finally {          if (cmd2 != null)             cmd2.release();       }    }    // Update information in the payroll system.    DeductionUpdateBean cmd = null;    try {       cmd = new DeductionUpdateBean(payroll);       cmd.setEmployee(employeeNumber);       cmd.setDeduction(payrollDeduction);       cmd.execute();    } catch (Exception ex) {       throw new EJBException();    } finally {       if (cmd != null) cmd.release();    } } 

The commitSelections method updates two databases: the selections table in BenefitsDatabase and, via the DeductionUpdateBean command bean and PayrollEJB enterprise bean, the paycheck table in PayrollDatabase. The EJB container ensures that the update of the multiple databases is performed as a transaction. As you can see, the bean developer does not have to write any code to manage the transaction.

The bean developer used the deployment descriptor to specify that the commitSelections method must run in a transaction. See Chapter 10, Understanding Transactions, for more information on how the transaction attributes specified in the deployment descriptor instruct the container to manage transactions on behalf of the enterprise bean.

Session Object Removal

When finished using a session object, a client removes the object by calling the remove method of the remote interface or home interface. The client's invocation of the remove method on the session object or home object causes the container to invoke the ejbRemove method on the session bean instance. The instance uses the ejbRemove method to release any resources that it has accumulated. For example, the EnrollmentBean bean instance uses the ejbRemove method to remove the Payroll session object, as shown in Code Example 4.12:

Code Example 4.12 Using the ejbRemove Method
 public void ejbRemove() {       try {          payroll.remove();       } catch (Exception ex) { } } 

The client invocation of the remove method is the normal way of removing a session object. In our application, EnrollmentWeb invokes the remove method. However, there are other ways to remove a session object.

When a deployer deploys the session bean in an EJB container, the container typically allows the deployer to specify a client inactivity timeout for the session bean. The client inactivity timeout is a specified period of time. If the client does not invoke a session object for the amount of time specified by the timeout value, the container automatically removes the session object. When this happens, the container may invoke the ejbRemove method on the session bean instance before removing the session object. The container is not obligated to invoke the ejbRemove method when the client inactivity timeout occurs.

Under what circumstances might the container not invoke the ejbRemove method? If the session bean object is in the passivated state at the time of the removal, the container is not required to activate a passivated session bean instance for the sole purpose of removing the instance. The EJB specification allows the container to skip invoking the ejbActivate method to activate a passivated session bean instance solely to invoke the ejbRemove method on the instance. This enables the container to avoid the overhead of activating the session bean instance. If the session bean objects allocate resources other than Java objects, such as records in a database, and these objects are normally released in ejbRemove, the application should provide a cleanup mechanism to avoid resource leakage because of the missed ejbRemove calls. For example, the application can include a program that periodically cleans up the resources that have not been released by the missed ejbRemove calls.

Note that the bean developer does not have to release resource manager connections, such as JDBC connections, in the ejbRemove method. The EJB container tracks all resource manager connections held by a session bean instance; the container automatically releases the connections when the instance is removed. This automatic release of JDBC connections is not shown in our example, because the EnrollmentEJB bean does not retain open database connections across business methods.

Data Access Command Beans

The EJB specification does not prescribe any specific data access strategy for a session bean. In our example application, the EnrollmentBean class delegates all database operations to command beans.

A command bean is a design pattern used frequently in enterprise applications. An application uses a command bean to encapsulate a call to another application or a database call. Note that a command bean is a regular JavaBean, not an enterprise bean. Code Example 4.11 on page 99 illustrates the use of a command bean.

The application creates a command bean instance and then invokes zero or more set methods to pass the input parameters to the intended application or database call. The application then invokes the execute method, which makes the call to the target application or database. Finally, the application invokes zero or more get methods to obtain the values of the output arguments.

The command bean design pattern ensures uniformity with the interface in calling an application or a database. That is, the calling sequence looks the same, regardless of the type of the called application or database. This uniformity makes it possible to use command beans in application development tools.

The EnrollmentBean class uses five command beans for its data access. Descriptions of these command beans and their code can be found in the Code Examples A.17 through A.21.

The DBQueryEmployee command bean reads employee information for an employee with a given employee number from a database (Code Example A.17). The DBQuerySelection command bean reads the benefits selections for an employee with a given employee number from a database (Code Example A.18). The DBInsertSelection command bean inserts benefits selections into the database (Code Example A.19). The DBUpdateSelection command bean updates benefits selections in the database (Code Example A.20). Code Example A.20 illustrates the database-related command beans' superclasses.

The DeductionUpdateBean command bean encapsulates the invocation of the setBenefitsDeduction method on the Payroll enterprise bean (see Code Example 4.11). In Chapter 6, Using Message-Driven Beans and Connectors, we show an alternative implementation of the DeductionUpdateBean, one that uses a message-driven bean to update the payroll database.

Database Connections

It is usually good practice to acquire JDBC connections just before they are needed and to release them as soon as they are not needed. The command beans follow this practice for JDBC connections:

  • Each command bean acquires a JDBC connection prior to executing a JDBC statement.

  • Once it has established the JDBC connection, each command bean object creates and executes a particular JDBC statement.

  • After the statement executes, each command bean releases the JDBC connection.

Adhering to this practice establishing a JDBC connection, executing the JDBC statement, and then releasing the connection allows the EJB container to maximize the reuse of the physical database connections by multiple session bean instances. Thus, a small number of physical database connections can be serially reused by a large number of session bean instances. This sharing of physical database connections is transparent to the session bean code. The EJB container provides the ability to reuse connections as a service to the enterprise bean applications. This service is usually called JDBC connection pooling.

What happens if an instance opens a JDBC connection and holds it across multiple client calls? In that case, the number of open JDBC connections would equal the number of active session bean instances, which in turn is equal to the number of users. When many users simultaneously access the application, the number of open JDBC connections could exceed a system-defined resource limit and potentially cause the application to fail. To avoid application failures resulting from open connection failures, the EJB container would have to passivate some instances so that they would release their connections. The application would run correctly, but its performance might be lower because of the overhead of excessive instance passivation and activation.

Session Object Passivation and Activation

A stateful session object lasts for the duration of the business process implemented by the session object. The business process typically spans multiple client-invoked business methods and may last for several minutes, hours, or even days. For example, an employee may start the Benefits Enrollment application, fill in the first two screens, leave for several hours, and, on returning, complete the remaining steps.

The state of a stateful session object often may occupy a nontrivial amount of main memory on the server. In addition, the state may include expensive resources such as TCP/IP (Transmission Control Protocol/Internet Protocol) or database connections. (Our example does not show these types of resources.) Therefore, it is important that the EJB container be able to reclaim the resources by having the capability to save the state of a stateful session object in some form of secondary memory, such as a database. Later, when the state of the session object is once again needed for the invocation of a business method, the EJB container can restore the state from the saved image.

The process of saving the session object's state to secondary memory is called passivation; the process of restoring the state, activation. The container typically passivates a session object when resources need to be freed to process requests from other clients or when the session bean instance needs to be transferred to a different process for load balancing. For example, the container passivates the instance by invoking the ejbPassivate method in the instance, then serializing it and moving it to secondary storage. When it activates the session object, the container restores the session bean instance's state by deserializing the saved image of the passivated instance and then invoking the ejbActivate method on the instance.

Recall from the discussion in Section 4.2.2, Understanding Conversational State, that the instance variables of the session bean class maintain the state of a stateful session object. To passivate a session object, the EJB container uses the Java serialization protocol or another functionally equivalent mechanism to serialize the state of the instance and save it in secondary storage.

For many session beans, including our example EnrollmentEJB bean, the passivation and activation processes do not require any programming effort from the bean developer. The bean developer has to ensure only that the objects held in the session bean instance variables are serializable at passivation.

In addition to serializable objects, the instance's state may include references to several objects that do not need to be serializable at passivation. These objects are

  • References to other enterprise beans' local and remote home and component interfaces

  • References to the SessionContext interface

  • References to the Java Naming and Dictionary Interface (JNDI) context java:comp/env and its subcontexts

  • References to resource manager connection factories, such as JDBC DataSource objects

  • References to the UserTransaction interface

The EJB container recognizes these objects during passivation; therefore, they do not have to be serializable.

Although not prohibited by the EJB specification, the session bean class should not declare fields as transient, because the EJB specification does not specify how the EJB container handles transient fields across instance passivation. Although some containers may preserve the values of transient fields across passivation, others may reset the transient fields to their Java language initial default value.

The EJB container allows the session bean class to participate in the session object passivation and activation protocol. For this purpose, the SessionBean interface defines the ejbPassivate and ejbActivate methods. The EJB container invokes the ejbPassivate method just before passivating the instance. The EJB container invokes the ejbActivate method just after activating the instance.

The session bean instance uses the ejbPassivate method to release any expensive resources that can be easily reconstructed at activation and to ensure that the instance fields contain only serializable objects. For example, an instance must close any open database connections or TCP/IP connections because these objects are not serializable.

The session bean instance uses the ejbActivate method to reacquire the resources released by the ejbPassivate method so that the instance is ready to accept a client-invoked business method. For example, the instance would reopen the database or TCP/IP connections closed in the ejbPassivate method.

The container can invoke the ejbPassivate method on a session bean instance while the instance is not involved in a transaction. Because the bean developer cannot prevent the container from invoking ejbPassivate, the bean must be coded to accept ejbPassivate at any time between transactions.

The EnrollmentEJB class leaves the ejbPassivate and ejbActivate methods empty. The reason is that all the instance variables either are serializable or refer to objects that do not require serialization, as indicated by the previously listed categories.

4.4.3 Client Developer's Perspective

This section describes the EnrollmentEJB session bean from the perspective of the client developer that is, how the client application uses the EnrollmentEJB session bean. In our example, the client is the EnrollmentWeb Web application, which consists of several JSPs.

In this section, we focus on the segments of the EnrollmentWeb code relevant to using a session bean. (The examples show only the relevant portions of the code.) We neither show nor explain EnrollmentWeb's functions outside of its interaction with the session bean. We do not show how the EnrollmentWeb application generates the HTML pages that are sent to the user's browser; nor do we show how the EnrollmentWeb application processes the user input that the browser sends as HTTP post requests.

Session Object Creation

The application process begins when a user visits the Benefits Web site. The Web container logs in the user and invokes the EnrollmentWeb application, which is the client of the EnrollmentSession bean. The EnrollmentWeb application executes the code segment shown in Code Example 4.13 to create an Enrollment session object, which drives the conversation with the user and stores the user-specific information from one HTTP request to the next:

Code Example 4.13 Creating a Session Object from a JSP
 ... import javax.naming.*; import com.star.benefits.*; ... String loginID = request.getUserPrincipal().getName(); int emplNumber = Integer.parseInt(loginID); InitialContext ictx = new InitialContext(); EnrollmentHome enrollmentHome = (EnrollmentHome)            ictx.lookup("java:comp/env/ejb/EnrollmentEJB"); enrollment = enrollmentHome.create(emplNumber); session.setAttribute("EnrollmentBean", enrollment); ... 

The getUserPrincipal method is a servlet API method that obtains the user's login identifier, which is a unique number assigned to each employee. The method returns the login ID that the user entered in the login page displayed to the user by the Web container. EnrollmentWeb then uses the Integer.parseInt method to convert the loginID string to an integer value representing the user's unique employee number.

Next, EnrollmentWeb must locate the bean's local home interface. To do so, EnrollmentWeb first needs to obtain a JNDI initial naming context. The code instantiates a new javax.naming.InitialContext object, which our example calls ictx. EnrollmentWeb then uses the context lookup method to obtain the Enrollment bean's home interface. Note that the deployer has previously configured the EnrollmentWeb's initial naming context such that the name java:comp/env/ejb/EnrollmentEJB resolves to the Enrollment bean's local home object.

The lookup method returns an object that the code must then cast to the expected type. Our example casts it to EnrollmentHome using the Java cast operator because the EnrollmentHome object is a local home reference.

In contrast, we use a remote home reference to access the Payroll bean. For remote home references, such as the one for the Payroll bean, you should use the javax.rmi.PortableRemoteObject.narrow method to perform type narrowing of the client-side representations of the remote home and remote interfaces. This method makes a remote client program interoperable with all compliant EJB container implementations. Note that it is not sufficient simply to use the Java cast operator for remote references; in fact, using the cast operator may not work in some container implementations. (Note that the EJB specification requires that applications use the javax.rmi.PortableRemoteObject.narrow method to perform type conversion of references of the EJB remote home and remote interfaces.)

Once it obtains a reference to the home interface, EnrollmentWeb can call a create method on that home interface to create a session object. In Code Example 4.13, EnrollmentWeb invokes the home interface create method and passes the user's employee number for the method argument. The create method returns a reference to the session object that implements the Enrollment local interface. At this point, EnrollmentWeb can use the session object reference to invoke the bean's business methods. See the next section, Business Method Invocation, for further details.

Finally, the EnrollmentWeb application stores the session object reference of the Enrollment session object in its HTTP session state. The application does this by using the servlet API setAttribute method. The session object represents the HTTP session for the current user.

Business Method Invocation

The local client the EnrollmentWeb application invokes the Enrollment object's business methods to accomplish the tasks of the Benefit Enrollment application. This is the business logic of a session bean, and each session bean has its own unique logic.

Code Example 4.14 shows the business logic portion of the EnrollmentWeb code for our example:

Code Example 4.14 Client Business Method Invocation
 // Get EmployeeInfo. EmployeeInfo employeeInfo = enrollment.getEmployeeInfo(); ... Options coverageOptions = enrollment.getCoverageOptions(); // Display coverageOptions and let the user make a selection. enrollment.setCoverageOption(selection); ... boolean smokerStatus = enrollment.getSmokerStatus(); // Display smoker status screen and let the user make a selection. enrollment.setSmokerStatus(smokerStatus); ... Options medicalOptions = enrollment.getMedicalOptions(); .. // Display medicalOptions and let the user make a selection. enrollment.setMedicalOption(selection); ... Options dentalOptions = enrollment.getDentalOptions(); // Display dentalOptions and let the user make a selection. enrollment.setDentalOption(selection); ... Summary summary = enrollment.getSummary(); // Display summary of selected choices, and prompt user to confirm. enrollment.commitSelections(); ... 

The logic in the code mirrors the sequence of the Benefit Enrollment application's HTML screens (see Figure 4.3 on page 70). EnrollmentWeb starts by gathering the necessary data in this case, the employee data and available coverage options. To accomplish this, EnrollmentWeb first invokes the Enrollment object's getEmployeeInfo method to gather employee information first and last names based on the employee's identifier. The getEmployeeInfo method uses the EmployeeInfo helper class to pass this information.

Next, EnrollmentWeb invokes the getCoverageOptions method, which uses the Options helper class to return the available benefits coverage options. The method returns the various coverage options and their descriptions and the employee's current benefits selection, if any. When it receives all the information, EnrollmentWeb formats the data into an HTML page, with the employee's current coverage option highlighted, and displays the first screen of the Benefits Enrollment application to the user. (This is the Step 1: Select Coverage Category screen.)

The user makes a benefits selection and clicks the Next button. EnrollmentWeb calls the setCoverageOption method to test the validity of the selection. If the user makes an invalid selection, the setCoverageOption method throws EnrollmentException, which displays that the error is the result of an invalid parameter. (Recall that because EnrollmentException is an application-defined exception, it does not cause the container to remove the session object.) The setCoverageOption method saves the selection.

EnrollmentWeb then displays the smoker-status screen and invokes the getSmokerStatus method. This method returns a Boolean type to EnrollmentWeb, indicating the user's smoker status. EnrollmentWeb invokes the setSmokerStatus method to save the user's indicated smoker status. The setSmokerStatus method in turn stores the smoker selection.

Before displaying the third screen in the sequence the medical options screen EnrollmentWeb invokes the getMedicalOptions method to extract and return the available medical coverage options. This method uses the Options helper class to return the medical options, with their descriptions and appropriate costs given the user's smoker status. EnrollmentWeb receives the medical coverage information and formats the data into an HTML page, which is returned to the user's browser as the Step 3: Medical Options screen.

The user selects the desired medical coverage, and EnrollmentWeb uses the setMedicalOption method to transmit the selection to the benefits application, which saves the selection. The method first checks that the selection is valid; if it is not, the method throws the EnrollmentException application exception.

Next, EnrollmentWeb retrieves the available dental options by invoking the getDentalOptions method, formatting the data into an HTML page, and returning the page to the user's browser for display. The user selects a dental option, which EnrollmentWeb saves by invoking the setDentalOption method.

Finally, EnrollmentWeb invokes the getSummary method to retrieve the individual data for the user's previously entered coverage selections and to calculate the total cost of all the options. EnrollmentWeb formats the data into an HTML page and returns it to the user's browser. The user can view his or her selections, see what each selection costs, and verify the total payroll deduction amount for these benefits. The user can modify the selections at this point or accept them. When they are accepted, EnrollmentWeb commits the user's coverage selections by invoking the commitSelections method.

Session Object Removal

The local client the EnrollmentWeb application removes the session object at the completion of the enrollment business process after processing the user's confirmation response that is, after it invokes the enrollment.commitSelections method on the Enrollment session object. This is illustrated as follows:

 ... enrollment.remove(); session.removeAttribute("EnrollmentBean"); ... 

Note that once it removes the stateful session bean, EnrollmentWeb cannot make additional invocations of the bean's business methods. If EnrollmentWeb attempted to invoke a business method on a stateful session object after removing the object, the application would receive the java.rmi.NoSuchObjectException error.

Session Object Identity

A stateful session object has a unique identity. The EJB container assigns the identity to the object at the object's creation. Each invocation of a create method on the session bean home interface results in the generation of a new unique identifier.

Unlike an entity object's object identity, which is visible to the client, the object identifier of a session bean is not available to the client. However, a client may use the isIdentical method of the EJBObject interface to determine whether two session object references refer to the same session object. Code Example 4.15 illustrates this:

Code Example 4.15 Comparing Session Object References
 ... Enrollment obj1 = ...; Enrollment obj2 = ...; if (obj1.isIdentical(obj2)) {    // obj1 and obj2 refer to the same session object.    ... } else {    // obj1 and obj2 refer to different session objects.    ... } 

Most session bean client applications, including our EnrollmentWeb application example, typically do not need to compare the references of session objects for identity purposes. Generally, only entity bean applications need to use comparisons for object identity.



Applying Enterprise Javabeans
Applying Enterprise JavaBeans(TM): Component-Based Development for the J2EE(TM) Platform
ISBN: 0201702673
EAN: 2147483647
Year: 2003
Pages: 110

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