HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

EJB servers are transactional servers that allow developers to concentrate on business logic. The EJB model implements two-phase commits, transaction context propagation and distributed transaction, although it's up to the vendors to decide which technique to use.

A transaction is formally defined as an "ACID" (atomic, consistent, isolated, durable) unit of work.

  • Atomic transactions are "all or nothing." They either work or they don't – they're never left incomplete.
  • Consistent transactions always leave the system in a consistent state.
  • Isolated transactions execute in a safe manner – they won't fail if other transactions running on the same server are failing.
  • Durable transactionscan survive system failures once they're completed and committed.
For example, an online sale may involve the following steps:
  1. Begin transaction.
    Charge card sale amount.
    Update sale database.
    Update shipping database.
  2. Commit transaction.
The transaction can end in one of two ways: (1) in a commit and everything is saved; or (2) if any step within the transaction fails, the effects of all preceding steps are rolled back or undone. For example, if the shipping database can't be updated, the charge isn't made and the sale database isn't updated.

In a distributed environment handling transactions involves coordinating the various databases that participate in the transaction. In the EJB framework the bean developer can simply define the transaction policy for the bean during the deployment process, using declarative statements, and let the container handle all distributed transactions (called bean-demarcated transactions). Alternatively, the developer can take explicit control of transactions (called client-demarcated transactions).

Transactional Scope and Attributes
Transactional scope is an important part of understanding how transactions work. In EJBs the scope of a transaction includes every bean that participates in a unit of work - the bean method. The scope of a transaction can be traced by looking at the thread of execution. The transaction is propagated to a bean when that bean is invoked and included in the scope of the transaction. Of course, the thread of execution is not the only determining factor in transactional propagation; transactional attributes is the other.

An enterprise bean can take one of the following six attributes in the deployment descriptor, and the container manages the transactions according to the specified attribute. Transaction attributes can be specified for the entire bean or the bean can be fine-tuned by specifying the attributes for individual methods.

1. TX_NOT_SUPPORTED (see Figure 1): This tells the container to invoke bean methods without a transaction context. If a client invokes a bean method from within a transaction context, the container suspends the association between the transaction and the current thread before invoking the method on the enterprise bean instance. The container then resumes the suspended association when the method invocation returns. The suspended transaction context isn't passed to any enterprise bean objects or resources that are used by this bean method.

Figure 1
Figure 1:

2. TX_SUPPORTS (see Figure 2): This tells the container to include the bean or method within the transaction scope in which it is invoked. If a method is part of a transactional scope and it invokes any bean with this attribute, the invoked bean and everything it accesses become a part of the transaction.

Figure 2
Figure 2:

If the client invokes the bean method without a transaction context, the container invokes the bean method without a transaction context.

3. TX_REQUIRED (see Figure 3): This tells the container that the bean method must be invoked within a transaction scope. If a client invokes a bean method from within a transaction context, the container invokes the bean method within the client transaction context. If a client invokes a bean method without a transaction context, the container creates a new transaction context for the invoked bean. The transaction context is then passed to any beans that are used by this bean method.

Figure 3
Figure 3:

4. TX_REQUIRES_NEW (see Figure 4): This tells the container to always invoke the bean method within a new transaction context, regardless of whether the client invokes the method within or without a transaction context. The transaction context is passed to any enterprise bean objects or resources that are used by this bean method.

Figure 4
Figure 4:

5. TX_MANDATORY (see Figure 5): This directs the container to always invoke the bean method within the transaction context associated with the client. The difference between this and the TX_REQUIRED attribute is that if the client attempts to invoke the bean method without a transaction context, the container throws the javax.jts.TransactionRequiredException exception. The transaction context is passed to any beans that are used by the invoked bean method.

Figure 5
Figure 5:

6. TX_BEAN_MANAGED (see Figure 6): This tells the container that the bean class doesn't have its transactional context managed by the server but it uses JTA, more specifically the javax.jts.UserTransaction, to explicitly manage transaction boundaries.

Figure 6
Figure 6:

