BEA Logo BEA Tuxedo Release 7.1

  Corporate Info  |  News  |  Solutions  |  Products  |  Partners  |  Services  |  Events  |  Download  |  How To Buy

 

   Tuxedo Doc Home   |   Programming   |   Topic List   |   Previous   |   Next   |   Contents

   Programming a BEA Tuxedo Application Using C

Terminating a Service Routine

The tpreturn(3c), tpcancel(3c), and tpforward(3c) functions specify that a service routine has completed with one of the following actions:

Sending Replies

The tpreturn(3c) function marks the end of the service routine and sends a message to the requester. Use the following signature to call the tpreturn() function.

void
tpreturn(int rval, int rcode, char *data, long len, long flags)

The following table describes the arguments to the tpreturn() function.

tpreturn( ) Function Arguments

Argument

Description

rval

Indicates whether or not the service has completed successfully on an application-level. The value is an integer that is represented by a symbolic name. Valid settings include:

For a description of the effect that the value of this argument has on global transactions, refer to Writing Global Transactions.

rcode

Returns an application-defined return code to the caller. The client can access the value returned in rcode by querying the tpurcode(5) global variable. The function returns this code regardless of success or failure.

data

Pointer to the reply message that is returned to the client process. The message buffer must have been allocated previously by tpalloc().

If you use the same buffer that was passed to the service in the SVCINFO structure, you need not be concerned with buffer allocation or disposition because both are handled by the system-supplied main(). You cannot free this buffer using the tpfree() command; any attempt to do so quietly fails. You can resize the buffer using the tprealloc() function.

If you use another buffer (that is, a buffer other than the one passed to the service routine) to return the message, it is your responsibility to allocate it. The system frees the buffer automatically when the application calls the tpreturn() function.

If no reply message needs to be returned, set this argument to the NULL pointer.

Note: If no reply is expected by the client (that is, if TPNOREPLY was set), the tpreturn() function ignores the data and len arguments and returns control to main().

len

Length of the reply buffer. The application accesses the value of this argument through the olen parameter of the tpcall() function or the len parameter of the tpgetrply() function.

Acting as the client, the process can use this returned value to determine whether the reply buffer has grown.

If a reply is expected by the client and there is no data in the reply buffer (that is, if the data argument is set to the NULL pointer), the function sends a reply with zero length, without modifying the client's buffer.

The system ignores the value of this argument if the data argument is not specified.

flag

Currently not used.

The primary function of a service routine is to process a request and return a reply to a client process. It is not necessary, however, for a single service to do all the work required to perform the requested function. A service can act as a requester and pass a request call to another service the same way a client issues the original request: through calls to tpcall() or tpacall().

Note: The tpcall() and tpacall() functions are described in detail in Writing Request/Response Clients and Servers.

When tpreturn() is called, control always returns to main(). If a service has sent requests with asynchronous replies, it must receive all expected replies or invalidate them with tpcancel() before returning control to main(). Otherwise, the outstanding replies are automatically dropped when they are received by the BEA Tuxedo system main(), and an error is returned to the caller.

If the client invokes the service with tpcall(), after a successful call to tpreturn(), the reply message is available in the buffer referenced by *odata. If tpacall() is used to send the request, and tpreturn() returns successfully, the reply message is available in the tpgetrply() buffer that is referenced by *data.

If a reply is expected and tpreturn() encounters errors while processing its arguments, it sends a failed message to the calling process. The caller detects the error by checking the value placed in tperrno. In the case of failed messages, the system sets tperrno to TPESVCERR. This situation takes precedence over the value of the tpurcode global variable. If this type of error occurs, no reply data is returned, and both the contents and length of the caller's output buffer remain unchanged.

If tpreturn() returns a message in a buffer of an unknown type or a buffer that is not allowed by the caller (that is, if the call is made with flags set to TPNOCHANGE), the system returns TPEOTYPE in tperrno(5). In this case, application success or failure cannot be determined, and the contents and length of the output buffer remain unchanged.

