JBoss.orgCommunity Documentation

ArjunaCore Development Guide

TxCore and TXOJ Programmers Guide

by Mark Little, Jonathan Halliday, Andrew Dinn, and Kevin Connor
edited by Misty Stanley-Jones

Abstract

This guide is most relevant to engineers who are responsible for administering Narayana installations. Although this guide is specifically intended for service developers, it will be useful to anyone who would like to gain an understanding of transactions and how they function.


Preface
1. Document Conventions
1.1. Typographic Conventions
1.2. Pull-quote Conventions
1.3. Notes and Warnings
2. We Need Feedback!
1. About This Guide
1.1. Audience
1.2. Prerequisites
2. Overview
2.1. TxCore
2.2. Saving object states
2.3. The object store
2.4. Recovery and persistence
2.5. The life cycle of a Transactional Object for Java
2.6. The concurrency controller
2.7. The transactional protocol engine
2.8. The class hierarchy
3. Using TxCore
3.1. State management
3.1.1. Object states
3.1.2. The object store
3.1.3. Selecting an object store implementation
3.2. Lock management and concurrency control
3.2.1. Selecting a lock store implementation
3.2.2. LockManager
3.2.3. Locking policy
3.2.4. Object constructor and finalizer
4. Advanced transaction issues with TxCore
4.1. Last resource commit optimization (LRCO)
4.2. Nested transactions
4.3. Asynchronously committing a transaction
4.4. Independent top-level transactions
4.5. Transactions within save_state and restore_state methods
4.6. Garbage collecting objects
4.7. Transaction timeouts
4.7.1. Monitoring transaction timeouts
5. Hints and tips
5.1. General
5.1.1. Using transactions in constructors
5.1.2. save_state and restore_state methods
5.2. Direct use of StateManager
6. Constructing a Transactional Objects for Java application
6.1. Queue description
6.2. Constructors and finalizers
6.3. Required methods
6.3.1. save_state, restore_state, and type
6.3.2. enqueue and dequeue methods
6.3.3. queueSize method
6.3.4. inspectValue and setValue methods
6.4. The client
6.5. Comments
A. Object store implementations
A.1. The ObjectStore
A.1.1. Persistent object stores
B. Class definitions
C. Revision History

This manual uses several conventions to highlight certain words and phrases and draw attention to specific pieces of information.

In PDF and paper editions, this manual uses typefaces drawn from the Liberation Fonts set. The Liberation Fonts set is also used in HTML editions if the set is installed on your system. If not, alternative but equivalent typefaces are displayed. Note: Red Hat Enterprise Linux 5 and later includes the Liberation Fonts set by default.

Four typographic conventions are used to call attention to specific words and phrases. These conventions, and the circumstances they apply to, are as follows.

Mono-spaced Bold

Used to highlight system input, including shell commands, file names and paths. Also used to highlight keycaps and key combinations. For example:

The above includes a file name, a shell command and a keycap, all presented in mono-spaced bold and all distinguishable thanks to context.

Key combinations can be distinguished from keycaps by the hyphen connecting each part of a key combination. For example:

The first paragraph highlights the particular keycap to press. The second highlights two key combinations (each a set of three keycaps with each set pressed simultaneously).

If source code is discussed, class names, methods, functions, variable names and returned values mentioned within a paragraph will be presented as above, in mono-spaced bold. For example:

Proportional Bold

This denotes words or phrases encountered on a system, including application names; dialog box text; labeled buttons; check-box and radio button labels; menu titles and sub-menu titles. For example:

The above text includes application names; system-wide menu names and items; application-specific menu names; and buttons and text found within a GUI interface, all presented in proportional bold and all distinguishable by context.

Mono-spaced Bold Italic or Proportional Bold Italic

Whether mono-spaced bold or proportional bold, the addition of italics indicates replaceable or variable text. Italics denotes text you do not input literally or displayed text that changes depending on circumstance. For example:

Note the words in bold italics above — username, domain.name, file-system, package, version and release. Each word is a placeholder, either for text you enter when issuing a command or for text displayed by the system.

Aside from standard usage for presenting the title of a work, italics denotes the first use of a new and important term. For example:

A transaction is a unit of work that encapsulates multiple database actions such that that either all the encapsulated actions fail or all succeed.

Transactions ensure data integrity when an application interacts with multiple datasources.

This chapter contains a description of the use of the TxCore transaction engine and the Transactional Objects for Java (TXOJ) classes and facilities. The classes mentioned in this chapter are the key to writing fault-tolerant applications using transactions. Thus, they are described and then applied in the construction of a simple application. The classes to be described in this chapter can be found in the com.arjuna.ats.txoj and com.arjuna.ats.arjuna packages.

Stand-Alone Transaction Manager

Although Narayana can be embedded in various containers, such as WildFly Application Server, it remains a stand-alone transaction manager as well. There are no dependencies between the core Narayana and any container implementations.

At the root of the class hierarchy is the class StateManager. StateManager is responsible for object activation and deactivation, as well as object recovery. Refer to Example 2.1, “Statemanager” for the simplified signature of the class.


Objects are assumed to be of three possible flavors.

Three Flavors of Objects

Recoverable

StateManager attempts to generate and maintain appropriate recovery information for the object. Such objects have lifetimes that do not exceed the application program that creates them.

Recoverable and Persistent

The lifetime of the object is assumed to be greater than that of the creating or accessing application, so that in addition to maintaining recovery information, StateManager attempts to automatically load or unload any existing persistent state for the object by calling the activate or deactivate operation at appropriate times.

Neither Recoverable nor Persistent

No recovery information is ever kept, nor is object activation or deactivation ever automatically attempted.

If an object is recoverable or recoverable and persistent, then StateManager invokes the operations save_state while performing deactivate, and restore_state while performing activate,) at various points during the execution of the application. These operations must be implemented by the programmer since StateManager cannot detect user-level state changes. This gives the programmer the ability to decide which parts of an object’s state should be made persistent. For example, for a spreadsheet it may not be necessary to save all entries if some values can simply be recomputed. The save_state implementation for a class Example that has integer member variables called A, B and C might be implemented as in Example 2.2, “save_state Implementation”.


Note

it is necessary for all save_state and restore_state methods to call super.save_state and super.restore_state. This is to cater for improvements in the crash recovery mechanisms.

The concurrency controller is implemented by the class LockManager, which provides sensible default behavior while allowing the programmer to override it if deemed necessary by the particular semantics of the class being programmed. As with StateManager and persistence, concurrency control implementations are accessed through interfaces. As well as providing access to remote services, the current implementations of concurrency control available to interfaces include:

The primary programmer interface to the concurrency controller is via the setlock operation. By default, the runtime system enforces strict two-phase locking following a multiple reader, single writer policy on a per object basis. However, as shown in Figure 2.1, “TxCore Class Hierarchy”, by inheriting from the Lock class, you can provide your own lock implementations with different lock conflict rules to enable type specific concurrency control.

