Exception handling

Unhandled exceptions in a transaction cause the current transaction to rollback and the current thread to exit. If the current thread is the thread in which main was executed, the JVM will exit. Any installed transaction notifiers are called before the thread exits (including the main thread).

[Warning]

Do not arbitrarily catch all exceptions in application code, only catch expected exceptions, per standard Java best practices. Unchecked exceptions are used to indicate conditions that require a transaction to be rolled back and locks released. Catching these exceptions, and not rethrowing them, will prevent transactions from being rolled back, causing unpredicatable behavior. For example, abandoned transaction locks will cause indefinite application hangs if the locked objects are attempted to be locked again.

Example 4.15, “Unhandled exceptions” shows an unhandled exception in the main thread.

Example 4.15. Unhandled exceptions

//     $Revision: 1.1.2.1 $

package com.kabira.snippets.transactions;

import com.kabira.platform.Transaction;
import com.kabira.platform.swbuiltin.TransactionNotifier;

/**
 * Snippet on unhandled exceptions 
 * <p>
 * <h2> Target Nodes</h2>
 * <ul>
 * <li> <b>domainnode</b> = A
 * </ul>
 */
public class UnhandledExceptions
{
    /**
     * An unhandled exception
     */
    public static class UnhandledException extends java.lang.Error
    {
        /**
         * Serialization UID
         */
        public final static long serialVersionUID = 1L;
    }

    /**
     * Transaction notifier
     */
    public static class Notifier extends TransactionNotifier
    {
        /**
         * Rollback notifier
         */
        @Override
        public void onRollback()
        {
            //
            //    Perform application specific rollback processing
            //
            System.out.println("onRollback called");
        }
    }
    /**
     * Main entry point
     * @param args  Not used
     */
    public static void main(String [] args)
    {
        new Transaction("Unhandled Exception")
        {
            @Override
            protected void run() throws Rollback
            {
                //
                //    Create a transaction notifier
                //
                new Notifier();

                //
                //    Throw an unhandled exception - transaction rolled back
                //
                throw new UnhandledException();
            }
        }.execute();
    }
}

When Example 4.15, “Unhandled exceptions” is run it outputs (annotation added):

Example 4.16. Unhandled exception output

#
#     Application onRollback method called before JVM exits
#
[A] onRollback called
[A] Java main class com.kabira.snippets.transactions.UnhandledExceptions.main exited with an exception.
[A] com.kabira.snippets.transactions.UnhandledExceptions$UnhandledException
[A]         at com.kabira.snippets.transactions.UnhandledExceptions.run(UnhandledExceptions.java:80)
[A]         at com.kabira.platform.Transaction.execute(Transaction.java:286)
[A]         at com.kabira.snippets.transactions.UnhandledExceptions.main(UnhandledExceptions.java:63)
[A]         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[A]         at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
[A]         at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
[A]         at java.lang.reflect.Method.invoke(Method.java:597)
[A]         at com.kabira.platform.MainWrapper.invokeMain(MainWrapper.java:49)

Transaction required exception

Attempting to use a Managed Object object outside of a transaction will cause the following exception to be thrown:

java.lang.IllegalAccessError

Example 4.17, “Required transaction exception” will cause this exception to be thrown.

Example 4.17. Required transaction exception

//     $Revision: 1.1.2.1 $

package com.kabira.snippets.transactions;

import com.kabira.platform.annotation.Managed;

/**
 * Snippet showing transaction required behavior
 * <p>
 * <h2> Target Nodes</h2>
 * <ul>
 * <li> <b>domainnode</b> = A
 * </ul>
 */
public class TransactionRequired
{
    /**
     * Transaction required class
     */
    @Managed
    public static class Required { }
     
    /**
     * Main entry point
     * @param args  Not used
     */
    public static void main(String [] args)
    {
          //
          //     Attempting to use a transactional required class
          //     outside of a transaction
          //
          new Required();
    }
}

When Example 4.17, “Required transaction exception” is run it outputs (annotation added):

Example 4.18. Required Transaction Exception Output

#
#    java.lang.IllegalAccessError thrown because class Required requires a transaction
#
[A] Java main class com.kabira.snippets.transactions.TransactionRequired.main exited with an exception.
[A] Java exception occurred: java.lang.IllegalAccessError: no active transaction
[A]         at com.kabira.platform.ManagedObject._createSMObject(Native Method)
[A]         at com.kabira.platform.ManagedObject.<init>(ManagedObject.java:112)
[A]         at com.kabira.snippets.transactions.TransactionRequired$Required.<init>(TransactionRequired.java)
[A]         at com.kabira.snippets.transactions.TransactionRequired.main(TransactionRequired.java:45)
[A]         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[A]         at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
[A]         at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
[A]         at java.lang.reflect.Method.invoke(Method.java:597)
[A]         at com.kabira.platform.MainWrapper.invokeMain(MainWrapper.java:47)