Explicit Transaction Management


Transaction Management in the Client

Explicit transaction management in the client means that the client itself takes over control of the transaction. Before a call to a bean it launches a global transaction. It then ends this transaction with a rollback or commit.

At a call to a bean the client's transaction is transmitted to the server and used by the EJB container. Depending on the bean's transaction attributes, the EJB container can react in various ways. One possibility is that it uses the client's transaction for executing the bean's methods. In another case one of the bean's own transactions is used, which is either started by the EJB container or is already in existence.

Explicit transaction management by the client thus does not stand in opposition to implicit transaction management. The concepts are interrelated, and each contributes to the other.

Figure 7-6 shows a schematic example of explicit transaction management by the client. Here is shown how the beans use the transaction context of the client and transmit it further. Possible transaction attributes for the beans that support the use of a transaction launched by the client are Required, Supports, and Mandatory.

click to expand
Figure 7-6: Example of data flow in a client's explicit transaction management.

The client is responsible for the transaction management. It access the transaction monitor directly over the Java Transaction API (JTA). To make this possible, JTA must be offered to the client as a service and be registered in the name service. Here, unfortunately, the various application services show differing behaviors, since the names in the name service differ. The relevant information must be taken from the documentation of the individual product.

After the client has obtained access to JTA, it can launch a global transaction. The transaction monitor will ensure the transmission of the transaction context via the Java Transaction Service (JTS).

The client can call any number of methods within the transaction, even from different beans. After all actions of the transaction have been executed, the client ends the transaction with a commit or rollback.

In the transaction, every method call to a bean on the server also transmits the transaction context. In our example the called bean uses the client's transaction for executing its methods.

When a method is called on further beans or when access is made to a transactional system, the transaction context is always inherited. Thus all actions are linked to the client's transaction and executed in common (commit) or rolled back together.

In the case of an error, each participating bean can determine that the transaction should be rolled back upon termination. The bean context offers the required interface. Therefore, the developer of a bean does not have to access JTA.

Explicit client transaction management allows a great deal of program logic to be implemented in the client. This must be viewed critically, since the architecture of an application and reusability can suffer as a result of explicit transaction management. One should always check whether the declarative transactions of the EJB concept perhaps offer a better solution.

The advantage of explicit transaction management by the client is that application development is possible by programming only the client. The existing beans on the server are simply used, without extending or changing them. In contrast to this, it is necessary with implicit transactions to develop a new bean when several method calls are to take place within a transaction.

Explicit client transaction management thus makes possible the more rapid development of prototypes. This path is also of interest when the greatest part of the application logic consists of purchased beans and one has not developed the beans oneself.

Example Producer

At this point we return to our Producer example, which we used in discussing implicit transactions.

The same logic is to be implemented, but this time with transaction management in the client. The reason for this might be that only the entity bean Stock exists, and we do not wish to develop any new beans. The client will have the same functionality as the session bean Producer and additionally, take over transaction management.

Listing 7-15 shows that the client obtains access to transactions in the constructor. From the name service it obtains a stub of the class UserTransaction, a lightweight object that offers the JTA interface to the client. The class UserTransaction is a central component of JTS (package javax.jts) and is also published in JTA (package javax.transaction) for ordinary application developers.

Listing 7-15: Client transactions: access to JTA.

