Previous Topic

Next Topic

Book Contents

Book Index

Transaction Manager service

The Transaction Manager service allows applications to execute operations in transactions. A transaction is a sequence of operations executed over a system to change its state in a certain way. However, it is important to guarantee the system's integrity and stability while performing transactions over it. Therefore, transactional systems should implement a special collection of features called ACID. In brief, they are:

The Transaction Manager is available as a service under the com.prosyst.mbs.services.transaction.TransactionManager interface in case the mbs.tm.class system property is set to a Transaction Manager implementation class name. The default implementation of the Transaction Manager is com.prosyst.mbs.impl.framework.module.transaction.TransactionManagerImpl.

You can implement your own Transaction Manager (com.prosyst.mbs.services.transaction.TransactionManager) and specify its class name under the mbs.tm.class property prior to starting the framework.

Registering bundle

This service package is exported by the System bundle.

Two-phase commit mechanism

Two-phase commit is the most effective and wide-spread mechanism to providing ACID in transactional systems. It consists of:

Transaction resources available in the framework

Transaction Resources are applications or services in the framework, which implement transactional behavior and can participate in transactions. For details on the functions Transaction Resources perform, refer to the "Transaction Manager" document.

Currently, the Transaction Resources available in the framework are com.prosyst.mbs.services.db.DB instances obtainable from the DB Manager service. You can create custom Transaction Resources by implementing the com.prosyst.mbs.services.transaction.TransactionResource interface. Instructions and recommendations on implementing a Transaction Resource are available in the "Recommendations on implementing a transaction resource" section below.

Creating a transactional application

To create a transactional application, you have to call some functionality provided by a Transaction Resource into a transaction. To manage the transaction's life cycle, you need a com.prosyst.mbs.services.transaction.Transaction instance obtainable through the Transaction Manager.

The source example listed from below is a transactional application, which uses the DB Service to create a phonebook database and populates it with entries. The operations performed using the DB Manager service are included into a transaction.

Creating a transactional application is shown in the following example:

package transactional.app.example;

import java.io.File;
import java.io.IOException;

import com.prosyst.mbs.services.transaction.Transaction;
import com.prosyst.mbs.services.transaction.TransactionException;
import com.prosyst.mbs.services.transaction.TransactionManager;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

import com.prosyst.mbs.services.db.DB;
import com.prosyst.mbs.services.db.DBManager;

public class DBTransactionExample implements BundleActivator {
    
  TransactionManager transMngr;
  Transaction dbTransaction;
  ServiceReference transMngrRef, dbMngrRef;
  DBManager dbMngr;
  static String DB_ALIAS = null;
  static String DB_ROOT = null;
  private DB aliasRootBD;
    
  private static final String PHONEBOOK_KEY = "Phonebook";
  private static final String LETTER_A = "A";
  private static final String LETTER_B = "B";
  private static final String LETTER_C = "C";
    
  public void start(BundleContext bc) throws Exception {
        
    //Get the Transaction Manager
    transMngrRef = bc.getServiceReference(TransactionManager.class.getName());
    transMngr = (TransactionManager) bc.getService(transMngrRef);  
    
    //Initiate a transaction    
    dbTransaction = transMngr.begin();
    //Get the newly created transaction's PID and print it in the system output
    long pid = dbTransaction.getPid();
    System.out.println("[DBTransactionExample] The transaction PID is:" + pid);
    //Print the transaction's current state
    printTransactionState(dbTransaction);
            
    try{
      //Call methods on the Transaction Resource in use - the DB service currently.
      DB_ALIAS = System.getProperty("my.db.alias", "mydb");
      DB_ROOT = System.getProperty("my.db.root", "mydb_root");
      System.out.println("[DBTransactionExample] Creating a custom DB...");
      System.out.println("[DBTransactionExample] alias = " + DB_ALIAS + ";\troot = " + DB_ROOT);
      dbMngrRef = bc.getServiceReference(DBManager.class.getName());
      dbMngr = (DBManager) bc.getService(dbMngrRef);
      aliasRootBD = dbMngr.getCustomDB(DB_ALIAS, new File(DB_ROOT));
    
      dbTransaction.commit();
      printTransactionState(dbTransaction);
            
    } catch (TransactionException e){
      e.printStackTrace();
      //Roll back the transaction in case problems occur
      dbTransaction.rollback();
      printTransactionState(dbTransaction);
    }          
  }

