|  | OpenVMS Programming Concepts Manual
 
 30.7.3.2 XA RM Configuration
Each XA-compliant transaction manager (TM) defines its own method for
including resource managers (RMs). Typically, a configuration file is
edited and used as input to build application programs that run under
the control of the TM. You may also need to configure and build a
separate TM worker process that performs transaction prepare and commit
operations.
 
See the documentation for your XA TM for specific instructions. You
will need the following information about the XA Gateway RM. This is
published in the XA Specification (Section 7.2).
 
  
    | xa_switch_t structure name: | DDTM$XG_RM_SWITCH |  
    | RM name within the RM switch: | DDTM$XG |  
    | Information string for xa_open: | "SYSTEM$
      gateway", where
      gateway is the name of the gateway for the local node of an
      OpenVMS Cluster. |  
    | Information string for xa_close: | Ignored. May be null. |  
    | Sharable image library: | SYS$LIBRARY:DDTM$XG.EXE |  
    | Transaction semantics: | See Section 30.7.3.3.2 |  
    | Protocol optimizations: | See Section 30.7.3.3.3 |  
    | Association migration: | Not allowed. |  
    | Dynamic registration: | Not used. |  
    | Asynchrony: | Not supported. |  
    | Heuristics: | Not used. |  
The Gateway is implemented by the shareable image
SYS$LIBRARY:DDTM$XG.EXE.
 
You must install the privileged sharable image
SYS$LIBRARY:DDTM$XG_SS.EXE. It provides system services for internal
use by the Gateway and the XGCP utility.
30.7.3.2.1 Hints 
You may find the following hints to be of help:
 
  The XA switch name is uppercase. Some transaction managers specify
  exact case compilation when generating references to RM, so you should
  specify the switch name in uppercase.
  Check any OpenVMS documentation for your transaction manager as
  well as the generic documentation. For example, the generic
  documentation for Tuxedo uses a colon (:) to separate the resource
  manager name and XA information strings in a configuration table.
  However, on OpenVMS, the separator has been changed to a comma (,).
 30.7.3.3 Implementation Characteristics
The following sections describe the implementation characteristics of
the XA Gateway.
30.7.3.3.1 Default Transaction 
The XA Gateway sets the DECdtm default transaction for each XA
transaction.
 
Most DECdtm RMs join the default transaction if an explicit TID is not
specified on a call to the RM. If an RM does require an explicit TID,
the application can use the $GET_DEFAULT_TRANS system service to read
the current default TID.
30.7.3.3.2 Locking Between Processes 
DECdtm does not distinguish between loosely coupled and tightly coupled
threads, as defined by the XA specification. Instead, each RM makes its
own decision whether to allow transaction branches in different
processes to share data.
 
The Gateway allocates a separate DECdtm TID for each branch of an XA
global transaction. This allows a branch to be prepared while other
branches continue to perform work, as required by Section 2.2.6 of the
XA specification.
 
Consequently, DECdtm RMs enforce isolation between the branches of an
XA global transaction. This behavior is consistent with the XA
specification, but not required by it.
 
When multiple processes perform work on a single branch within a single
node of an OpenVMS Cluster, the gateway allocates a single DECdtm TID
for the branch. In principle, this allows the RMs to recognize that
work in multiple processes is part of a single transaction, and to use
tightly coupled threads. However, it depends on the RM whether this is
implemented.
 
The Gateway does not use the same TID for a single branch of a
transaction seen on multiple nodes of an OpenVMS Cluster. However, it
is unlikely that any XA TM will use the same branch on different nodes,
or that any DECdtm RM is capable of implementing tightly coupled
threads between nodes.
30.7.3.3.3 Read-Only Optimization 
DECdtm RMs may choose to implement a read-only optimization when a
transaction is prepared (see Section 2.3.2 of the XA specification). If
all DECdtm RMs use the optimization for a given transaction, the
Gateway uses the same optimization on the xa_prepare call for the
transaction.
30.7.3.3.4 Blocking Conditions 
The Gateway is unable to determine if a blocking condition exists or
not. Consequently, it always returns XA_RETRY when the TMNOWAIT flag is
set.
30.7.3.3.5 XA Return Values 
The Gateway translates DECdtm reason codes to XA return codes as
follows:
 
  
    | DECdtm Reason Code | XA Return Code |  
    | DDTM$_ABORTED | XA_RBROLLBACK |  
    | DDTM$_COMM_FAIL | XA_RBCOMMFAIL |  
    | DDTM$_INTEGRITY | XA_RBINTEGRITY |  
    | DDTM$_PART_SERIAL | XA_RBDEADLOCK |  
    | DDTM$_PART_TIMEOUT | XA_RBTIMEOUT |  
    | DDTM$_SERIALIZATION | XA_RBDEADLOCK |  
    | DDTM$_TIMEOUT | XA_RBTIMEOUT |  
    | DDTM$_VETOED | XA_RBROLLBACK |  
    | All others | XA_RBOTHER |  
