147 Transaction Control Service Specification

147.1 Introduction

Software Transactions are an important aspect of most modern applications. The job of a Transaction is to ensure logical consistency for units of work within the application. Any time that the application accesses a persistent external resource then a Transaction ensures that the set of changes made to the resource(s) are Atomic, Consistent, Isolated, and Durable (ACID).

There are a variety of techniques for managing the lifecycle of software Transactions used in an application. The most primitive mechanisms are for the application code to directly interact with the Transaction Manager, but higher level abstractions can automatically manage the lifecycle of Transactions through the use of Aspect Oriented Programming. Whatever techniques are used to manage the Transaction lifecycle it is also necessary for any resource access that occurs within the Transaction to be registered with the Transaction manager. As with managing the Transaction lifecycle, this work may be performed by the client, or by a an intermediate framework without direct action from the client.

OSGi applications consist of a set of independent modules which interact via the OSGi service registry; as such there is no single container which can be relied upon to manage the range of tasks needed to successfully use a Transaction. This leaves OSGi clients with little choice but to depend on specific environments, sacrificing portability, or to directly use Transactions via the JTA Transaction Services Specification. The purpose of the Transaction Control Service is twofold:

  • To enable a portable, modular abstraction for Transaction lifecycle management

  • To allow different resource types to be easily used within a Transaction

147.1.1 Essentials

  • Scoped Work - A function or code block with an associated execution context, known as a Scope. The Scope may be Transactional, that is, associated with a Transaction, or a No Transaction Scope, that is, with no associated Transaction.

  • Client - Application code that wishes to invoke one or more pieces of Scoped Work.

  • Transaction Control Service - The OSGi service representing the Transaction Control Service implementation. Used by the Client to execute pieces of Scoped Work.

  • Resource - A local or remote software component which is stateful and can participate in a transaction.

  • Resource Provider - A service or object which provides managed access to a Scoped Resource, that is, a managed connection to the Resource which integrates with ongoing Transactions as necessary.

  • Transaction Context - A Java object representing the state of a Scope

Figure 147.1 Class and Service overview

Class and Service overview

147.1.2 Entities

  • Transaction Control Service - A service that can execute pieces of work within a Scope, and be queried to establish the current Scope.

  • Client - The code that requests for Work to be run in a particular Scope.

  • Work - A collection of instructions that interact with zero or more Resources within a Scope

  • Scoped Resource - A resource connection with a managed lifecycle. The connection will automatically participate in Transactions associated with Transactional Scopes, and its lifecycle is tied to the Scope within which it is used.

147.2 Usage

This section is an introduction in the usage of the Transaction Control Service. It is not the formal specification, the normative part starts at Transaction Control Service. This section leaves out some of the details for clarity.

147.2.1 Synopsis

The Transaction Control Service provides a mechanism for a client to run work within a defined Scope. Typically a Scope is also associated with a Transaction. The purpose of a Scope is to simplify the lifecycle of resources, and to allow those resources to participate in any ongoing Transaction. Any Scoped Resources accessed during a Scope will remain available throughout the scope, and be automatically cleaned up when the Scope completes.

Each Scope is started by the Client by passing piece of work to the Transaction Control Service. The transaction control service will then begin a scope if needed, execute the work, and then complete the scope if needed. The different methods on the Transaction Control Service provide different lifecycle semantics for the Scope. Some methods establish a Transactional Scope, others may suspend an active Transactional Scope replacing it with a No Transaction Scope.

When a piece of Scoped Work is executing it may access one or more Scoped Resources. When a Scoped Resource is first accessed within a Scope it is bound to that Scope so that future accesses use the same physical resource. At the end of the Scope the resource is detached from the scope and the physical resource is released. If the Scope is Transactional then the Scoped Resource will also participate in the transaction.

At the end of a piece of Scoped Work the Scope is finished. For a No Transaction Scope this simply involves calling any registered callbacks. For a Transactional Scope, however, the Transaction must be completed or rolled back. If the Scoped Work exits normally, and no call has been made to force the Transaction to roll back, then the Transaction will commit. If, however, the Work exits with an Exception or the Transaction has been marked for roll back, then the Transaction will roll back. The result of the Work then flows back to the caller in an appropriate way.

147.2.2 Running Scoped Work

The general pattern for a client is to obtain the Transaction Control Service and one or more Resource Provider instances. The Resource Provider(s) may come from the Service Registry, or from a Factory, and are used to create Scoped Resource instances. These instances can then be used in the scoped work. This is demonstrated in the following example:

@Reference
TransactionControl control;

Connection connection;

@Reference
void setResourceProvider(JDBCConnectionProvider provider) {
    connection = provider.getResrouce(control)
}

public void addMessage(String message) {
    control.required(() -> {
            PreparedStatement ps = connection.prepareStatement(
                            "Insert into TEST_TABLE values ( ? )");
                    ps.setString(1, message);
                    return ps.executeUpdate();
        });
}

public List<String> listMessages(String message) {
    control.notSupported(() -> {
            List<String> results = new ArrayList<String>();
            ResultSet rs = connection.createStatement()
                    .executeQuery("Select * from TEST_TABLE");
            while(rs.next()) {
                results.add(rs.getString(1));
            }
            return results;
        });
}

This example demonstrates how simply clients can execute scoped work using the Transaction Control Service. In this case write operations always occur in a Transactional Scope, but read operations may occur in a Transactional Scope or a No Transaction Scope. In all cases the lifecycle of the underlying connection is automatically managed, and there is no need to close or commit the connection.

147.2.3 Accessing Scoped Resources

The Transaction Control Service can be used to manage the Scope of any piece of Work, but Scopes are primarily used to simplify resource lifecycle management when using Scoped Resources. A Scoped Resource is created using a Resource Provider, and the returned object can then be used in any scope to access the associated Resource.

The example in Running Scoped Work uses a JDBCConnectionProvider, which is a specialization of the generic ResourceProvider interface that returns JDBC Connection objects. Other specializations of the Resource Provider exist in this specification, and third party providers may provide their own specializations for proprietary resource types.

Once a Resource Provider has been obtained, a Scoped Resource is created from it by passing the Transaction Control Service to the getResource method. This returns the Scoped Resource object that can then be used in Scoped Work.

147.2.4 Exception Management

One of the most significant sources of error in applications that use transactions is caused by incorrect Exception Handling. These errors are the primary reason for using a framework or container to manage transactions, rather than trying to manage them in the application code.

Exceptions tend to be more common in code that makes use of transactions because the code is usually performing actions that may fail, for example making updates to a database. Also, many of these exceptions (such as java.sql.SQLException) are checked exceptions. As Scoped Work will typically raise both checked and unchecked exceptions it is defined as a Callable. As the callable interface throws Exception it is not necessary to catch or wrap any exception generated within Scoped Work.

// An SQLException may be raised by the query, 
// but we don't need to catch it
control.required(() -> connection.createStatement()
    .executeQuery("Insert into TEST_TABLE values ( 'Hello World!' )"));

An exception indicates that a problem has occurred in a piece of code therefore, by default, any exception thrown from inside a Transactional Scope will cause the Transaction to roll back. This means that the Scoped Work can safely ignore any updates that were made in the event of an exception.

147.2.4.1 Handling Exceptions

Scoped Work is free to throw checked or unchecked exceptions, however these exceptions cannot be directly thrown on by the Transaction Control Service. The primary reason for this is that directly rethrowing the exception would force users of the Transaction Control Service to either:

  • Declare throws Exception on the calling method

  • Add try/catch Exception blocks around the calls to the Transaction Control Service.

Both of these solutions are undesirable, as they force unnecessary boilerplate code, and potentially shadow real checked exceptions in the API. Exceptions generated as part of Scoped Work are therefore wrapped by the Transaction Control Service in a ScopedWorkException. ScopedWorkException is an unchecked exception and so can be ignored if no special handling is required.

In the case where the callers API requires the unwrapped exception type to be thrown a ScopedWorkException can be easily unwrapped using the as method.

try {
    control.required(() -> connection.createStatement()
        .executeQuery("Insert into TEST_TABLE values ( 'Hello World!' )"));
} catch (ScopedWorkException swe) {
    // This line throws the cause of the ScopedWorkException as
    // an SQLException or as a RuntimeException if appropriate
    throw swe.as(SQLException.class);
}

If there is more than one potential checked Exception type that should be rethrown then the asOneOf method can be used.

try {
    control.required(() -> connection.createStatement()
        .executeQuery("Insert into TEST_TABLE values ( 'Hello World!' )"));
} catch (ScopedWorkException swe) {
    // This line throws the cause of the ScopedWorkException as
    // an SQLException or as a RuntimeException if appropriate
    throw swe.asOneOf(SQLRecoverableException.class, SQLTransientException.class);
}

147.2.4.2 Avoiding Transaction Rollback

In general if a piece of Work running in a Transactional Scope exits with an exception the associated Transaction will roll back. Sometimes, however, certain exception types should not cause the Transaction to roll back. This can be indicated to the Transaction Control Service when the Scope is being declared.

control.build()
    .noRollbackFor(URISyntaxException.class)
    .required(() -> {
            ...
        });

In this example the Transaction does not roll back for any URISyntaxException. Sometimes this is too coarse grained, and the Transaction should only avoid rolling back for one specific exception instance. In this case the instance can be passed to the Transaction Control Service ignoreException method.

control.required(() -> {
        try {    
            // A URISynaxException from here is safe
            ...
        } catch (URISyntaxException e) {
            control.ignoreException(e);
            throw e;
        }
        // A URISynaxException from here is *not* safe
        ...
    });

147.2.5 Multi Threading

By its very definition a Scope is associated with a single piece of Work, and therefore a single thread. If a piece of Scoped Work starts new threads, or submits tasks to other threads, then any code executed on those threads will not occur within the Scope.

Scoped Resources are always thread-safe, and can be used concurrently in different Scopes. This is true even if the underlying physical resources are not thread safe. It is the responsibility of the Scoped Resource implementation to ensure that the underlying physical resources are protected correctly.

