Example for Developing EJB with CMT

For EJB applications with container-managed transactions, a basic transaction works in the following way:

  1. In the EJB's standard deployment descriptor, the EJB developer or assembler specifies the transaction type by setting the <transaction-type> tag with the value Container (for CMT).

  2. In the standard deployment descriptor, the EJB developer or assembler specifies the transaction attribute by setting the value of the <trans-attribute> tag to one of the following values: NotSupported, Required, Supports, RequiresNew, Mandatory, or Never. This tag enables you to set a default value for all methods of the entire EJB, or a value for each specific method of the EJB. Refer to the “Setting Transaction Attributes” section earlier today for more information.

    Note

    On invoking a method in the EJB, the EJB container checks the <trans-attribute> setting in the deployment descriptor for that method. If no setting is specified for the method, the EJB uses the default <trans-attribute> setting for that EJB.


  3. Depending on the <trans-attribute> setting, the EJB container takes the appropriate action (refer to Table 17.1).

  4. During the execution of the business method, if it is determined (by the application logic) that a rollback is required, the business method calls the setRollbackOnly() method of the EJBContext. This will notify the EJB container that the transaction is to be rolled back at the end of the business method.

    Note

    Calling the setRollbackOnly() method of the EJBContext is allowed only for EJB with CMT.


  5. At the end of the business method (and before it returns), the EJB container completes the transaction either by committing the transaction or rolling it back (if the setRollbackOnly() method was called in step 4).

  6. You can also control transaction timeouts by setting its attribute in the vendor-specific deployment descriptor.

Defining the Bean's Remote Interface

For this example, the EnrollmentCart EJB is implemented as a stateful session bean with container-managed transaction, the remote interface EnrollmentCart defines all business methods, and is stored on disk in the file EnrollmentCart.java, as shown in Listing 17.1.

Listing 17.1. The Full Text of day17/EnrollmentCart.java
package day17;
import java.util.*;
import javax.ejb.*;
import java.rmi.RemoteException;
/**
  EnrollmentCart is the remote interface for enrollment cart
  stateful session bean.
 */
public interface EnrollmentCart extends EJBObject
{
   public void addEnrollment(EnrollmentInfo enroll) throws RemoteException;
   public void deleteEnrollment(int itemId) throws RemoteException;
   public Collection getEnrollments() throws RemoteException;
   public void emptyCart() throws RemoteException;
   public void register() throws InsufficientRoomException, RemoteException;
}

The remote interface uses the EnrollmentInfo class as a helper class to transfer data between the EJB tier and the client tier. The InsufficientRoomException is an application exception to detect any failure to register for a course due to insufficient room in the enrolled class.

Defining the Bean's Home Interface

The home interface is stored in the file EnrollmentCartHome.java, and defines the life cycle method of the EJB, as shown in Listing 17.2.

Listing 17.2. The Full Text of day17/EnrollmentCartHome.java
package day17;
import java.rmi.RemoteException;
import javax.ejb.*;
/**
  EnrollmentCartHome is the remote home interface for
  the enrollment cart stateful session bean.
 */
public interface EnrollmentCartHome extends EJBHome
{
  EnrollmentCart create() throws CreateException, RemoteException;
}

Implementing the Bean's Class

The bean class implements all the methods in both the remote and home interfaces of EnrollmentCart EJB, as shown in Listing 17.3.

Listing 17.3. The Full Text of day17/EnrollmentCartEJB.java
package day17;

import java.util.*;
import javax.ejb.*;
import javax.naming.*;
import java.sql.*;
import java.rmi.RemoteException;

/**
   EnrollmentCartEJB is stateful session bean, representing  the
   private conversation of a specific client. It keeps track of the
   user's current selection of courses.
 */

public class EnrollmentCartEJB implements SessionBean, SessionSynchronization
{
   private SessionContext ejbCtx;
   private HashSet cart;
   private boolean isFailed = false;