The Gateway uses XAER_RMFAIL to indicate a failure to access data on
disk, while XAER_RMERR indicates an internal failure. It translates
DECdtm error codes to XA return codes as follows:
 
  
    | DECdtm Error Code | XA Return Code |  
    | SS$_ALRCURTID | XAER_PROTO |  
    | SS$_BRANCHSTARTED | XAER_PROTO |  
    | SS$_NOLOG | XAER_RMFAIL |  
    | SS$_TPDISABLED | XAER_RMFAIL |  
    | SS$_WRONGSTATE | XAER_RBROLLBACK |  
    | All others | XAER_RMERR |  
An exception is xa_commit. This function returns XAER_RMFAIL instead of
XA_RMERR, because the XA specification states that XA_RMERR indicated a
catastrophic failure for this function.
30.7.3.4 Error Logging 
The Gateway error log file records errors that prevent it from passing
transaction information to DECdtm resource managers. The log file shows
more detailed error information than that revealed by XA return values.
 
To enable error logging, define the logical name SYS$DECDTM_XG_ERROR to
specify an error file. You can define the logical name processwide,
groupwide, or systemwide. However, you must define it for both TP
processes and the Gateway server process. The error file is created
automatically and is shared between processes.
 
Error records have the following formats:
 
  
    | Record Type | Format |  
    | General | time csid pid "VMS" vms_status "on" operation |  
    | Transaction | time csid pid "VMS" vms_status "on" operation ", DECdtm TID"
      tid |  
    | TP process | time csid pid "XA" xa_status "VMS" vms_status "on" operation ",
      DECdtm TID" tid |  30.7.3.5 Tracing
The Gateway includes a trace facility to help investigate problems of
interaction between an XA TM and DECdtm resource managers. The trace
file shows the sequence of operations. It also shows more detailed
error information than that revealed by XA return values.
 
To enable tracing, define the logical name SYS$DECDTM_XG_TRACE to
specify a trace file. You can define the logical name processwide,
groupwide, or systemwide. However, you must define it for TP processes
and for the Gateway server process. The trace file is created
automatically and is shared between processes.
 
The trace file records the following information:
 
  All xa_ calls to the Gateway.
  XA and OpenVMS error status results returned by the XA functions.
  Transaction events reported to DECdtm by the Gateway.
 
Trace records have the following formats:
 
  
    | Record Type | Format |  
    | Operation | time csid pid operation [flags] |  
    | Status | time csid pid xa_status ["VMS" vms_status] [extra_info] |  
30.7.4 XA Gateway Control Program (XGCP) UtilityThis section describes the XA Gateway Control Program (XGCP) utility.30.7.4.1 XGCP Description 
The XGCP utility creates the transaction logs used by the DECdtm XA
Gateway. You can also use it to stop and restart the XA Gateway server.
 
The Gateway allows a resource manager compliant with DECdtm, such as
RMS Journaling or Oracle Rdb, to be used with an XA-compliant
transaction manager.
30.7.4.2 XGCP Usage Summary 
XGCP provides the management interface to the DECdtm XA Gateway.
30.7.4.3 XGCP Description 
To invoke XGCP, enter the RUN SYS$SYSTEM:XGCP command at the DCL
command prompt. The command has no parameters. At the XGCP> prompt,
you can enter any of the XGCP commands described in Section 30.7.4.4.
 
To exit from XGCP, enter the EXIT command at the XGCP> prompt, or
press Ctrl/Z.
30.7.4.4 XGCP Commands 
The following table summarizes the XGCP commands.
 
  
    | Command | Format | Description |  
    | CREATE_LOG | CREATE_LOG | Creates a new XA Gateway log.  This command requires SYSPRV privilege or read/write access to the
      SYS$JOURNAL directory.
        Create a gateway log with the name SYS$JOURNAL:SYSTEM$
      name.DDTM$XG_JOURNAL.
        Create a separate log for each node of an OpenVMS Cluster.
        The log file is automatically expanded when necessary.
       
        
          | Qualifier | Description |  
          | /GATEWAY_NAME=name | Specifies a gateway name of up to 15 characters. This qualifier is
            required. |  
          | /SIZE=size | Specifies the initial size of the log, in blocks. If you omit this
            qualifier, the log is created with an initial size of 242 blocks. |  |  
    | EXIT | EXIT | Exits XGCP |  
    | START_SERVER | START_SERVER | Starts the XA Gateway server.  Requires the IMPERSONATE privilege.
        This command executes the DCL command file
      SYS$STARTUP:DDTM$XG_STARTUP.COM. The server process is called
      DDTM$XG_SERVER.
     |  
    | STOP_SERVER | STOP_SERVER | Stops the XA Gateway server. Requires OPER privilege. |  30.8 Program Examples Using DECdtm
