Transactions with EJB 2.0

This example lets Hogwarts students sign up for classes online. We're using two CMP beans, `Student` and `Course`, to store the Student and Course entities. We're also using a Session Bean to act as a facade to the entity beans.

This example uses the following schema:

        CREATE TABLE transaction_student (
          id INTEGER NOT NULL,
          name VARCHAR(250) NOT NULL,
          password VARCHAR(250) NOT NULL,
          gender VARCHAR(6) NOT NULL,

          PRIMARY KEY(name)
        );

        CREATE TABLE transaction_course(
          id INTEGER NOT NULL,
          name VARCHAR(250) NOT NULL,
          room VARCHAR(250) NOT NULL,
          teacher VARCHAR(250) NOT NULL,
          max_student_amount INTEGER,

          PRIMARY KEY(id)
        );

        CREATE TABLE transaction_student_course_mapping (
          transaction_student VARCHAR(250) NOT NULL,
          transaction_course VARCHAR(250) NOT NULL
        );

Session Beans

Unlike CMP beans, session beans don't represent entities. They contain business logic and are typically used to group calls to CMP beans, presenting the Client with a simple API. Using a session bean facade keeps our Client servlet clean because much of the business logic can be put inside the session bean. Furthermore, using a session bean facade allows us to take advantage of Resin-CMPs inbuilt transaction management.

Session Beans have the same general structure as CMP beans. They consist of a Home Interface, a Local Interface, and an Implementation Class. Note that the implementation class needs to implement `javax.ejb.SessionBean`. In Resin-CMP, you can simply extend `com.caucho.servlet.http.AbstractSessionBean`, which provides an empty implementation of the `SessionBean` interface.

Session beans are defined in the deployment descriptor within a <session> block. We have set the <session-type> value to `Stateful` to indicate that we want one dedicated Session Bean instance for each client. This lets us save data inside the bean between method calls.
We could have declared our bean to be stateless. Stateless Session beans are more efficient because they can be pooled. But stateless session beans are not as convenient because they cannot store data between client requests.

     <session>
      <ejb-name>transaction_registration_session</ejb-name>
      <local-home>example.cmp.transaction.RegistrationSessionHome</local-home>
      <local>example.cmp.transaction.RegistrationSession</local>
      <ejb-class>example.cmp.transaction.RegistrationSessionBean</ejb-class>
      <session-type>Stateful</session-type>
      <transaction-type>Container</transaction-type>
     </session>

EJB Transactions

Methods in Session and CMP beans are configured individually for Resin-CMPs automatic transaction support. Transaction management setup is performed in the deployment descriptor's <assembly-descriptor> section. For the `transaction_registration_session` bean, we set the `finalizeRegistration` method's <trans-attribute> to "Required". This means that the `finalizeRegistration()` method will always be part of a transaction.

All other methods in the `transaction_registration_session` bean are exempted from Resin-CMP's transaction management because their <trans-attribute> property is set to "Never". This results in a small performance gain because the container has less work to do for these methods.

The <assembly-descriptor> can be ommitted in Resin-CMP. The container defaults to setting the "Required" attribute for all methods in all EJBs.

     <assembly-descriptor>
      <container-transaction>
       <method>
        <ejb-name>transaction_registration_session</ejb-name>
        <method-intf>Remote</method-intf>
        <method-name>*</method-name>
       </method>
       <trans-attribute>Never</trans-attribute>
      </container-transaction>
      <container-transaction>
       <method>
        <ejb-name>transaction_registration_session</ejb-name>
        <method-intf>Remote</method-intf>
        <method-name>finalizeRegistration</method-name>
       </method>
       <trans-attribute>Required</trans-attribute>>
      </container-transaction>
      ...
     </assembly-descriptor>

Security

The latest EJB 2.0 draft specification does not specify how the container may pass security credentials to its beans. Containers have to come up with their own schemes. Resin-CMP obtains a user `Principal` object during servlet authentication via Resin Core's `Authenticator` mechanism. This `Principal` is automatically passed to any EJBs that your servlet or jsp clients may use.

The servlet's security is set up in web.xml. Resin's authentication system retains a `Principal` for each successfully logged in user.

       <login-config auth-method='form'>
         <form-login-config form-login-page='/login.xtp'
                            form-error-page='/login_error.xtp'/>
         <authenticator id='example.cmp.transaction.CMPAuthenticator'/>
       </login-config>
       <security-constraint
        url-regexp='/servlet/example.cmp.transaction.RegistrationServlet'
        role-name='student'/>

In `RegistrationSessionBean`'s `ejbCreate()` method, we can obtain this `Principal` from the SessionContext:

      student = (Student) ctx.getCallerPrincipal();

Resin-CMP makes sure that the SessionContext knows about the caller `Principal` that was created during servlet authentication.

The example

When first called during an `HttpServletSession`, the `RegistrationServlet` obtains an instance of the `RegistrationSession` bean. `RegistrationSession` is a stateful session bean, dedicated to the current client. The servlet saves a reference in its `HttpServletSession` object so that we can work with the same Session Bean during subsequent web requests.

The `RegistrationSession` bean lets the user select any number of courses and saves the current selections in a `Collection` object. Selecting and deselecting courses does not cause any interaction with the database.

The `finalizeRegistration()` method tries to commit the selected courses to the database. If this succeeds for all courses, the transaction has committed and is successful. If one of the courses cannot be submitted for any reason, all previous course submission to the database are rolled back and the transaction has failed.

We notify Resin-CMP by calling `SessionContext.setRollbackOnly()` that the transaction has failed. Resin then rolls back any previous course submissions.

Our `RegistraionSessionBean.java` implementation class implements the `javax.ejb.SessionSynchronization` interface. One of the three methods defined by this interface is `afterCompletion(boolean committed)`. We can use this method to perform any task based on the outcome of a transaction. For example, in this example, we could send an email notification whenever a new schedule has been successfully committed.