147.3 Transaction Control Service

The Transaction Control Service is the primary interaction point between a client and the Transaction Control Service implementation. A Transaction Control Service implementation must expose a service implementing the TransactionControl interface.

Clients obtain an instance of the Transaction Control Service using the normal OSGi service registry mechanisms, either directly using the OSGi framework API, or using dependency injection.

The Transaction Control Service is used to:

  • Execute work within a defined scope

  • Query the current execution scope

  • Associate objects with the current execution scope

  • Register for callbacks when the scope ends

  • Enlist resource with the current transaction (if there is a Transaction Scope active)

  • Mark the current scope for rollback (if there is a Transaction scope)

147.3.1 Scope Life Cycle

The life cycle of a scope is tied to the execution of a piece of scoped work. Unless a scope is being inherited then a scope starts immediately before the scoped work executes and ends immediately after the scoped work completes, even if the scoped work throws an exception.

The first action that a client wishing to execute scoped work must take is to identify the type of scope that they wish to use. The work should then be passed to the relevant method on the TransactionControl service:

Table 147.1 Methods for executing scoped work

Method Name Existing Scope Description
required(Callable) Unscoped

Begins a new Transaction scope and executes the work inside it

required(Callable) No Transaction scope

Suspends the No Transaction Scope and begins a new Transaction scope, executing the work inside it. After the work completes the original scope is restored.

required(Callable) Transaction scope

Runs the work within the existing scope

requiresNew(Callable) Unscoped

Begins a new Transaction scope and executes the work inside it

requiresNew(Callable) No Transaction scope

Suspends the No Transaction Scope and begins a new Transaction scope, executing the work inside it. After the work completes the original scope is restored.

requiresNew(Callable) Transaction scope

Suspends the Transaction Scope and begins a new Transaction scope, executing the work inside it. After the work completes the original scope is restored.

supports(Callable) Unscoped

Begins a new No Transaction scope and executes the work inside it

supports(Callable) No Transaction scope

Runs the work within the existing scope

supports(Callable) Transaction scope

Runs the work within the existing scope

notSupported(Callable) Unscoped

Begins a new No Transaction scope and executes the work inside it

notSupported(Callable) No Transaction scope

Runs the work within the existing scope

notSupported(Callable) Transaction scope

Suspends the Transaction Scope and begins a new No Transaction scope, executing the work inside it. After the work completes the original transaction scope is restored.


Once the relevant method has been identified the client passes the scoped work to the Transaction Control Service. In the typical case the Transaction Control Service must then:

  1. Establish a new scope

  2. Execute the scoped work

  3. Finish the scope, calling any registered callbacks and committing the Transaction if the scope is a Transaction Scope

  4. Return the result of the scoped work to the client

The Transaction Control Service must only finish a scope once, after the execution of the Scoped Work which originally started the scope. This means that callbacks registered by a piece of Scoped Work may not run immediately after the work finishes, but will be delayed until the parent task has finished if the scope was inherited.

147.3.2 Scopes and Exception Management

Resource access is intrinsically error-prone, and therefore there are many potential failure scenarios. Exceptions therefore form an important part of the scope lifecycle.

147.3.2.1 Client Exceptions

The work provided by the client to the Transaction Control Service is passed as a Callable, meaning that the work may throw an Exception. An Exception thrown by the work is known as a Client Exception.

If a client exception is thrown then it must be caught by the Transaction Control Service and handled appropriately by finishing the scope as required. Once the scope has completed the client exception must be wrapped in a ScopedWorkException and rethrown by the Transaction Control service.

If a number of scopes are nested then a ScopedWorkException may be received as a client Exception. A ScopedWorkException must not be re-wrapped by the Transaction Control Service using the normal Exception chaining mechanism, but instead a new ScopedWorkException must be created initialized with the original cause. The caught ScopedWorkException must then be added to the new ScopedWorkException as a suppressed Exception. This prevents clients from having to deeply introspect the exception cause chain to locate the original error.

147.3.2.2 Rethrowing Client Exceptions

In the general case clients will not need to catch a ScopedWorkException, and it can be left to report/handle at a higher level. Sometimes, however, the Exceptions thrown by a piece of work represent an important part of the API, and they need to be thrown on without being wrapped in a ScopedWorkException. The ScopedWorkException provides a simple mechanism to do this. The client simply calls one of the asOneOf(Class,Class) methods which will throw the cause of the Exception as one of the supplied checked Exception types, or directly as an unchecked Exception if the cause is unchecked.

The asOneOf() methods always throw an Exception, but the method return value is declared as a RuntimeException. This can be used to simplify the act of rethrowing the cause when using this method.

try {
    txControl.required(() -> {
                // Do some work in here that may throw IOException
                // or ClassNotFoundException
                return result;
        });
} catch (ScopedWorkException swe) {
    throw swe.asOneOf(IOException.class, ClassNotFoundException.class);
}

If the cause of a ScopedWorkException is a checked exception, but that exception is not assignable to any of the types passed to the asOneOf() method then the cause of the ScopedWorkException will still be thrown, however there will be no compiler assistance for the user when writing their throws clause.

147.3.2.3 Exceptions Generated by the Transaction Control Service

Many operations performed by the Transaction Control Service, particularly when finishing a scope, may result in an Exception. Internal failures, for example a failure when attempting to commit a resource, must be wrapped in a TransactionException and thrown to the client.

A TransactionException must never override a ScopedWorkException. In the case where a ScopedWorkException should be thrown and a Transaction Control Service failure occurs then the TransactionException must be set as a suppressed exception in the ScopedWorkException.

147.3.3 Transaction Scope lifecycle

In addition to callbacks and scoped variables Transaction scopes also provide an ongoing software transaction which shares the lifecycle of the scope. There are therefore additional lifecycle rules for Transaction Scopes

147.3.3.1 Triggering Rollback in Transaction Scopes

