![]() ![]() ![]() ![]() ![]() ![]() |
This topic includes the following sections:
In request/response communication mode, one software module sends a request to a second software module and waits for a response. Because the first software module performs the role of the client, and the second, the role of the server, this mode is also referred to as client/server interaction. Many online banking tasks are programmed in request/response mode. For example, a request for an account balance is executed as follows:
Once a client process has joined an application, allocated a buffer, and placed a request for input into that buffer, it can then send the request message to a service subroutine for processing and receive a reply message.
The tpcall(3c) function sends a request to a service subroutine and synchronously waits for a reply. Use the following signature to call the tpcall()
function:
int
tpcall(char *svc
, char *idata
, longilen
, char **odata
, long *olen
, long flags)
The following table describes the arguments to the tpcall()
function.
Pointer that contains the address of the data portion of the request. The pointer must reference a typed buffer that was allocated by a prior call to tpalloc(). Note that the
type (and optionally the subtype ) of idata must match the type (and optionally the subtype ) expected by the service routine. If the types do not match, the system sets tperrno to TPEITYPE and the function call fails.
|
|
Address of a pointer to the output buffer that receives the reply. You must allocate the output buffer using the tpalloc() function. If the reply message contains no data, upon successful return from
tpcall() , the system sets * olen to zero, and the pointer and the contents of the output buffer remain unchanged.
|
|
tpcall()
waits for the expected reply.
Note: | Calling the tpcall() function is logically the same as calling the tpacall() function immediately followed by tpgetrply(), as described in Sending Asynchronous Messages. |
The request carries the priority set by the system for the specified service (svc
) unless a different priority has been explicitly set by a call to the tpsprio() function (described in Setting and Getting Message Priorities).
tpcall()
returns an integer. On failure, the value of this integer is -1 and the value of tperrno(5) is set to a value that reflects the type of error that occurred. For information on valid error codes, refer to tpcall(3c) in the BEA Tuxedo ATMI C Function Reference.
Note: | Communication calls may fail for a variety of reasons, many of which can be corrected at the application level. Possible causes of failure include: application defined errors (TPESVCFAIL ), errors in processing return arguments (TPESVCERR ), typed buffer errors (TPEITYPE , TPEOTYPE ), timeout errors (TPETIME ), and protocol errors (TPEPROTO ), among others. For a detailed discussion of errors, refer to Managing Errors. For a complete list of possible errors, refer to tpcall(3c) in the BEA Tuxedo ATMI C Function Reference. |
The BEA Tuxedo system automatically adjusts a buffer used for receiving a message if the received message is too large for the allocated buffer. You should test for whether or not the reply buffers have been resized.
To access the new size of the buffer, use the address returned in the *olen
parameter. To determine whether a reply buffer has changed in size, compare the size of the reply buffer before the call to tpcall()
with the value of *
olen
after its return. If *
olen
is larger than the original size, the buffer has grown. If not, the buffer size has not changed.
You should reference the output buffer by the value returned in odata
after the call because the output buffer may change for reasons other than an increase in buffer size. You do not need to verify the size of request buffers because the request data is not adjusted once it has been allocated.
Note: | If you use the same buffer for the request and reply message, and the pointer to the reply buffer has changed because the system adjusted the size of the buffer, then the input buffer pointer no longer references a valid address. |
The following example shows how the client program, audit.c
, makes a synchronous call using the same buffer for both the request and reply messages. In this case, using the same buffer is appropriate because the *audv
message buffer has been set up to accommodate both request and reply information. The following actions are taken in this code:
b_id
field, but does not overwrite it. bal
and ermsg
fields to zero and the NULL string, respectively, in preparation for the values to be returned by the service. svc_name
and hdr_type
variables represent the service name and the balance type requested, respectively. In this example, these variables represent account
and teller
, respectively.. . .
/* Create buffer and set data pointer */
audv = (struct aud *)tpalloc("VIEW", "aud", sizeof(struct aud));
/* Prepare aud structure */
audv->b_id = q_branchid;
audv->balance = 0.0;
(void)strcpy(audv->ermsg, "");
/* Do tpcall */
if (tpcall(svc_name,(char *)audv,sizeof(struct aud),
(char **)&audv,(long *)&audrl,0)== -1){
(void)fprintf (stderr, "%s service failed\n %s: %s\n",
svc_name, svc_name, audv->ermsg);
retc = -1;
}
else
(void)printf ("Branch %ld %s balance is $%.2f\n",
audv->b_id, hdr_type, audv->balance);
. . .
The following code provides a generic example of how an application test for a change in buffer size after a call to tpcall()
. In this example, the input and output buffers must remain equal in size.
char *svc, *idata, *odata;
long ilen, olen, bef_len, aft_len;
. . .
if (idata = tpalloc("STRING", NULL, 0) == NULL)
error
if (odata = tpalloc("STRING", NULL, 0) == NULL)
error
place string value into idata buffer
ilen = olen = strlen(idata)+1;
. . .
bef_len = olen;
if (tpcall(svc, idata, ilen, &odata, &olen, flags) == -1)
error
aft_len = olen;
if (aft_len > bef_len){ /* message buffer has grown */
if (idata = tprealloc(idata, olen) == NULL)
error
}
The following example is based on the TRANSFER
service, which is part of the XFER
server process of bankapp
. (bankapp
is a sample ATMI application delivered with the BEA Tuxedo system.) The TRANSFER
service assumes the role of a client when it calls the WITHDRAWAL
and DEPOSIT
services. The application sets the communication flag to TPSIGRSTRT
in these service calls to give the transaction a better chance of committing. The TPSIGRSTRT
flag specifies the action to take if there is a signal interrupt. For more information on communication flags, refer to tpcall(3c) in the BEA Tuxedo ATMI C Function Reference.
/* Do a tpcall to withdraw from first account */
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);
}
...
/* Do a tpcall to deposit to second account */
if (tpcall("DEPOSIT", (char *)reqfb, 0, (char **)&reqfb,
(long *)&reqlen, TPSIGRSTRT) == -1) {
(void)Fchg(transf, STATLIN, 0,
"Cannot deposit into credit account", (FLDLEN)0);
tpfree((char *)reqfb);
}
The following example illustrates a communication call that suppresses transaction mode. The call is made to a service that is not affiliated with a resource manager; it would be an error to allow the service to participate in the transaction. The application prints an accounts receivable report, accrcv
, generated from information obtained from a database named accounts
.
The service routine REPORT
interprets the specified parameters and sends the byte stream for the completed report as a reply. The client uses tpcall()
to send the byte stream to a service called PRINTER
, which, in turn, sends the byte stream to a printer that is conveniently close to the client. The reply is printed. Finally, the PRINTER
service notifies the client that the hard copy is ready to be picked up.
Note: | The example Sending an Asynchronous Message with TPNOREPLY | TPNOTRAN shows a similar example using an asynchronous message call. |
#include <stdio.h>
#include "atmi.h"
main()
{
char *rbuf; /* report buffer */
long r1len, r2len, r3len; /* buffer lengths of send, 1st reply,
and 2nd reply buffers for report */
join application
if (rbuf = tpalloc("STRING", NULL, 0) == NULL) /* allocate space for report */
leave application and exit program
(void)strcpy(rbuf,
"REPORT=accrcv DBNAME=accounts"); /* send parms of report */
r1len = strlen(rbuf)+1; /* length of request */
start transaction
if (tpcall("REPORT", rbuf, r1len, &rbuf,
&r2len, 0) == -1) /* get report print stream */
error routine
if (tpcall("PRINTER", rbuf, r2len, &rbuf,
&r3len, TPNOTRAN) == -1) /* send report to printer */
error routine
(void)printf("Report sent to %s printer\n",
rbuf); /* indicate which printer */
terminate transaction
free buffer
leave application
}
Note: | In the preceding example, the term error routine indicates that the following tasks are performed: an error message is printed, the transaction is aborted, allocated buffers are freed, the client leaves the application, and the program is exited. |
The following example shows how the TPNOCHANGE
communication flag is used to enforce strong buffer type checking by indicating that the reply message must be returned in the same type of buffer that was originally allocated. This example refers to a service routine called REPORT
. (The REPORT
service is also shown in Example: Sending a Synchronous Message with TPNOTRAN Set.)
In this example, the client receives the reply in a VIEW
typed buffer called rview1
and prints the elements in printf()
statements. The strong type check flag, TPNOCHANGE
, forces the reply to be returned in a buffer of type VIEW
and of subtype rview1
.
A possible reason for this check is to guard against errors that may occur in the REPORT
service subroutine, resulting in the use of a reply buffer of an incorrect type. Another reason is to prevent changes that are not made consistently across all areas of dependency. For example, another programmer may have changed the REPORT
service to standardize all replies in another VIEW
format without modifying the client process to reflect the change.
#include <stdio.h>
#include "atmi.h"
#include "rview1.h"
main(argc, argv)
int argc;
char * argv[];
{
char *rbuf; /* report buffer */
struct rview1 *rrbuf; /* report reply buffer */
long rlen, rrlen; /* buffer lengths of send and reply
buffers for report */
if (tpinit((TPINIT *) tpinfo) == -1)
fprintf(stderr, "%s: failed to join application\n", argv[0]);
if (rbuf = tpalloc("STRING", NULL, 0) == NULL) { /* allocate space for report */
tpterm();
exit(1);
}
/* allocate space for return buffer */
if (rrbuf = (struct rview1 *)tpalloc("VIEW", "rview1", sizeof(struct rview1)) \ == NULL{
tpfree(rbuf);
tpterm();
exit(1);
}
(void)strcpy(rbuf, "REPORT=accrcv DBNAME=accounts FORMAT=rview1");
rlen = strlen(rbuf)+1; /* length of request */
/* get report in rview1 struct */
if (tpcall("REPORT", rbuf, rlen, (char **)&rrbuf, &rrlen, TPNOCHANGE) == -1) {
fprintf(stderr, "accounts receivable report failed in service call\n");
if (tperrno == TPEOTYPE)
fprintf(stderr, "report returned has wrong view type\n");
tpfree(rbuf);
tpfree(rrbuf);
tpterm();
exit(1);
}
(void)printf("Total accounts receivable %6d\n", rrbuf->total);
(void)printf("Largest three outstanding %-20s %6d\n", rrbuf->name1, rrbuf->amt1);
(void)printf("%-20s %6d\n", rrbuf->name2, rrbuf->amt2);
(void)printf("%-20s %6d\n", rrbuf->name3, rrbuf->amt3);
tpfree(rbuf);
tpfree(rrbuf);
tpterm();
}
The type of asynchronous processing discussed in this section is sometimes referred to as fan-out parallelism because it allows a client's requests to be distributed (or "fanned out") simultaneously to several services for processing.
The other type of asynchronous processing supported by the BEA Tuxedo system is pipeline parallelism in which the tpforward() function is used to pass (or forward) a process from one service to another. For a description of the tpforward()
function, refer to Writing Servers.
The tpacall(3c) function sends a request to a service and immediately returns. Use the following signature to call the tpacall()
function:
int
tpacall(char *svc
, char *data
, longlen
, long flags)
The following table describes the arguments to the tpacall()
function.
Pointer that contains the address of the data portion of the request. The pointer must reference a typed buffer that was allocated by a prior call to tpalloc(). Note that the
type (and optionally the subtype ) of idata must match the type (and optionally the subtype ) expected by the service routine. If the types do not match, the system sets tperrno to TPEITYPE and the function call fails.
|
|
Flag options. You can list a group of flags by using the logical
OR operator. If you set this value to zero, the communication is conducted in the default manner. For a list of valid flags and defaults, refer to tpacall(3c) in the BEA Tuxedo ATMI C Function Reference.
|
The tpacall()
function sends a request message to the service named in the svc
parameter and immediately returns from the call. Upon successful completion of the call, the tpacall()
function returns an integer that serves as a descriptor used to access the correct reply for the relevant request. While tpacall()
is in transaction mode (as described in Writing Global Transactions) there may not be any outstanding replies when the transaction commits; that is, within a given transaction, for each request for which a reply is expected, a corresponding reply must eventually be received.
If the value TPNOREPLY
is assigned to the flags
parameter, the parameter signals to tpacall()
that a reply is not expected. When this flag is set, on success tpacall()
returns a value of 0
as the reply descriptor. If subsequently passed to the tpgetrply() function, this value becomes invalid. Guidelines for using this flag value correctly when a process is in transaction mode are discussed in Writing Global Transactions.
On error, tpacall()
returns -1
and sets tperrno(5) to a value that reflects the nature of the error. tpacall()
returns many of the same error codes as tpcall(). The differences between the error codes for these functions are based on the fact that one call is synchronous and the other, asynchronous. These errors are discussed at length in Managing Errors.
The following example shows how tpacall()
uses the TPNOTRAN
and TPNOREPLY
flags. This code is similar to the code in Example: Sending a Synchronous Message with TPNOTRAN Set. In this case, however, a reply is not expected from the PRINTER
service. By setting both TPNOTRAN
and TPNOREPLY
flags, the client is indicating that no reply is expected and the PRINTER
service will not participate in the current transaction. This situation is discussed more fully in Managing Errors.
#include <stdio.h>
#include "atmi.h"
main()
{
char *rbuf; /* report buffer */
long rlen, rrlen; /* buffer lengths of send, reply buffers for report */
join application
if (rbuf = tpalloc("STRING", NULL, 0) == NULL) /* allocate space for report */
error
(void)strcpy(rbuf, "REPORT=accrcv DBNAME=accounts");/* send parms of report */
rlen = strlen(rbuf)+1; /* length of request */
start transaction
if (tpcall("REPORT", rbuf, rlen, &rbuf, &rrlen, 0)
== -1) /* get report print stream */
error
if (tpacall("PRINTER", rbuf, rrlen, TPNOTRAN|TPNOREPLY)
== -1) /* send report to printer */
error
. . .
commit transaction
free buffer
leave application
}
The following example shows a series of asynchronous calls that make up the total bank balance query. Because the banking application data is distributed among several database sites, an SQL query needs to be executed against each one. The application performs these queries by selecting one branch identifier per database site, and calling the ABAL
or TBAL
service for each site. The branch identifier is not used in the actual SQL query, but it enables the BEA Tuxedo system to route each request to the proper site. In the following code, the for
loop invokes tpacall()
once for each site.
audv->balance = 0.0;
(void)strcpy(audv->ermsg, "");
for (i=0; i<NSITE; i++) {
/* Prepare aud structure */
audv->b_id = sitelist[i]; /* routing done on this field */
/* Do tpacall */
if ((cd[i]=tpacall(sname, (char *)audv, sizeof(struct aud), 0))
== -1) {
(void)fprintf (stderr,
"%s: %s service request failed for site rep %ld\n",
pgmname, sname, sitelist[i]);
tpfree((char *)audv);
return(-1);
}
}
A reply to a service call can be received asynchronously by calling the tpgetrply(3c) function. The tpgetrply()
function dequeues a reply to a request previously sent by tpacall().
Use the following signature to call the tpgetrply()
function:
int
tpgetrply(int *cd
, char **data
, long *len
, longflags
)
The following table describes the arguments to the tpgetrply()
function.
Pointer to the call descriptor returned by the tpacall() function.
|
|
Address of a pointer to the output buffer that receives the reply. You must allocate the output buffer using the tpalloc() function. If the reply message contains no data, upon successful return from tpcall(), the system sets
* data to zero. The pointer and the contents of the output buffer remain unchanged.
|
|
Flag options. You can list a group of flags using the logical
OR operator. If you set this value to zero, the communication is conducted in the default manner. For a list of valid flags and defaults, refer to tpcall(3c)) in the BEA Tuxedo ATMI C Function Reference.
|
By default, the function waits for the arrival of the reply that corresponds to the value referenced by the cd
parameter. During this waiting interval, a blocking timeout may occur. A time-out occurs when tpgetrply()
fails and tperrno(5) is set to TPETIME
(unless the flags
parameter is set to TPNOTIME
).
Two ATMI functions allow you to determine and set the priority of a message request: tpsprio(3c) and tpgprio(3c). The priority affects how soon the request is dequeued by the server; servers dequeue requests with the highest priorities first.
The tpsprio(3c) function enables you to set the priority of a message request.
The tpsprio()
function affects the priority level of only one request: the next request to be sent by tpcall() or tpacall(), or to be forwarded by a service subroutine.
Use the following signature to call the tpsprio()
function:
int
tpsprio(intprio
, longflags
);
The following table describes the arguments to the tpsprio()
function.
The following sample code is an excerpt from the TRANSFER
service. In this example, the TRANSFER
service acts as a client by sending a synchronous request, via tpcall(), to the WITHDRAWAL
service. TRANSFER
also invokes tpsprio()
to increase the priority of its request message to WITHDRAWAL
, and to prevent the request from being queued for the WITHDRAWAL
service (and later the DEPOSIT
service) after waiting on the TRANSFER
queue.
/* 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);
}
The tpgprio(3c) function enables you to get the priority of a message request.
Use the following signature to call the tpgprio()
function:
int
tpgprio();
A requester can call the tpgprio()
function after invoking the tpcall() or tpacall() function to retrieve the priority of the request message. If a requester calls the function but no request is sent, the function fails, returning -1
and setting tperrno(5) to TPENOENT
. Upon success, tpgprio()
returns an integer value in the range of 1 to 100 (where the highest priority value is 100).
If a priority has not been explicitly set using the tpsprio() function, the system sets the message priority to that of the service routine that handles the request. Within an application, the priority of the request-handling service is assigned a default value of 50 unless a system administrator overrides this value.
The following example shows how to determine the priority of a message that was sent in an asynchronous call.
#include <stdio.h>
#include "atmi.h"
main ()
{
int cd1, cd2; /* call descriptors */
int pr1, pr2; /* priorities to two calls */
char *buf1, *buf2; /* buffers */
long buf1len, buf2len; /* buffer lengths */
join application
if (buf1=tpalloc("FML", NULL, 0) == NULL)
error
if (buf2=tpalloc("FML", NULL, 0) == NULL)
error
populate FML buffers with send request
if ((cd1 = tpacall("service1", buf1, 0, 0)) == -1)
error
if ((pr1 = tpgprio()) == -1)
error
if ((cd2 = tpacall("service2", buf2, 0, 0)) == -1)
error
if ((pr2 = tpgprio()) == -1)\
error
if (pr1 >= pr2) { /* base the order of tpgetrplys on priority of calls */
if (tpgetrply(&cd1, &buf1, &buf1len, 0) == -1)
error
if (tpgetrply(&cd2, &buf2, &buf2len, 0) == -1)
error
}
else {
if (tpgetrply(&cd2, &buf2, &buf2len, 0) == -1)
error
if (tpgetrply(&cd1, &buf1, &buf1len, 0) == -1)
error
}
. . .
}
![]() ![]() ![]() |