Lock acquisition is, of necessity, under programmer control, since just as StateManager cannot determine if an operation modifies an object, LockManager cannot determine if an operation requires a read or write lock. Lock release, however, is under control of the system and requires no further intervention by the programmer. This ensures that the two-phase property can be correctly maintained.

public class LockResult

{
    public static final int GRANTED;
    public static final int REFUSED;
    public static final int RELEASED;
};
public class ConflictType
{
    public static final int CONFLICT;
    public static final int COMPATIBLE;
    public static final int PRESENT;
};
public abstract class LockManager extends StateManager
{
    public static final int defaultRetry;
    public static final int defaultTimeout;
    public static final int waitTotalTimeout;
    public final synchronized boolean releaselock (Uid lockUid);
    public final synchronized int setlock (Lock toSet);
    public final synchronized int setlock (Lock toSet, int retry);
    public final synchronized int setlock (Lock toSet, int retry, int sleepTime);
    public void print (PrintStream strm);
    public String type ();
    public boolean save_state (OutputObjectState os, int ObjectType);
    public boolean restore_state (InputObjectState os, int ObjectType);
    protected LockManager ();
    protected LockManager (int ot);
    protected LockManager (int ot, int objectModel);
    protected LockManager (Uid storeUid);
    protected LockManager (Uid storeUid, int ot);
    protected LockManager (Uid storeUid, int ot, int objectModel);
    protected void terminate ();
};

The LockManager class is primarily responsible for managing requests to set a lock on an object or to release a lock as appropriate. However, since it is derived from StateManager, it can also control when some of the inherited facilities are invoked. For example, LockManager assumes that the setting of a write lock implies that the invoking operation must be about to modify the object. This may in turn cause recovery information to be saved if the object is recoverable. In a similar fashion, successful lock acquisition causes activate to be invoked.

Example 2.3, “Example Class” shows how to try to obtain a write lock on an object.


The transaction protocol engine is represented by the AtomicAction class, which uses StateManager to record sufficient information for crash recovery mechanisms to complete the transaction in the event of failures. It has methods for starting and terminating the transaction, and, for those situations where programmers need to implement their own resources, methods for registering them with the current transaction. Because TxCore supports sub-transactions, if a transaction is begun within the scope of an already executing transaction it will automatically be nested.

You can use TxCore with multi-threaded applications. Each thread within an application can share a transaction or execute within its own transaction. Therefore, all TxCore classes are also thread-safe.

Example 2.4. Relationships Between Activation, Termination, and Commitment

{

    . . .
    O1 objct1 = new objct1(Name-A);/* (i) bind to "old" persistent object A */
    O2 objct2 = new objct2();  /* create a "new" persistent object */
    OTS.current().begin();     /* (ii) start of atomic action */
    objct1.op(...);           /* (iii) object activation and invocations */
    objct2.op(...);
    . . .
    OTS.current().commit(true);  /* (iv) tx commits & objects deactivated */
}              /* (v) */
Creation of bindings to persistent objects

This could involve the creation of stub objects and a call to remote objects. Here, we re-bind to an existing persistent object identified by Name-A, and a new persistent object. A naming system for remote objects maintains the mapping between object names and locations and is described in a later chapter.

Start of the atomic transaction

Operation invocations

As a part of a given invocation, the object implementation is responsible to ensure that it is locked in read or write mode, assuming no lock conflict, and initialized, if necessary, with the latest committed state from the object store. The first time a lock is acquired on an object within a transaction the object’s state is acquired, if possible, from the object store.

Commit of the top-level action

This includes updating of the state of any modified objects in the object store.

Breaking of the previously created bindings


The principal classes which make up the class hierarchy of TxCore are depicted below.

Programmers of fault-tolerant applications will be primarily concerned with the classes LockManager, Lock, and AtomicAction. Other classes important to a programmer are Uid and ObjectState.

Most TxCore classes are derived from the base class StateManager, which provides primitive facilities necessary for managing persistent and recoverable objects. These facilities include support for the activation and de-activation of objects, and state-based object recovery.

The class LockManager uses the facilities of StateManager and Lock to provide the concurrency control required for implementing the serializability property of atomic actions. The concurrency control consists of two-phase locking in the current implementation. The implementation of atomic action facilities is supported by AtomicAction and TopLevelTransaction.

Consider a simple example. Assume that Example is a user-defined persistent class suitably derived from the LockManager. An application containing an atomic transaction Trans accesses an object called O of type Example, by invoking the operation op1, which involves state changes to O. The serializability property requires that a write lock must be acquired on O before it is modified. Therefore, the body of op1 should contain a call to the setlock operation of the concurrency controller.


Procedure 2.1. Steps followed by the operation setlock

The operation setlock, provided by the LockManager class, performs the following functions in Example 2.5, “Simple Concurrency Control”.

  1. Check write lock compatibility with the currently held locks, and if allowed, continue.

  2. Call the StateManager operation activate.activate will load, if not done already, the latest persistent state of O from the object store, then call the StateManager operation modified, which has the effect of creating an instance of either RecoveryRecord or PersistenceRecord for O, depending upon whether O was persistent or not. The Lock is a WRITE lock so the old state of the object must be retained prior to modification. The record is then inserted into the RecordList of Trans.

  3. Create and insert a LockRecord instance in the RecordList of Trans.

Now suppose that action Trans is aborted sometime after the lock has been acquired. Then the rollback operation of AtomicAction will process the RecordList instance associated with Trans by invoking an appropriate Abort operation on the various records. The implementation of this operation by the LockRecord class will release the WRITE lock while that of RecoveryRecord or PersistenceRecord will restore the prior state of O.

It is important to realize that all of the above work is automatically being performed by TxCore on behalf of the application programmer. The programmer need only start the transaction and set an appropriate lock; TxCore and TXOJ take care of participant registration, persistence, concurrency control and recovery.

This section describes TxCore and Transactional Objects for Java (TXOJ) in more detail, and shows how to use TxCore to construct transactional applications.

TxCore needs to be able to remember the state of an object for several purposes, including recovery (the state represents some past state of the object), and for persistence (the state represents the final state of an object at application termination). Since all of these requirements require common functionality they are all implemented using the same mechanism - the classes Input/OutputObjectState and Input/OutputBuffer.

Example 3.1. OutputBuffer and InputBuffer

public class OutputBuffer

{
    public OutputBuffer ();
    public final synchronized boolean valid ();
    public synchronized byte[] buffer();
    public synchronized int length ();
    /* pack operations for standard Java types */
    public synchronized void packByte (byte b) throws IOException;
    public synchronized void packBytes (byte[] b) throws IOException;
    public synchronized void packBoolean (boolean b) throws IOException;
    public synchronized void packChar (char c) throws IOException;
    public synchronized void packShort (short s) throws IOException;
    public synchronized void packInt (int i) throws IOException;
    public synchronized void packLong (long l) throws IOException;
    public synchronized void packFloat (float f) throws IOException;
    public synchronized void packDouble (double d) throws IOException;
    public synchronized void packString (String s) throws IOException;
};
public class InputBuffer