The following sections present Fortran, C, and BLISS examples of
applications using DECdtm.
30.8.1 Fortran Program Example 
The following is a sample Fortran application that uses DECdtm system
services. (See SYS$EXAMPLES:DECDTM$EXAMPLE1.)
 
The application opens two files, sets a counter, then enters a loop to
perform the following steps:
 
  Increments the counter by 1.
  Calls SYS$START_TRANSW to start a new transaction.
  Writes the counter value to the two files.
  Either calls SYS$END_TRANSW to attempt to commit the transaction,
  or calls SYS$ABORT_TRANSW to abort the transaction.
 
The application repeats these steps until either an error occurs or the
user requests an interrupt. Because DECdtm services are used, the two
files will always be in step with each other. If DECdtm services were
not used, one file could have been updated while the other was not.
This would result in the files being out of step.
 
This example contains numbered callouts, which are explained after the
program listing.
 
 
  
    | 
 
C
C This program assumes that the files DECDTM$EXAMPLE1.FILE_1 and
C DECDTM$EXAMPLE1.FILE_2 are created and marked for recovery unit
C journaling using the command file SYS$EXAMPLES:DECDTM$EXAMPLE1.COM
C
C To run this example, enter the following:
C   $ FORTRAN SYS$EXAMPLES:DECDTM$EXAMPLE1
C   $ LINK DECDTM$EXAMPLE1
C   $ @SYS$EXAMPLES:DECDTM$EXAMPLE1
C   $ RUN DECDTM$EXAMPLE1
C
C
C SYS$EXAMPLES also contains an example C application, DECDTM$EXAMPLE2.C
C The C application performs the same operations as this Fortran example.
C
        IMPLICIT    NONE
        INCLUDE     '($SSDEF)'
        INCLUDE     '($FORIOSDEF)'
        CHARACTER*12 STRING
        INTEGER*2   IOSB(4)
        INTEGER*4   STATUS,COUNT,TID(4)
        INTEGER*4   SYS$START_TRANSW,SYS$END_TRANSW,SYS$ABORT_TRANSW
        EXTERNAL    SYS$START_TRANSW,SYS$END_TRANSW,SYS$ABORT_TRANSW
        EXTERNAL    JOURNAL_OPEN
C
C Open the two files
C
(1)        OPEN (UNIT = 10, FILE = 'DECDTM$EXAMPLE1.FILE_1', STATUS = 'OLD',
        1     ACCESS = 'DIRECT', RECL = 3, USEROPEN = JOURNAL_OPEN)
        OPEN (UNIT = 11, FILE = 'DECDTM$EXAMPLE1.FILE_2', STATUS = 'OLD',
        1     ACCESS = 'DIRECT', RECL = 3, USEROPEN = JOURNAL_OPEN)
        COUNT = 0
        TYPE *, 'Running DECdtm example program'
        TYPE *, 'Press CTRL-Y to interrupt'
C
C Loop forever, updating both files under transaction control
C
        DO WHILE (.TRUE.)
C
C Update the count and convert it to ASCII
C
(2)          COUNT = COUNT + 1
          ENCODE (12,8000,STRING) COUNT
8000      FORMAT (I12)
C
C Start the transaction
C
(3)          STATUS = SYS$START_TRANSW (%VAL(1),,IOSB,,,TID)
          IF (STATUS .NE. SS$_NORMAL .OR. IOSB(1) .NE. SS$_NORMAL) GO TO 9040
C
C Update the record in each file
C
(4)          WRITE (UNIT = 10, REC = 1, ERR = 9000, IOSTAT = STATUS) STRING
          WRITE (UNIT = 11, REC = 1, ERR = 9010, IOSTAT = STATUS) STRING