start example
 package ejb.supplychain.client; import ejb.supplychain.stock.Stock; import ejb.supplychain.stock.StockHome; import ejb.util.Lookup; import javax.transaction.UserTransaction; public class Client {     private final static String STOCK_HOME =         "Stock";     private final static String USER_TA =         "javax.transaction.UserTransaction";     private UserTransaction userTx = null;     private StockHome stockHome = null;     private Stock stock1;     private Stock stock2;     private Stock stock3;     public Client() {         try {             this.userTx = (UserTransaction)                 Lookup.narrow(USER_TA, UserTransaction.class);             this.stockHome = (StockHome)                 Lookup.narrow(STOCK_HOME, StockHome.class);         } catch(javax.naming.NamingException ex) {             ex.printStackTrace();             throw new IllegalStateException(ex.getMessage());         }     } ... 
end example

As with implicit transaction management, in Listing 7-16 the client initializes three instances of the entity bean Stock. Here we would like to point out that these actions are executed without a global transaction. The EJB container automatically launches a transaction via the transaction attribute Mandatory. This shows how simple it is to combine implicit and explicit transaction management.

Listing 7-16: Client transactions: initialization of three instances of the entity bean Stock.

start example
 ...     public void preconditions()         throws java.rmi.RemoteException     {         this.stock1 = this.createStock("stock1", 100, 100);         this.stock2 = this.createStock("stock2", 100, 100);         this.stock3 = this.createStock("stock3", 100, 0);         System.out.println("Stock1 created. Current Volume: + this.stock1.getVolume());         System.out.println("Stock2 created. Current Volume: + this.stock2.getVolume());         System.out.println("Stock3 created. Current Volume: + this.stock3.getVolume());     }     private Stock createStock(String name, int max, int cap)         throws java.rmi.RemoteException     {         Stock stock = null;         try {             stock = this.stockHome.findByPrimaryKey(name);             stock.remove();         } catch(javax.ejb.FinderException ex) {             //do nothing         } catch(javax.ejb.RemoveException ex) {             ex.printStackTrace();             throw new IllegalStateException(ex.getMessage());         }         try {             stock = this.stockHome.create(name, max, cap);         } catch(javax.ejb.CreateException ex) {             ex.printStackTrace();             throw new IllegalStateException(ex.getMessage());         }         return stock;     } ... 
end example

The actual functionality (see Listing 7-17) is executed by the client in a global transaction. For transaction management the interface UserTransaction is used. Three methods are used for transaction management: begin, commit, and rollback.

Listing 7-17: Client transactions: program segment that executes production.

start example
 ...     public void doProduction(int volume)         throws java.rmi.RemoteException     {         boolean rollback = true;         try {             this.userTx.begin();             System.out.println("Producing " + volume + " units ...");             this.stock1.get(volume);             this.stock2.get(volume*2);             this.stock3.put(volume);             System.out.println("done.");             rollback = false;         } catch(Exception ex) {             System.out.println("FAILED.");             System.err.println(ex.toString());         } finally {             if(!rollback) {                 try {                     this.userTx.commit();                 } catch(Exception ex) {}             } else {                 try {                     this.userTx.rollback();                 } catch(Exception ex) {}             }         }         System.out.println("Stock1 Volume: " + this.stock1.getVolume());         System.out.println("Stock2 Volume: " + this.stock2.getVolume());         System.out.println("Stock3 Volume: " + this.stock3.getVolume());     }     public static void main(String[] args)         throws java.rmi.RemoteException     {         Client c = new Client();         c.preconditions();         c.doProduction(10);         c.doProduction(20);         c.doProduction(50);     } } 
end example

With begin the client launches a new transaction. Here we mention once more that nested transactions are not supported. Each transaction in a thread must be ended before the next one may be started.

However, this does not mean that a client with a transaction of its own is not permitted to call a bean method that itself uses its own transaction. The client program and the bean method are executed in two different threads. The EJB container propagates the transaction only if the relevant transaction attributes are defined for the method. Thus in this case as well nested transactions are not needed.

With commit the client signals that the transaction was successful from its point of view and should be ended. The transaction is ended with a commit only if no other beans involved in the transaction have previously called setRollbackOnly.

It is possible that errors will arise in a transaction. For example, a store can exceed its capacity. Since the entity bean Stock does not react to an error with setRollbackOnly, but simply triggers an exception, it would be possible to implement a procedure for removing the error. However, our example simply responds with a rollback.

Now the functionality of the client is complete. Listing 7-18 shows the output that the client produces at run time.

Listing 7-18: Client transactions: client output at run time.

start example
 Stock1 created. Current Volume: 100 Stock2 created. Current Volume: 100 Stock3 created. Current Volume: 0 Producing 10 units ... done. Stock1 Volume: 90 Stock2 Volume: 80 Stock3 Volume: 10 Producing 20 units ... done. Stock1 Volume: 70 Stock2 Volume: 40 Stock3 Volume: 30 Producing 50 units ... FAILED. ejb.supplychain.stock.ProcessingErrorException: volume to small Stock1 Volume: 70 Stock2 Volume: 40 Stock3 Volume: 30 
end example

In comparison to implicit transaction management, with explicit transaction management the client has considerably more functionality. Given the choice between these two solutions, implicit transaction management should be used where possible. The effort to implement the necessary functionality is better invested in a reusable server component.

Transaction Management in the Bean

Session beans can themselves take over complete control of a transaction (since the EJB 1.1 specification this is no longer possible for entity beans). The client's transaction context is not transmitted by the EJB container to the bean. The transaction context of the bean remains completely isolated from this.

Message-driven beans can also manage transactions. However, a bean can begin a transaction only within the onMessage method. The transaction must be terminated before the end of the onMessage method (either with commit or rollback). A transaction cannot extend over several onMessage calls. Moreover, the receipt of a message via the onMessage method cannot be a component of a transaction controlled by a message-driven bean. Only actions that the message-driven bean executes upon receipt of a message in the onMessage method can belong to the transaction.

An Enterprise Bean is able to work with both global and local transactions. With the use of global transactions there can be only one active transaction at a given time. This means that before the start of a global transaction all local transactions must be terminated as well as any previous global transactions.

Figure 7-7 shows a schematic example of the explicit transaction management by a bean that is working with a global transaction. The bean does not inherit the transaction context from the client, but launches its own global transaction via direct access to the transaction service over JTS. As we know, the global transaction is transmitted if the bean uses other beans or transactional systems.

click to expand
Figure 7-7: Data flow example for explicit transaction management by a bean with a global transaction.

The programming of such a scenario can be derived from the example of a client with explicit transaction management. In particular, access to JTA is identical.

The bean can also use the same transaction over several method calls by the client. The EJB container manages the transaction context for the bean. If the bean does not close a global transaction before the end of a method, then a subsequent method call will be automatically associated with the same transaction context.

However, transaction management in the bean does not have to work with global transactions. By restricting to local transactions the bean developer can go more freely into the peculiarities of the database or other transactional system in use. Figure 7-8 shows a schematic example for explicit transaction management of a bean that works with local transactions.

click to expand
Figure 7-8: Example of data flow with explicit transaction management of a bean with a local transaction.

Systems that participate in a global transaction must offer the JTS interfaces or be addressed specially by the EJB container. For example, it is only since JDBC 2.0 that JTS global transactions have been supported. Older database systems can be used only because EJB containers take the older interfaces into account. The exclusive use of local transactions in a bean makes it possible to use any transactional system. Here the bean uses the specific interfaces for the system in question for managing a local transaction.

In the following section the example Migration will be used to clarify the use of local transactions in a bean.

Example Migration

The following example shows how a bean explicitly manages the local transactions of a database. The program could also serve as an example of programming JDBC. Local database transactions are managed solely by beans over JDBC.

The example Migration shows how currency in a database is converted ("migrates") from German marks (DM) to euros. A session bean carries out this conversion in a local transaction.

Migration is only a simple example. However, the local transactions used here would offer the bean further opportunities. For similar scenarios it might be necessary to use long-term transactions or locks on tables or rows (to avoid data inconsistencies due to concurrent access to the data being converted).

First, we show the deployment descriptor in Listing 7-19. Note that in the segment transaction-type the keyword bean is specified. This indicates that the bean takes over the explicit transaction management. At the same time, this value prohibits the definition of transaction attributes in the assembly descriptor.

Listing 7-19: Deployment descriptor of the session bean Migration.

start example
 <?xml version="1.0"?> <ejb-jar version="2.1" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd">   <enterprise-beans>     <session>       <ejb-name>Migration</ejb-name>       <home>ejb.migration.MigrationHome</home>       <remote>ejb.migration.Migration</remote>       <ejb-class>ejb.migration.MigrationBean</ejb-class>       <session-type>Stateless</session-type >       <transaction-type>Bean</transaction-type>       <resource-ref>         <description>           reference to the target database         </description>         <res-ref-name>jdbc/Migration</res-ref-name>         <res-type>javax.sql.DataSource</res-type>         <res-auth>Container</res-auth>       </resource-ref>     </session>   </enterprise-beans>   <assembly-descriptor>   </assembly-descriptor> </ejb-jar> 
end example

The home (see Listing 7-20) and the remote (Listing 7-21) interfaces show the simple interface. The conversion is initiated with a call to the method migrate. The exception MigrationErrorException signals an error in execution.

Listing 7-20: Home interface of the session bean Migration.

start example
 package ejb.migration; import javax.ejb.CreateException; import javax.ejb.EJBHome; import java.rmi.RemoteException; public interface MigrationHome extends EJBHome {     public Migration create()         throws CreateException, RemoteException; } 
end example

Listing 7-21: Remote interface of the session bean Migration.

start example
 package ejb.migration; import java.rmi.RemoteException; import javax.ejb.EJBObject; public interface Migration extends EJBObject {     public void migrate()         throws MigrationErrorException, RemoteException; } 
end example

Listing 7-22 shows the implementation of the bean class. In the method ejbCreate the bean obtains a reference to a data source over which connections to the database can be established. In the method ejbRemove this reference is deleted. The entire functionality of the bean is implemented in the method migrate.

Listing 7-22: Bean class of the session bean Migration.

start example
 package ejb.migration; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.Statement; import java.sql.SQLException; import javax.ejb.CreateException; import javax.ejb.EJBException; import javax.ejb.SessionBean; import javax.ejb.SessionContext; import javax.sql.DataSource; import ejb.util.Lookup; public class MigrationBean implements SessionBean {     public static final float EXCHANGE_RATE = 1.98f;     public static final String RESOURCE_REF =         "java:comp/env/jdbc/Migration";     private SessionContext sessionCtx;     private DataSource dataSource;     public MigrationBean() { }     public void ejbCreate()         throws CreateException     {         try {             this.dataSource = (DataSource)                 Lookup.narrow(RESOURCE_REF, DataSource.class);         } catch(Exception ex) {             String msg = "Cannot get DataSource:" + ex.getMessage();             throw new EJBException(msg);         }     }     public void ejbRemove() {         this.dataSource = null;     }     public void ejbActivate() { }     public void ejbPassivate() { }     public void setSessionContext(SessionContext ctx) {         this.sessionCtx = ctx;     }     private static final String QUERY1 =         "UPDATE INVOICE SET AMOUNT=AMOUNT/? " + "WHERE CURRENCY='DEM'";     private static final String QUERY2 =         "UPDATE INVOICE SET CURRENCY='EU' " + "WHERE CURRENCY='DEM'";     public void migrate()         throws MigrationErrorException     {         Connection con = null;         PreparedStatement st1 = null;         Statement st2 = null;         boolean success = false;         try {             con = this.dataSource.getConnection();             con.setAutoCommit(false);             st1 = con.prepareStatement(QUERY1);             st1.setFloat(1, EXCHANGE_RATE);             st1.executeUpdate();             st2 = con.createStatement();             st2.executeUpdate(QUERY2);             success = true;         } catch(SQLException ex) {             String msg = "Failed migrating data:" + ex.getMessage();             throw new MigrationErrorException(msg);         } finally {             if(success) {                 try { con.commit(); } catch(Exception ex) {}             } else {                 try { con.rollback(); } catch(Exception ex) {}             }             try { st1.close(); } catch(Exception ex) {}             try { st2.close(); } catch(Exception ex) {}             try { con.setAutoCommit(true); } catch(Exception ex) {}             try { con.close(); } catch(Exception ex) {}         }     } } 
end example

For readability we have split the conversion of German marks (DM) to euros into two separate statements. After a database connection has been established via the data source, the auto-commit mode is turned off. In auto-commit mode a commit would be carried out automatically in the active transaction after the execution of a statement. In our case we could then not guarantee that the data would be in a consistent state at the end of the method.

With the turning off of the auto-commit mode we are ensuring that both statements in the same (local) database transaction will be executed. If an error occurs in one or both statements, then the flag success is not set to true. This results in a rollback being executed for the transaction in the finally block. If both statements have been executed successfully, then a commit is executed. Before we close the database connection with Connection.close, we reset auto-commit to its default value before closing it and returning it to the connection pool. The bean could also be provided the possibility of setting a particular isolation level for the transaction using the method Connection.setTransactionIsolation.

In summary, we can say that a session bean with explicit transaction management is programmed like a normal database client if it works with local transactions. If global transactions are in play, then the programming is comparable to that of an EJB client with explicit transaction management (see the earlier section on the example Producer, with explicit client transactions).




Enterprise JavaBeans 2.1
Enterprise JavaBeans 2.1
ISBN: 1590590880
EAN: 2147483647
Year: 2006
Pages: 103

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