   public EnrollmentCartEJB(){}
   // SessionSynchronization methods
   public void afterBegin() {}
   public  void beforeCompletion() {
         if (isFailed)
            ejbCtx.setRollbackOnly();
   }
   public  void afterCompletion(boolean committed) {
        if (committed == false)
          throw new EJBException("Transaction afterCompletion failed");
        System.out.println("afterCompletion: Transaction succeeds...");
     }
   // Business methods
   public void addEnrollment(EnrollmentInfo enroll) throws RemoteException {
       cart.add(enroll);
   }
   public void register() throws InsufficientRoomException, RemoteException {
       java.sql.Statement stmt = null;
       java.sql.Statement stmt2 = null;
       java.sql.Connection conn = null;
       System.out.println("register: started..");
       try{
           InitialContext initCtx  = new InitialContext();
         javax.sql.DataSource ds = (javax.sql.DataSource)
         initCtx.lookup ("java:comp/env/jdbc/styejbDB");
         conn = ds.getConnection();
         stmt = conn.createStatement();
         stmt2 = conn.createStatement();
      } catch (Exception e){
         e.printStackTrace();
      }
      try{
       Iterator it = cart.iterator();
       while (it.hasNext()){
         EnrollmentInfo enroll=(EnrollmentInfo)it.next();
         ResultSet rs = stmt.executeQuery
            ("SELECT * FROM COURSES WHERE COURSEID='" +
                   enroll.getCourseId()+"'");
         while (rs.next()){
              int mlimit = rs.getInt("MAX_LIMIT");
              int curr   = rs.getInt("CURR");
              if (mlimit < curr + 1){
                isFailed = true;
                ejbCtx.setRollbackOnly();
                throw new InsufficientRoomException();
              }else{
                curr++;
                stmt2.executeUpdate("UPDATE COURSES SET CURR =" + curr
                  + " WHERE COURSEID='" + enroll.getCourseId()+"'");
                System.out.println("register: Database updated for: "+
                      enroll.getCourseId());
              }
          }
        }
        System.out.println("register: success..");
      }catch (SQLException ex){
        // This is a system level exception
        throw new EJBException
        ("Transaction rollback due to SQLException: "+ ex.getMessage());
      }
   }
   public Collection getEnrollments() throws RemoteException {
      return cart;
   }

   public void emptyCart() throws RemoteException {
      cart.clear();
   }
   public void deleteEnrollment(int itemId) throws RemoteException {
        for(Iterator i = getEnrollments().iterator(); i.hasNext(); ) {
           EnrollmentInfo tmpEnrol = (EnrollmentInfo) i.next();
           if (tmpEnrol.getItemId() == itemId) {
              getEnrollments().remove(tmpEnrol);
              break;
           }
        }
     }
   // EJB methods
   public void setSessionContext(SessionContext ctx){
      this.ejbCtx = ctx;
   }
   public void ejbCreate() throws CreateException{
      cart = new HashSet();
   }
   public void ejbRemove() {}
   public void ejbActivate(){}
   public void ejbPassivate(){}
}

In the preceding code, the business method register() of the EnrollmentCart is executed within a transaction that is maintained by the container. The EJB implements the SessionSynchronization interface to synchronize its state with the database. An application exception will be raised if any course is out of room while registering all the selected courses in the enrollment cart.

Developing Helper Classes

Listing 17.4 is the code of the helper class EnrollmentInfo, which is used as a data container to transfer data between the EJB tier and the client tier. The EnrollmentInfo class implements the java.io.Serializable interface in order to use it for transferring data across tiers.

Listing 17.4. The Full Text of day17/EnrollmentInfo.java
package day17;
public class EnrollmentInfo implements java.io.Serializable {
  public int itemId = 0;
  public int studentId = 0;
  public String courseId = null;
  public EnrollmentInfo (int itemId, int studentId, String courseId) {
    this.itemId = itemId;
    this.studentId = studentId;
    this.courseId = courseId;
  }
  public int getItemId(){
         return itemId;
  }
  public int getStudentId(){
         return studentId;
  }
  public String getCourseId(){
         return courseId;
  }
}

Listing 17.5 is the code for the InsufficientRoomException used as an application exception.

Listing 17.5. The Full Text of day17/InsufficientRoomException.java
package day17;
public class InsufficientRoomException extends Exception {
   public InsufficientRoomException() {
      super();
   }
   public InsufficientRoomException(Exception e) {
      super(e.toString());
   }
   public InsufficientRoomException(String s) {
      super(s);
   }
}

Packaging the Beans into a JAR File

The deployment descriptor for the EnrollmentCart EJB is listed in Listing 17.6. The EJB developer sets the <session> tag, with the <transaction-type> to be set to Container for the EJB to manage its own state. There is no mechanism for an application assembler to affect enterprise beans with bean-managed transaction demarcation. The application assembler must define transaction attributes for an enterprise bean with container-managed transaction demarcation. We use only the default Required transaction attribute for all methods of the EJB. Listing 17.6 displays the ejb-jar.xml file.

Listing 17.6. The Full Text of day17/ejb-jar.xml
<?xml version="1.0"?>

<!DOCTYPE ejb-jar PUBLIC
'-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN'
'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>

<ejb-jar>
  <enterprise-beans>
    <session>
       <ejb-name>EnrollmentCart</ejb-name>
       <home>day17.EnrollmentCartHome</home>
       <remote>day17.EnrollmentCart</remote>
       <ejb-class>day17.EnrollmentCartEJB</ejb-class>
       <session-type>Stateful</session-type>
       <transaction-type>Container</transaction-type>

        <resource-env-ref>
         <resource-env-ref-name>jdbc/styejbDB</resource-env-ref-name>
         <resource-env-ref-type>javax.sql.DataSource</resource-env-ref-type>
       </resource-env-ref>
    </session>
  </enterprise-beans>
    <assembly-descriptor>
      <container-transaction>
         <description>no description</description>
         <method>
            <ejb-name>EnrollmentCart</ejb-name>
            <method-name>*</method-name>
         </method>
         <trans-attribute>Required</trans-attribute>
      </container-transaction>
   </assembly-descriptor>
