Transaction management

Secondary store notifiers are responsible for managing transactions, or simulating transactional behavior, to external secondary stores. Any errors detected when communicating with a secondary store should be handled in the notifier and the current transaction rolled back to restore shared memory state to its value before the transaction was started. If the transaction is not rolled back the state in shared memory is out of sync with the external secondary store.

Secondary store notifiers should define and create a transaction notifier to manage transactional interaction with a secondary store. The transaction notifier should be created the first time a notifier is called. In general, only a single transaction notifier should be created per transaction. For example, if the external secondary store is an RDBMS, the transaction notifier should be allocated when a connection to the data base is allocated.

The Example 10.2, “Transaction management” demonstrates creating a transaction notifier and simulating an error when communciating with an external secondary store.

Example 10.2. Transaction management

//     $Revision: 1.1.2.1 $
package com.kabira.snippets.store;

import com.kabira.platform.Transaction;
import com.kabira.platform.annotation.Managed;
import com.kabira.platform.swbuiltin.TransactionNotifier;
import com.kabira.store.Record;

/**
 * Secondary store transaction management
 * <p>
 * <h2> Target Nodes</h2>
 * <ul>
 * <li> <b>domainnode</b> = A
 * </ul>
 */
public class TransactionManagement
{
    @Managed
    private static class A
    {
        A(boolean error)
        {
            this.error = error;
        }
        final boolean error;
    };

    private static class RecordNotifier extends Record<A>
    {
        RecordNotifier()
        {
            super(A.class);
        }

        @Override
        public void created(A a)
        {
            System.out.println("INFO: Object created");

            //
            //    Create transaction notifier to transactionally manage
            //    external store
            //
            new TransactionBoundary();

            //
            //    Simulate an error.  Throw an exception to rollback the 
            //    transaction so that shared memory state is restored to 
            //    its value before the transaction was started.
            //
            if (a.error == true)
            {
                System.out.println("INFO:\tRolling back transaction");
                throw new RuntimeException("Simulating external store error");
            }
        }
    }

    private static class TransactionBoundary extends TransactionNotifier
    {

        @Override
        public void onRollback()
        {
            System.out.println("INFO: Transaction "
                + Transaction.getIdentifier()
                + " rolled back.");

            //
            //    Undo any work in an external store
            //
        }

        @Override
        public void onCommit()
        {
            System.out.println("INFO: Transaction "
                + Transaction.getIdentifier()
                + " committed.");

            //
            //    Commit any work in an external store
            //    Commits cannot fail
            //
        }

        @Override
        public void onPrepare()
        {
            System.out.println("INFO: Transaction "
                + Transaction.getIdentifier()
                + " prepared.");

            //
            //    Prepare any work in an external store
            //
        }
    }

    /**
     * Main entry point
     *
     * @param args Not used
     */
    public static void main(final String[] args)
    {
        //
        //    Initialize record notifier
        //
        new Transaction("Initialize")
        {
            @Override
            protected void run()
            {
                new RecordNotifier();
            }
        }.execute();

        //
        //    Create an object with no error
        //
        new Transaction("Create object")
        {
            @Override
            protected void run()
            {
                new A(false);
            }
        }.execute();

        //
        //    Create an object with an error
        //
        new Transaction("Create object with error")
        {
            @Override
            protected void run()
            {
                new A(true);
            }
        }.execute();
    }
}

When Example 10.2, “Transaction management” is run it outputs these messages (annotation added):

#
#  created method called
#
INFO: Object created

#
#  Prepare and commit transaction notifier methods called
#
INFO: Transaction 129:2 prepared.
INFO: Transaction 129:2 committed.

#
#  created method called
#
INFO: Object created

#
#  Simulate error to external secondary store
#
INFO:    Rolling back transaction

#
#  Transaction rolled back
#
INFO: Transaction 129:3 rolled back.
Java main class com.kabira.snippets.store.TransactionManagement.main exited with an exception.
com.kabira.platform.ResourceUnavailableException: java.lang.RuntimeException: Simulating external store error
    at com.kabira.snippets.store.TransactionManagement$RecordNotifier.$createdImpl(TransactionManagement.java:55)
    at com.kabira.snippets.store.TransactionManagement$RecordNotifier.created(TransactionManagement.java)
    at com.kabira.snippets.store.TransactionManagement$RecordNotifier.created(TransactionManagement.java:29)
    at com.kabira.store.Record.$modifiedInternalImpl(Record.java:151)
    at com.kabira.store.Record.modifiedInternal(Record.java)
    at com.kabira.platform.Transaction.prepare(Native Method)
    at com.kabira.platform.Transaction.execute(Transaction.java:485)
    at com.kabira.platform.Transaction.execute(Transaction.java:542)
    at com.kabira.snippets.store.TransactionManagement.main(TransactionManagement.java:142)