Release 10.1A: OpenEdge Application Server:
Developing AppServer Applications

> ## Implementing automatic transactions

> To implement an automatic transaction, you provide a specially-coded remote procedure (transaction initiating procedure) that initializes a transaction object in the AppServer agent where it executes. This transaction object is a permanent object in the session context that provides methods and attributes on a transaction object handle that allow the AppServer session to commit, rollback, or set and check the status of an automatic transaction. Because an automatic transaction terminates when the context of the transaction initiating procedure is deleted, you must execute the transaction initiating procedure persistently to make practical use of the transaction that it initiates.

> ##### Initializing the automatic transaction object

> To initialize the transaction object, you must create a transaction initiating procedure by specifying the `TRANSACTION-MODE` statement as the first executable statement in the procedure file:

> Syntax
>     TRANSACTION-MODE AUTOMATIC [ CHAINED ]

> When the client calls this transaction initiating procedure as a remote persistent procedure, an automatic transaction starts in the context of the AppServer session and can remain open after the remote procedure call returns to the client. The context for this open automatic transaction is the entire AppServer session, not (as you might think) the context internal to the transaction initiating procedure instantiated in the AppServer session. Thus, any remote procedure (persistent or non-persistent) that is subsequently executed participates in this open automatic transaction.

> ##### Controlling the transaction

> You can access the transaction object handle for the initialized transaction object using the `TRANSACTION` attribute on the procedure of any procedure executing in the AppServer session by using the `THIS-PROCEDURE` system handle, as shown:

>     DEFINE VARIABLE hTran AS HANDLE NO-UNDO.
>     hTran = THIS-PROCEDURE:TRANSACTION.

> You can then access the transaction object methods and attributes to control the transaction, as described in Table 2–2.

> Table 2–2: Transaction handle methods and attributes
> Method/Attribute | Return/Data type | Description
> --- | --- | ---
> `DEFAULT-COMMIT` | `LOGICAL` | A writable attribute whose setting tells the transaction object how to complete any open automatic transaction if the transaction terminates with no prior invocation of the `SET-COMMIT( )` or `SET-ROLLBACK( )` method (for example, if the transaction initiating procedure is deleted). Setting it to `TRUE` ensures that the transaction is committed. Setting it to `FALSE` ensures that the transaction is rolled back. The default value is `FALSE`.
> `IS-OPEN` | `LOGICAL` | A read-only attribute that returns `TRUE` if a transaction is active. The function of this attribute is identical to the Progress 4GL `TRANSACTION` function. See also the `TRANS-INIT-PROCEDURE` attribute.
> `SET-COMMIT( )` | `LOGICAL` | Tells the transaction object to commit any active automatic transaction after the current request returns execution to the client. If no automatic transaction is active, the method returns `FALSE`. Note that you cannot invoke this method after invoking a prior `SET-ROLLBACK( )` within the same client request.
> `SET-ROLLBACK( )` | `LOGICAL` | Tells the transaction object to roll back any active automatic transaction after the current request returns execution to the client. If no automatic transaction is active, the method returns `FALSE`. Note that you can invoke this method after invoking a prior `SET-COMMIT( )` within the same client request.
> `TRANS-INIT-PROCEDURE` | `HANDLE` | A read-only attribute that returns the procedure handle to the transaction initiating procedure for any active automatic transaction. If no automatic transaction is active this attribute returns an invalid handle. (Validate with the `VALID-HANDLE` function.)

> ##### Terminating automatic transactions

> An automatic transaction remains active as long as the context of the transaction initiating procedure remains active and you do not otherwise terminate the transaction using a transaction handle method. Thus, you can terminate an automatic transaction using two techniques:

> - Explicit termination — You can explicitly terminate an automatic transaction on the AppServer with a reference to the `SET-COMMIT( )` method or the `SET-ROLLBACK( )` method, as shown:

>     hTran = THIS-PROCEDURE:TRANSACTION.
>     hTran:SET-COMMIT().

>     hTran = THIS-PROCEDURE:TRANSACTION.
>     hTran:SET-ROLLBACK().

> As long as an automatic transaction is open, you can execute any internal procedure of the transaction initiating procedure from any other procedure running in the AppServer session. However, if no transaction is open, any such attempt to call these internal procedures from within the AppServer agent context returns an error. Without an open transaction, only a client application can call a remote internal procedure of the transaction initiating procedure (see the "Restarting automatic transactions" section).

> Note: You can determine whether an automatic transaction is open in an AppServer session by either checking for an error after executing an internal procedure of the transaction initiating procedure or by checking the value of the `TRANS-INIT-PROCEDURE` attribute.

> - Implicit termination — You can also terminate the automatic transaction from either the client or the AppServer session by deleting the proxy persistent procedure handle (on a Progress 4GL client) or remote persistent procedure handle (on the AppServer) of the transaction initiating procedure. If you delete the transaction initiating procedure, the transaction commits or rolls back depending on the value of the transaction handle `DEFAULT-COMMIT` attribute.

> ##### Restarting automatic transactions