</ejb-jar>

For WebLogic Server, Listing 17.7 shows the vendor-specific weblogic-ejb-jar.xml that is used to set the transaction timeout and isolation level.

Listing 17.7. The Full Text of day17/weblogic-ejb-jar.xml
<?xml version="1.0"?>

<!DOCTYPE weblogic-ejb-jar PUBLIC
'-//BEA Systems, Inc.//DTD WebLogic 6.0.0 EJB//EN'
'http://www.bea.com/servers/wls600/dtd/weblogic-ejb-jar.dtd'>

<weblogic-ejb-jar>
  <weblogic-enterprise-bean>
    <ejb-name>EnrollmentCart</ejb-name>
       <stateful-session-descriptor>
         <stateful-session-cache>
            <max-beans-in-cache>5</max-beans-in-cache>
         </stateful-session-cache>
       </stateful-session-descriptor>
       <transaction-descriptor>
        <trans-timeout-seconds>60</trans-timeout-seconds>
       </transaction-descriptor>
       <reference-descriptor>
         <resource-env-description>
							<res-env-ref-name>jdbc/styejbDB</res-env-ref-name>
							<jndi-name>jdbc.styejbDB</jndi-name>
							</resource-env-description>
        </reference-descriptor>
    <jndi-name>day17/EnrollmentCartHome</jndi-name>
 </weblogic-enterprise-bean>
    <transaction-isolation>
     <isolation-level>TRANSACTION_SERIALIZABLE</isolation-level>
       <method>
          <ejb-name>EnrollmentCart</ejb-name>
          <method-name>*</method-name>
       </method>
    </transaction-isolation>
</weblogic-ejb-jar>

Developing and Testing the Client

Listing 17.8 is the client program to test the EnrollmentCart EJB (as saved in the Client.java file). The client adds the selected courses as enrollments, and then tries to register all the courses. If one of the courses cannot be registered due to insufficient room in the class, an exception will be raised by the EJB and printed by the client in the catch clause.

Listing 17.8. The Full Text of day17/Client.java
package day17;
import java.util.*;
import java.rmi.*;
import java.io.*;
import javax.naming.*;
import javax.ejb.*;

/*
  This client demonstrates usage of stateful session bean with
  CMT and SessionSynchronization
 */
public class Client
{

  public static void main(String[] argv){
      System.out.print("Day 17: Demonstration the use of CMT
");
    try{
    Context initialContext = new InitialContext();
    Object object = initialContext.lookup("day17/EnrollmentCartHome");
    EnrollmentCartHome enrollmentCartHome = (EnrollmentCartHome)
     javax.rmi.PortableRemoteObject.narrow(object, EnrollmentCartHome.class);
    EnrollmentCart enrollmentCart = (EnrollmentCart)
                            enrollmentCartHome.create();
    // Add enrollment courses
    enrollmentCart.addEnrollment(new EnrollmentInfo(1, 15, "CS201"));
    enrollmentCart.addEnrollment(new EnrollmentInfo(2, 15, "CS205"));
    enrollmentCart.addEnrollment(new EnrollmentInfo(3, 15, "CS231"));
    // register the courses
    enrollmentCart.register();
    print("Following courses are registered successfully:");
    Collection coll = enrollmentCart.getEnrollments();
    for (Iterator i = coll.iterator(); i.hasNext();){
       EnrollmentInfo enroll = (EnrollmentInfo) i.next();
       print("Course id: " + enroll.getCourseId());
       }
    enrollmentCart.remove();
    }
    catch ( Exception e){
       print("Enrolled courses failed to register.
");
    e.printStackTrace();
    }
  }
  static void print(String s){
     System.out.println(s);
  }
}

Build and Run the Example

To build the example, a build script is provided for WebLogic Server and the JBoss server.

  1. Configure both the JDBC DataSource and connection pool as described on Day 9.

  2. Build the example for the appropriate application server. From the directory Day17, run the build script. This creates a subdirectory named build that contains all the compiled code:

    c:>cd c:styejb
    c:styejb>setEnvWebLogic.bat
    c:styejb>cd day17
    c:styejbday17>buildWebLogic.bat
    								
  3. To run the example, use the appropriate script for each server. Set up the environment for the client in a new command window, and then use the run script in the Day17 directory:

    c:styejb>setEnvWebLogic.bat
    c:styejb>cd day17
    c:styejbday17> runClientWebLogic.bat
    								
..................Content has been hidden....................

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