# hp Reliable Transaction Router
Application Design Guide

Previous | Contents | Index

## XA Oracle Example

Note In this example, error checking has been omitted for clarity.

     int main( int argc, char argv[] )
    {
         server_key[0].ks_type = rtr_keyseg_unsigned;
         server_key[0].ks_length = sizeof(rtr_uns_8_t);
         server_key[0].ks_offset = 0;
         server_key[0].ks_lo_bound = &low;
         server_key[0].ks_hi_bound = &high;
         server_key[1].ks_type = rtr_keyseg_rmname;          /* RM name */
         server_key[1].ks_length = 0;                        /* not applicable */
         server_key[1].ks_offset = 0;
         server_key[1].ks_lo_bound = rm_name;
         server_key[1].ks_hi_bound = xa_open_string;

         (flag = RTR_F_OPE_SERVER |
          RTR_F_OPE_NOSTANDBY |
          RTR_F_OPE_XA_MANAGED |                       /* XA flag */
          RTR_F_OPE_EXPLICIT_PREPARE |
          RTR_F_OPE_EXPLICIT_ACCEPT;
    rtr_open_channel(&server_channel, flag, fac_name, NULL, RTR_NO_PEVTNUM,
                     NULL, 2, server_key);
    while (rtr_receive_message(&server_channel, RTR_NO_FLAGS, RTR_ANYCHAN,
        &receive_msg,sizeof(receive_msg), RTR_NO_TIMOUTMS, &msgsb)
             == RTR_STS_OK)
       { ...
        msg = receive_msg.receive_data_msg;

        (switch(msgsb.msgtype)
           {
         case rtr_mt_msg1:
         case rtr_mt_msgn:
           switch(msg.txn_type)
             {
         case ...
            EXEC SQL ...
             }
                 ...
    rtr_reply_to_client(server_channel, RTR_NO_FLAGS, &reply_msg,
    sizeof(reply_msg), RTR_NO_MSGFMT);
                  ...
         case rtr_mt_prepare:
                  ...
    rtr_accept_tx(s_chan,RTR_NO_FLAGS,RTR_NO_REASON);
                  ...
         case rtr_mt_accepted:
          /* EXEC SQL COMMIT;   Comment out SQL Commits */
          break;
         case rtr_mt_rejected:
          /* EXEC SQL ROLLBACK;  Comment out SQL rollbacks */
          break;
          /*
         case rtr_mt_msg1_uncertain:
                  ...
          */
           }      ...
        }
           EXEC SQL COMMIT WORK RELEASE;
                  ...
    }

## Using XA with MS DTC

The XA software architecture of RTR provides interoperability with the Distributed Transaction Controller of Microsoft, MS DTC . Thus RTR users can develop application programs that update MS SQL databases, MS MQ, or other Microsoft resource managers under the control of a true distributed transaction. RTR as a distributed transaction manager communicates directly with MS DTC to manage a transaction or perform recovery using the XA protocol. For each standard XA call received from RTR, MS DTC translates it into a corresponding OLE transaction call that SQL Server or MS MQ expects to perform database updates. This is shown in Figure 5-2.

Figure 5-2 MS DTC and RTR

For example, using XA and DTC (Compaq Tru64 UNIX and Microsoft Windows NT only) eliminates the need to process uncertain messages `rtr_mt_msg1_uncertain` ). To use the XA protocol with RTR, you must:

- Use the RTR REGISTER RM (register resource manager) command to register the resource manager, specifying the appropriate libraries. For Oracle 7RDBMS, the V$XATRANS$ view must exist on the database.
- Create a facility with the referenced resource manager by executing the CREATE FACILITY/RM= xxx command, where xxx is the same as that used in the previous REGISTER RM command. For more details on the REGISTER RM command and other commands for working with other database managers, refer to the Reliable Transaction Router System Manager's Manual.
- In the `rtr_open_channel` call in the server application, specify which resource manager the application is using. The application must submit a resource manager instance name with [OPEN_STRING] to the `rtr_open_channel` call before accessing the desired database.
- Remove unnecessary SQL calls from existing server code such as commit or rollback, to avoid vendor-specific warnings.

Both the resource manager instance name and the database (RM) name in [OPEN- STRING] must be identical to that in the previously executed REGISTER RM command. The information is stored in the RTR key segment structure, and the `RTR_F_OPE_XA_MANAGED` flag associates the channel with the XA interface.

Only one transaction at a time is processed on an RTR channel; thus a server process or thread of control can only open one channel to handle a single XA request. Better throughput can be achieved by using a multithreaded application.

For example, the following code from a sample server application shows use of the RM key, the XA flag, and commenting out commits and rollbacks for the Oracle and DTC environments.

## XA DTC Example

The following XA/DTC server application example is for a Windows NT environment only.

Note In this example, error checking has been omitted for clarity.

    int main( int argc, char argv[] )
    {
     server_key[0].ks_type = rtr_keyseg_unsigned;
     server_key[0].ks_length = sizeof(rtr_uns_8_t);
     server_key[0].ks_offset = 0;
     server_key[0].ks_lo_bound = &low;
     server_key[0].ks_hi_bound = &high;
     server_key[1].ks_type = rtr_keyseg_rmname;  /* RM name */
     server_key[1].ks_length = sizeof(String32)+sizeof(String128);
     server_key[1].ks_offset = 0;
     server_key[1].ks_lo_bound = rm_name;
     server_key[1].ks_hi_bound = xa_open_string;

     flag = RTR_F_OPE_SERVER |
      RTR_F_OPE_XA_MANAGED |               /* XA flag */
      RTR_F_OPE_NOSTANDBY |
      RTR_F_OPE_EXPLICIT_PREPARE |
      RTR_F_OPE_EXPLICIT_ACCEPT;
    /* Connect SQL server thru DB-Library */
    dbinit();
    login = dblogin();
    DBSETLUSER(login, sql_username);
    DBSETLPWD(login, sql_password);
    dbproc = dbopen(login, sql_servername);
    dbfreelogin(login);
    dbuse(dbproc, sql_dbname);

    rtr_open_channel(&server_channel, flag, fac_name,
     NULL, RTR_NO_PEVTNUM, NULL,2, server_key);
                 ...
    rtr_receive_message(&server_channel, RTR_NO_FLAGS, RTR_ANYCHAN,
               &receive_msg,sizeof(receive_msg), RTR_NO_TIMOUTMS, &msgsb)
               == RTR_STS_OK)
                                 ...
    while (rtr_receive_message(&server_channel, RTR_NO_FLAGS, RTR_ANYCHAN,
         &receive_msg, sizeof(receive_msg), RTR_NO_TIMOUTMS, &msgsb);
                  ...
       msg = receive_msg.receive_data_msg;
         switch(msgsb.msgtype)
         {
            case rtr_mt_msg1:
       dbenlistxatrans(dbproc, RTR_TRUE);
          /* Remove uncertain processing
            case rtr_mt_msg1_uncertain:
                  ...
          */
            case rtr_mt_msgn:
       switch(msg.txn_type)
        {
                case
                 ...
                   dbfcmd(dbproc, "...");
                   dbsqlexec(dbproc);
                  while(1) {
                             dbresults(dbproc);
              ...
                             break;
                            }
                    ...
        rtr_reply_to_client(server_channel, RTR_NO_FLAGS,
                       &reply_msg, sizeof(reply_msg), RTR_NO_MSGFMT);
                    ...
              case rtr_mt_prepare:
                    ...
        rtr_accept_tx(s_chan,RTR_NO_FLAGS,RTR_NO_REASON);
                     ...
              case rtr_mt_accepted:
              /* EXEC SQL COMMIT;    Comment out SQL Commits */
              case rtr_mt_rejected:
               /* EXEC SQL ROLLBACK;Comment out SQL rollbacks */
                     ...
     }
      exit(0);
    }

# Using DECdtm

You can use the DECdtm protocol to communicate with OpenVMS Rdb. This provides a two-phase commit capability. For additional information on using this protocol, refer to the OpenVMS documentation, for example, Managing DECdtm Services in the OpenVMS System Manager's Manual, the OpenVMS System Services Reference Manual, the OpenVMS Programming Concepts Manual and the Oracle Rdb Guide to Distributed Transactions available from Oracle.

# RTR Transaction Processing

To pass transactions from client to server, RTR (with the C API) uses channels as identifiers. Each application communicates with RTR on a particular channel. In a multithreaded application, when multiple transactions are outstanding, the application uses the channel to inform RTR which transaction a command is for.

With RTR, the client or server application can:

- Open a channel
- Send a transaction to a channel
- Bind a transaction to a channel
- Receive a transaction on a channel
- Specify a receive programming style (blocked, polled, or signaled)
- Close a channel

To open a channel, the application uses the `rtr_open_channel` call. This opens a channel for communication with a client or server application on a specific facility. Each application process can open up to 255 channels.

For example, the `rtr_open_channel` call in this client application opens a single channel for the facility called DESIGN:

    status = rtr_open_channel(&Channel,
             RTR_F_OPE_CLIENT, [1]
             DESIGN,            /* Facility name */ [2]
             client_name,
             rtrEvents,
             NULL,              /* Access key / [3]
             RTR_NO_NUMSEG,
             RTR_NO_PKEYSEG     /* Key range */ [4]
             );

The application uses parameters on the `rtr_open_channel` call to define the application environment. Typically, the application defines the:

- [1] Role of the application (client or server)
- [2] Name of the application facility
- [3] Facility access key, a password; in this case no password is used
- [4] Key range or key segment for data content routing

For a server application, the `rtr_open_channel` call additionally supplies the number of key segments, `numseg` , and the partition name, in `pkeyseg` .

The syntax of the `rtr_open_channel` call is as follows:

       status = rtr_open_channel (pchannel,flags,facnam,rcpnam,
       pevtnum,access,numseg,pkeyseg)

You can set up a variable section in your client program to define the required parameters and then set up your `rtr_open_channel` call to pass those parameters. For example, the variables definition would contain code similar to the following:

    /*
    ** ---------------- Variables -------------------
    */
    rtr_status_t Status;
    rtr_channel_t Channel;
    rtr_ope_flag_t Flags  = RTR_F_OPE_CLIENT;
    rtr_facnam_t        Facility  = "DESIGN";
    rtr_rcpnam_t        Recipient  = RTR_NO_RCPNAM;
    rtr_access_t        Access  = RTR_NO_ACCESS;

The `rtr_open_channel` call would contain:

    status = rtr_open_channel(&Channel,
                              Flags,
                              Facility,
                              Recipient,
                              Evtnum,
                              Access,
                              RTR_NO_NUMSEG,
                              RTR_NO_PKEYSEG);
    if (Status != RTR_STS_OK)
    /*
    { Provide for error return */}

You will find more complete samples of client and server code in the appendix of this document and on the RTR software kit in the Examples directory.

## Channel Identifier

To specify the location to return the channel identifier, use the channel argument in the `rtr_open_channel` call. For example,

> `rtr_channel_t channel;`
> or
> `rtr_channel_t
> *p_channel = &channel;`

This parameter points to a valid channel identifier when the application receives an `rtr_mt_opened` message.

## Flags Parameter

To define the application role type (client or server), use the flags parameter . For example,

     rtr_ope_flag_t
     flags = RTR_F_OPE_CLIENT;

or

     flags = RTR_F_OPE_SERVER;

## Facility Name

The facility name is a required string supplied by the application. It identifies the RTR facility used by the application. The default facility name for the RTR CLI only is `RTR$DEFAULT_FACILITY` ; there is no default facility name for an RTR application. You must supply one.

To define the facility name, use the facnam parameter. For example,

     rtr_facnam_t
     facnam = "DESIGN";

## Recipient Name

To specify a recipient name, use the rcpnam parameter, which is case sensitive. For example,

     rtr_rcpnam_t
     rcpnam  = "* Rogers";

## Event Number

To specify user event numbers, use the evtnum parameter. For example,

    rtr_evtnum_t all user_events[]={
        RTR_EVTNUM_USERDEF,
            RTR_EVTNUM_USERBASE,
            RTR_EVTNUM_UP_TO,
            RTR_EVTNUM_USERMAX,
        RTR_EVTNUM_ENDLIST
    };

There are both RTR events and user events. For additional information on employing events, see the Broadcast Messaging Processes section of this chapter, and the section on RTR Events in the Reliable Transaction Router C Application Programmer's Reference Manual.

## Access Key

You can use the facility access key to restrict client or server access to a facility. The key acts as a password to restrict access to the specific facility for which it is declared.

To define the facility access key, use the access parameter. For example,

      rtr_access_t
       access = "amalasuntha";

The facility access key is a string supplied by the application. The first server channel in an RTR facility defines the access key; all subsequent server and client open channel requests must specify the same access value. To use no access key, use `RTR_NO_ACCESS` or `NULL` for the access argument.

You can also use this feature for version control. By changing the access code whenever an incompatible protocol change is made in the application message format, client applications are prevented from processing transactions with the server applications.

## Key Segments

To specify the number of key segments defined for a server application, use the numseg parameter. For example,

     rtr_numseg_t
      numseg = 2;

## Partition or Key Range

To specify the key range for a partition to do data-content routing, the server application defines the routing key when it opens a channel on a facility with the `rtr_open_channel` call. All servers in a facility must specify the same offset, length, and data type for the key segments in the `rtr_open_channel` call; only high and low bounds ( `*ks_lo_bound, *ks_hi_bound` ) can be unique to each server key segment. By application convention, the client places key data in the message at the offset, length, and data type defined by the server.

## Channel-Open Operation

The channel-open operation completes asynchronously. Call completion status is returned in a subsequent message. RTR sends a message to the application indicating successful or unsuccessful completion; the application receives the status message using an `rtr_receive_message` call. If status is `rtr_mt_opened` , the open operation is successful. If status is `rtr_mt_closed` , the open operation is unsuccessful, and the application must examine the failure and respond accordingly. The channel is closed.

## Determining Message Status

Data returned in the user buffer with `rtr_mt_opened` and `rtr_mt_closed` include both the status and a reason. For example,

    case rtr_mt_opened:
      printf(" Channel %d opened\n", channel);
      status = RTR_STS_OK;
      break;
    case rtr_mt_closed:
      p_status_data = (rtr_status_data_t *)txn_msg;
      printf(" cannot open channel because %s\n",
       rtr_error_text(p_status_data->status));
      exit(-1);

Use the call `rtr_error_text` to find the meaning of returned status. A client channel will receive no message at all if a facility is configured but no server is available. Once a server becomes available, RTR sends the `rtr_mt_opened` message.

## Closing a Channel

To close a channel, the application uses the `rtr_close_channel` call. A channel can be closed at any time after it has been opened. Once closed, no further operations can be performed on a channel, and no further messages for the channel are received.

## Receiving on a Channel

To receive on a channel and obtain status information from RTR, use the `rtr_receive_message` call. To receive on any open channel, use the `RTR_ANYCHAN` value for the p_rcvchan parameter in the `rtr_receive_message` call. To receive from a list of channels, use the p_rcvchan parameter as a pointer to a list of channels, ending the list with `RTR_CHAN_ENDLIST` . An application can receive on one or more opened channels. RTR returns the channel identifier. A pointer to a channel is supplied on the `rtr_open_channel` call, and RTR returns the channel identification (ID) by filling in the contents of that pointer.

## User Handles

To simplify matching an RTR channel ID with an application thread, an application can associate a user handle with a channel. The handle is returned in the message status block with the `rtr_receive_message` call. The application can use the message status block ( `MsgStatusBlock` ) to identify the message type of a received message. For example,

    {
    rtr_receive_message (&channel, RTR_NO_FLAGS, RTR_ANYCHAN, txn_msg, maxlen,
    RTR_NO_TIMOUTMS,
    MsgStatusBlock);
    } . . .
       typedef struct {
       rtr_msg_type_t  msgtype;
       rtr_usrhdl_t      usrhdl;
       rtr_msglen_t     msglen;
       rtr_tid_t  tid;
       rtr_evtnum_t     evtnum;
       } rtr_msgsb_t;

RTR delivers both RTR and application messages when the `rtr_receive_message` call completes. The application can use the message type indicator in the message status block to determine relevant buffer format. For further details on using message types and interpreting the contents of the user buffer, refer to the Reliable Transaction Router C Application Programmer's Reference Manual.

# Message Reception Styles

An application can specify one of three reception styles for the `rtr_receive_message` call. These are:

- Blocked
- Polled
- Signaled

## Blocking Receive

An application can use a blocking receive to wait until a message arrives. To use a blocking receive, include `RTR_NO_TIMOUTMS` in the `rtr_receive_message` call. The call completes only when a message is available on one of the specified channels. For example,

    rtr_receive_message (&channel, RTR_NO_FLAGS, RTR_ANYCHAN,
      MsgBuffer, DataLen, RTR_NO_TIMOUTMS, &MsgStatusBlock);

## Polled Receive

An application can use a polled receive to poll RTR with a specified timeout. To use a polled receive, the application can set a value in milliseconds on the timoutms parameter. The timeout can be:

- Immediate: timoutms = 0 (Do not wait for messages; RTR notifies the application if there are any.)
- Infinite: `RTR_NO_TIMOUTMS`
- A user-specified value. Current clock granularity is 1 second (1000 milliseconds).

The call completes after the specified timeout or when a message is available on one of the specified channels.

For example, the following declaration sets polling at 1 second (1000 milliseconds).

    rtr_receive_message(&channel, RTR_NO_FLAGS, RTR_ANYCHAN, MsgBuffer, DataLen,
    1000, &MsgStatusBlock);

Note The `rtr_receive_message` timeout is not the same as a transaction timeout.

## Signaled Receive

An application can use a signaled receive to be alerted by RTR when a message is received. The application establishes a signal handler using the `rtr_set_wakeup` call, informing RTR where to call it back when the message is ready.

To use a signaled receive, the application uses the `rtr_set_wakeup` call and provides the address of a routine to be called by RTR when a message is available. When the wakeup routine is called, the application can use the `rtr_receive_message` call to get the message. For example,

    rtr_status_t
    rtr_set_wakeup(
      procedure  )
    void
    wakeup_handler(void){
     rtr_receive_message();
    }

    main(){
     rtr_set_wakeup(wakeup_handler);
     sleep();
    }

Note To disable wakeup, call `rtr_set_wakeup` with a null routine address.

When using `rtr_set_wakeup` in a multithreaded application, be careful not to call any non-reentrant functions or tie up system resources unnecessarily inside the callback routine.

The `rtr_open_channel` parameters are further described in the Reliable Transaction Router C Application Programmer's Reference Manual.

Previous | Next | Contents | Index