Transaction boundaries

Transaction boundaries are defined using the com.kabira.platform.Transaction class.

An application implements the abstract run method to execute application code in a transaction. A transaction is implicitly started when the execute method is called. The execute method calls the application provided run method and executes the application code in a transaction. A transaction is terminated in the following ways:

An application can explicitly control the outcome of a transaction by throwing the Transaction.Rollback exception in the run method. The Transaction.Rollback exception causes the current transaction to rollback. Returning normally from the run method causes the transaction to commit.

The Transaction.Rollback exception has two variants - with and without a Throwable cause.

A Transaction.Rollback exception that does not contain a Throwable cause exception is transparently caught by the Transaction class, the transaction is rolled back, and Result.ROLLBACK is returned from the Transaction.execute() method.

A Transaction.Rollback exception that contains a Throwable cause exception is caught by the Transaction class, the transaction is rolled back, and the exception is rethrown by the Transaction class as an InvocationRunException. The cause of the InvocationRunException is set to the Throwable exception that was set in the Transaction.Rollback exception. This provides a mechanism for application code to communicate the cause of a rollback to the caller of the Transaction.execute() method.

Unhandled exceptions in a run method cause the transaction to be rolled back. They are then re-thrown unmodified.

Example 4.1, “Counting example” shows a simple counting program that demonstrates a field value being rolled back.

Example 4.1. Counting example

//     $Revision: 1.1.2.1 $

package com.kabira.snippets.transactions;

import com.kabira.platform.Transaction;
import com.kabira.platform.annotation.Managed;

/**
 * A simple counting program showing transactional consistency
 * <p>
 * <h2> Target Nodes</h2>
 * <ul>
 * <li> <b>domainnode</b> = A
 * </ul>
 */
@Managed
class Counter
{
    int m_count = 0;
}

public class Consistency
{
    private static boolean m_commit;
    private static Counter m_counter;

    /**
     * Main entry point
     * @param args  Not used
     */
    public static void main(String[] args)
    {
        //
        //  Create a counter object
        //
        new Transaction("Create Counter")
        {
            @Override
            protected void run() throws Rollback
            {
                m_counter = new Counter();
            }
        }.execute();

        //
        //  Increment the counter and commit the transaction
        //
        m_commit = true;
        incrementCounter();
        printCounter();
        
        incrementCounter();
        printCounter();

        // 
        //  Increment the counter and rolback the transaction
        //
        m_commit = false;
        incrementCounter();
        printCounter();
    }

    private static void incrementCounter()
    {
        new Transaction("Increment Counter")
        {
            @Override
            protected void run() throws Rollback
            {
                m_counter.m_count += 1;

                if (m_commit == true)
                {
                    return;
                }
                throw new Transaction.Rollback();
            }
        }.execute();
    }

    private static void printCounter()
    {
        new Transaction("Print Counter")
        {
            @Override
            protected void run() throws Rollback
            {
                System.out.println(m_counter.m_count);
            }
        }.execute();
    }
}

When run, this simple program outputs (annotation added):

Example 4.2. Counting example output

#
#     Initial call to execute that commits
#
1

#
#     Second call to execute that commits
#
2

#
#     Third call to execute that rolls back - field restored to value before call
#
2

Example 4.3, “Throwable cause example” shows the use of a Throwable cause to communicate rollback information to the caller of the Transaction.execute().

Example 4.3. Throwable cause example

//     $Revision: 1.1.2.1 $

package com.kabira.snippets.transactions;

import com.kabira.platform.Transaction;

class Cause extends Throwable
{
    final static long serialVersionUID = 1L;

    Cause(String message)
    {
        super (message);
    }
}
    
/**
 * Rollback a transaction with a Throwable exception
 * <p>
 * <h2> Target Nodes</h2>
 * <ul>
 * <li> <b>domainnode</b> = A
 * </ul>
 */
public class RollbackWithCause
{
    /**
     * Main entry point
     * @param args  Not used
     */
    public static void main(String [] args)
    {
        try
        {
            new Transaction("Rollback")
            {
                @Override
                protected void run() throws Rollback
                {
                    Cause   cause = new Cause("rollback because of error");
                    throw new Transaction.Rollback(cause);
                }
            }.execute();
        }
        catch (Transaction.InvocationRunException ex)
        {
            System.out.println("INFO: " + ex.getCause().getMessage());
        }
    }
}

When run, this simple program outputs:

Example 4.4. Throwable cause example output

[A] INFO: rollback because of error