  //A method which prints a transaction's current state in the system output    
  public void printTransactionState (Transaction t){
        
    int transactionState = t.getStatus();
        
    switch(transactionState){
      case Transaction.STATUS_ACTIVE:
      System.out.println("[DBTransactionExample] The transaction is in STATUS_ACTIVE"); break;
      case Transaction.STATUS_COMMITTED:
      System.out.println("[DBTransactionExample] The transaction is in STATUS_COMMITTED");break;
      case Transaction.STATUS_ROLLEDBACK:
      System.out.println("[DBTransactionExample] The transaction is in STATUS_ROLLEDBACK");break;
    }
  }

  public void stop(BundleContext bc) throws Exception {
  }
  private void fillDb(DB db) throws IOException {
     // Writing a main node for phone numbers
    db.setValue(new String[] { PHONEBOOK_KEY },
                null,
                DB.NODE);
    // Creating a node for a latter
    db.setValue(new String[] { PHONEBOOK_KEY, LETTER_A },
                null,
                DB.NODE);
    // Writing an entry for a person's phone number
    db.setValue(new String[] { PHONEBOOK_KEY, LETTER_A, "Mr. R. Armstrong" },
                "2121 4343",
                DB.STRING);
    db.setValue(new String[] { PHONEBOOK_KEY, LETTER_B },
                null,
                DB.NODE);
    db.setValue(new String[] { PHONEBOOK_KEY, LETTER_B, "Mr. A. Been" },
                "9988 9988",
                DB.STRING);
    db.setValue(new String[] { PHONEBOOK_KEY, LETTER_C },
                null,
                DB.NODE);
    db.setValue(new String[] { PHONEBOOK_KEY, LETTER_C, "Mr. P. Cage" },
                "1234 1234",
                DB.STRING);
  }
}

Recommendations on implementing a transaction resource

To create a TransactionResource service, you have to implement the com.prosyst.mbs.services.transaction.TransactionResource interface. It provides three methods - prepare, commit and rollback. The implementation of these methods is tightly connected to the functionality of the Transaction Resource and the design of the transactional system it will be part of. Therefore, there are no strict rules on implementing a Transaction Resource. However, the following recommendations can be outlined:

  1. A Transaction Resource must hide the changes made by non committed transactions.
  2. A Transaction Resource have to lock all items it modifies in the context of the current transaction and do not allow other applications to perform any changes over these items. The locking mechanism should be valid only for write operations, read operations must return the value of the locked items before the Transaction Resource had modified them.
  3. In case a Transaction Resource modifies an item in a context of a transaction and another transaction wants to read the same item, the Transaction Resource must first commit the new value of this item, then provide it to the concurrent transaction.
  4. A Transaction Resource have to write in a persistent log information on all the operations it had executed as a transaction participant.
  5. A Transaction Resource should store into a persistent log information on all the operations it will perform as a transaction participant before joining the transaction. This is the so called write ahead logging and is a wide-spread technique to providing atomicity and durability to transactional systems.
  6. On system restart, a Transaction Resources must check weather it participates into unfinished transactions and rejoin them.
  7. A Transaction Resource must be able to work in both the transactional and non-transactional contexts.

Enabling the transaction support

To turn on the transaction support in the framework, set the mbs.tm.type system property to default.

Retrieving transaction manager

The Transaction Manager is available as a service under the com.prosyst.mbs.services.transaction.TransactionManager interface, as described at the beginning of the document.

Implement transaction manager

You can provide your own implementation of the Transaction Manager and set its implementation class to the mbs.tm.type system property. Make sure that the implementation class is present in the classpath of the framework.

Transactions in the framework

The OSGi framework provides support for creation and management of applications that implement transactional behavior. This support is provided by the Transaction Manager module.

Scope

Transactions in the framework have the following scope:

Only one transaction is supported per thread. An error will be thrown if you try to run more than one transaction at the same thread.