> After an automatic transaction terminates, how an automatic transaction can restart depends on how it terminates. If the transaction terminates by deleting the transaction initiating procedure, only a client application can restart an automatic transaction by again remotely instantiating a transaction initiating procedure.

> If a transaction initiating procedure is still active in the AppServer session, you can support the restarting of an automatic transaction using one of the following techniques, depending on how you code the transaction initiating procedure:

> - Automatic (chained) restarting — If you specify the `CHAINED` option for the `TRANSACTION-MODE` `AUTOMATIC` statement, a new automatic transaction starts immediately after the previous automatic transaction commits or rolls back. Thus, with the `CHAINED` option an automatic transaction is always open in the AppServer session until the transaction initiating procedure, itself, is made inactive (is deleted).

> - Manual restarting — If you do not specify the `CHAINED` option for the `TRANSACTION-MODE` `AUTOMATIC` statement, the client application can cause another automatic transaction to restart by calling any internal procedure of the active transaction initiating procedure. Thus, a remote call to an empty internal procedure can start a new automatic transaction.

> Caution: In general, try to keep automatic transactions on an AppServer as short as possible. The multi-request feature of automatic transactions tend to encourage long transactions, and long transactions can make large or important segments of a database inaccessible to other OpenEdge sessions for an indefinite period. This can create database deadlocks and traffic jams that can seriously retard the performance of multiple sessions. For more information on techniques to shorten automatic transactions, see the information on transaction management considerations in Chapter 4, " Design and Implementation Considerations."

> ##### Automatic transaction example

> By including appropriate calls to internal procedures of the transaction initiating procedure in your remote procedures, you can encapsulate the management of a multi-request automatic transaction in a manner completely hidden from the client. However, the following example is relatively simple in that the client calls all of the internal procedures of the transaction initiating procedure directly, thus more openly exposing the transaction mechanism to the client.

> The following two procedures include a transaction initiating procedure and a Progress 4GL client procedure that accesses it.

> The transaction initiating procedure (`a-txtest.p`) implements an unchained automatic transaction. This procedure provides three internal procedures that allow the client to explicitly start (`startme`), commit (`stopme`), and roll back (`abortme`) transactions.

> a-txtest.p
>     TRANSACTION-MODE AUTOMATIC. /*this statement makes this procedure a */
>                                 /*transaction initiating procedure          */

>     PROCEDURE startme:  /*this empty internal proc starts a transaction*/
>     END.

>     PROCEDURE stopme: /*this internal proc arranges for a commit*/
>       DEFINE VARIABLE x AS HANDLE.
>       x = THIS-PROCEDURE:TRANSACTION.
>       x:SET-COMMIT().
>     END.

>     PROCEDURE abortme: /*this internal proc arranges for a rollback*/
>       DEFINE VARIABLE x AS HANDLE.
>       x = THIS-PROCEDURE:TRANSACTION.
>       x:SET-ROLLBACK().
>     END.

> The client procedure (`a-txclnt.p`) establishes the initial transaction object by instantiating `a-txtest.p` on the AppServer. After calling two database update procedures, the client commits the transaction with a call to `stopme`. It then begins a new transaction by calling `startme`, and after calling the same two database update procedures, rolls back the transaction by calling `abortme`.

> As noted in the comments `a-txtest.p` establishes an unchained transaction object. If the procedure instead establishes a chained transaction object, each time the current transaction terminates (`stopme`, `abortme`), the AppServer agent automatically starts a new transaction with no need for the client to call a procedure (`startme`) to initiate it.

> a-txclnt.p1
>     DEFINE VARIABLE ph AS HANDLE.
>     /*create a transaction initiating procedure and start the transaction*/
>     RUN a-txtest.p ON SERVER h TRANSACTION DISTINCT PERSISTENT SET ph.

>     RUN custupdate.p ON SERVER h TRANSACTION DISTINCT.
>     RUN orderupdate.p ON SERVER h TRANSACTION DISTINCT.

>     RUN stopme IN ph.  /*causes the transaction to commit including both
>                          custupdate and orderupdate*/

>     /*start another transaction by running an internal proc of a-txtest.p*/
>     RUN startme IN ph.  /*starts a new transaction -- this is
>                           unnecessary if the TRANSACTION-MODE statement
>                           in a-txtest.p specifies the CHAINED option*/

>     RUN custupdate.p ON SERVER h TRANSACTION DISTINCT.
>     RUN orderupdate.p ON SERVER h TRANSACTION DISTINCT.

>     RUN abortme IN ph.  /*causes the transaction to roll back including
>                           both custupdate and orderupdate*/

>     DELETE PROCEDURE ph.

> 1The `RUN` statements in this example use the Progress 4GL syntax for executing remote procedures of various types. For more information, see Chapter 3, " Programming Progress 4GL Client Applications."

> Note that while these examples show a close correlation between transaction directives and remote procedure requests, there is no requirement that this be so. You can completely hide any sense that the client is ultimately initiating and managing a transaction by hiding all calls to the internal procedures of the transaction initiating procedure within the logic of your application API. For example, you can encapsulate the remote procedure calls in `a-txclnt.p` in higher-level procedures, such as `start-updates.p`, `rerun-updates.p`, and `end-updates.p`.

Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095