The value returned in the tpurcode(5) global variable is not relevant if the tpreturn() function is invoked and a time-out occurs for the call waiting for the reply. This situation takes precedence over all others in determining the value that is returned in tperrno(5). In this case, tperrno(5) is set to TPETIME and the reply data is not sent, leaving the contents and length of the caller's reply buffer unchanged. There are two types of time-outs in the BEA Tuxedo system: blocking and transaction time-outs (discussed in Writing Global Transactions).

The example code in this section shows the TRANSFER service that is part of the XFER server. Basically, the TRANSFER service makes synchronous calls to the WITHDRAWAL and DEPOSIT services. It allocates a separate buffer for the reply message since it must use the request buffer for the calls to both the WITHDRAWAL and the DEPOSIT services. If the call to WITHDRAWAL fails, the service writes the message cannot withdraw on the status line of the form, frees the reply buffer, and sets the rval argument of the tpreturn() function to TPFAIL. If the call succeeds, the debit balance is retrieved from the reply buffer.

Note: In the following example, the application moves the identifier for the "destination account" (which is retrieved from the cr_id variable) to the zeroth occurrence of the ACCOUNT_ID field in the transf fielded buffer. This move is necessary because this occurrence of the field in an FML buffer is used for data-dependent routing. Refer to Setting Up a BEA Tuxedo Application for more information.

A similar scenario is followed for the call to DEPOSIT. On success, the service frees the reply buffer that was allocated in the service routine and sets the rval argument to TPSUCCESS, returning the pertinent account information to the status line.

tpreturn( ) Function


#include <stdio.h>      /* UNIX */
#include <string.h> /* UNIX */
#include "fml.h" /* BEA Tuxedo System */
#include "atmi.h" /* BEA Tuxedo System */
#include "Usysflds.h" /* BEA Tuxedo System */
#include "userlog.h" /* BEA Tuxedo System */
#include "bank.h" /* BANKING #defines */
#include "bank.flds.h" /* bankdb fields */


/*
* Service to transfer an amount from a debit account to a credit
* account
*/

void
#ifdef __STDC__
TRANSFER(TPSVCINFO *transb)

#else

TRANSFER(transb)
TPSVCINFO *transb;
#endif