{
    public InputBuffer ();
    public final synchronized boolean valid ();
    public synchronized byte[] buffer();
    public synchronized int length ();
    /* unpack operations for standard Java types */
    public synchronized byte unpackByte () throws IOException;
    public synchronized byte[] unpackBytes () throws IOException;
    public synchronized boolean unpackBoolean () throws IOException;
    public synchronized char unpackChar () throws IOException;
    public synchronized short unpackShort () throws IOException;
    public synchronized int unpackInt () throws IOException;
    public synchronized long unpackLong () throws IOException;
    public synchronized float unpackFloat () throws IOException;
    public synchronized double unpackDouble () throws IOException;
    public synchronized String unpackString () throws IOException;
};

The InputBuffer and OutputBuffer classes maintain an internal array into which instances of the standard Java types can be contiguously packed or unpacked, using the pack or unpack operations. This buffer is automatically resized as required should it have insufficient space. The instances are all stored in the buffer in a standard form called network byte order to make them machine independent.



The object store provided with TxCore deliberately has a fairly restricted interface so that it can be implemented in a variety of ways. For example, object stores are implemented in shared memory, on the Unix file system (in several different forms), and as a remotely accessible store. More complete information about the object stores available in TxCore can be found in the Appendix.

All of the object stores hold and retrieve instances of the class InputObjectState or OutputObjectState. These instances are named by the Uid and Type of the object that they represent. States are read using the read_committed operation and written by the system using the write_uncommitted operation. Under normal operation new object states do not overwrite old object states but are written to the store as shadow copies. These shadows replace the original only when the commit_state operation is invoked. Normally all interaction with the object store is performed by TxCore system components as appropriate thus the existence of any shadow versions of objects in the store are hidden from the programmer.



When a transactional object is committing, it must make certain state changes persistent, so it can recover in the event of a failure and either continue to commit, or rollback. When using TXOJ, TxCore will take care of this automatically. To guarantee ACID properties, these state changes must be flushed to the persistence store implementation before the transaction can proceed to commit. Otherwise, the application may assume that the transaction has committed when in fact the state changes may still reside within an operating system cache, and may be lost by a subsequent machine failure. By default, TxCore ensures that such state changes are flushed. However, doing so can impose a significant performance penalty on the application.

To prevent transactional object state flushes, set the ObjectStoreEnvironmentBean.objectStoreSync variable to OFF.

TxCore comes with support for several different object store implementations. The Appendix describes these implementations, how to select and configure a given implementation on a per-object basis using the ObjectStoreEnvironmentBean.objectStoreType property variable, and indicates how additional implementations can be provided.

The TxCore class StateManager manages the state of an object and provides all of the basic support mechanisms required by an object for state management purposes. StateManager is responsible for creating and registering appropriate resources concerned with the persistence and recovery of the transactional object. If a transaction is nested, then StateManager will also propagate these resources between child transactions and their parents at commit time.

Objects are assumed to be of three possible flavors.

This object property is selected at object construction time and cannot be changed thereafter. Thus an object cannot gain (or lose) recovery capabilities at some arbitrary point during its lifetime.

Example 3.5. Object Store Implementation Using StateManager

public class ObjectStatus

{
    public static final int PASSIVE;
    public static final int PASSIVE_NEW;
    public static final int ACTIVE;
    public static final int ACTIVE_NEW;
    public static final int UNKNOWN_STATUS;
};
public class ObjectType
{
    public static final int RECOVERABLE;
    public static final int ANDPERSISTENT;
    public static final int NEITHER;
};
public abstract class StateManager
{
    public synchronized boolean activate ();
    public synchronized boolean activate (String storeRoot);
    public synchronized boolean deactivate ();
    public synchronized boolean deactivate (String storeRoot, boolean commit);
    public synchronized void destroy ();
 
    public final Uid get_uid ();
    public boolean restore_state (InputObjectState, int ObjectType);
    public boolean save_state (OutputObjectState, int ObjectType);
    public String type ();
    . . .
        protected StateManager ();
    protected StateManager (int ObjectType, int objectModel);
    protected StateManager (Uid uid);
    protected StateManager (Uid uid, int objectModel);
    . . .
        protected final void modified ();
    . . .
};
public class ObjectModel
{
    public static final int SINGLE;
    public static final int MULTIPLE;
};

If an object is recoverable or persistent, StateManager will invoke the operations save_state (while performing deactivation), restore_state (while performing activation), and type at various points during the execution of the application. These operations must be implemented by the programmer since StateManager does not have access to a runtime description of the layout of an arbitrary Java object in memory and thus cannot implement a default policy for converting the in memory version of the object to its passive form. However, the capabilities provided by InputObjectState and OutputObjectState make the writing of these routines fairly simple. For example, the save_state implementation for a class Example that had member variables called A, B, and C could simply be Example 3.6, “Example Implementation of Methods for StateManager”.


In order to support crash recovery for persistent objects, all save_state and restore_state methods of user objects must call super.save_state and super.restore_state.

Note