Nested transactions are not supported. You are not allowed to run a transaction within another one.

Transaction resources

Transaction Resources are valid applications that implement transactional behavior and can participate in transactions. A Transaction Resource can be the Configuration Admin service, for example.

Transaction Resources perform the following functions:

Transaction manager functions

The Transaction Manager performs the following functions:

Preventing overlap

To prevent from overlapping of the components participating in its different layers, the Transaction Manager imposes certain restrictions on the applications wanting to implement transactional behavior. These restrictions are provided on two levels:

System recovery support

Transaction Manager provides recovery support of the system after major crashes resulted in unusual circumstances like power failure, as an instance. For this purpose, Transaction Manager keeps a record of all currently active transactions that includes their PIDs, state and resources it uses. When the Transaction Manager is started, it checks if there are any interrupted transactions and waits for the Transaction Resources to rejoin them. Then it decides whether to commit or roll back the interrupted transactions depending on their life cycle states.

Deadlock recovery mechanism

To prevent the occurrence of deadlocks in the system, transactional applications can specify a time-out after which the Transaction Manager will terminate the transactions associated with them. Applications may reset this time-out after each transaction since different transactions may take different time to fulfil.

For details on the Transaction and TransactionContext objects, refer to the Transaction Manager API.

Data model

The diagram below shows the sequence of executing management actions over a transaction during its life cycle:

The depicted management operations are executed in the following order in time:

  1. The transactional application initiates a transaction using the Transaction Manager.
  2. The transactional application calls some methods on certain Transaction Resources that can be services in the framework or other applications.
  3. The participating Transaction Resources use the Transaction Manager to attach themselves to the corresponding transaction.
  4. The transactional application calls commit on its transaction.
  5. Transaction Manager calls prepare to each one of the Transaction Resources participating in the transaction. This means that all operations executed by the Transaction Resources will be hidden from the rest of the system and from the other transactions.
  6. Optional. Transaction Manager calls rollback on the Transaction Resources if an error occurs in some of the operations they perform.
  7. Transaction Manager calls commit on the Transaction Resources. All changes they have made to the system's state will become public and persistent.
  8. Optional. Transactional application calls rollback on the transaction for some reason.
  9. Optional. Transaction Manager calls rollback on the transaction resources.

Transaction lifecycle

The diagram below depicts the life cycle of a transaction in the framework:

STATUS_ACTIVE

A transaction's initial state. It indicates the transaction has joined the current thread of its creator application and has received all needed resources.

STATUS_PREPARING

A transaction passes into this state when the execution of its composite operations begins. None of the participating modules is aware of the actions the system performs while the transaction is into this state. Other currently active transactions are not informed as well. If a problem is raised, all operations made so far will be rolled back and the transaction will go into STATUS_ROLLING_BACK.

STATUS_PREPARED

This state is associated with a transaction when all of its operations are executed successfully.

STATUS_COMMITING

This state indicates that all changes made by a transaction are going to be committed.

STATUS_COMMITED

A transaction's final state where the changes it has made to the system are declared publicly and will remain persistent. Disposing the transaction follows in order to free the corresponding thread for new transactions.

STATUS_NO_TRANSACTION

A transaction can pass into this state if some of the changes it has made fail to commit properly because of some reason. It indicates the transaction was made persistent and public but not all the changes it has made were committed correctly. This state is also final for the transaction and its disposal will follow.

STATUS_MARKED_ROLLBACK

A transaction can pass into this state if some of the modules taking part in its execution or management detects a circumstance that threatens the system's stability and integrity. It indicates that the transaction can only be rolled back from this point on. Even if some transactional application tries to commit the transaction, it will result into rolling it back.

STATUS_ROLLING_BACK

If an error occurs on some stage of a transaction's execution, the operation that has caused it will be rolled back and the transaction will pass into STATUS_ROLLING_BACK. The transaction will remain into this state until all of the operations it has performed so far will be rolled back.

STATUS_ROLLEDBACK

This state indicates that all operations performed by a transaction were rolled back. It is a final state for the transaction and it will be destroyed to free the corresponding thread for new transactions.