C
C Attempt to commit the transaction
C
(5)          STATUS = SYS$END_TRANSW (%VAL(1),,IOSB,,,TID)
          IF (STATUS .NE. SS$_NORMAL .OR. IOSB(1) .NE. SS$_NORMAL) GO TO 9050
        END DO
C
C Errors that should cause the transaction to abort
C
(6)
9000    TYPE *, 'Failed to update DECDTM$EXAMPLE1.FILE_1'
        GO TO 9020
9010    TYPE *, 'Failed to update DECDTM$EXAMPLE1.FILE_2'
9020    STATUS = SYS$ABORT_TRANSW (%VAL(1),,IOSB,,,TID)
        IF (STATUS .NE. SS$_NORMAL .OR. IOSB(1) .NE. SS$_NORMAL) GO TO 9060
        STOP
C
C Errors from DECdtm system services
C
9040    TYPE *, 'Unable to start a transaction'
        GO TO 9070
9050    TYPE *, 'Failed to commit the transaction'
        GO TO 9070
9060    TYPE *, 'Failed to abort the transaction'
9070    TYPE *, 'Status = ', STATUS, ' IOSB = ', IOSB(1)
        END
C
C Switch off TRUNCATE access and PUT with truncate on OPEN for RU Journaling
C
        INTEGER FUNCTION JOURNAL_OPEN (FAB, RAB, LUN)
        INCLUDE '($FABDEF)'
        INCLUDE '($RABDEF)'
        INCLUDE '($SYSSRVNAM)'
        RECORD  /FABDEF/ FAB, /RABDEF/ RAB
        FAB.FAB$B_FAC = FAB.FAB$B_FAC .AND. .NOT. FAB$M_TRN
        RAB.RAB$L_ROP = RAB.RAB$L_ROP .AND. .NOT. RAB$M_TPT
        JOURNAL_OPEN = SYS$OPEN (FAB)
        IF (.NOT. JOURNAL_OPEN) RETURN
        JOURNAL_OPEN = SYS$CONNECT (RAB)
        RETURN
        END
 |  
  The application opens DECDTM$EXAMPLE1.FILE_1
  and DECDTM$EXAMPLE1.FILE_2 for writing. It then zeroes the variable
  COUNT and enters an infinite loop.
  The application increments the count by one
  and converts it to an ASCII string.
  The application calls SYS$START_TRANSW to
  start a transaction. The application checks the immediate return status
  and service completion status to see whether they signify an error.
  The application attempts to write the string
  to the two files. If it cannot, the application aborts the transaction.
  Because the files are OpenVMS RMS journaled files, the default
  transaction is assumed.
  The application calls SYS$END_TRANSW to
  attempt to commit the transaction. It checks the immediate return
  status and service completion status to see whether they signify an
  error. If they do, the application reports the error and exits. If
  there are no errors, the transaction is committed and the application
  continues with the loop.
  If either of the two files cannot be
  updated, the application calls SYS$ABORT_TRANSW to abort the
  transaction. It checks the immediate return status and service
  completion status to see whether they signify an error. If they do, the
  application reports the error and exits.
 30.8.2 C Program Examples
The C examples are taken from the Transactional Array of Strings (TAOS)
sample resource manager. It implements a file holding an array of
string values that are updated by transactions. The sample is too large
to reproduce in this manual, but is available in SYS$EXAMPLES.
 
TAOS uses three in-memory data structures:
 
  taos: This holds global information about the string array,
  including the rm_id. It is passed an opaque handle to
  applications using TAOS.
  part: This is created when TAOS participates in a transaction. It
  holds the TID and is specified as the rm_context to
  $JOIN_RM. The taos structure holds a list of par structures indexed by
  TID.
  res: This is created when a TAOS resource (a string) is referenced
  or updated in a transaction. The part structure holds a list of res
  structures indexed by array number.
 
The C examples use the following OpenVMS include files:
 
 
  
    | 
 
    #include <ddtmdef.h>
    #include <ddtmmsgdef.h>
    #include <descrip.h>
    #include <dtidef.h>
    #include <iosbdef.h>
    #include <ssdef.h>
    #include <starlet.h>
    #include <stsdef.h>
 |  30.8.2.1 $DECLARE_RMW
This example shows the declaration of a resource manager to DECdtm.
 
 
  
    | 
 