The type method is used to determine the location in the object store where the state of instances of that class will be saved and ultimately restored. This location can actually be any valid string. However, you should avoid using the hash character (#) as this is reserved for special directories that TxCore requires.

The get_uid operation of StateManager provides read-only access to an object’s internal system name for whatever purpose the programmer requires, such as registration of the name in a name server. The value of the internal system name can only be set when an object is initially constructed, either by the provision of an explicit parameter or by generating a new identifier when the object is created.

The destroy method can be used to remove the object’s state from the object store. This is an atomic operation, and therefore will only remove the state if the top-level transaction within which it is invoked eventually commits. The programmer must obtain exclusive access to the object prior to invoking this operation.

Since object recovery and persistence essentially have complimentary requirements (the only difference being where state information is stored and for what purpose), StateManager effectively combines the management of these two properties into a single mechanism. It uses instances of the classes InputObjectState and OutputObjectState both for recovery and persistence purposes. An additional argument passed to the save_state and restore_state operations allows the programmer to determine the purpose for which any given invocation is being made. This allows different information to be saved for recovery and persistence purposes.

TxCore supports two models for objects, which affect how an objects state and concurrency control are implemented.

TxCore Object Models

Single

Only a single copy of the object exists within the application. This copy resides within a single JVM, and all clients must address their invocations to this server. This model provides better performance, but represents a single point of failure, and in a multi-threaded environment may not protect the object from corruption if a single thread fails.

Figure 3.1. Single Object Model


Multiple

Logically, a single instance of the object exists, but copies of it are distributed across different JVMs. The performance of this model is worse than the SINGLE model, but it provides better failure isolation.

Figure 3.2. Multiple Object Model


The default model is SINGLE. The programmer can override this on a per-object basis by using the appropriate constructor.

In summary, the TxCore class StateManager manages the state of an object and provides all of the basic support mechanisms required by an object for state management purposes. Some operations must be defined by the class developer. These operations are: save_state, restore_state, and type.

boolean save_state(OutputObjectState state, intobjectType)

Invoked whenever the state of an object might need to be saved for future use, primarily for recovery or persistence purposes. The objectType parameter indicates the reason that save_state was invoked by TxCore. This enables the programmer to save different pieces of information into the OutputObjectState supplied as the first parameter depending upon whether the state is needed for recovery or persistence purposes. For example, pointers to other TxCore objects might be saved simply as pointers for recovery purposes but as Uids for persistence purposes. As shown earlier, the OutputObjectState class provides convenient operations to allow the saving of instances of all of the basic types in Java. In order to support crash recovery for persistent objects it is necessary for all save_state methods to call super.save_state.

save_state assumes that an object is internally consistent and that all variables saved have valid values. It is the programmer's responsibility to ensure that this is the case.

boolean restore_state (InputObjectState state, int objectType)

Invoked whenever the state of an object needs to be restored to the one supplied. Once again the second parameter allows different interpretations of the supplied state. In order to support crash recovery for persistent objects it is necessary for all restore_state methods to call super.restore_state.

String type ()

The TxCore persistence mechanism requires a means of determining the type of an object as a string so that it can save or restore the state of the object into or from the object store. By convention this information indicates the position of the class in the hierarchy. For example, /StateManager/LockManager/Object.

The type method is used to determine the location in the object store where the state of instances of that class will be saved and ultimately restored. This can actually be any valid string. However, you should avoid using the hash character (#) as this is reserved for special directories that TxCore requires.

Consider the following basic Array class derived from the StateManager class. In this example, to illustrate saving and restoring of an object’s state, the highestIndex variable is used to keep track of the highest element of the array that has a non-zero value.

Example 3.7. Array Class

public class Array extends StateManager

{
    public Array ();
    public Array (Uid objUid);
    public void finalize ( super.terminate(); super.finalize(); };
    /* Class specific operations. */
    public boolean set (int index, int value);
    public int get (int index);
    /* State management specific operations. */
    public boolean save_state (OutputObjectState os, int ObjectType);
    public boolean restore_state (InputObjectState os, int ObjectType);
    public String type ();
    public static final int ARRAY_SIZE = 10;
    private int[] elements = new int[ARRAY_SIZE];
    private int highestIndex;
};
The save_state, restore_state and type operations can be defined as follows:
    /* Ignore ObjectType parameter for simplicity */
    public boolean save_state (OutputObjectState os, int ObjectType)
    {
        if (!super.save_state(os, ObjectType))
            return false;
        try
            {
                packInt(highestIndex);
                /*
                 * Traverse array state that we wish to save. Only save active elements
                 */
                for (int i = 0; i <= highestIndex; i++)
                    os.packInt(elements[i]);
                return true;
            }
        catch (IOException e)
            {
                return false;
            }
    }
public boolean restore_state (InputObjectState os, int ObjectType)
{
    if (!super.restore_state(os, ObjectType))
        return false;
    try
        {
            int i = 0;
            highestIndex = os.unpackInt();
            while (< ARRAY_SIZE)
                {
                    if (<= highestIndex)
                        elements[i] =  os.unpackInt();
                    else
                        elements[i] = 0;
                    i++;
                }
            return true;
        }
    catch (IOException e)
        {
            return false;
        }
}
public String type ()
{
    return "/StateManager/Array";
}

Concurrency control information within TxCore is maintained by locks. Locks which are required to be shared between objects in different processes may be held within a lock store, similar to the object store facility presented previously. The lock store provided with TxCore deliberately has a fairly restricted interface so that it can be implemented in a variety of ways. For example, lock stores are implemented in shared memory, on the Unix file system (in several different forms), and as a remotely accessible store. More information about the object stores available in TxCore can be found in the Appendix.


TxCore comes with support for several different object store implementations. If the object model being used is SINGLE, then no lock store is required for maintaining locks, since the information about the object is not exported from it. However, if the MULTIPLE model is used, then different run-time environments (processes, Java virtual machines) may need to share concurrency control information. The implementation type of the lock store to use can be specified for all objects within a given execution environment using the TxojEnvironmentBean.lockStoreType property variable. Currently this can have one of the following values:

The concurrency controller is implemented by the class LockManager, which provides sensible default behavior, while allowing the programmer to override it if deemed necessary by the particular semantics of the class being programmed. The primary programmer interface to the concurrency controller is via the setlock operation. By default, the TxCore runtime system enforces strict two-phase locking following a multiple reader, single writer policy on a per object basis. Lock acquisition is under programmer control, since just as StateManager cannot determine if an operation modifies an object, LockManager cannot determine if an operation requires a read or write lock. Lock release, however, is normally under control of the system and requires no further intervention by the programmer. This ensures that the two-phase property can be correctly maintained.

The LockManager class is primarily responsible for managing requests to set a lock on an object or to release a lock as appropriate. However, since it is derived from StateManager, it can also control when some of the inherited facilities are invoked. For example, if a request to set a write lock is granted, then LockManager invokes modified directly assuming that the setting of a write lock implies that the invoking operation must be about to modify the object. This may in turn cause recovery information to be saved if the object is recoverable. In a similar fashion, successful lock acquisition causes activate to be invoked.

Therefore, LockManager is directly responsible for activating and deactivating persistent objects, as well as registering Resources for managing concurrency control. By driving the StateManager class, it is also responsible for registering Resources for persistent or recoverable state manipulation and object recovery. The application programmer simply sets appropriate locks, starts and ends transactions, and extends the save_state and restore_state methods of StateManager.


The setlock operation must be parametrized with the type of lock required (READ or WRITE), and the number of retries to acquire the lock before giving up. If a lock conflict occurs, one of the following scenarios will take place:

  • If the retry value is equal to LockManager.waitTotalTimeout, then the thread which called setlock will be blocked until the lock is released, or the total timeout specified has elapsed, and in which REFUSED will be returned.

  • If the lock cannot be obtained initially then LockManager will try for the specified number of retries, waiting for the specified timeout value between each failed attempt. The default is 100 attempts, each attempt being separated by a 0.25 seconds delay. The time between retries is specified in micro-seconds.

  • If a lock conflict occurs the current implementation simply times out lock requests, thereby preventing deadlocks, rather than providing a full deadlock detection scheme. If the requested lock is obtained, the setlock operation will return the value GRANTED, otherwise the value REFUSED is returned. It is the responsibility of the programmer to ensure that the remainder of the code for an operation is only executed if a lock request is granted. Below are examples of the use of the setlock operation.


The concurrency control mechanism is integrated into the atomic action mechanism, thus ensuring that as locks are granted on an object appropriate information is registered with the currently running atomic action to ensure that the locks are released at the correct time. This frees the programmer from the burden of explicitly freeing any acquired locks if they were acquired within atomic actions. However, if locks are acquired on an object outside of the scope of an atomic action, it is the programmer's responsibility to release the locks when required, using the corresponding releaselock operation.

Unlike many other systems, locks in TxCore are not special system types. Instead they are simply instances of other TxCore objects (the class Lock which is also derived from StateManager so that locks may be made persistent if required and can also be named in a simple fashion). Furthermore, LockManager deliberately has no knowledge of the semantics of the actual policy by which lock requests are granted. Such information is maintained by the actual Lock class instances which provide operations (the conflictsWith operation) by which LockManager can determine if two locks conflict or not. This separation is important in that it allows the programmer to derive new lock types from the basic Lock class and by providing appropriate definitions of the conflict operations enhanced levels of concurrency may be possible.


The Lock class provides a modifiesObject operation which LockManager uses to determine if granting this locking request requires a call on modified. This operation is provided so that locking modes other than simple read and write can be supported. The supplied Lock class supports the traditional multiple reader/single writer policy.

Recall that TxCore objects can be recoverable, recoverable and persistent, or neither. Additionally each object possesses a unique internal name. These attributes can only be set when that object is constructed. Thus LockManager provides two protected constructors for use by derived classes, each of which fulfills a distinct purpose

Protected Constructors Provided by LockManager

LockManager ()

This constructor allows the creation of new objects, having no prior state.

LockManager (int objectType, int objectModel)

As above, this constructor allows the creation of new objects having no prior state. exist. The objectType parameter determines whether an object is simply recoverable (indicated by RECOVERABLE), recoverable and persistent (indicated by ANDPERSISTENT), or neither (indicated by NEITHER). If an object is marked as being persistent then the state of the object will be stored in one of the object stores. The shared parameter only has meaning if it is RECOVERABLE. If the object model is SINGLE (the default behavior) then the recoverable state of the object is maintained within the object itself, and has no external representation). Otherwise an in-memory (volatile) object store is used to store the state of the object between atomic actions.

Constructors for new persistent objects should make use of atomic actions within themselves. This will ensure that the state of the object is automatically written to the object store either when the action in the constructor commits or, if an enclosing action exists, when the appropriate top-level action commits. Later examples in this chapter illustrate this point further.

LockManager(Uid objUid)

This constructor allows access to an existing persistent object, whose internal name is given by the objUid parameter. Objects constructed using this operation will normally have their prior state (identified by objUid) loaded from an object store automatically by the system.

LockManager(Uid objUid, int objectModel)

As above, this constructor allows access to an existing persistent object, whose internal name is given by the objUid parameter. Objects constructed using this operation will normally have their prior state (identified by objUid) loaded from an object store automatically by the system. If the object model is SINGLE (the default behavior), then the object will not be reactivated at the start of each top-level transaction.

The finalizer of a programmer-defined class must invoke the inherited operation terminate to inform the state management mechanism that the object is about to be destroyed. Otherwise, unpredictable results may occur.

Atomic actions (transactions) can be used by both application programmers and class developers. Thus entire operations (or parts of operations) can be made atomic as required by the semantics of a particular operation. This chapter will describe some of the more subtle issues involved with using transactions in general and TxCore in particular.

In some cases it may be necessary to enlist participants that are not two-phase commit aware into a two-phase commit transaction. If there is only a single resource then there is no need for two-phase commit. However, if there are multiple resources in the transaction, the Last Resource Commit Optimization (LRCO) comes into play. It is possible for a single resource that is one-phase aware (i.e., can only commit or roll back, with no prepare), to be enlisted in a transaction with two-phase commit aware resources. This feature is implemented by logging the decision to commit after committing the one-phase aware participant: The coordinator asks each two-phase aware participant if they are able to prepare and if they all vote yes then the one-phase aware participant is asked to commit. If the one-phase aware participant commits successfully then the decision to commit is logged and then commit is called on each two-phase aware participant. A heuristic outcome will occur if the coordinator fails before logging its commit decision but after the one-phase participant has committed since each two-phase aware participant will eventually rollback (as required under presumed abort semantics). This strategy delays the logging of the decision to commit so that in failure scenarios we have avoided a write operation. But this choice does mean that there is no record in the system of the fact that a heuristic outcome has occurred.

In order to utilize the LRCO, your participant must implement the com.arjuna.ats.arjuna.coordinator.OnePhase interface and be registered with the transaction through the BasicAction.add operation. Since this operation expects instances of AbstractRecord, you must create an instance of com.arjuna.ats.arjuna.LastResourceRecord and give your participant as the constructor parameter.


By default, the Transaction Service executes the commit protocol of a top-level transaction in a synchronous manner. All registered resources will be told to prepare in order by a single thread, and then they will be told to commit or rollback. A similar comment applies to the volatile phase of the protocol which provides a synchronization mechanism that allows an interested party to be notified before and after the transaction completes. This has several possible disadvantages:

Therefore, Narayana provides runtime options to enable possible threading optimizations. By setting the CoordinatorEnvironmentBean.asyncBeforeSynchronization environment variable to YES, during the beforeSynchronization phase a separate thread will be created for each synchronization registered with the transaction. By setting the CoordinatorEnvironmentBean.asyncPrepare environment variable to YES, during the prepare phase a separate thread will be created for each registered participant within the transaction. By setting CoordinatorEnvironmentBean.asyncCommit to YES, a separate thread will be created to complete the second phase of the transaction provided knowledge about heuristics outcomes is not required. By setting the CoordinatorEnvironmentBean.asyncAfterSynchronization environment variable to YES, during the afterSynchronization phase a separate thread will be created for each synchronization registered with the transaction provided knowledge about heuristics outcomes is not required.

Exercise caution when writing the save_state and restore_state operations to ensure that no atomic actions are started, either explicitly in the operation or implicitly through use of some other operation. This restriction arises due to the fact that TxCore may invoke restore_state as part of its commit processing resulting in the attempt to execute an atomic action during the commit or abort phase of another action. This might violate the atomicity properties of the action being committed or aborted and is thus discouraged.

Example 4.2. 

If we consider the Example 3.7, “Array Class” given previously, the set and get operations could be implemented as shown below.

This is a simplification of the code, ignoring error conditions and exceptions.

public boolean set (int index, int value)

{
   boolean result = false;
   AtomicAction A = new AtomicAction();
   A.begin();
   // We need to set a WRITE lock as we want to modify the state.
   if (setlock(new Lock(LockMode.WRITE), 0) == LockResult.GRANTED)
   {
      elements[index] = value;
      if ((value > 0) &&(index > highestIndex
         highestIndex = index;
      A.commit(true);
      result = true;
   }
   else
      A.rollback();
   return result;
}
public int get (int index)  // assume -1 means error

{
   AtomicAction A = new AtomicAction();
   A.begin();
   // We only need a READ lock as the state is unchanged.
   if (setlock(new Lock(LockMode.READ), 0) == LockResult.GRANTED)
   {
      A.commit(true);
             return elements[index];
   }
   else
      A.rollback();
   return -1;
}

By default, transactions live until they are terminated by the application that created them or a failure occurs. However, it is possible to set a timeout (in seconds) on a per-transaction basis such that if the transaction has not terminated before the timeout expires it will be automatically rolled back.

In TxCore, the timeout value is provided as a parameter to the AtomicAction constructor. If a value of AtomicAction.NO_TIMEOUT is provided (the default) then the transaction will not be automatically timed out. Any other positive value is assumed to be the timeout for the transaction (in seconds). A value of zero is taken to be a global default timeout, which can be provided by the property CoordinatorEnvironmentBean.defaultTimeout, which has a default value of 60 seconds.

When a top-level transaction is created with a non-zero timeout, it is subject to being rolled back if it has not completed within the specified number of seconds. Narayana uses a separate reaper thread which monitors all locally created transactions, and forces them to roll back if their timeouts elapse. If the transaction cannot be rolled back at that point, the reaper will force it into a rollback-only state so that it will eventually be rolled back.

By default this thread is dynamically scheduled to awake according to the timeout values for any transactions created, ensuring the most timely termination of transactions. It may alternatively be configured to awake at a fixed interval, which can reduce overhead at the cost of less accurate rollback timing. For periodic operation, change the CoordinatorEnvironmentBean.txReaperMode property from its default value of DYNAMIC to PERIODIC and set the interval between runs, in milliseconds, using the property CoordinatorEnvironmentBean.txReaperTimeout. The default interval in PERIODIC mode is 120000 milliseconds.

If a value of 0 is specified for the timeout of a top-level transaction, or no timeout is specified, then Narayana will not impose any timeout on the transaction, and the transaction will be allowed to run indefinitely. This default timeout can be overridden by setting the CoordinatorEnvironmentBean.defaultTimeout property variable when using to the required timeout value in seconds, when using ArjunaCore, ArjunaJTA or ArjunaJTS.

Examples throughout this manual use transactions in the implementation of constructors for new persistent objects. This is deliberate because it guarantees correct propagation of the state of the object to the object store. The state of a modified persistent object is only written to the object store when the top-level transaction commits. Thus, if the constructor transaction is top-level and it commits, the newly-created object is written to the store and becomes available immediately. If, however, the constructor transaction commits but is nested because another transaction that was started prior to object creation is running, the state is written only if all of the parent transactions commit.

On the other hand, if the constructor does not use transactions, inconsistencies in the system can arise. For example, if no transaction is active when the object is created, its state is not saved to the store until the next time the object is modified under the control of some transaction.


The two objects are created outside of the control of the top-level action A. obj1 is a new object. obj2 is an old existing object. When the remember operation of obj2 is invoked, the object will be activated and the Uid of obj1 remembered. Since this action commits, the persistent state of obj2 may now contain the Uid of obj1. However, the state of obj1 itself has not been saved since it has not been manipulated under the control of any action. In fact, unless it is modified under the control of an action later in the application, it will never be saved. If, however, the constructor had used an atomic action, the state of obj1 would have automatically been saved at the time it was constructed and this inconsistency could not arise.

TxCore may invoke the user-defined save_state operation of an object at any time during the lifetime of an object, including during the execution of the body of the object’s constructor. This is particularly a possibility if it uses atomic actions. It is important, therefore, that all of the variables saved by save_state are correctly initialized. Exercise caution when writing the save_state and restore_state operations, to ensure that no transactions are started, either explicitly in the operation, or implicitly through use of some other operation. The reason for this restriction is that TxCore may invoke restore_state as part of its commit processing. This would result in the attempt to execute an atomic transaction during the commit or abort phase of another transaction. This might violate the atomicity properties of the transaction being committed or aborted, and is thus discouraged. In order to support crash recovery for persistent objects, all save_state and restore_state methods of user objects must call super.save_state and super.restore_state.

The examples throughout this manual derive user classes from LockManager. These are two important reasons for this.

However, if you only require access to TxCore's persistence and recovery mechanisms, direct derivation of a user class from StateManager is possible.

Classes derived directly from StateManager must make use of its state management mechanisms explicitly. These interactions are normally undertaken by LockManager. From a programmer's point of view this amounts to making appropriate use of the operations activate, deactivate, and modified, since StateManager's constructors are effectively identical to those of LockManager.




Development Phases of a TxCore Application

  1. First, develop new classes with characteristics like persistence, recoverability, and concurrency control.

  2. Then develop the applications that make use of the new classes of objects.

Although these two phases may be performed in parallel and by a single person, this guide refers to the first step as the job of the class developer, and the second as the job of the applications developer. The class developer defines appropriate save_state and restore_state operations for the class, sets appropriate locks in operations, and invokes the appropriate TxCore class constructors. The applications developer defines the general structure of the application, particularly with regard to the use of atomic actions.

This chapter outlines a simple application, a simple FIFO Queue class for integer values. The Queue is implemented with a doubly linked list structure, and is implemented as a single object. This example is used throughout the rest of this manual to illustrate the various mechanisms provided by TxCore. Although this is an unrealistic example application, it illustrates all of the TxCore modifications without requiring in depth knowledge of the application code.

Note

The application is assumed not to be distributed. To allow for distribution, context information must be propagated either implicitly or explicitly.

The queue is a traditional FIFO queue, where elements are added to the front and removed from the back. The operations provided by the queue class allow the values to be placed on to the queue (enqueue) and to be removed from it (dequeue), and values of elements in the queue can also be changed or inspected. In this example implementation, an array represents the queue. A limit of QUEUE_SIZE elements has been imposed for this example.


Using an existing persistent object requires the use of a special constructor that takes the Uid of the persistent object, as shown in Example 6.2, “Class TransactionalQueue”.


The use of an atomic action within the constructor for a new object follows the guidelines outlined earlier and ensures that the object’s state will be written to the object store when the appropriate top level atomic action commits (which will either be the action A or some enclosing action active when the TransactionalQueue was constructed). The use of atomic actions in a constructor is simple: an action must first be declared and its begin operation invoked; the operation must then set an appropriate lock on the object (in this case a WRITE lock must be acquired), then the main body of the constructor is executed. If this is successful the atomic action can be committed, otherwise it is aborted.

The finalizer of the queue class is only required to call the terminate and finalizer operations of LockManager.

public void finalize ()

{
    super.terminate();
    super.finalize();
}     



If the operations of the queue class are to be coded as atomic actions, then the enqueue operation might have the structure given below. The dequeue operation is similarly structured, but is not implemented here.

Example 6.6. Method enqueue

public void enqueue (int v) throws OverFlow, UnderFlow, QueueError

{
    AtomicAction A = new AtomicAction();
    boolean res = false;
    try
        {
            A.begin(0);
            if (setlock(new Lock(LockMode.WRITE), 0) == LockResult.GRANTED)
                {
                    if (numberOfElements < QUEUE_SIZE)
                        {
                            elements[numberOfElements] = v;
                            numberOfElements++;
                            res = true;
                        }
                    else
                        {
                            A.rollback();
                            throw new UnderFlow();
                        }
                }
            if (res)
                A.commit(true);
            else
                {
                    A.rollback();
                    throw new Conflict();
                }
        }
    catch (Exception e1)
        {
            throw new QueueError();
        }
}       

Example 6.8. Method inspectValue

public int inspectValue (int index) throws UnderFlow,

                                           OverFlow, Conflict, QueueError
{
    AtomicAction A = new AtomicAction();
    boolean res = false;
    int val = -1;
    try
        {
            A.begin();
            if (setlock(new Lock(LockMode.READ), 0) == LockResult.GRANTED)
                {
                    if (index < 0)
                        {
                            A.rollback();
                            throw new UnderFlow();
                        }
                    else
                        {
                            // array is 0 - numberOfElements -1
                            if (index > numberOfElements -1)
                                {
                                    A.rollback();
                                    throw new OverFlow();
                                }
                            else
                                {
                                    val = elements[index];
                                    res = true;
                                }
                        }
                }
            if (res)
                A.commit(true);
            else
                {
                    A.rollback();
                    throw new Conflict();
                }
        }
    catch (Exception e1)
        {
            throw new QueueError();
        }
    return val;
}

Rather than show all of the code for the client, this example concentrates on a representative portion. Before invoking operations on the object, the client must first bind to the object. In the local case this simply requires the client to create an instance of the object.


This appendix examines the various TxCore object store implementations and gives guidelines for creating other implementations and plugging into an application.

This release of Narayana contains several different implementations of a basic object store. Each serves a particular purpose and is generally optimized for that purpose. Each of the implementations implements the ObjectStoreAPI interface, which defines the minimum operations which must be provided for an object store implementation to be used by the Transaction Service. You can override the default object store implementation at runtime by setting the com.arjuna.ats.arjuna.objectstore.objectStoreType property variable to one of the types described below.

Example A.1. Class StateStatus



/*
 * This is the base class from which all object store types are derived.
 * Note that because object store instances are stateless, to improve
 * efficiency we try to only create one instance of each type per process.
 * Therefore, the create and destroy methods are used instead of new
 * and delete. If an object store is accessed via create it *must* be
 * deleted using destroy. Of course it is still possible to make use of
 * new and delete directly and to create instances on the stack.
 */
public class StateStatus
{
    public static final int OS_ORIGINAL;
    public static final int OS_SHADOW;
    public static final int OS_UNCOMMITTED;
    public static final int OS_UNCOMMITTED_HIDDEN;
    public static final int OS_UNKNOWN;
}
public class StateType
{
    public static final int OS_COMMITTED;
    public static final int OS_COMMITTED_HIDDEN;
    public static final int OS_HIDDEN;
    public static final int OS_INVISIBLE;
}
public abstract class ObjectStore implements BaseStore, ParticipantStore,
                                             RecoveryStore, TxLog
{
    public ObjectStore (String osRoot);
    public synchronized boolean allObjUids (String s, InputObjectState buff)
        throws ObjectStoreException;
    public synchronized boolean allObjUids (String s, InputObjectState buff,
                                            int m) throws ObjectStoreException;
    public synchronized boolean allTypes (InputObjectState buff)
        throws ObjectStoreException;
    public synchronized int currentState(Uid u, String tn)
        throws ObjectStoreException;
    public synchronized boolean commit_state (Uid u, String tn)
        throws ObjectStoreException;
    public synchronized boolean hide_state (Uid u, String tn)
        throws ObjectStoreException;
    public synchronized boolean reveal_state (Uid u, String tn)
        throws ObjectStoreException;
    public synchronized InputObjectState read_committed (Uid u, String tn)
        throws ObjectStoreException;
    public synchronized InputObjectState read_uncommitted (Uid u, String tn)
        throws ObjectStoreException;
    public synchronized boolean remove_committed (Uid u, String tn)
        throws ObjectStoreException;
    public synchronized boolean remove_uncommitted (Uid u, String tn)
        throws ObjectStoreException;
    public synchronized boolean write_committed (Uid u, String tn,
                                                 OutputObjectState buff)
        throws ObjectStoreException;
    public synchronized boolean write_uncommitted (Uid u, String tn,
                                                   OutputObjectState buff)
        throws ObjectStoreException;
    public static void printState (PrintStream strm, int res);
};

Narayana programmers do not usually need to interact with any of the object store implementations directly, apart from possibly creating them in the first place. Even this is not necessary if the default store type is used, since Narayana creates stores as necessary. All stores manipulate instances of the class ObjectState. These instances are named using a type (via the object's type() operation) and a Uid.

For atomic actions purposes, object states in the store can be principally in two distinct states: OS_COMMITTED or OS_UNCOMMITTED. An object state starts in the OS_COMMITTED state, but when it is modified under the control of an atomic action, a new second object state may be written that is in the OS_UNCOMMITTED state. If the action commits, this second object state replaces the original and becomes OS_COMMITTED. If the action aborts, this second object state is discarded. All of the implementations provided with this release handle these state transitions by making use of shadow copies of object states. However, any other implementation that maintains this abstraction is permissible.

Object states may become hidden, and thus inaccessible, under the control of the crash recovery system.

You can browse the contents of a store through the allTypes and allObjUids operations. allTypes returns an InputObjectState containing all of the type names of all objects in a store, terminated by a null name. allObjUids returns an InputObjectState containing all of the Uids of all objects of a given type, terminated by the special Uid.nullUid().

This section briefly describes the characteristics and optimizations of each of the supplied implementations of the persistent object store. Persistent object states are mapped onto the structure of the file system supported by the host operating system.

The JDBCStore uses a JDBC database to save persistent object states. When used in conjunction with the Transactional Objects for Java API, nested transaction support is available. In the current implementation, all object states are stored as Binary Large Objects (BLOBs) within the same table. The limitation on object state size imposed by using BLOBs is 64k. If you try to store an object state which exceeds this limit, an error is generated and the state is not stored. The transaction is subsequently forced to roll back.

When using the JDBC object store, the application must provide an implementation of the JDBCAccess interface, located in the com.arjuna.ats.arjuna.objectstore package:


The implementation of this class is responsible for providing the Connection which the JDBC ObjectStore uses to save and restore object states:

getConnection

Returns the Connection to use. This method is called whenever a connection is required, and the implementation should use whatever policy is necessary for determining what connection to return. This method need not return the same Connection instance more than once.

putConnection

Returns one of the Connections acquired from getConnection. Connections are returned if any errors occur when using them.

initialise

Used to pass additional arbitrary information to the implementation.

The JDBC object store initially requests the number of Connections defined in the ObjectStoreEnvironmentBean.jdbcPoolSizeInitial property and will use no more than defined in the ObjectStoreEnvironmentBean.jdbcPoolSizeMaximum property.

The implementation of the JDBCAccess interface to use should be set in the ObjectStoreEnvironmentBean.jdbcUserDbAccessClassName property variable.

If overriding the object store implementation, the type of this object store is JDBCStore.

A JDBC object store can be used for managing the transaction log. In this case, the transaction log implementation should be set to JDBCActionStore and the JDBCAccess implementation must be provided via the ObjectStoreEnvironmentBean.jdbcTxDbAccessClassName property variable. In this case, the default table name is JBossTSTxTable.

You can use the same JDBCAccess implementation for both the user object store and the transaction log.

This object store uses the hashed object store, but does not read or write states to the persistent backing store immediately. It maintains the states in a volatile memory cache and either flushes the cache periodically or when it is full. The failure semantics associated with this object store are different from the normal persistent object stores, because a failure could result in states in the cache being lost.

If overriding the object store implementation, the type of this object store is CacheStore.

This appendix contains an overview of those classes that the application programmer will typically use. The aim of this appendix is to provide a quick reference guide to these classes for use when writing applications in TxCore. For clarity only the public and protected interfaces of the classes will be given.

Example B.1. Class LockManager

public class LockResult

{
    public static final int GRANTED;
    public static final int REFUSED;
    public static final int RELEASED;
};
public class ConflictType
{
    public static final int CONFLICT;
    public static final int COMPATIBLE;
    public static final int PRESENT;
};
public abstract class LockManager extends StateManager
{
    public static final int defaultRetry;
    public static final int defaultTimeout;
    public static final int waitTotalTimeout;
    public final synchronized boolean releaselock (Uid lockUid);
    public final synchronized int setlock (Lock toSet);
    public final synchronized int setlock (Lock toSet, int retry);
    public final synchronized int setlock (Lock toSet, int retry, int sleepTime);
    public void print (PrintStream strm);
    public String type ();
    public boolean save_state (OutputObjectState os, int ObjectType);
    public boolean restore_state (InputObjectState os, int ObjectType);
    protected LockManager ();
    protected LockManager (int ot);
    protected LockManager (int ot, int objectModel);
    protected LockManager (Uid storeUid);
    protected LockManager (Uid storeUid, int ot);
    protected LockManager (Uid storeUid, int ot, int objectModel);
    protected void terminate ();
};

Example B.2. Class StateManager

public class ObjectStatus

{
    public static final int PASSIVE;
    public static final int PASSIVE_NEW;
    public static final int ACTIVE;
    public static final int ACTIVE_NEW;
};
public class ObjectType
{
    public static final int RECOVERABLE;
    public static final int ANDPERSISTENT;
    public static final int NEITHER;
};
 
public abstract class StateManager
{
    public boolean restore_state (InputObjectState os, int ot);
    public boolean save_state (OutputObjectState os, int ot);
    public String type ();
    public synchronized boolean activate ();
    public synchronized boolean activate (String rootName);
    public synchronized boolean deactivate ();
    public synchronized boolean deactivate (String rootName);
    public synchronized boolean deactivate (String rootName, boolean commit);
    public synchronized int status ();
    public final Uid get_uid ();
    public void destroy ();
    public void print (PrintStream strm);
    protected void terminate ();
    protected StateManager ();
    protected StateManager (int ot);
    protected StateManager (int ot, int objectModel);
    protected StateManager (Uid objUid);
    protected StateManager (Uid objUid, int ot);
    protected StateManager (Uid objUid, int ot, int objectModel);
    protected synchronized final void modified ();
};


Example B.4. Classes OutputBuffer and InputBuffer

public class OutputBuffer

{
    public  OutputBuffer ();
    public final synchronized boolean valid ();
    public synchronized byte[] buffer();
    public synchronized int length ();
    /* pack operations for standard Java types */
    public synchronized void packByte (byte b) throws IOException;
    public synchronized void packBytes (byte[] b) throws IOException;
    public synchronized void packBoolean (boolean b) throws IOException;
    public synchronized void packChar (char c) throws IOException;
    public synchronized void packShort (short s) throws IOException;
    public synchronized void packInt (int i) throws IOException;
    public synchronized void packLong (long l) throws IOException;
    public synchronized void packFloat (float f) throws IOException;
    public synchronized void packDouble (double d) throws IOException;
    public synchronized void packString (String s) throws IOException;
};
public class InputBuffer
{
    public  InputBuffer ();
    public final synchronized boolean valid ();
    public synchronized byte[] buffer();
    public synchronized int length ();
    /* unpack operations for standard Java types */
    public synchronized byte unpackByte () throws IOException;
    public synchronized byte[] unpackBytes () throws IOException;
    public synchronized boolean unpackBoolean () throws IOException;
    public synchronized char unpackChar () throws IOException;
    public synchronized short unpackShort () throws IOException;
    public synchronized int unpackInt () throws IOException;
    public synchronized long unpackLong () throws IOException;
    public synchronized float unpackFloat () throws IOException;
    public synchronized double unpackDouble () throws IOException;
    public synchronized String unpackString () throws IOException;
};


Example B.6. Class AtomicAction

public class AtomicAction

{
    public AtomicAction ();
    public void begin () throws SystemException, SubtransactionsUnavailable,
                                NoTransaction;
    public void commit (boolean report_heuristics) throws SystemException, 
                                                          NoTransaction, HeuristicMixed,
                                                          HeuristicHazard,TransactionRolledBack;
    public void rollback () throws SystemException, NoTransaction;
    public Control control () throws SystemException, NoTransaction;
    public Status get_status () throws SystemException;
    /* Allow action commit to be supressed */    
    public void rollbackOnly () throws SystemException, NoTransaction;
    public void registerResource (Resource r) throws SystemException, Inactive;
    public void registerSubtransactionAwareResource (SubtransactionAwareResource sr)
        throws SystemException, NotSubtransaction;
    public void registerSynchronization (Synchronization s) throws SystemException,
                                                                   Inactive;
};

Revision History
Revision 0Fri Sep 24 2010Misty Stanley-Jones
Convert existing documentation to Publican.
Update to 4.13.
Revision 0Thu Apr 14 2011Tom Jenkinson
Moved some content to main developer's guide