By default a transaction will commit automatically when the piece of work completes normally. If this is not desired (for example if the work's business logic determines that the transaction should not complete) then the work may trigger a rollback in one of two ways.

Calling setRollbackOnly() on the Transaction Control object will mark the transaction for rollback so that it will never commit, even if the method completes normally. This is a one-way operation, and the rollback state can be queried using getRollbackOnly()

txControl.required(() -> {
            // Do some work in here
            ...
            // This work will not be committed!
            txControl.setRollbackOnly();
            return result;
    });

Throwing an exception from the piece of work will, by default, cause the transaction to be rolled back. Note that this is different from Java EE behavior, where a checked exceptions does not trigger rollback. This is a deliberate difference as many applications get the wrong behavior based on this default. For example SQLException is a commonly thrown Exception in JDBC, but is rarely, if ever, a “safe return”. Forgetting to override this behavior means that production code will fail to enforce the correct transaction boundaries.

txControl.required(() -> {
            // Do some work in here
            ...
            // Uh oh – something went wrong!
            throw new IllegalStateException(“Kaboom!”);
    });

147.3.3.2 Avoiding Rollback

Sometimes it is preferable for a piece of work to throw an exception, but for that exception not to trigger a rollback of the transaction. For example some business exceptions may be considered “normal”, or it may be the case that the work performed so far must be persisted for audit reasons.

There are two ways to prevent a transaction from rolling back when a particular exception occurs

The Transaction Control service provides a TransactionBuilder. The builder can be used to define sets of Exception types that should, or should not, trigger rollback. The most specific match will be used to determine whether the transaction should roll back or not.

The Transaction Control service provides an ignoreException(Throwable) method. This can be used from within an Active Transaction to declare a specific Exception object that should not trigger rollback.

If a transaction is marked for rollback using setRollbackOnly() then it must roll back, even if the work throws an exception which would not normally trigger a rollback.

147.3.3.3 Rollback in inherited transactions

If a piece of scoped work inherits a transaction scope then the transaction is not committed until the inherited scope completes. Therefore if the nested scoped work throws an exception then this must mark the transaction for rollback, unless the exception has been explicitly ignored or configured not to cause rollback.

Any exception thrown by the nested scoped work must result in a ScopedWorkException in exactly the same way as it would when not nested.

txControl.required(() -> {
            // Do some work in here
            ...
            try {
                txControl.required(() -> {
                            // The outer transaction is inherited
                            throw new RuntimeException();
                    });
            } catch (ScopedWorkException swe) {
                // The transaction is still active, but now marked for rollback
            }
    });

147.3.3.4 Read Only transactions

Resources accessed within a transaction are frequently used to update persistent data, however in some cases it is known in advance that no changes will be made to the data. In the case where no changes are going to be made then different, more optimal, algorithms can be used by the resource to improve performance. It is therefore useful for applications to be able to indicate when resources are going to be used in a read-only way.

To indicate that a transaction is read-only the TransactionBuilder must be used.

txControl.build()
    .readOnly()
    .required(() -> {
            // Do some work in here
            ...
            return result;
        });

The readOnly method provides a hint to the TransactionControl service that the scoped work only uses read access to resources. The TransactionControl service is free to ignore this hint if it does not offer read-only optimizations. Also, read-only only applies to Transaction Scopes. No Transaction Scopes always ignore the call to readOnly.

147.3.3.4.1 Determining whether a Transaction is read only

The TransactionContext provides access to whether the transaction is read only using the isReadOnly() method. This method will return true if the transaction was started using the read only flag, and the TransactionControl service supports read-only optimization.

This method is primarily available so that resource providers can set their read-only status correctly when they first enlist with the transaction. Resource providers are free to ignore the read only status as it is provided for optimization only.

147.3.3.4.2 Writing to resources using in a read only transaction

When a client begins a transaction in read-only mode there is no API restriction that prevents them from writing to one or more resources. If the scoped work does write to the resource then the result is undefined. The write may succeed, or it may result in an exception, triggering a rollback.

Clients should avoid declaring a transaction as read only unless they are certain that no resources are updated within the scope of the work. This includes any operations performed by external services which inherit the transaction.

147.3.3.4.3 Changing the read state in nested transactions

When a client begins a Transaction Scope using the required method then it inherits the existing Transaction Scope if it exists. It is not possible to change the writability of an inherited transaction.

In the case where the inherited transaction is a writable transaction then the readOnly() state declared for the nested scope will be ignored. In the case where the inherited transaction is read only then an attempt to change the transaction to a writable transaction will fail with a TransactionException.

If the nested transaction is declared using requiresNew then it will create a new transaction which may have a different writability from the outer scope.

147.4 The TransactionContext

When a client uses the TransactionControl service to scope a piece of work, the scope gains an associated Transaction Context. The current transaction context is not normally needed by clients, but is an important integration point for ResourceProviders, and for clients that wish to register transaction completion callbacks.

The Transaction Control Service provides methods that can be used to query the current transaction context.

  • activeTransaction() - returns true if there is a Transaction scope associated with the currently executing work.

  • activeScope() - returns true if there is a Transaction Scope or a No Transaction Scope associated with the currently executing work.

  • getCurrentContext() - returns the current TransactionContext, or null if the currently executing code is unscoped. If the current work has a No Transaction scope then the returned Transaction Context will report its status as NO_TRANSACTION

If a Transaction scope is active then it may either be backed by a Local Transaction, or by an XA Transaction, which affects the types of resource that can be used with the Transaction Context. The transaction support can be queried using the supportsLocal() and supportsXA() methods on the transaction context object. Some implementations may support both XA and Local resources in the same transaction, but these are still considered to be XA Transactions.

147.4.1 Transaction Lifecycle callbacks

In addition to registering Resources with the Transaction Context clients or resources may register callback functions. Callback functions can run either before or after the transaction finishes, depending as to whether they are registered using preCompletion(Runnable) or postCompletion(Consumer) to register their callbacks.

Lifecycle callbacks may be registered at any point during the execution of scoped work. Once the scoped work has finished it is no longer possible to register a pre-completion callback (for example inside another lifecycle callback). Attempts to register a pre-completion callback outside the execution of the scoped work must fail with an IllegalStateException. Post-completion callbacks may be also be registered with the Transaction Context after the scoped work completes, up to the point where the first post-completion callback is called. Specifically a pre-completion callback, or a resource participating in the transaction may register a post-completion callback. Attempts to register a post-completion callback after this must fail with an IllegalStateException.

147.4.1.1 Pre-completion Callbacks

Pre-completion callbacks run immediately after the end of the scoped work, and before any associated transaction finishes. Because pre-completion callbacks run before the end of the transaction they are able to prevent it from committing, either by calling setRollbackOnly() or potentially by throwing a RuntimeException. If the scope is a No Transaction scope then there is no commit to prevent.

If scoped work completes with an exception that triggers rollback, then the Transaction Context must be marked for rollback before calling any pre-completion callbacks.

Exceptions generated by pre-completion callbacks are gathered, If any of the generated Exceptions would trigger rollback then the transaction is treated as having failed with the first of those exceptions. Any other exceptions are added as suppressed exceptions. Assuming that no Client Exception occurred then the failure must be reported by throwing a TransactionRolledBackException, or in the case of a No Transaction scope, a TransactionException.

147.4.1.2 Post-completion Callbacks

Post-completion callbacks are run after any associated transaction finishes. As the transaction has completed, post-completion callbacks receive the completion state of the transaction as a method parameter. In the case of a No Transaction context there is no transaction, so the post-completion callbacks immediately follow the pre-completion callbacks, and are passed a status of NO_TRANSACTION.

Exceptions generated by post-completion callbacks are unable to affect the outcome of any transaction, and must therefore be logged, but not acted on further by the Transaction Control service.

Although Post-completion callbacks run after the transaction, the Transaction Context must still be valid when they execute. In particular post-completion callbacks must have access to any scoped variables registered with the Transaction Context

147.4.2 Scoped variables

A Transaction context may be used to store scoped variables. These variables are attached to the TransactionContext, and will be released after the Context finishes. Scoped resources are guaranteed to be accessible in lifecycle callbacks.

Variables may be added to the scope using putScopedValue(Object,Object) and retrieved using getScopedValue(Object). These methods are valid both for Active Transactions and the No Transaction scope.

147.4.3 Transaction Key

Every Active Transaction has an associated key, which will be unique within the lifetime of the TransactionControl service's registration. That is, a registered Transaction Control instance will never reuse a key. The key object is opaque, but is guaranteed to be suitable for use as a key in a HashMap. Note that the Transaction Key is not globally unique, but only unique to the registered TransactionControl service. In particular, two concurrently registered TransactionControl services may simultaneously use the same key, and/or a Transaction Control implementation may reuse keys if it unregisters and then re-registers its service with a different service id.

TransactionContexts for the No Transaction scope have a null key.

147.4.4 The Transaction Status

The current state of a Transaction Context is represented by a Java enum, and can be queried by calling getTransactionStatus(). The status of a Transaction Context will change over time until it reaches a terminal state. Once a terminal state has been reached the status of the Transaction Context will not change again.

The status of a Transaction Context will always move forward through the enum values, that is, the status can never move from one state to another state with a lower sort order. Note that a Transaction Context will not necessarily enter all of the intermediate states between two values.

Table 147.2 Transaction Status Values

Status Terminal Description
NO_TRANSACTION yes

This Transaction Context is for a No Transaction Scope

ACTIVE no

This Transaction Scope is executing and not marked for rollback

MARKED_ROLLBACK no

This Transaction Scope is executing and has been marked for rollback

PREPARING no

A two phase commit is occurring and the transaction is being prepared. This state is visible during the prepare calls on XA resources

PREPARED no

A two phase commit is occurring and the transaction has been prepared. This state is visible immediately prior to committing or rolling back XA resources

COMMITTING no

The transaction is being committed. This state is visible during the commit calls on resources

COMMITTED yes

The transaction was successfully committed.

ROLLING_BACK no

The transaction is being rolled back. This state is visible during the rollback calls on resources

ROLLED_BACK yes

The transaction was successfully rolled back.


147.4.5 Local Transaction scopes

A Local Transaction is not persistent, and therefore not recoverable. It also may not be atomic or consistent if multiple resources are involved. Local transactions do, however, provide isolation and durability, even when multiple resources are involved.

A Local Transaction is therefore a very good choice when a single resource is involved as it is extremely lightweight and provides ACID behavior. Local Transactions do provide benefits when multiple resources are involved, however it is important to realize that Local Transactions may end up in a state where some commits have succeeded and others failed.

147.4.5.1 The Local Transaction Lifecycle

The transaction context for a local transaction begins in the ACTIVE state, and may enter the MARKED_ROLLBACK state if the client calls setRollbackOnly().

A local transaction must always return true from the supportsLocal() method, indicating that LocalResource participants may be registered using the registerLocalResource(LocalResource) method.

Once the transactional work has completed and the pre-completion callbacks have run the transaction will be proceed as follows:

Table 147.3 Lifecycle rules for Local Transactions

Active Marked for Rollback
  1. Set the Transaction Status to COMMITTING

  2. Call commit on the first LocalResource

  3. If the first commit fails set the status Transaction Status to ROLLING_BACK and initialize a TransactionRolledBackException with its cause set to the failure.

  4. Continue committing or rolling-back resources based on the Transaction Status. If a failure occurs then add it as a suppressed exception of an existing TransactionException, creating a new TransactionException if this is the first failure.

  5. Set the Transaction Status to COMMITTED or ROLLED_BACK as appropriate

  6. Call the post-completion callbacks, passing the Transaction Status

  1. Set the Transaction Status to ROLLING_BACK

  2. Call rollback on each of the LocalResources

  3. If a failure occurs then add it as a suppressed exception of an existing TransactionException, creating a new TransactionException if this is the first failure.

  4. Set the Transaction Status to ROLLED_BACK

  5. Call the post-completion callbacks, passing the Transaction Status


147.4.5.2 Local Transaction Support Service Properties

A TransactionControl Service which supports local transactions may be identified using the osgi.local.enabled property which will be set to Boolean.TRUE.

147.4.6 XA Transaction scopes

An XA transaction is persistent, and therefore can be recoverable. It is also atomic and consistent even if multiple resources are involved.

An XA Transaction is therefore a very good choice when a multiple resource are involved as it provides ACID behavior. XA transactions are, however, more heavyweight than local transactions, and should only be used where they are needed.

147.4.6.1 The XA Transaction Lifecycle

The transaction context for an XA transaction begins in the ACTIVE state, and may enter the MARKED_ROLLBACK state if the client calls setRollbackOnly().

An XA transaction must always return true from the supportsXA() method, indicating that XA participants may be registered using the registerXAResource method. XA transactions may also support one or more LocalResource participants. In this case the Transaction Context should also return true from the supportsLocal() method, indicating that LocalResource participants may be registered using the registerLocalResource method.

Once the transactional work has completed and the pre-completion callbacks have run the transaction should be completed using the normal XA algorithm. If the transaction fails during a commit attempt, resulting in a rollback, then the Transaction Control Service must generate a TransactionRolledBackException. If the transaction fails in any other way then the Transaction Control service must generate a TransactionException. Exceptions from the commit should be added to an existing ScopedWorkException if it exists.

147.4.6.2 XA Transaction Support Service Properties

A Transaction Control Service which supports XA transactions may be identified using the osgi.xa.enabled property which will be set to Boolean.TRUE.

If the Transaction Control Service also supports Local transactions then it must also set the osgi.local.enabled property to Boolean.TRUE.

147.5 Resource Providers

It is important that clients can easily control the transaction boundaries within their application, but it is equally important that the resources that the clients use participate in these transactions. In a Java EE Application server this is achieved by having the central application container create and manage all of the resources. In the Spring framework the Application context is responsible for ensuring that the resources are linked to a Transaction Manager.

There is no central container in OSGi, and so a modular solution is required. This specification defines the concept of a Resource Provider. A Resource Provider is a generic service which can provide a resource of some kind to the client. The Resource Provider exists to ensure that the resource being used will always be enlisted with the correct transaction context.

147.5.1 Generic Resource Providers

The purpose of a ResourceProvider is to provide the client with a configured resource which will automatically integrate with the correct transaction context at runtime.

Resources are created from a Resource Provider using the following method:

public <T> T getResource(TransactionControl txControl);

Typically clients will not use a plain Resource Provider, but will search for a specific subclass instead, which reifies the type parameter T. This allows for type safe access to resources, and ensures that the correct ResourceProvider implementation has been found.

147.5.1.1 The Basic Resource Lifecycle

Resources returned by a Resource Provider are proxies to an underlying factory for physical resources. Whenever the proxy is accessed then it should check the current transaction scope. If this is the first time the proxy has been accessed in the scope then the proxy should associate a new physical resource with the scope. If the scope is a Transaction scope then the resource must also be enlisted into the transaction at this point. Subsequent uses of the proxy within the same scope must use the same backing physical resource.

When a scope finishes any resources associated with the scope must be cleaned up without action required by the client. This rule applies to both the Transaction scope and the No Transaction scope, meaning that a client can safely write code using TransactionControl#supports without being concerned about resource leaks.

147.5.1.2 Unscoped Resource Access

If a resource is accessed by unscoped code then it must throw a TransactionException to indicate that it cannot be used without an active scope.

147.5.1.3 Closing, Flushing and Committing Resources

Most resources offer programmatic APIs for transaction and lifecycle management. For example java.sql.Connection has methods called commit and close.

If a client attempts to close a scoped resource then this operation should be silently ignored. The resource will be automatically cleaned up when the current scope completes and so there is no need to manually close the resource. Furthermore, if the resource were prematurely closed then it may prevent other services from accessing the resource within this scope.

If the resource is being used in a Transaction Scope then any transaction lifecycle methods, such as commit or rollback, must not delegate to the real resource and must throw a TransactionException instead.

147.5.1.4 Releasing Resource Providers

Resource Provider instances typically hold references to one or more physical resources, often in a pool. When a Resource Provider is no longer needed then it is important that these physical resources can be released to avoid resource leaks. The way in which a Resource Provider can determine it is no longer needed depends upon how the Resource Provider is created.

If the Resource Provider is registered directly as a service then it may release its physical resources when it is no longer used by any bundles. One way to achieve this is through the use of an OSGi Service Factory.

In some cases a Resource Provider is created by the client using a service from the service registry. In this case the lifecycle of the Resource Provider must be bounded by the lifecycle of the service that created it. In particular if the client bundle releases the service which created the Resource Provider then the Resource Provider must also be released. This mechanism ensures that Resource Providers do not need to be explicitly released by a client bundle when it stops. In addition services which create Resource Provider instances should provide a method which can be used to immediately release a particular Resource Provider instance without releasing service which created it. This allows client bundles to independently manage the lifecycle of multiple Resource Providers, and also to dynamically replace a Resource Provider instance.

Once a Resource Provider has been released then all proxy instances associated with it must be invalidated, and all methods on the proxies throw TransactionException.

147.5.2 JDBC Resource Providers

One of the most common resources to use in a transaction is a JDBC Connection. This specification therefore defines a specialized resource provider for obtaining JDBC Connections called a JDBCConnectionProvider. The purpose of this type is simply to reify the generic type of the ResourceProvider interface.

The scoped resource for a JDBC Connection Provider is a JDBC connection. The scoped resource allows for JDBC connections to be transparently pooled, enlisted in Transaction Scopes, and automatically closed.

147.5.2.1 JDBC and Transaction Scopes

When enlisted in an Active Transaction a JDBC connection will have autocommit set to false. Also the following methods must not be called by the client and must trigger a TransactionException if called.

  • commit()

  • setAutoCommit()

  • setSavepoint()

  • setSavepoint(String)

  • releaseSavepoint()

  • rollback()

  • rollback(Savepoint)

If the Active Transaction commits the JDBC Connection must commit any work performed in the transaction. Similarly if the Active Transaction rolls back then the JDBC Connection must roll back any work performed in the transaction. After the transaction completes the JDBC connection must be cleaned up in an appropriate way, for example by closing it or returning it to a connection pool. There is no need for the client to close the connection, and any attempt to do so must be ignored by the resource provider.

147.5.2.2 JDBC and No Transaction Scopes

When accessed with from the No Transaction scope the JDBC connection may have autocommit set to true or false depending on the underlying configuration of the resource provider. This value may be changed by the client by using setAutoCommit within the scope, but the value will be reset after the end of the scope.

In the No Transaction context the JDBC connection will not be committed or rolled back by the Transaction Control Service or the Resource Provider. It is therefore the client's responsibility to call commit or rollback if appropriate. Savepoints may be used for partial rollback if desired.

After the end of the scope the JDBC connection must be automatically cleaned up by the Resource Provider in an appropriate way, for example by closing it or returning it to a connection pool. There is no need for the client to close the connection, and any attempt to do so must be ignored by the resource provider.

147.5.2.3 Closing the JDBC connection

As for all resource providers, calls to close() the connection must be ignored. JDBC connections also have an abort() method. Abort is effectively an asynchronous close operation for a JDBC connection, and so must also be ignored for any scoped connection.

147.5.2.4 The JDBCConnectionProviderFactory

The JDBCConnectionProvider may be provided as a service directly in the OSGi service registry, however this may not be acceptable in all use cases. JDBC Connections are often authenticated using a username and password. If the username and password relate to a specific bundle then it may not be appropriate to have the fully configured connections available in the Service Registry. In this case the JDBCConnectionProviderFactory offers several factory methods that can programmatically create a JDBCConnectionProvider.

147.5.2.4.1 JDBCConnectionProvider Configuration

Each factory method on the JDBCConnectionProviderFactory supplies set of properties which are used to configure the JDBCConnectionProvider, including the connection pooling behavior, and whether the ResourceProvider can be enlisted with XA and/or Local transactions.

By default the JDBCConnectionProvider will have a pool of 10 connections with a connection timeout of 30 seconds, an idle timeout of 10 minutes and a maximum connection lifetime of 3 hours. The JDBCConnectionProvider will also, by default, work all transaction types supported by the resource provider.

If the JDBCConnectionProvider is configured to enable XA then the DataSourceFactory being used must support XADataSource creation. If a pre-configured DataSource is supplied then it must be able to be unwrapped to an XADataSource.

147.5.2.4.2 Creating a JDBCConnectionProvider Using a DataSourceFactory

In this case the client provides the DataSourceFactory that should be used, along with the properties that should be used to create the DataSource/XADataSource. If XA transactions are enabled then the factory must create an XADataSource, otherwise the “osgi.use.driver” property can be used to force the creation of a Driver instance rather than a DataSource.

147.5.2.4.3 Creating a JDBCConnectionProvider Using a DataSource

In this case the client provides a pre-configured DataSource that should be used. If XA transactions are enabled then the DataSource must be able to be unwrapped to an XADataSource using the unwrap method.

147.5.2.4.4 Creating a JDBCConnectionProvider Using an XADataSource

In this case the client provides a preconfigured XADataSource that should be used by the resource provider.

147.5.2.4.5 Creating a JDBCConnectionProvider Using a Driver

In this case the client provides an instantiated driver class that should be used, along with the properties that should be used to create the JDBC connection. The JDBC properties must include a JDBC url to use when connecting to the database. XA transactions may not be enabled when using a Driver instance.

147.5.2.4.6 Releasing a JDBCConnectionProvider

In some cases a client of the JDBCConnectionProviderFactory may wish to release a created JDBCConnectionProvider without releasing the JDBCConnectionProviderFactory service. In this case the JDBCConnectionProvider instance should be passed to the releaseProvider method, which will immediately release the Resource Provider.

147.5.2.5 JDBCResourceProvider Examples

Setting up data Access with Declarative Services:

@Reference
TransactionControl txControl;

@Reference
JDBCConnectionProviderFactory resourceProviderFactory;

@Reference
DataSourceFactory dsf;

Connection connection;

@Activate
public void setUp(Config config) {
    Properties jdbc = new Properties();
    jdbc.setProperty(JDBC_URL, config.getURL());
        
    connection = resourceProviderFactory.getProviderFor(dsf, jdbc, null)
                    .getResource(txControl);
}

Reading data from a table:

txControl.supports(() -> {
        ResultSet rs = connection.createStatement()
                .executeQuery("Select message from TEST_TABLE");

        rs.next();
        return rs.getString(1);
    });
         

Updating a table:


txControl.required(() -> 
        connection.createStatement()
           .execute("Insert into TEST_TABLE values ( 'Hello World!' )")
    );

147.5.3 JPA

JPA is a popular Object Relational Mapping (ORM) framework used to abstract away the low-level database access from business code. As an alternative means of accessing a database it is just as important for JPA resources to participate in transactions as it is for JDBC resources. This RFC therefore defines the JPAEntityManagerProvider interface as a specialized resource provider for JPA.

147.5.3.1 JPA and Transaction Scopes

When enlisted in a Transaction a JPA EntityManager will automatically track the state of persisted entity types and update the database as necessary. When participating in a transaction it is forbidden to call the getTransaction method on the EntityManager as manual transaction management is disabled. The joinTransaction method, however must be a no-op, and the isJoinedToTransaction must always return true.

If the Transaction commits the JPA EntityManager must commit any work performed in the transaction. Similarly if the Transaction rolls back then the JPA EntityManager must roll back any work performed in the transaction. After the transaction completes the JPA EntityManager must be cleaned up in an appropriate way, for example by closing it or returning it to a pool. There is no need for the client to close the entity manager, and any attempt to do so must be ignored by the resource provider.

147.5.3.2 JPA and No Transaction Scopes

When accessed with from the No Transaction scope the JPA EntityManager will not be participating in a Transaction or rolled back, it is therefore the client's responsibility to set up an EntityTransaction and to call commit or rollback as appropriate.

The joinTransaction method must throw a TransactionException, and the isJoinedToTransaction must always return false.

After the end of the scope the EntityManager must be automatically cleaned up in an appropriate way, for example by closing it or returning it to a pool.

147.5.3.3 RESOURCE_LOCAL and JTA EntityManagerFactory instances

When defining a JPA Persistence Unit the author must declare whether the EntityManagerFactory integrates with JTA transactions, or is suitable for resource local usage. The JPAEntityManagerProvider must take this into account when creating the transactional resource.

JTA scoped EntityManager instances may not manage their own transactions and must throw a JPA TransactionRequiredException if the client attempts to use the EntityTransaction interface. In effect the EntityManager behaves as a Synchronized, Transaction-Scoped, Managed Persistence Context as per the JPA 2.1 Specification. It is important to ensure that the Database connections used in a JTA Persistence Unit are integrated with the ongoing transaction.

RESOURCE_LOCAL scoped EntityManager instances may not participate in XA transactions, but otherwise behave in much the same way as JTA EntityManager instances. The one significant difference is that RESOURCE_LOCAL EntityManager instances may obtain an EntityTransaction when running in the No Transaction context.

147.5.3.4 The JPAEntityManagerProvider Factory

The JPAEntityManagerProvider may be provided directly in the OSGi service registry, however this may not be acceptable in all use cases. Database Connections are often authenticated using a username and password. If the username and password relate to a specific bundle then it may not be appropriate to have the configured connections available in the Service Registry. In this case the JPAEntityManagerProviderFactory offers several factory methods that can programmatically create a JPAEntityManagerProvider.

147.5.3.4.1 Creating a JPAEntityManagerProvider Using an EntityManagerFactoryBuilder

In this case the client provides the EntityManagerFactoryBuilder that should be used, along with the properties that should be used to create the EntityManagerFactory.

The typical reason for using an EntityManagerFactoryBuilder is to allow for the late binding of configuration, such as the database location. To support this usage pattern it is best to specify as few properties as possible inside the persistence descriptor. For example:

<persistence-unit name="test-unit">
  <description>My application's persistence unit</description>
</persistence-unit>

Passing String class names and expecting the JPA provider to load the Database driver reflectively should be avoided, however a configured DataSource can be passed using the javax.persistence.jtaDataSource property. If the JPA resource provider supports XA transactions then this property may be used to pass a configured XADataSource to be enlisted by the provider.

The osgi.jdbc.provider property can be passed to the resource provider defining a JDBCConnectionProvider that should be converted into a DataSource and passed to the EntityManageFactoryBuilder using the javax.persistence.jtaDataSource property. This allows the same physical database connection to be used by JPA and by JDBC within the same scope. Note that when using the osgi.jdbc.provider property to provide a connection to the database the JPA Resource Provider implementation should ignore configuration properties that cannot be acted upon, for example connection pool configuration, or setting an XA recovery identifier.

When configured to use JTA transactions most JPA implementations require integration with the transaction lifecycle. The JPA resource provider is required introspect the Entity Manager Factory Builder and to provide sufficient configuration to integrate the JPA provider with the supplied Transaction Control service. If the JPA resource provider is unable to supply the necessary configuration for the JPA implementation being used then it must log a warning.

147.5.3.4.2 Creating a JPAEntityManagerProvider Using an EntityManagerFactory

In this case the client provides the configured EntityManagerFactory that should be used, along with the properties that should be used to create the EntityManager.

When using an EntityManagerFactory to create the JPA resource provider there is no possibility for the resource provider implementation to customize the configuration of the EntityManagerFactory. This means that the client is responsible for fully configuring the EntityManagerFactory in this case. For Local Transactions this is reasonably simple, however for XA transactions this configuration process may be very involved. For example JPA providers typically require custom plugins to integrate with external Transaction lifecycle management. It is recommended that clients use the Entity Manager Factory Builder when XA transactions are needed.

147.5.3.4.3 Releasing a JPAEntityManagerProvider

In some cases a client of the JPAEntityManagerProviderFactory may wish to release a created JPAEntityManagerProvider without releasing the JPAEntityManagerProviderFactory service. In this case the JPAEntityManagerProvider instance should be passed to the releaseProvider method, which will immediately release the Resource Provider.

147.5.4 Connection Pooling

Database connections are usually heavyweight objects that require significant time to create. They may also consume physical resources such as memory or network ports. Creating a new database connection for every request is therefore wasteful, and adds unnecessary load to both the application and the database. Caching of database connections is therefore a useful way of improving performance. On the other hand applications must be careful not to create too many database connections. If one thousand requests arrive simultaneously then creating one thousand database connections is likely to crash the database server. These two requirements make database connections an excellent candidate for pooling. A small number of connections are made available and recycled after use. This saves the cost of recreating the connection and limits the overall load on the database.

In fact pooling is an excellent solution for many transactional resources, including JMS and EIS access.

147.5.4.1 Pooling in OSGi

Pooling has traditionally been difficult in OSGi because most connection pooling libraries use reflective access to load the underlying resource connector. This obviously fails unless the pooling library creates a static wiring to the connector, or has dynamic package imports. Both of these solutions are bad practices which create brittle dependencies.

The correct way to obtain Database connections in OSGi is to use a DataSourceFactory, however this offers no Connection Pooling. There is no real equivalent of a DataSourceFactory for JMS ConnectionFactory instances, but they also require manual decoration to enable connection pooling.

As pooling is such a core requirement for transactional resource access it is required for JDBCConnectionProviderFactory and JPAEntityManagerProviderFactory instances to offer connection pooling. The resource provider properties can be used to override the connection pooling configuration defaults (or to disable connection pooling entirely).

Third party resource providers should offer connection pooling using the same configuration properties and defaults wherever possible.

Table 147.4 Pooling configuration properties

Property Name Default Description
osgi.connection.pooling.enabled true

Whether connection pooling is enabled for this ResourceProvider

osgi.connection.timeout 30000

The maximum time that a client will wait for a connection (in ms)

osgi.idle.timeout 180000

The time that a connection will remain idle before being closed (in ms)

osgi.connection.lifetime 10800000

The maximum time that a connection will remain open (in ms)

osgi.connection.min 10

The minimum number of connections that will be kept alive

osgi.connection.max 10

The maximum number of connections that will exist in the pool


147.6 Transaction Recovery

The XA transaction protocol defines a recovery mechanism which can be used to resolve in-doubt transactions. This is based upon the interaction of an XA Transaction Manager with an XAResource. In an OSGi environment resources may come and go at any time, as may Transaction Manager instances. Transaction recovery in OSGi is therefore a continuous, rather than a one-time process.

There are two main recovery scenarios that must be resolved by a Transaction Manager:

  • Failure of one or more remote resources before the end of the transaction. In this case the Transaction Manager remains running and can roll-back or commit the other resources as appropriate. When the failed resource(s) eventually become available again the Transaction Manager can complete the in-doubt Transaction branch by committing it or rolling it back as appropriate.

  • Failure of the Transaction Manager before the end of the transaction. In this case the Transaction Manager must use its recovery log to discover any in-doubt transaction branches. When the resources associated with the in-doubt transaction branches become available the Transaction Manager can resolve the in-doubt branch by committing or rolling it back as appropriate.

In both of these cases it is crucial that the Transaction Manager can uniquely identify the resource that is being recovered. The Transaction Manager must be able to tell that a returning resource is suitable for recovering an in-doubt transaction branch.

The transaction branch itself has an Xid, which could theoretically be used to identify the resource. The problem with this, however, is that if the resource has already completed the transaction branch (for example if the failure occurred after sending a commit operation) then the resource may have discarded the Xid. We therefore require another identifier for a resource. The identifier must be unique to the Transaction Manager, but need not be Globally Unique. The identifier must also be persistent, that is, the same resource must have the same identifier after a restart of the OSGi framework. This ensures that transaction recovery can occur after a system crash.

147.6.1 Enlisting a Recoverable Resource in a Transaction

When a recoverable XA resource is associated with a TransactionContext using the registerXAResource method the resource identifier String is passed as a second argument. This is the identifier that will be used to locate the resource during recovery. If the XAResource is not recoverable then it may simply pass null as the second argument when registering.

147.6.2 Providing an XAResource for Recovery

When recovery is required the Transaction Manager may or may not be actively processing transactions involving the required recoverable resource. Therefore the Transaction Control service must be able to locate and obtain an XAResource instance for a named ResourceProvider.

To enable this the ResourceProvider must provide a whiteboard service which implements the RecoverableXAResource interface. This interface provides the resource identifier, and acts as a factory for XAResources that can be used to recover Transaction Branches.

The Transaction Control service can use this whiteboard to locate the correct XAResource to use. It may be, however, that when recovery is attempted it is not possible to provide a valid XAResource. In this case the RecoverableXAResource service may throw an exception. For example if the ResourceProvider is providing pooling and the pool is currently fully used then this may result in an exception.

Once the Transaction Control service has finished attempting to recover a Transaction branch then it must release the XAResource it obtained from the RecoverableXAResource using the releaseXAResource method.

147.6.3 Identifying implementations which support recovery

Transaction Control implementations which support recovery must register the Transaction Control service with the osgi.recovery.enabled service property with a value of true if recovery is enabled. Recovery may only be enabled if the implementation is configured for recovery, for example by configuring a transaction log.

Resource Provider factory services which support creating recoverable scoped resources must also register the osgi.recovery.enabled service property with a value of true. The recovery identifier of a scoped resource created by the factory is specified using the osgi.recovery.identifier property. It is an error to attempt to create a recoverable scoped resource from a factory which does not support recovery, and a TransactionException will be thrown to the caller if they attempt to set a recovery identifier when using a factory that does not support recovery.

147.7 Capabilities

Implementations of the Transaction Control Service specification must provide a capability in the osgi.service namespace representing the TransactionControl service. This capability must also declare a uses constraint for the org.osgi.service.transaction.control package, and attributes indicating whether the service supports local transactions, XA transactions, and recovery. For example, an XA capable, recoverable Transaction Control implementation which also supports recovery would offer the following capability.

Provide-Capability: osgi.service;objectClass:List<String>=
    "org.osgi.service.transaction.control.TransactionControl";
    uses:="org.osgi.service.transaction.control";osgi.local.enabled="true";
    osgi.xa.enabled="true";osgi.recovery.enabled="true"

Resource Provider Implementations must provide capabilities in the osgi.service namespace representing the ResourceProvider services and any factory services that they provide. These capabilities must also declare uses constraints for the org.osgi.service.transaction.control package and any other packages that they provide. In the case where a more specific type is registered (for example JDBCConnectionProvider) then that type should be used instead. The service properties that indicate whether the resource provider supports local transactions, XA transactions, and recovery must be advertised as attributes. For example:

Provide-Capability: osgi.service;objectClass:List<String>=
    "org.osgi.service.transaction.control.jdbc.JDBCConnectionProvider";
    uses:="org.osgi.service.transaction.control,org.osgi.service.transaction.
    control.jdbc";osgi.local.enabled="true";osgi.xa.enabled="true";
    osgi.recovery.enabled="true", 
    osgi.service;objectClass:List<String>=
    "org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory";
    uses:="org.osgi.service.transaction.control,org.osgi.service.transaction.
    control.jdbc";osgi.local.enabled="true";osgi.xa.enabled="true";
    osgi.recovery.enabled="true"

These capabilities must follow the rules defined for the osgi.service Namespace.

147.8 Security

Access to the Transaction Control service and to Resource Provider services can be secured through the standard OSGi service permission model.

Clients should be aware that when they run scoped work there will be code from the Transaction Control service on the stack. Client operations that require specific privileges will therefore have to be performed inside a doPrivileged block.

147.9 org.osgi.service.transaction.control

Version 1.0

Transaction Control Service Package Version 1.0.

Bundles wishing to use this package must list the package in the Import-Package header of the bundle's manifest. This package has two types of users: the consumers that use the API in this package and the providers that implement the API in this package.

Example import for consumers using the API in this package:

Import-Package: org.osgi.service.transaction.control; version="[1.0,2.0)"

Example import for providers implementing the API in this package:

Import-Package: org.osgi.service.transaction.control; version="[1.0,1.1)"

147.9.1 Summary

  • LocalResource - Resources that can integrate with local transactions should do so using this interface

  • ResourceProvider - A resource provider is used to provide a transactional resource to the application

  • ScopedWorkException - An Exception that is thrown when a piece of scoped work exits with an Exception.

  • TransactionBuilder - A builder for a piece of transactional work

  • TransactionContext - A transaction context defines the current transaction, and allows resources to register information and/or synchronizations

  • TransactionControl - The interface used by clients to control the active transaction context

  • TransactionException - An Exception indicating that there was a problem with starting, finishing, suspending or resuming a transaction

  • TransactionRolledBackException - An Exception indicating that the active transaction was unexpectedly rolled back

  • TransactionStarter - Implementations of this interface are able to run a piece of work within a transaction

  • TransactionStatus - The status of the transaction A transaction may not enter all of the states in this enum, however it will always traverse the enum in ascending order.

147.9.2 public interface LocalResource

Resources that can integrate with local transactions should do so using this interface

147.9.2.1 public void commit() throws TransactionException

Commit the resource

TransactionException– 

147.9.2.2 public void rollback() throws TransactionException

Roll back the resource

TransactionException– 

147.9.3 public interface ResourceProvider<T>

A resource provider is used to provide a transactional resource to the application

147.9.3.1 public T getResource(TransactionControl txControl) throws TransactionException

Get a resource which will associate with the current transaction context when used

The resource which will participate in the current transaction

TransactionException– if the resource cannot be registered with the transaction

147.9.4 public class ScopedWorkException
extends RuntimeException

An Exception that is thrown when a piece of scoped work exits with an Exception.

If the scope was inherited and therefore is still active when this exception is raised then the current TransactionContext will be available from the ongoingContext() method.

Consumers of this API must not implement this type

147.9.4.1 public ScopedWorkException(String message, Throwable cause, TransactionContext context)

Creates a new TransactionException with the supplied message and cause

147.9.4.2 public T extends Throwable as(Class<T> throwable) throws T

<T extends Throwable>

Throws the cause of this Exception as a RuntimeException the supplied Exception type.

Usage is of the form:

 public void doStuff() throws IOException {
     try {
         ...
     } catch (ScopedWorkException swe) {
         throw swe.as(IOException.class);
     }
 }

This method will always throw an exception

T– 

147.9.4.3 public RuntimeException asOneOf(Class<A> a, Class<B> b) throws A, B

<A extends Throwable, B extends Throwable>

Throws the cause of this Exception as a RuntimeException or one of the supplied Exception types.

Usage is of the form:

 public void doStuff() throws IOException, ClassNotFoundException {
     try {
         ...
     } catch (ScopedWorkException swe) {
         throw swe.asOneOf(IOException.class, ClassNotFoundException.class);
     }
 }

This method will always throw an exception

A– 

B– 

147.9.4.4 public RuntimeException asOneOf(Class<A> a, Class<B> b, Class<C> c) throws A, B, C

<A extends Throwable, B extends Throwable, C extends Throwable>

Throws the cause of this Exception as a RuntimeException or one of the supplied Exception types.

This method will always throw an exception

A– 

B– 

asOneOf(Class, Class)

147.9.4.5 public RuntimeException asOneOf(Class<A> a, Class<B> b, Class<C> c, Class<D> d) throws A, B, C, D

<A extends Throwable, B extends Throwable, C extends Throwable, D extends Throwable>

Throws the cause of this Exception as a RuntimeException or one of the supplied Exception types.

This method will always throw an exception

A– 

B– 

C– 

D– 

asOneOf(Class, Class)

147.9.4.6 public RuntimeException asRuntimeException()

The cause of this Exception as a RuntimeException if it is one, or this otherwise

147.9.4.7 public TransactionContext ongoingContext()

The ongoing transaction context if the current scope was still active when this exception was raised or null otherwise. Note that this property will not be persisted during serialization.

147.9.5 public abstract class TransactionBuilder
implements TransactionStarter

A builder for a piece of transactional work

Consumers of this API must not implement this type

147.9.5.1 protected final List<Class<? extends Throwable>> noRollbackFor

The list of Throwable types that must not trigger rollback

147.9.5.2 protected final List<Class<? extends Throwable>> rollbackFor

The list of Throwable types that must trigger rollback

147.9.5.3 public TransactionBuilder()

147.9.5.4 public final TransactionBuilder noRollbackFor(Class<? extends Throwable> t, Class<? extends Throwable>... throwables)

An exception type that should not trigger rollback

further exception types that should not trigger rollback

Declare a list of Exception types (and their subtypes) that must not trigger a rollback. By default the transaction will rollback for all Exceptions. If an Exception type is registered using this method then that type and its subtypes will not trigger rollback. If the same type is registered using both rollbackFor(Class, Class...) and noRollbackFor(Class, Class...) then the transaction will not begin and will instead throw a TransactionException

Note that the behavior of this method differs from Java EE and Spring in two ways:

  • In Java EE and Spring transaction management checked exceptions are considered "normal returns" and do not trigger rollback. Using an Exception as a normal return value is considered a bad design practice. In addition this means that checked Exceptions such as java.sql.SQLException do not trigger rollback by default. This, in turn, leads to implementation mistakes that break the transactional behavior of applications.

  • In Java EE it is legal to specify the same Exception type in rollbackFor and noRollbackFor. Stating that the same Exception should both trigger and not trigger rollback is a logical impossibility, and clearly indicates an API usage error. This API therefore enforces usage by triggering an exception in this invalid case.

this builder

147.9.5.5 public abstract TransactionBuilder readOnly()

Indicate to the Transaction Control service that this transaction will be read-only. This hint may be used by the Transaction Control service and associated resources to optimize the transaction.

Note that this method is for optimization purposes only. The TransactionControl service is free to ignore the call if it does not offer read-only optimization.

If a transaction is marked read-only and then the scoped work performs a write operation on a resource then this is a programming error. The resource is free to raise an exception when the write is attempted, or to permit the write operation. As a result the transaction may commit successfully, or may rollback.

this builder

147.9.5.6 public final TransactionBuilder rollbackFor(Class<? extends Throwable> t, Class<? extends Throwable>... throwables)

The Exception types that should trigger rollback

Declare a list of Exception types (and their subtypes) that must trigger a rollback. By default the transaction will rollback for all Exceptions. If a more specific type is registered using noRollbackFor(Class, Class...) then that type will not trigger rollback. If the same type is registered using both rollbackFor(Class, Class...) and noRollbackFor(Class, Class...) then the transaction will not begin and will instead throw a TransactionException

Note that the behavior of this method differs from Java EE and Spring in two ways:

  • In Java EE and Spring transaction management checked exceptions are considered "normal returns" and do not trigger rollback. Using an Exception as a normal return value is considered a bad design practice. In addition this means that checked Exceptions such as java.sql.SQLException do not trigger rollback by default. This, in turn, leads to implementation mistakes that break the transactional behavior of applications.

  • In Java EE it is legal to specify the same Exception type in rollbackFor and noRollbackFor. Stating that the same Exception should both trigger and not trigger rollback is a logical impossibility, and clearly indicates an API usage error. This API therefore enforces usage by triggering an exception in this invalid case.

this builder

147.9.6 public interface TransactionContext

A transaction context defines the current transaction, and allows resources to register information and/or synchronizations

Consumers of this API must not implement this type

147.9.6.1 public boolean getRollbackOnly() throws IllegalStateException

Is this transaction marked for rollback only

true if this transaction is rollback only

IllegalStateException– if no transaction is active

147.9.6.2 public Object getScopedValue(Object key)

Get a value scoped to this transaction

The resource, or null

147.9.6.3 public Object getTransactionKey()

Get the key associated with the current transaction

the transaction key, or null if there is no transaction

147.9.6.4 public TransactionStatus getTransactionStatus()

The current transaction status

147.9.6.5 public boolean isReadOnly()

true if the TransactionContext supports read-only optimizations and the transaction was marked read only. In particular it is legal for this method to return false even if the transaction was marked read only by the initiating client.

147.9.6.6 public void postCompletion(Consumer<TransactionStatus> job) throws IllegalStateException

Register a callback that will be made after the scope completes

For transactional scopes the state of the scope will be either TransactionStatus.COMMITTED or TransactionStatus.ROLLED_BACK.

For no-transaction scopes the state of the scope will always be TransactionStatus.NO_TRANSACTION.

Post-completion callbacks should not throw Exceptions and cannot affect the outcome of a piece of scoped work

IllegalStateException– if no transaction is active

147.9.6.7 public void preCompletion(Runnable job) throws IllegalStateException

The action to perform before completing the scope

Register a callback that will be made before a scope completes.

For transactional scopes the state of the scope will be either TransactionStatus.ACTIVE or TransactionStatus.MARKED_ROLLBACK. Pre-completion callbacks may call setRollbackOnly() to prevent a commit from proceeding.

For no-transaction scopes the state of the scope will always be TransactionStatus.NO_TRANSACTION.

Exceptions thrown by pre-completion callbacks are treated as if they were thrown by the scoped work, including any configured commit or rollback behaviors for transactional scopes.

IllegalStateException– if the transaction has already passed beyond the TransactionStatus.MARKED_ROLLBACK state

147.9.6.8 public void putScopedValue(Object key, Object value)

Associate a value with this transaction

147.9.6.9 public void registerLocalResource(LocalResource resource) throws IllegalStateException

Register a Local resource with the current transaction

IllegalStateException– if no transaction is active, or the current transaction does not support local resources.

147.9.6.10 public void registerXAResource(XAResource resource, String recoveryId) throws IllegalStateException

The resource id to be used for recovery, the id may be null if this resource is not recoverable.

If an id is passed then a RecoverableXAResource with the same id must be registered in the service registry for recovery to occur.

If the underlying TransactionControl service does not support recovery then it must treat the resource as if it is not recoverable.

Register an XA resource with the current transaction

IllegalStateException– if no transaction is active, or the current transaction is not XA capable

147.9.6.11 public void setRollbackOnly() throws IllegalStateException

Mark this transaction for rollback

IllegalStateException– if no transaction is active

147.9.6.12 public boolean supportsLocal()

true if the current transaction supports Local resources

147.9.6.13 public boolean supportsXA()

true if the current transaction supports XA resources

147.9.7 public interface TransactionControl
extends TransactionStarter

The interface used by clients to control the active transaction context

Consumers of this API must not implement this type

147.9.7.1 public boolean activeScope()

true if a transaction is currently active, or if there is a "no transaction" context active

147.9.7.2 public boolean activeTransaction()

true if a transaction is currently active

147.9.7.3 public TransactionBuilder build()

Build a transaction context to surround a piece of transactional work

A builder to complete the creation of the transaction

147.9.7.4 public TransactionContext getCurrentContext()

The current transaction context, which may be a "no transaction" context, or null if there is no active context

147.9.7.5 public boolean getRollbackOnly() throws IllegalStateException

Gets the rollback status of the active transaction

true if the transaction is marked for rollback

IllegalStateException– if no transaction is active

147.9.7.6 public void ignoreException(Throwable t) throws IllegalStateException

The exception to ignore

Marks that the current transaction should not be rolled back if the supplied Exception is thrown by the current transactional work

IllegalStateException– if no transaction is active

147.9.7.7 public void setRollbackOnly() throws IllegalStateException

Marks the current transaction to be rolled back

IllegalStateException– if no transaction is active

147.9.8 public class TransactionException
extends RuntimeException

An Exception indicating that there was a problem with starting, finishing, suspending or resuming a transaction

Consumers of this API must not implement this type

147.9.8.1 public TransactionException(String message)

Creates a new TransactionException with the supplied message

147.9.8.2 public TransactionException(String message, Throwable cause)

Creates a new TransactionException with the supplied message and cause

147.9.9 public class TransactionRolledBackException
extends TransactionException

An Exception indicating that the active transaction was unexpectedly rolled back

Consumers of this API must not implement this type

147.9.9.1 public TransactionRolledBackException(String message)

Create a new TransactionRolledBackException with the supplied message

147.9.9.2 public TransactionRolledBackException(String message, Throwable cause)

Create a new TransactionRolledBackException with the supplied message

147.9.10 public interface TransactionStarter

Implementations of this interface are able to run a piece of work within a transaction

Consumers of this API must not implement this type

147.9.10.1 public T notSupported(Callable<T> work) throws TransactionException, ScopedWorkException

<T>

The supplied piece of work must be run outside the context of a transaction. If an existing transaction is active then it must be suspended and a "no transaction" context associated with the work. After the work has completed any suspended transaction must be resumed.

The "no transaction" context does not support resource enlistment, and will not commit or rollback any changes, however it does provide a post completion callback to any registered functions. This function is suitable for final cleanup, such as closing a connection

The value returned by the work

TransactionException– if there is an error starting or completing the transaction

ScopedWorkException– if the supplied work throws an Exception

147.9.10.2 public T required(Callable<T> work) throws TransactionException, TransactionRolledBackException, ScopedWorkException

<T>

A transaction is required to run the supplied piece of work. If no transaction is active then it must be started and associated with the work and then completed after the transactional work has finished.

The value returned by the work

TransactionException– if there is an error starting or completing the transaction

TransactionRolledBackException– if the transaction rolled back due to a failure in one of the resources or an internal error in the TransactionControl service

ScopedWorkException– if the supplied work throws an Exception

147.9.10.3 public T requiresNew(Callable<T> work) throws TransactionException, TransactionRolledBackException, ScopedWorkException

<T>

A new transaction is required to run the supplied piece of work. If an existing transaction is active then it must suspended and a new transaction started and associated with the work. After the work has completed the new transaction must also complete and any suspended transaction be resumed.

The value returned by the work

TransactionException– if there is an error starting or completing the transaction

TransactionRolledBackException– if the transaction rolled back due to a failure

ScopedWorkException– if the supplied work throws an Exception

147.9.10.4 public T supports(Callable<T> work) throws TransactionException, ScopedWorkException

<T>

The supplied piece of work may run inside or outside the context of a transaction. If an existing transaction or "no transaction" context is active then it will continue, otherwise a new "no transaction" context is associated with the work. After the work has completed any created transaction context must be completed.

The "no transaction" context does not support resource enlistment, and will not commit or rollback any changes, however it does provide a post completion callback to any registered functions. This function is suitable for final cleanup, such as closing a connection

The value returned by the work

TransactionException– if there is an error starting or completing the transaction

ScopedWorkException– if the supplied work throws an Exception

147.9.11 enum TransactionStatus

The status of the transaction A transaction may not enter all of the states in this enum, however it will always traverse the enum in ascending order. In particular if the TransactionStatus is reported as X then it will never proceed into a state Y where X.compareTo(Y) >= 0;

147.9.11.1 NO_TRANSACTION

No transaction is currently active

147.9.11.2 ACTIVE

A transaction is currently in progress

147.9.11.3 MARKED_ROLLBACK

A transaction is currently in progress and has been marked for rollback

147.9.11.4 PREPARING

A two phase commit is occurring and the transaction is being prepared

147.9.11.5 PREPARED

A two phase commit is occurring and the transaction has been prepared

147.9.11.6 COMMITTING

The transaction is in the process of being committed

147.9.11.7 COMMITTED

The transaction has committed

147.9.11.8 ROLLING_BACK

The transaction is in the process of rolling back

147.9.11.9 ROLLED_BACK

The transaction has been rolled back

147.10 org.osgi.service.transaction.control.jdbc

Version 1.0

Transaction Control JDBC Package Version 1.0.

Bundles wishing to use this package must list the package in the Import-Package header of the bundle's manifest. This package has two types of users: the consumers that use the API in this package and the providers that implement the API in this package.

Example import for consumers using the API in this package:

Import-Package: org.osgi.service.transaction.control.jdbc; version="[1.0,2.0)"

Example import for providers implementing the API in this package:

Import-Package: org.osgi.service.transaction.control.jdbc; version="[1.0,1.1)"

147.10.1 Summary

147.10.2 public interface JDBCConnectionProvider
extends ResourceProvider<Connection>

A specialized ResourceProvider suitable for obtaining JDBC connections.

Instances of this interface may be available in the Service Registry, or can be created using a JDBCConnectionProviderFactory.

147.10.3 public interface JDBCConnectionProviderFactory

A factory for creating JDBCConnectionProvider instances

This factory can be used if the JDBCConnectionProvider should not be a public service, for example to protect a username/password.

Consumers of this API must not implement this type

147.10.3.1 public static final String CONNECTION_LIFETIME = "osgi.connection.lifetime"

The property used to set the maximum amount of time that connections in the pool should remain open

147.10.3.2 public static final String CONNECTION_POOLING_ENABLED = "osgi.connection.pooling.enabled"

The property used to determine whether connection pooling is enabled for this resource provider

147.10.3.3 public static final String CONNECTION_TIMEOUT = "osgi.connection.timeout"

The property used to set the maximum amount of time that the pool should wait for a connection

147.10.3.4 public static final String IDLE_TIMEOUT = "osgi.idle.timeout"

The property used to set the maximum amount of time that connections in the pool should remain idle before being closed

147.10.3.5 public static final String LOCAL_ENLISTMENT_ENABLED = "osgi.local.enabled"

The property used to determine whether local enlistment is enabled for this resource provider

147.10.3.6 public static final String MAX_CONNECTIONS = "osgi.connection.max"

The property used to set the maximum number of connections that should be held in the pool

147.10.3.7 public static final String MIN_CONNECTIONS = "osgi.connection.min"

The property used to set the minimum number of connections that should be held in the pool

147.10.3.8 public static final String OSGI_RECOVERY_IDENTIFIER = "osgi.recovery.identifier"

The property used to set the recovery identifier that should be used by this resource

147.10.3.9 public static final String USE_DRIVER = "osgi.use.driver"

The property used to set the maximum number of connections that should be held in the pool

147.10.3.10 public static final String XA_ENLISTMENT_ENABLED = "osgi.xa.enabled"

The property used to determine whether XA enlistment is enabled for this resource provider

147.10.3.11 public static final String XA_RECOVERY_ENABLED = "osgi.recovery.enabled"

The property used to determine whether XA recovery is enabled for this resource provider

147.10.3.12 public JDBCConnectionProvider getProviderFor(DataSourceFactory dsf, Properties jdbcProperties, Map<String, Object> resourceProviderProperties)

The properties to pass to the DataSourceFactory in order to create the underlying DataSource

Configuration properties to pass to the JDBC Resource Provider runtime

Create a private JDBCConnectionProvider using a DataSourceFactory. This call may fail with a TransactionException if the supplied configuration is invalid. Examples of invalid configuration include:

  • The properties request XA enlistment, but the provider implementation only supports local enlistment

  • The properties attempt to set a recovery alias, but the provider does not support recovery.

A JDBCConnectionProvider that can be used in transactions

147.10.3.13 public JDBCConnectionProvider getProviderFor(DataSource ds, Map<String, Object> resourceProviderProperties)

Configuration properties to pass to the JDBC Resource Provider runtime

Create a private JDBCConnectionProvider using an existing DataSource. This call may fail with a TransactionException if the supplied configuration is invalid. Examples of invalid configuration include:

  • The properties request XA enlistment, but the provider implementation only supports local enlistment

  • The properties attempt to set a recovery alias, but the provider does not support recovery.

A JDBCConnectionProvider that can be used in transactions

147.10.3.14 public JDBCConnectionProvider getProviderFor(Driver driver, Properties jdbcProperties, Map<String, Object> resourceProviderProperties)

The properties to pass to the Driver in order to create a Connection

Configuration properties to pass to the JDBC Resource Provider runtime

Create a private JDBCConnectionProvider using an existing Driver. This call may fail with a TransactionException if the supplied configuration is invalid. Examples of invalid configuration include:

  • The properties request XA enlistment, but the provider implementation only supports local enlistment

  • The properties attempt to set a recovery alias, but the provider does not support recovery.

A JDBCConnectionProvider that can be used in transactions

147.10.3.15 public JDBCConnectionProvider getProviderFor(XADataSource ds, Map<String, Object> resourceProviderProperties)

Configuration properties to pass to the JDBC Resource Provider runtime

Create a private JDBCConnectionProvider using an existing XADataSource. This call may fail with a TransactionException if the supplied configuration is invalid. Examples of invalid configuration include:

  • The properties request XA enlistment, but the provider implementation only supports local enlistment

  • The properties attempt to set a recovery alias, but the provider does not support recovery.

A JDBCConnectionProvider that can be used in transactions

147.10.3.16 public void releaseProvider(JDBCConnectionProvider provider)

Release a JDBCConnectionProvider instance that has been created by this factory. Released instances are eligible to be shut down and have any remaining open connections closed.

Note that all JDBCConnectionProvider instances created by this factory service are implicitly released when the factory service is released by this bundle.

IllegalArgumentException– if the supplied resource was not created by this factory service instance.

147.11 org.osgi.service.transaction.control.jpa

Version 1.0

Transaction Control JPA Package Version 1.0.

Bundles wishing to use this package must list the package in the Import-Package header of the bundle's manifest. This package has two types of users: the consumers that use the API in this package and the providers that implement the API in this package.

Example import for consumers using the API in this package:

Import-Package: org.osgi.service.transaction.control.jpa; version="[1.0,2.0)"

Example import for providers implementing the API in this package:

Import-Package: org.osgi.service.transaction.control.jpa; version="[1.0,1.1)"

147.11.1 Summary

147.11.2 public interface JPAEntityManagerProvider
extends ResourceProvider<EntityManager>

A specialized ResourceProvider suitable for obtaining JPA EntityManager instances.

Instances of this interface may be available in the Service Registry, or can be created using a JPAEntityManagerProviderFactory.

147.11.3 public interface JPAEntityManagerProviderFactory

A factory for creating JPAEntityManagerProvider instances

This factory can be used if the JPAEntityManagerProvider should not be a public service, for example to protect a username/password.

Consumers of this API must not implement this type

147.11.3.1 public static final String CONNECTION_LIFETIME = "osgi.connection.lifetime"

The property used to set the maximum amount of time that connections in the pool should remain open

147.11.3.2 public static final String CONNECTION_POOLING_ENABLED = "osgi.connection.pooling.enabled"

The property used to determine whether connection pooling is enabled for this resource provider

147.11.3.3 public static final String CONNECTION_TIMEOUT = "osgi.connection.timeout"

The property used to set the maximum amount of time that the pool should wait for a connection

147.11.3.4 public static final String IDLE_TIMEOUT = "osgi.idle.timeout"

The property used to set the maximum amount of time that connections in the pool should remain idle before being closed

147.11.3.5 public static final String LOCAL_ENLISTMENT_ENABLED = "osgi.local.enabled"

The property used to determine whether local enlistment is enabled for this resource provider

147.11.3.6 public static final String MAX_CONNECTIONS = "osgi.connection.max"

The property used to set the maximum number of connections that should be held in the pool

147.11.3.7 public static final String MIN_CONNECTIONS = "osgi.connection.min"

The property used to set the minimum number of connections that should be held in the pool

147.11.3.8 public static final String OSGI_RECOVERY_IDENTIFIER = "osgi.recovery.identifier"

The property used to set the recovery identifier that should be used by this resource

147.11.3.9 public static final String PRE_ENLISTED_DB_CONNECTION = "osgi.jdbc.enlisted"

The property used to indicate that database connections will be automatically enlisted in ongoing transactions without intervention from the JPA resource provider

147.11.3.10 public static final String TRANSACTIONAL_DB_CONNECTION = "osgi.jdbc.provider"

The property used to provide a JDBCConnectionProvider to the resource provider. This will be converted into a DataSource by the factory, and passed to the EntityManagerFactoryBuilder using the javax.persistence.jtaDataSource property

147.11.3.11 public static final String XA_ENLISTMENT_ENABLED = "osgi.xa.enabled"

The property used to determine whether XA enlistment is enabled for this resource provider

147.11.3.12 public static final String XA_RECOVERY_ENABLED = "osgi.recovery.enabled"

The property used to determine whether XA recovery is enabled for this resource provider

147.11.3.13 public JPAEntityManagerProvider getProviderFor(EntityManagerFactoryBuilder emfb, Map<String, Object> jpaProperties, Map<String, Object> resourceProviderProperties)

The properties to pass to the EntityManagerFactoryBuilder in order to create the underlying EntityManagerFactory and EntityManager instances

Configuration properties to pass to the JPA Resource Provider runtime

Create a private JPAEntityManagerProvider using an EntityManagerFactoryBuilder. This call may fail with a TransactionException if the supplied configuration is invalid. Examples of invalid configuration include:

  • The properties request XA enlistment, but the provider implementation only supports local enlistment

  • The properties attempt to set a recovery alias, but the provider does not support recovery.

If XA transactions are used then this factory will provide configuration to ensure that the JPA Provider can participate correctly in ongoing transactions.

A JPAEntityManagerProvider that can be used in transactions

147.11.3.14 public JPAEntityManagerProvider getProviderFor(EntityManagerFactory emf, Map<String, Object> resourceProviderProperties)

Configuration properties to pass to the JDBC Resource Provider runtime

Create a private JPAEntityManagerProvider using an existing EntityManagerFactory. This call may fail with a TransactionException if the supplied configuration is invalid. Examples of invalid configuration include:

  • The properties request XA enlistment, but the provider implementation only supports local enlistment

  • The properties attempt to set a recovery alias, but the provider does not support recovery.

When using this method the client is responsible for all configuration of the EntityManagerFactory. This includes setting any relevant integration plugins for ensuring that the JPA provider can participate in the ongoing transaction context.

A JPAEntityManagerProvider that can be used in transactions

147.11.3.15 public void releaseProvider(JPAEntityManagerProvider provider)

Release a JPAEntityManagerProvider instance that has been created by this factory. Released instances are eligible to be shut down and have any remaining open connections closed.

Note that all JPAEntityManagerProvider instances created by this factory service are implicitly released when the factory service is released by this bundle.

IllegalArgumentException– if the supplied resource was not created by this factory service instance.

147.12 org.osgi.service.transaction.control.recovery

Version 1.0

Transaction Control Service Recovery Package Version 1.0.

Bundles wishing to use this package must list the package in the Import-Package header of the bundle's manifest. This package has two types of users: the consumers that use the API in this package and the providers that implement the API in this package.

Example import for consumers using the API in this package:

Import-Package: org.osgi.service.transaction.control.recovery; version="[1.0,2.0)"

Example import for providers implementing the API in this package:

Import-Package: org.osgi.service.transaction.control.recovery; version="[1.0,1.1)"

147.12.1 Summary

147.12.2 public interface RecoverableXAResource

A RecoverableXAResource service may be provided by a ResourceProvider if they are able to support XA recovery operations. There are two main sorts of recovery:

  • Recovery after a remote failure, where the local transaction manager runs throughout

  • Recovery after a local failure, where the transaction manager replays in-doubt transactions from its log

This service is used in both of these cases. The identifier returned by getId() provides a persistent name that can be used to correlate usage of the resource both before and after failure. This identifier must also be passed to TransactionContext.registerXAResource(XAResource, String) each time the recoverable resource is used.

147.12.2.1 public static final String OSGI_RECOVERY_ENABLED = "osgi.recovery.enabled"

This service property key is used by TransactionControl services and ResourceProvider factories to indicate that they can support transaction recovery.

147.12.2.2 public String getId()

Get the id of this resource. This should be unique, and persist between restarts

an identifier, never null

147.12.2.3 public XAResource getXAResource() throws Exception

Get a new, valid XAResource that can be used in recovery This XAResource will be returned later using the releaseXAResource(XAResource) method

a valid, connected, XAResource

Exception– If it is not possible to acquire a valid XAResource at the current time, for example if the database is temporarily unavailable.

147.12.2.4 public void releaseXAResource(XAResource xaRes)

An XAResource previously returned by getXAResource()

Release the XAResource that has been used for recovery