struct taos {
    uint    tmLogId[4];    /* transaction manager log ID */
    uint    efn;                 /* event flag for TAOS operations */
    uint    rmId;                       /* resource manager ID */
    struct dsc$descriptor_s  resNameDsc;  /* resource name */
    char        resName[24];      /* "TAOS____" + array ID */
};
int taos_Open(...) {
    int     status;
    IOSB    iosb;
    BOOL    declaredRm = FALSE;
    status = sys$declare_rmw(pTaos->efn, 0, &iosb, NULL, 0, &pTaos->rmId,
                         &HandleEvent, &pTaos->resNameDsc, NULL,
                         0, pTaos->tmLogId, 0);
            if (SUCCESS(status))
                status = iosb.iosb$w_status;
            if (SUCCESS(status))
                declaredRm = TRUE;
    return status;
}
 |  30.8.2.2 $GET_DEFAULT_TRANS and $JOIN_RMW
This example shows how to check for a default transaction, and join the
resource manager to a transaction.
 
The function GetParticipantData() (not shown here) searches a list of
part structures for an existing TID. If one is not found, a new part
structure is allocated.
 
 
  
    | 
 
int taos_Write(.., uint pTid[4]) {
    int     status;
    /* get transaction ID */
    if (pTid != NULL)
                CopyUid(tid, pTid);
    else {
                status = sys$get_default_trans(tid);
        if (FAILURE(status))
                    return status;
        }
    /* if this is a new transaction, join it */
    if (GetParticipantData(pTaos, tid, &pPart)) {
        status = sys$join_rmw(pTaos->efn, 0, &iosb, NULL, 0,
                               pTaos->rmId, tid, NULL, pPart);
        if (SUCCESS(status))
                    status = iosb.iosb$w_status;
                if (FAILURE(status))
                    return status;
    }
}
 |  30.8.2.3 Event Handler and $ACK_EVENT
This example shows the event handler specified to DECdtm with
$DECLARE_RM.
 
 
  
    | 
 
static int HandleEvent(DDTM$R_REPORTDEF *pReport) {
    struct taos         *pTaos;
    switch (pReport->ddtm$l_event_type) {
    case DDTM$K_PREPARE:
                Prepare(pReport);
                break;
    case DDTM$K_ABORT:
                Abort(pReport);
                break;
    case DDTM$K_ONE_PHASE_COMMIT:
                OnePhaseCommit(pReport);
                break;
    case DDTM$K_COMMIT:
                Commit(pReport);
                break;
    return SS$_NORMAL;
}
/* Abort the transaction */
static void Abort(DDTM$R_REPORTDEF *pReport) {
    struct part    *pPart = (struct part *) pReport->ddtm$l_rm_context;
    /* Undo the transaction here, using the list of resources
     * attached to the part structure.
     */
    /* DECdtm can forget the transaction */
    sys$ack_event(0, pReport->ddtm$l_report_id, SS$_FORGET);
}
/* Prepare transaction (phase 1 commit) */
static void Prepare(DDTM$R_REPORTDEF *pReport) {
    int     status = SS$_NORMAL;
    BOOL    updates = FALSE;
    /* Save updates on disk, using the list of resources attached to
     * the part structure. Set updates if there are any. Set status
     * on error;
    /* vote on transaction */
    if (FAILURE(status))
                status = SS$_VETO;     /* can't prepare, so abort tran */
    else if (!updates)
                status = SS$_FORGET;   /* read-only transaction */
    else
                status = SS$_PREPARED;  /* ready to commit or abort */
    sys$ack_event(0, pReport->ddtm$l_report_id, status);
}
/* Commit transaction (phase 2) */
static void Commit(DDTM$R_REPORTDEF *pReport) {
    int     status = SS$_NORMAL;
    /* Make updates permanent and visible to other users here.
     * Set status on error.
     */
    if (SUCCESS(status))
        status = SS$_FORGET;        /* DECdtm can forget transaction */
    else {
        /* We can't commit the transaction yet. We must ask DECdtm to
         * remember the transaction, and we must terminate operations
         * until a successful recovery is performed.
         */
                pTaos->status = status;
                status = SS$_REMEMBER;
    }
    /* acknowledge event */
    sys$ack_event(0, pReport->ddtm$l_report_id, status);
}
/* Prepare and commit transaction in a single phase */
static void OnePhaseCommit(DDTM$R_REPORTDEF *pReport) {
    int     status = SS$_NORMAL;
    /* Combine operations from Prepare() and Commit() here.
     * Set status on error.
     */
    /* report outcome to DECdtm */
    if (FAILURE(status))
        status = SS$_VETO;    /* aborted */
    else
        status = SS$_NORMAL;  /* committed */
        status = SS$_NORMAL;  /* committed */
    sys$ack_event(0, pReport->ddtm$l_report_id, status);
}
 |  
 
   |