ActiveSpaces® Transactions
supports notification of transaction
resolution using the com.kabira.platform.swbuiltin.TransactionNotifier
class. This class provides onPrepare
,
onRollback
and onCommit
methods that
can be implemented as required to integrate with external transactional
and non-transactional resources.
Multiple transaction notifiers can be created during the same transaction. The appropriate method is called for each notifier instance created as the transaction progresses. The order in which multiple notifiers is called is undefined so there should be no order assumptions in the notifiers. A notifier that is created in a transaction can be deleted before the transaction completes. In this case, the notifier is not called when the transaction completes.
The
method is always
called before onPrepare
onCommit
. The
onPrepare
method can be used to
prepare external XA transactional resources.
onPrepare
can report a failure by throwing an
exception. If that happens, the transaction will be rolled back.
When a failure occurs compensation must be done to ensure
that any work that was completed before the failure is restored to its
initial state. ActiveSpaces® Transactions
transactional resources are automatically
restored to their initial state by rolling back any changes when a
transaction aborts - eliminating any need for compensation. However,
non-transactional resources (e.g. a file or network connection) that are
modified during a transaction, may require state changes to be explicitly
restored by the application using manual compensation. The
onPrepare
, onCommit
, and
onRollback
methods can be used by applications to
implement manual compensation for non-transactional resources.
Example 4.12. Transaction notifiers
// $Revision: 1.1.2.1 $ package com.kabira.snippets.transactions; import com.kabira.platform.Transaction; import com.kabira.platform.swbuiltin.TransactionNotifier; /** * Snippet showing transaction notifiers * <p> * <h2> Target Nodes</h2> * <ul> * <li> <b>domainnode</b> = A * </ul> */ public class Notifiers extends Transaction { /** * Transaction notifier */ static class Compensation extends TransactionNotifier { String name; /* * Prepare notifier */ @Override public void onPrepare() { // // Perform application specific prepare processing // System.out.println(name + ": onPrepare called"); } /** * Rollback notifier */ @Override public void onRollback() { // // Perform application specific rollback processing // System.out.println(name + ": onRollback called"); // // Do not need to call delete. The notifier instance // deletes itself. // } /** * Commit notifier */ @Override public void onCommit() { // // Perform application specific commit processing // System.out.println(name + ": onCommit called"); // // Do not need to call delete. The notifier instance // deletes itself. // } } Transaction.Result result; /** * Main entry point * @param args Not used */ public static void main(String [] args) { Notifiers notifiers = new Notifiers(); notifiers.result = Result.COMMIT; notifiers.execute(); notifiers.result = Result.ROLLBACK; notifiers.execute(); } /** * Transaction run method * * @throws com.kabira.platform.Transaction.Rollback */ @Override protected void run() throws Transaction.Rollback { op1(); op2(); op3(); if (result == Result.ROLLBACK) { throw new Transaction.Rollback(); } } /** * Operation 1 * <p> * Create a transaction notifier to compensate for this operation */ public void op1() { Compensation compensation = new Compensation(); compensation.name = "op1"; } /** * Operation 2 * <p> * Create a transaction notifier to compensate for this operation */ public void op2() { Compensation compensation = new Compensation(); compensation.name = "op2"; } /** * Operation 3 * <p> * Create a transaction notifier to compensate for this operation */ public void op3() { Compensation compensation = new Compensation(); compensation.name = "op3"; } }
When Example 4.12, “Transaction notifiers” is run it outputs (annotation added):
Example 4.13. Transaction notifier output
# # prepare methods call before commit # [A] op1: onPrepare called [A] op2: onPrepare called [A] op3: onPrepare called # # commit compensation methods # [A] op1: onCommit called [A] op2: onCommit called [A] op3: onCommit called # # no prepare methods called because transaction was explicitly rolled back in execute # before commit processing started # # # rollback compensation methods # [A] op1: onRollback called [A] op2: onRollback called [A] op3: onRollback called
The
and
onCommit
onRollback
methods in a transaction notifier have the
following restrictions:
Partitioned objects cannot be modified.
No new transaction locks can be taken.
Attempting to modify a partitioned object in the
onCommit
or onRollback
methods
will cause a
com.kabira.platform.ResourceUnavailableException
to
be thrown causing the transactional work to be discarded.
All transaction locks must be taken before the
onCommit
or onRollback
methods are
called. This restriction also precludes any Managed Objects from being
created or deleted in these methods because an object create takes an
implicit write lock. It is legal to take new transaction locks in the
onPrepare
method.
![]() | |
Incorrect usage of transaction notifiers can cause an ActiveSpaces® Transactions node to exit with a fatal error. |
Managed Objects can be deleted in the
onPrepare
, onCommit
or
onRollback
methods if they are already
write-locked.
See the section called “Explicit locking”.
The example below shows both legal and illegal object creates in
the onCommit
method.
![]() | |
Running Example 4.14, “Transaction notifier object locks”
with the Attempted to create a new com.kabira.snippets.transactions.Extent object from within a commit trigger. |
Example 4.14. Transaction notifier object locks
// $Revision: 1.1.2.1 $ package com.kabira.snippets.transactions; import com.kabira.platform.Transaction; import com.kabira.platform.annotation.Managed; import com.kabira.platform.swbuiltin.TransactionNotifier; /** * Snippet showing illegal transaction locking in notifiers * <p> * <h2> Target Nodes</h2> * <ul> * <li> <b>domainnode</b> = A * </ul> */ public class NotifierLocks { /** * Managed objects have an extent */ @Managed public static class Extent { }; /** * Non-managed objects do not have an extent */ public static class NoExtent { }; /** * Transaction notifier that takes an illegal lock */ public static class IllegalLock extends TransactionNotifier { /** * Commit notifier */ @Override public void onCommit() { System.out.println("IllegalLock: onCommit called"); // // Attempt to create a new extented object // at commit time - this is illegal since an // implicit write lock is taken on the extent. // // Uncommenting this line will cause the Fluency node to // exit with a fatal error. // // new Extent(); } } /** * Transaction notifier that takes a legal lock */ public static class LegalLock extends TransactionNotifier { /** * Commit notifier */ @Override public void onCommit() { System.out.println("LegalLock: onCommit called"); // // Create a new object that does not have an extent. // This is legal because no write locks are taken. // new NoExtent(); } } /** * Main entry point * @param args Not used */ public static void main(String[] args) { new Transaction("Locking") { @Override protected void run() throws Rollback { new LegalLock(); new IllegalLock(); } }.execute(); } }