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
 

Undoubtedly, the support for distributed transactions is a part of any enterprise system. Part One of this series (JDJ, March 1998) explored the X/Open Distributed Transaction Processing (DTP) Model - a common model for distributed transaction processing. We also explained the concepts of Microsoft Transaction Server (MTS) that quickly become de facto standard for developing business components on the Windows platform. We demonstrated how to write "first-class" MTS COM components in Java. The described process of creating these components has been greatly simplified by the release of Microsoft Visual J++ 6.0.

In this second part we will take a closer look at CORBA Object Transaction Service (OTS). As Java gains wider acceptance in enterprise computing, we are likely to see a number of simplified object models and API layers that will rely on OTS to provide transaction support (e.g., EJB, Java Transaction Service, IBM's San Francisco Project, etc.). These models greatly reduce the amount of code necessary to create applications with full transaction support. Nevertheless, as the number of components in the system increases, the complexities of real-world systems are often encountered. These include managing object interdependencies, state and context management, and dealing with various hardware and network limitations. To make the situation worse, the problems often go undetected until the deployment time because of insufficient prototype evaluation and stress testing. In solving these issues, an understanding of the underlying CORBA specification and implementations is critical. Therefore, we will describe in this article the fundamentals of CORBA OTS specification as defined by Object Management Group (OMG). We will also show some details of CORBA OTS commercial implementations and discuss the relationship between CORBA OTS and EJB. CORBA Object Transaction Service Specification

CORBA OTS specification is loosely based on X/Open DTP Model. The specification defines a model that complies with ACID transaction properties: atomicity, consistency, isolation and durability. The specification also describes the set of standard interfaces and behaviors that define how a client, the Transaction Service and the transactional objects participate in transaction processing. Figure 1 illustrates the main entities of CORBA OTS.

Figure 1
Figure 1:

In simple view, the client starts a transaction by contacting the Transaction Service. The service creates a transaction context for the new transaction. The client then makes a series of requests on transactional and nontransactional objects. The transactional objects that maintain durable data and state are called recoverable objects. These objects must register with the Transaction Service using a resource object. The resource object participates in coordinating a two-phase commit process, and is used to access the underlying resources consumed by the objects. When finished, the client commits or rolls back the changes by instructing the Transactional Service to commit or roll back, respectively. The service then coordinates the two-phase commit process with all resources registered within the scope of the current transaction. Listing 1 shows the definitions of all OTS-defined data types and interfaces in Interface Definition Language (IDL).

The CORBA OTS specification defines two basic approaches to carrying out transaction processing. These are based on the level of involvement in the context management and transaction propagation. The context management refers to the process of the transaction context creation, initialization and control. In general, the participants can choose the indirect context management and rely on the Transaction Service to perform all context-related responsibilities. On the other hand, the client can elect to manage the transaction context directly by using the set of standard OTS interfaces. Similarly, when creating transactional objects, the client can use the Transaction Service implicit propagation, or explicitly pass the transaction context to the objects. Clearly, there are four possible combinations of how to perform the context management and transaction propagation:

  • Indirect context management and implicit propagation
  • Indirect context management and explicit propagation
  • Direct context management and implicit propagation
  • Direct context management and explicit propagation
While all of these combinations are valid, the ones described in the following sections are the most common. These two combinations reflect the fundamental design tradeoff between ease of use and flexibility.

Indirect Context Management and Implicit Transaction Propagation
When using the indirect context management and the implicit transaction propagation, we rely on the Transaction Service to perform all context-related responsibilities. This model requires the simplest code by utilizing a predefined standard pseudo object Current. The client uses the Current object to begin, commit or roll back the transaction. The client also instantiates and uses the recoverable objects as any other CORBA objects. Upon object creation, the transaction context is automatically associated with the server thread by the Transaction Service. The recoverable objects can be marked as "transactional" at design time by implementing the TransactionalObject interface. This interface does not have any methods -- it simply signals that all of the object's methods are transactional, and the service should propagate the transaction context to the object implicitly. The server object can also implement the Resource interface. This indicates that the object is recoverable and it is capable of participating in the two-phase commit process.

Figure 2 depicts the typical scenario in which both the client and the recoverable object use the Current pseudo objects. The client starts the transaction by calling Current.begin() method. It then performs the series of operations on the server objects. When finished, the client issues Current.commit() or Current.rollback() commands to commit or roll back the current transaction, respectively. On the server side, the recoverable object retrieves the Control object by calling Current.get_control(). The Control object in turn is used to obtain the Coordinator object. The Coordinator object provides an access to the single transaction information such as the transaction name, transaction status, parent transaction and the propagation context. The recoverable objects use the Coordinator object to register its resources with the current transaction by calling Coordinator.register_resource(), which returns the RecoveryCoordinator object. The resources use this object when recovering from the failure after being previously prepared. Finally, the recoverable object carries out the client's request.

Figure 2
Figure 2:

At the client's request, the Transaction Service commits the changes through the two-phase commit process with all registered resources. When the client requests to commit the transaction, the Coordinator issues the Resource.prepare() command. The resources then prepare to commit and return the Vote object accordingly: VoteCommit, VoteRollback or VoteReadOnly. The VoteReadOnly indicates that no changes were made to the underlying persistent data managed by the resource. If all registered resources voted VoteReadOnly, the Coordinator informs the client about the successful commit. If at least one resource replied VoteCommit and the rest of the registered resources voted VoteReadOnly, the Coordinator first saves the current transaction information in case of later failure, and then completes the two-phase commit process by calling Resource.commit().

Direct Context Management and Explicit Transaction Propagation
The direct context management allows the clients to access the transaction context directly by using TransactionFactory, Control and Terminator interfaces. The client starts the new transaction by calling TransactionFactory.create() method. This call returns the Control object, which is used to retrieve references to the Terminator and Coordinator objects. The client subsequently performs calls on transactional objects. The client explicitly passes the reference to the Control object as a method parameter. To finish the current transaction, the client uses the Terminator.commit() method. Upon the request to commit, the OTS will initiate the two-phase commit process with all of the registered resources as described in the previous section.

The server objects do not have to implement TransactionalObject interface. Instead, these objects receive the reference to the Control object as an explicit parameter for each method. This allows the same object to have both transactional and nontransactional methods. All transactional methods must use the passed-in Control object to retrieve the reference to the Coordinator object and register all their resources. Figure 3 illustrates how the client and the recoverable server engage in transaction processing using the direct context management and explicit transaction propagation.

Figure 3
Figure 3:

Nested Transactions and X/Open Interoperability
Although the X/Open DTP model does not define the nested transactions, the Transaction Service supports this model of transaction processing. The nested transactions are implemented by establishing a parent-child relationship among transactions and thus building so-called transaction families. The transaction family comprises one top-level transaction and one or more subtransactions. The subtransactions can be nested. A transaction will not commit until all subtransactions are committed. Similarly, the changes committed by the subtransactions could be rolled back by ancestor transactions. The nested transactions prove useful in optimizing system performance. By isolating failures in subtransactions, rollbacks and resource recovery are kept to minimum.

The specification also establishes guidelines for integrating with X/Open compliant systems. In particular, transactions can be imported and exported across the two models. The OTS implementations should allow XA-compliant resource managers to participate directly in OTS transactions. The OTS specification explicitly allows a single transaction service to support both X/Open DTP and OTS models. This decision not to engage in "purist" arguments will certainly prove critical to the specification's success with vendors and customers. The transaction monitor vendors can leverage their experience with X/Open and incrementally add OTS support onto the existing products (e.g., IBM TXSeries Transaction Server). On the other hand, users can easily integrate their "legacy" systems with the new CORBA-compliant applications.

CORBA OTS Implementations
While the service specification defines a set of standard interfaces and scenarios, ORB vendors provide the actual implementations of the service. Contrary to popular belief, the common specification and language mapping do not necessarily lead to interchangeable implementations. While this allows vendors to differentiate their products, it also causes significant problems when porting server objects across different ORBs. Object Management Group recognized the problem and defined the set of interfaces for porting the Transaction Service across various ORBs (CosTSPortability module). The service portability is achieved by defining specific interfaces that lie between the ORB and the Transaction Service. While this model virtually guarantees the portability of the service, using the same vendor's ORB and CORBAservices is still the safest and most practical decision.

From the user's standpoint, implementing CORBA solutions can be greatly simplified by leveraging a complete CORBA "suite" that usually allows for easy integration of CORBAservices, transaction monitor, server utilities, common IDE and possibly a set of classes for simpler client-side programming. For example, in addition to the implementation of standard CORBAservices, Iona's Orbix Object Transaction Monitor (OrbixOTM) also offers a set of GUI development tools, server-side manager with SMTP support, fault "resilience" and server load balancing. As CORBA matures, we are likely to see more of these complete-suite products, which will help to push CORBA further into the enterprise mainstream.

On the Java side, OMG provides a standard Java mapping of IDL data types, modules and other constructs. It also offers details on server-side mapping of transient and persistent objects, Java ORB portability and mapping of CORBA pseudo objects into Java. JDK 1.2 contains a great example of this mapping -- an implementation of CORBA 2.0-compliant ORB and CORBA Naming service.

In addition to JDK 1.2, the release of the Java Transaction Service (JTS) and Enterprise JavaBeans (EJB) specifications further strengthened the bond between CORBA and Java. These technologies also helped to clear up some of the confusion over the relationship of CORBA and the Java platform.

EJB was designed to provide a high-level component architecture for writing business applications. In general, the enterprise bean object can use automatic declarative transaction management by specifying a transaction attribute. The bean container interposes all client requests, and the container is then responsible for managing the transaction according to the transaction attribute. EJB defines valid values for the bean transaction attribute (TX_NOT_SUPPORTED, TX_BEAN_MANAGED, TX_REQUIRED, TX_SUPPORTS, and TX_REQUIRES_NEW) that are very similar to Microsoft MTS transaction attributes. For example, the bean marked TX_ REQUIRED will execute in the client's transaction context, if the client has one. Otherwise, the new transaction is automatically created. The bean can also choose to use manual transaction management by specifying TX_BEAN_MANAGED attribute. Under this scenario, the clients and the beans use the JTS javax.jts.UserTransaction interface (see Listing 2) to directly manage the transaction. In fact, UserTransaction interface is the only JTS interface that the bean container must provide to be EJB compliant.

The JTS specification defines a standard transaction management API for the Java platform. It includes org.omg.CosTransactions and org.omg.CosTSPortability packages that contain the standard Java mapping of the CORBA OTS modules. As mentioned before, the JTS also defines a high-level application transaction "demarcation" API (javax.jts package). Since this API provides an isolation layer between the enterprise beans and the service implementation, in theory any transaction manager exposing this API can participate in the EJB model.

Conclusion
In order to develop enterprise business components in Java, we need to provide support for distributed transaction processing. The X/Open DTP model defines the set of standard interfaces and behaviors for implementing distributed transactions. On the Windows platform, it is Microsoft Transaction Server that provides COM components with common services, such as resource management, object pooling and the DTP-like transaction model.

The CORBA Object Transaction Service offers an alternative model for two-phase commit transactions. The OTS specification describes how the clients, transactional objects, resources and the OTS participate in transaction creation, context management, transaction propagation and two-phase commit protocol. In addition to the flat transaction model, the Transaction Service provides support for nested transactions. While a variety of OTS commercial implementations are available, their success will often be determined by features like transaction monitor, CORBA server management and productivity tools, and ease of integration with "legacy" X/Open DTP-based applications.

With the release of JDK 1.2, Java Transaction Service and Enterprise JavaBeans specifications, CORBA and Java platform integration has never been better. EJB provides necessary higher-level abstractions for creating business components in Java. At the same time, EJB relies on technologies like CORBA to provide the underlying service implementation. As a result, understanding of CORBA, CORBAservices and CORBA OTS in particular will prove to be essential for building large business systems in Java.

References and Resources
Arnold, K., and Gosling, J., "The Java Programming Language," Addison-Wesley, Reading, MA, 1996.
"Distributed TP: XA Specification, X /Open Document C193," X/Open Company Ltd., Reading, UK, 1992.
"Enterprise JavaBeans, Version 1.0," Sun Microsystems, Palo Alto, CA, March 1998. "OrbixOTS Administrator's and Programmer's Guide," IONA Technologies PLC, Cambridge, MA, 1997.
"OrbixOTM Guide," IONA Technologies PLC, Cambridge, MA, 1997.
"Transaction Service Specification: Version 1.1," Object Management Group, November 1997.
"White paper - The Object Transaction Service," IONA Technologies PLC, Cambridge, MA, 1997.

About the Author
Maros Cunderlik is a lead consultant with 3M. He focuses on applications design and distributed object architectures. He can be reached at [email protected]

	

Listing 1: CosTransactions Module (OMG IDL). 

//The CosTransactions Module 

#include <Corba.idl> 
module CosTransactions  
{ 
  //DATATYPES 
  enum Status  
  {  
    StatusActive, 
    StatusMarkedRollback, 
    StatusPrepared, 
    StatusCommitted, 
    StatusRolledBack, 
    StatusUnknown, 
    StatusNoTransaction, 
    StatusPreparing, 
    StatusCommitting, 
    StatusRollingBack 
  }; 
  enum Vote  
  { 
    VoteCommit, 
    VoteRollback, 
    VoteReadOnly 
  }; 

  //Structure definitions 
  struct otid_t  
  { 
    long formatID; /*format identifier. 0 is OSI TP */ 
    long bqual_length; 
    sequence <octet> tid; 
  }; 
  struct TransIdentity  
  { 
    Coordinator coord; 
    Terminator term; 
    otid_t otid; 
  }; 
  struct PropagationContext  
  { 
    unsigned long timeout; 
    TransIdentity current; 
    sequence <TransIdentity> parents; 
    any implementation_specific_data; 
  }; 

//Forward references for interfaces defined later in module 
interface Current; 
interface TransactionFactory; 
interface Control; 
interface Terminator; 
interface Coordinator; 
interface RecoveryCoordinator; 
interface Resource; 
interface Synchronization; 
interface SubtransactionAwareResource; 
interface TransactionalObject; 

//Heuristic exceptions 
exception HeuristicRollback {}; 
exception HeuristicCommit {}; 
exception HeuristicMixed {}; 
exception HeuristicHazard {}; 

//Other transaction-specific exceptions 
exception SubtransactionsUnavailable {}; 
exception NotSubtransaction {}; 
exception Inactive {}; 
exception NotPrepared {}; 
exception NoTransaction {}; 
exception InvalidControl {}; 
exception Unavailable {}; 
exception SynchronizationUnavailable {}; 

//Current transaction 
interface Current : CORBA::Current  
{ 
void begin() 
raises(SubtransactionsUnavailable); 
void commit(in boolean report_heuristics) 
raises( 
NoTransaction, 
HeuristicMixed, 
HeuristicHazard 
); 
void rollback() 
raises(NoTransaction); 
void rollback_only() 
raises(NoTransaction); 
Status get_status(); 
string get_transaction_name(); 
void set_timeout(in unsigned long seconds); 
Control get_control(); 
Control suspend(); 
void resume(in Control which) 
raises(InvalidControl); 
}; 
interface TransactionFactory { 
Control create(in unsigned long time_out); 
Control recreate(in PropagationContext ctx); 
}; 
interface Control { 
Terminator get_terminator() 
raises(Unavailable); 
Coordinator get_coordinator() 
raises(Unavailable); 
}; 
interface Terminator { 
void commit(in boolean report_heuristics) 
raises( 
HeuristicMixed, 
HeuristicHazard 
); 
void rollback(); 
}; 
interface Coordinator { 
Status get_status(); 
Status get_parent_status(); 
Status get_top_level_status(); 
boolean is_same_transaction(in Coordinator tc); 
boolean is_related_transaction(in Coordinator tc); 
boolean is_ancestor_transaction(in Coordinator tc); 
boolean is_descendant_transaction(in Coordinator tc); 
boolean is_top_level_transaction(); 
unsigned long hash_transaction(); 
unsigned long hash_top_level_tran(); 
RecoveryCoordinator register_resource(in Resource r) 
raises(Inactive); 
void register_synchronization (in Synchronization sync) 
raises(Inactive, SynchronizationUnavailable); 
void register_subtran_aware(in SubtransactionAwareResource r) 
raises(Inactive, NotSubtransaction); 
void rollback_only() 
raises(Inactive); 
string get_transaction_name(); 
Control create_subtransaction() 
raises(SubtransactionsUnavailable, Inactive); 
PropagationContext get_txcontext () 
raises(Unavailable); 
}; 
interface RecoveryCoordinator { 
Status replay_completion(in Resource r) 
raises(NotPrepared); 
}; 
interface Resource { 
Vote prepare() 
raises( 
HeuristicMixed, 
HeuristicHazard 
); 
void rollback() 
raises( 
HeuristicCommit, 
HeuristicMixed, 
HeuristicHazard 
); 
void commit() 
raises( 
NotPrepared, 
HeuristicRollback, 
HeuristicMixed, 
HeuristicHazard 
); 
void commit_one_phase() 
raises( 
HeuristicHazard 
); 
void forget(); 
}; 
interface TransactionalObject { 
}; 
interface Synchronization : TransactionalObject { 
void before_completion(); 
void after_completion(in Status status); 
}; 
interface SubtransactionAwareResource : Resource { 
void commit_subtransaction(in Coordinator parent); 
void rollback_subtransaction(); 
}; 
}; // End of CosTransactions Module 

The CosTSPortability Module 
module CosTSPortability  
{ // PIDL 
  typedef long ReqId; 
  interface Sender  
  { 
    void sending_request(in ReqId id, out CosTransactions::PropagationContext ctx); 
    void received_reply(in ReqId id, in CosTransactions:
	:PropagationContext ctx, in CORBA::Environment env); 
  }; 
  interface Receiver  
  { 
    void received_request(in ReqId id, in CosTransactions::PropagationContext ctx); 
    void sending_reply(in ReqId id, out CosTransactions::PropagationContext ctx); 
  }; 
}; 

Listing 2: JTS UserTransaction Interface. 

package javax.jts; 

public interface UserTransaction extends java.lang.Object 
{ 
  //Variables 
  public static final int STATUS_ACTIVE; 
  public static final int STATUS_MARKED_ROLLBACK; 
  public static final int STATUS_PREPARED; 
  public static final int STATUS_COMMITTED; 
  public static final int STATUS_ROLLEDBACK; 
  public static final int STATUS_UNKNOWN; 
  public static final int STATUS_NO_TRANSACTION; 
  public static final int STATUS_PREPARING; 
  public static final int STATUS_COMMITTING; 
  public static final int STATUS_ROLLING_BACK; 

  //Methods 
  public abstract void begin() 
    throws IllegalStateException; 

  public abstract void commit()  
    throws TransactionRolledbackException, HeuristicMixedException,  
    HeuristicRollbackException, SecurityException, IllegalStateException; 
  
  public abstract void rollback() 
    throws IllegalStateException, SecurityException; 

  public abstract void setRollbackOnly() 
    throws IllegalStateException; 

  public abstract int getStatus(); 

  public abstract void setTransactionTimeout(int seconds) 
  
      
 

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.