Previous | Contents | Index |
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; ... } |
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.
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:
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.
The following XA/DTC server application example is for a Windows NT environment only.
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); } |
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.
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:
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:
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.
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.
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; |
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"; |
To specify a recipient name, use the rcpnam parameter, which is case sensitive. For example,
rtr_rcpnam_t rcpnam = "* Rogers"; |
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.
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.
To specify the number of key segments defined for a server application, use the numseg parameter. For example,
rtr_numseg_t numseg = 2; |
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.
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.
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.
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.
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.
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.
An application can specify one of three reception styles for the rtr_receive_message call. These are:
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); |
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:
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); |
The rtr_receive_message timeout is not the same as a transaction timeout. |
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(); } |
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 |