{
FBFR *transf; /* fielded buffer of decoded message */
long db_id, cr_id; /* from/to account id's */
float db_bal, cr_bal; /* from/to account balances */
float tamt; /* amount of the transfer */
FBFR *reqfb; /* fielded buffer for request message*/
int reqlen; /* length of fielded buffer */
char t_amts[BALSTR]; /* string for transfer amount */
char db_amts[BALSTR]; /* string for debit account balance */
char cr_amts[BALSTR]; /* string for credit account balance */

/* Set pointr to TPSVCINFO data buffer */
transf = (FBFR *)transb->data;

/* Get debit (db_id) and credit (cr_id) account IDs */

/* must have valid debit account number */
if (((db_id = Fvall(transf, ACCOUNT_ID, 0)) < MINACCT) || (db_id > MAXACCT)) {
(void)Fchg(transf, STATLIN, 0,"Invalid debit account number",(FLDLEN)0);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
/* must have valid credit account number */
if ((cr_id = Fvall(transf, ACCOUNT_ID, 1)) < MINACCT || cr_id > MAXACCT) {
(void)Fchg(transf,STATLIN, 0,"Invalid credit account number",(FLDLEN)0);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}

/* get amount to be withdrawn */
if (Fget(transf, SAMOUNT, 0, t_amts, < 0) 0 || strcmp(t_amts,"") == 0) {
(void)Fchg(transf, STATLIN, 0, "Invalid amount",(FLDLEN)0);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
(void)sscanf(t_amts,"%f",tamt);

/* must have valid amount to transfer */
if (tamt = 0.0) {
(void)Fchg(transf, STATLIN, 0,
"Transfer amount must be greater than $0.00",(FLDLEN)0);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}

/* make withdraw request buffer */
if ((reqfb = (FBFR *)tpalloc("FML",NULL,transb->len)) == (FBFR *)NULL) {
(void)userlog("tpalloc failed in transfer\n");
(void)Fchg(transf, STATLIN, 0,
"unable to allocate request buffer", (FLDLEN)0);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
reqlen = Fsizeof(reqfb);

/* put ID in request buffer */
(void)Fchg(reqfb,ACCOUNT_ID,0,(char *)&db_id, (FLDLEN)0);

/* put amount in request buffer */
(void)Fchg(reqfb,SAMOUNT,0,t_amts, (FLDLEN)0);

/* increase the priority of withdraw call */
if (tpsprio(PRIORITY, 0L) == -1)
(void)userlog("Unable to increase priority of withdraw\n");

if (tpcall("WITHDRAWAL", (char *)reqfb,0, (char **)&reqfb,
(long *)&reqlen,TPSIGRSTRT) == -1) {
(void)Fchg(transf, STATLIN, 0,
"Cannot withdraw from debit account", (FLDLEN)0);
tpfree((char *)reqfb);
tpreturn(TPFAIL, 0,transb->data, 0L, 0);
}

/* get "debit" balance from return buffer */

(void)strcpy(db_amts, Fvals((FBFR *)reqfb,SBALANCE,0));
void)sscanf(db_amts,"%f",db_bal);
if ((db_amts == NULL) || (db_bal < 0.0)) {
(void)Fchg(transf, STATLIN, 0,
"illegal debit account balance", (FLDLEN)0);
tpfree((char *)reqfb);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}

/* put deposit account ID in request buffer */
(void)Fchg(reqfb,ACCOUNT_ID,0,(char *)&cr_id, (FLDLEN

Invalidating Descriptors

If a service calling tpgetrply() (described in detail in Writing Request/Response Clients and Servers) fails with TPETIME and decides to cancel the request, it can invalidate the descriptor with a call to tpcancel(3c). If a reply subsequently arrives, it is silently discarded.

Use the following signature to call the tpcancel() function.

void
tpcancel(int cd)

The cd (call descriptor) argument identifies the process you want to cancel.

tpcancel() cannot be used for transaction replies (that is, for replies to requests made without the TPNOTRAN flag set). Within a transaction, tpabort(3c) does the same job of invalidating the transaction call descriptor.

The following example shows how to invalidate a reply after timing out.

Invalidating a Reply After Timing Out


int cd1;
.
.
.
if ((cd1=tpacall(sname, (char *)audv, sizeof(struct aud),
TPNOTRAN)) == -1) {
.
.
.
}
if (tpgetrply(cd1, (char **)&audv,&audrl, 0) == -1) {
if (tperrno == TPETIME) {
tpcancel(cd1);
.
.
.
}
}
tpreturn(TPSUCCESS, 0,NULL, 0L, 0);


Forwarding Requests

The tpforward(3c) function allows a service to forward a request to another service for further processing.

Use the following signature to call the tpforward() function.

void
tpforward(char *svc, char *data, long len, long flags)

The following table describes the arguments to the tpreturn() function.

tpreturn() Function Arguments

Argument

Description

svc

Character pointer to the name of the service to which the request is to be forwarded.

data

Pointer to the reply message that is returned to the client process. The message buffer must have been allocated previously by tpalloc().

If you use the same buffer that was passed to the service in the SVCINFO structure, you need not be concerned with buffer allocation or disposition because both are handled by the system-supplied main(). You cannot free this buffer using the tpfree() command; any attempt to do so quietly fails. You can resize the buffer using the tprealloc() function.

If you use another buffer (that is, a buffer other than the one that is passed to the service routine) to return the message, it is your responsibility to allocate it. The system frees the buffer automatically when the application calls the tpreturn() function.

If no reply message needs to be returned, set this argument to the NULL pointer.

Note: If no reply is expected by the client (that is, if TPNOREPLY was set), the tpreturn() function ignores the data and len arguments and returns control to main().

len

Length of the reply buffer. The application accesses the value of this argument through the olen parameter of the tpcall() function or the len parameter of the tpgetrply() function.

Acting as the client, the process can use this returned value to determine whether the reply buffer has grown.

If a reply is expected by the client and there is no data in the reply buffer (that is, if the data argument is set to the NULL pointer), the function sends a reply with zero length, without modifying the client's buffer.

The system ignores the value of this argument if the data argument is not specified.

flag

Currently not used.

The functionality of tpforward() differs from a service call: a service that forwards a request does not expect a reply. The responsibility for providing the reply is passed to the service to which the request has been forwarded. The latter service sends the reply to the process that originated the request. It becomes the responsibility of the last server in the forward chain to send the reply to the originating client by invoking tpreturn().

The following figure shows one possible sequence of events when a request is forwarded from one service to another. Here a client initiates a request using the tpcall() function and the last service in the chain (SVC_C) provides a reply using the tpreturn() function.

Forwarding a Request

Service routines can forward requests at specified priorities in the same manner that client processes send requests, by using the tpsprio() function.

When a process calls tpforward(), the system-supplied main() regains control, and the server process is free to do more work.

Note: If a server process is acting as a client and a reply is expected, the server is not allowed to request services from itself. If the only available instance of the desired service is offered by the server process making the request, the call fails, indicating that a recursive call cannot be made. However, if a service routine sends a request (to itself) with the TPNOREPLY communication flag set, or if it forwards the request, the call does not fail because the service is not waiting for itself.

Calling tpforward() can be used to indicate success up to that point in processing the request. If no application errors have been detected, you can invoke tpforward(), otherwise, you can call tpreturn() with rval set to TPFAIL.

The following example is borrowed from the OPEN_ACCT service routine which is part of the ACCT server. This example illustrates how the service sends its data buffer to the DEPOSIT service by calling tpforward(). The code shows how to test the SQLCODE to determine whether the account insertion is successful. If the new account is added successfully, the branch record is updated to reflect the new account, and the data buffer is forwarded to the DEPOSIT service. On failure, tpreturn() is called with rval set to TPFAIL and the failure is reported on the status line of the form.

tpforward( ) Function


 ...
/* set pointer to TPSVCINFO data buffer */
transf = (FBFR *)transb->data;
...
/* Insert new account record into ACCOUNT*/
account_id = ++last_acct; /* get new account number */
tlr_bal = 0.0; /* temporary balance of 0 */
EXEC SQL insert into ACCOUNT (ACCOUNT_ID, BRANCH_ID, BALANCE,
ACCT_TYPE, LAST_NAME, FIRST_NAME, MID_INIT, ADDRESS, PHONE) values
(:account_id, :branch_id, :tlr_bal, :acct_type, :last_name,
:first_name, :mid_init, :address, :phone);
if (SQLCODE != SQL_OK) { /* Failure to insert */
(void)Fchg(transf, STATLIN, 0,
"Cannot update ACCOUNT", (FLDLEN)0);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}

/* Update branch record with new LAST_ACCT */

EXEC SQL update BRANCH set LAST_ACCT = :last_acct where BRANCH_ID = :branch_id;
if (SQLCODE != SQL_OK) { /* Failure to update */
(void)Fchg(transf, STATLIN, 0,
"Cannot update BRANCH", (FLDLEN)0);
tpreturn(TPFAIL, 0, transb->data, 0L, 0);
}
/* up the priority of the deposit call */
if (tpsprio(PRIORITY, 0L) == -1)
(void)userlog("Unable to increase priority of deposit\n");

/* tpforward same buffer to deposit service to add initial balance */
tpforward("DEPOSIT", transb->data, 0L, 0);