Using this attribute, however, imposes a restriction that the attributes of different methods cannot be mixed; if even one method has this attribute, then all methods must manage transaction on their own.

Making a bean transactional is expensive at runtime; since it participates in a transaction and conforms to ACID rules, its services can't be shared during the life of a transaction. Declaring a bean to be TX_NOT_SUPPORTED improves performance and may be desirable for EJBs that provide stateless service as they need to conform to the ACID rules.

Transaction Isolation Levels
The transaction isolation level determines how isolated one transaction is from another for read purposes only. These isolation levels are defined in terms of three phenomena (defined in the ANSI/ISO SQL standard -SQL92) that must be prevented between concurrently executing transactions.

  • Dirty reads: A transaction reads data written by another transaction that hasn't been committed yet. In other words, a transaction reads a database row containing uncommitted changes from a second transaction.
  • Nonrepeatable reads: A transaction rereads data it has previously read and finds that another committed transaction has modified or deleted the data. In other words, one transaction reads a row in a table, a second transaction changes the same row and the first transaction rereads the row and gets a different value.
  • Phantom reads: A transaction reexecutes a query, returning a set of rows that satisfies a search condition, and finds that another committed transaction has inserted additional rows that satisfy the condition. In other words, one transaction reads all rows that satisfy a SQL WHERE condition and a second transaction inserts a row that also satisfies the WHERE condition. The first transaction applies the same WHERE condition and gets the row inserted by the second transaction.

Isolation levels aren't new to EJBs; EJB defines these levels based on the ANSI-SQL92 standards. They're mapped in JDBC to the static variables defined in the java.sql.Connection interface. Isolation level, like attributes, can be fine-tuned by specifying them at the method level for EJBs; however, all methods invoked in the same transaction must have the same isolation level.

  1. TRANSACTION_READ_UNCOMMITTED: The transaction can read uncommitted data (data changed by another transaction still in progress).
  2. TRANSACTION_READ_COMMITTED: The transaction can't read uncommitted data.
  3. TRANSACTION_REPEATABLE_READ: The transaction can't change data that's being read by another transaction. Methods with this isolation level, besides having the same behavior as TRANSACTION_READ_COMMITTED, can only execute repeatable reads.
  4. TRANSACTION_SERIALIZABLE: The transaction has exclusive read and update privileges to data by locking it; other transactions can neither write nor read the same data. (This does to transaction what the synchronized keyword does to methods.) It is the most restrictive transaction.
Make no mistake – although the TX_SERIALIZABLE attribute guarantees the highest level of data integrity, it is offset by a performance slag because even simple reads must wait in line. EJBs that need to handle a large number of concurrent transactions should avoid this level. By understanding the level of reads that will occur on the database and how the database handles locking and choosing the correct isolation level, the EJB can be fine-tuned to peak performance (see Table 1).

Table 1

Client-Demarcated Transactions
JTS is a specification based on the CORBA OTS 1.1 for implementing a Java transaction manager that serves as an intermediary between an application and one or more transaction-capable resource managers, such as database servers and messaging systems. The JTS specification includes the JTA API that is used by application programmers to group operations into one or more logical transactions. The Java mapping of the OMG OTS 1.1 specification is specified in two packages: org.omg.CosTransactions and org.omg.CosTSPortability. JTA actually provides three types of services:

  • Transactional operations in client applications
  • Transactional operations in application servers performed on behalf of clients
  • Global transactional management in a Java transaction manager, coordinating multiple transaction-capable resource managers such as database servers and messaging systems
EJBs use the high-level transaction manager interface provided by JTA, while the EJB server uses the JTA high-level transaction manager interface and a standard Java mapping of the X/Open XA protocol to handle transactions (javax.transaction.xa package). EJBs use the simple javax.transaction.UserTransaction interface to communicate with the transaction manager and control transaction boundaries programmatically. (The EJB specification doesn't stipulate any specific transaction service or protocol but requires that the javax.transaction.UserTransaction interface of the JTS be exposed to enterprise beans.) Important methods in UserTransaction interface are:

public void begin()
public void commit()
public int getStatus()
public void rollback()
public void setRollbackOnly()
public void setTransactionTimeout()

The UserTransaction.begin() method starts a global transaction and associates a javax.transaction.Transaction with the execution thread. It throws the NotSupportedException when the calling thread is already associated with a transaction and the transaction manager implementation doesn't support nested transactions.

The UserTransaction.commit() method completes the transaction. If at this point the transaction needs to be rolled back instead of being committed, the transaction manager does so and throws a RollbackException to indicate it.

The UserTransaction.rollback() method undoes any changes made since the start of the transaction and removes the association between the Transaction and the execution thread.

Getting Transaction Access
As mentioned earlier, the ability of the bean to access the transaction service can't be selectively applied to only certain methods of the bean. All methods must have the TX_BEAN_MANAGED attribute. That said, bean methods can get access to the transaction service through the getUserTransaction() method of the javax.ejb.EJBContext interface (the superinterface for SessionContext and EntityContext).

public void doTransaction(String customerName, String password,int
age) throws MyException{
try{
UserTransaction trans=sessionctx. ctx.getUserTransaction().begin();
Context ctx = new InitialContext();
TranstestHome home = (TranstestHome)
ctx.lookup("ejb.TranstestHome"); Transtest
bean = home.create();
bean.putUser("Sameer","word");
bean.putAge("Sameer",10);
trans.commit();
}catch(Exception e){
trans.rollback();
throw new MyException("an exception occurred" +e);
}
}

This UserTransaction also defines two methods: setRollbackOnly() and getRollbackOnly(). The first method allows the bean to veto a transaction explicitly. Once invoked, the transaction can't be committed by anyone, including the container. The second method remains true if the transaction has been so marked and can be used to avoid further unnecessary work in the method.

JTA allows the UserTransaction object to be exposed via JNDI. EJBs shouldn't use this approach as it compromises the "middleware portability"; that is, other EJB servers might not support that approach.

Context ctx = new InitialContext();
UserTransaction utx = (UserTransaction)ctx.lookup("ajndiname");
utx.begin();
// do work
utx.commit();

With entity beans and stateless session beans, a transaction managed with the UserTransaction must start and end in the same method. The reason is that entity and stateless session bean instances are shared across many clients by instance pooling and instance swapping on the server.

Stateful session beans allow the UserTransaction object to span multiple method calls because there's always one instance associated with a client, and it maintains conversational state. This bean state (and state of the transaction) is consistent even when the container makes it undergo an internal activation-passivation cycle to conserve server resources.

public class MyStatefulBean implements SessionBean {
public SessionContext ctx;

public void setSessionContext(SessionContext ctx){
this.ctx=ctx;
}
public void method1(){
ctx.getUserTransaction().begin();
// do some work
}
public void method2(){
// do some more work
}
public void method3(){
// do yet some more work
// and finally commit
ctx.getUserTransaction().commit();
}

Repeated calls to getUserTransaction() in a stateful session bean return a reference to the same UserTransaction object. Its state can be checked using a UserTransaction.getStatus() call.

Stateful session beans involve conversational state between method calls. Sometimes it may be desirable to cache this transactional state and postpone database updates. The javax.ejb.SessionSynchronization allows the server to inform a stateful session bean of the various stages in the transaction by invoking callback methods such as:

  • afterBegin(): Notifies the bean that a new transaction has started – called before the EJB delegates the business methods to the instance
  • afterCompletion(): Notifies the bean that the transaction has completed – can be used to reset instance variables since it will always be invoked
  • beforeCompletion(): Notifies the bean that the transaction is about to be committed
Client-demarcated transactions in stateful session beans across methods should be avoided since they tend to be overtly complex and any improper method invocation locks up resources. Once a stateful session bean is a part of a transaction, there's no way for it to be accessed by any other transaction context (e.g., a method with TX_REQUIRES_NEW is invoked) and the bean can't be removed. In the example above, resource locking will happen when the client doesn't invoke method3().

Exception Handling and Transactions
What happens when exceptions occur depends on the type of exception (checked or unchecked), isolation level and the transactional attribute of the bean method.

The first rule, which may sound strange, is that any exception thrown outside the transaction scope causes the transaction to roll back.

Consider an example of a client invoking a method called doTransaction() on a stateless session bean called MiddleBean. The bean internally then invokes methods on another stateless session bean called TranstestBean. The required code for these beans and the client can be seen in Listings 1 to 4. The sequence of events that occur as a result of the client call is shown in Figure 7.

Figure 7
Figure 7:

The two beans are deployed with the transaction attributes shown in Table 2 and what happens as a result of these attributes is summarized in Figure 8.

Figure 8
Figure 8:

Table 2

In the first case the transaction context is propagated to the TranstestBean bean.

When the exception is thrown in the second bean, it falls within the transaction context; it propagates up and the container traps it in the first bean and rolls back the transaction.

In case two the second bean doesn't participate in the transaction and the transaction context is not propagated to it. The exception thrown falls outside the transaction context; the container detects this and rolls back the transaction. There is no way, however, to undo any changes made in the second bean.

In the third case the second bean has a new transaction context for each of the methods. The transaction for the exception-throwing method is rolled back, the exception moves up and the container rolls back the initial transaction. The other method executes successfully in its own transaction.

In general, methods should be logically atomic – all or nothing. If an exception is intended to indicate that the method can't complete successfully, it shouldn't be caught. If it is caught, the method should try to correct the problem and continue. The method must throw the exception and propagate out of the method for the transaction to be rolled back.

Application exceptions won't cause a rollback if they're thrown and caught within the transactional scope. Runtime exceptions or unchecked exceptions, on the other hand, always cause a transaction to roll back, regardless of the transaction attribute or transactional scope.

Unilateral Decisions
The transaction manager allows certain heuristic or speculative decisions to be made based on the state of all participating resources in a transaction and the underlying two-phase commit protocol. A heuristic decision occurs when one of the resources in the transaction unilaterally decides to commit or roll back the transaction without permission from the transaction manager. This breaks the atomicity of the transaction and is captured by the manager in the following exceptions:

  • javax.transaction.HeuristicCommitException is thrown when a rollback is requested but a heuristic decision was made and all updates were committed.
  • javax.transaction.HeuristicMixedException is thrown when a heuristic decision was made and some updates have been committed and others were rolled back.
  • javax.transaction.HeuristicRollbackException is thrown when a commit is requested but a heuristic decision was made and all relevant updates were rolled back.
Resources
  1. A complete definition of two-phase commits by SEI: www.sei.cmu.edu/activities/str/descriptions/dtpc_body.html
  2. Datamation Magazine. "What a two-phase commit is and how it works": www.datamation.com/datab/twophase.html
  3. SQL 92 standards: www.ansi.org
  4. Java Transaction API: http://java.sun.com/jta
  5. EJB home page at SUN: http://java.sun.com/products/ejb
  6. The Oracle technical network: http://technet.oracle.com/
  7. The org.omg.CosTSPortability and other similar packages: http://java.sun.com/products/jts/javadoc/org/omg/CosTransactions/package-summary.html
  8. Complete source code, console dumps, SQL test queries and UML diagrams: www.JavaDevelopersJournal.com
Author Bio
Sameer Tyagi has almost four years' experience in n-tier Internet and intranet application development. He currently architects server-side and enterprise Java applications and writes regularly for online and print publications. [email protected]

	

Listing 1A: The remote interface of the stateless session bean MiddleBean.java


package sameer.ejb;

import javax.ejb.*;
import java.rmi.RemoteException;

public interface Middle extends EJBObject {
  public void doTransaction (String customerName,String password,int age) throws RemoteException;

}


Listing 1B: The home interface of the stateless session bean MiddleBean.java

package sameer.ejb;

import javax.ejb.*;
import java.rmi.RemoteException;

public interface MiddleHome extends EJBHome {
  Middle create() throws CreateException, RemoteException;
}


Listing 1C: The stateless session bean MiddleBean.java

package sameer.ejb;

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

public class MiddleBean implements SessionBean {

  public void ejbActivate() {}
  public void ejbRemove() {}
  public void ejbPassivate(){}
  public void setSessionContext(SessionContext ctx) {}
  public void ejbCreate () throws CreateException {}


  public void doTransaction(String customerName, String pass-
  word,int age) 
throws MyException{
          try{
           Context ctx = new InitialContext();
           TranstestHome home = (TranstestHome)   
    ctx.lookup("ejb.TranstestHome");
           Transtest bean = home.create();
                bean.putUser("Sameer","word");
       bean.putAge("Sameer",10);

   }catch(Exception e){
                throw new MyException("an exception occured" +e);
        }
  }


}


Listing 2A: The remote interface of the stateless session bean TranstestBean

package sameer.ejb;

import javax.ejb.*;
import java.rmi.RemoteException;

public interface Transtest extends EJBObject {

public void putUser (String customerName,String password) 
throws RemoteException,MyException;
  public void putAge (String customerName,int age) throws RemoteException,MyException;
}


Listing 2B: The home interface of the stateless session bean TranstestBean

package sameer.ejb;

import javax.ejb.*;
import java.rmi.RemoteException;

public interface TranstestHome extends EJBHome {
  Transtest create() throws CreateException, RemoteException;
}


Listing 2C: The stateless session bean TranstestBean

package sameer.ejb;

import javax.ejb.*;
import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.*;
import java.sql.*;
public class TranstestBean implements SessionBean {

  public void ejbActivate() {}
  public void ejbRemove() {}
  public void ejbPassivate(){}
  public void setSessionContext(SessionContext ctx) {}

public void ejbCreate () throws CreateException {}


  public void putUser(String customerName, String password)   
  throws MyException{
try{
String str = "INSERT INTO USERTABLE (name, pwd) VALUES ('" + customerName + "','" +
  password +"')";
        System.out.println ("Executing stmt: " + str);
        new weblogic.jdbc.jts.Driver();
          Connection   conn=DriverManager.getConnection("jdbc:weblogic:jts:demoPool")  ;
      Statement stmt=conn.createStatement();
      int rs=stmt.executeUpdate(str);
      System.out.println(">>>>Username/Pwd insert succeeded   
      for "+ customerName +"
   and password "+password);

         throw new MyException(); // delibrately throw an applica-
  tion exception

        }catch(SQLException se){
                throw new MyException("There was an exceptin "+se);
                }
        }

  public void putAge(String customerName,int age) throws  
  MyException {
  try{
          String str ="INSERT INTO AGETABLE (name, age) VALUES ('" +   
   customerName + "',"
  + age +")";
          System.out.println ("Executing stmt: " + str);
         // Class.forName("weblogic.jdbc.jts.Driver");
          new weblogic.jdbc.jts.Driver();
          Connection conn=DriverManager.getConnection("jdbc:weblog- 
   ic:jts:demoPool");
          Statement stmt=conn.createStatement();
          int rs=stmt.executeUpdate(str);
          System.out.println(">>>>Username/Age insert succeeded for  
   "+ customerName +"
  and age "+age);
        }catch(SQLException se){
                throw new MyException("There was an exceptin "+se);
        }
  }

}


Listing 3: The application exception class that is deliberately thrown

package sameer.ejb;

public class MyException extends Exception {
  public MyException() {}
  public MyException(String message) {
          super(message);
          }
}


Listing 4: The client for the sessionbean MiddleBean

package sameer.ejb;

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


public class Client {
  static String url = "t3://localhost:7001";
  static String user= null;
  static String password= null;

public static void main(String[] args) throws Exception {
      Context ctx = getInitialContext();
      MiddleHome home = (MiddleHome) ctx.lookup("ejb.MiddleHome");
      Middle bean = home.create();
      bean.doTransaction("Sameer","word",26);
  }

  public static Context getInitialContext() throws Exception {
    Hashtable h = new Hashtable();

h.put(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
    h.put(Context.PROVIDER_URL, url);
    return new InitialContext(h);
  }
}



 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.