This chapter covers the subject of conversational clients and services.
A conversational client differs in the following ways from a request/response client (described in Chapter 2, "Writing Client Programs,"):
Writing Conversational Clients and Services
tpconnect
() rather than tpcall
() or tpacall
().
A conversational service differs in the following ways from a request/response service (described in Chapter 3, "Writing Service Routines,"):
Both conversational clients and servers have the following characteristics:
tpsend
() and tprecv
() calls to send and receive data in conversations.
In the conversational mode of communication, a half-duplex connection is established between the client (or initiator) and a server. Control of the connection can be passed back and forth between the initiator and the subordinate server. At any point in the conversation, the process that has control can send messages; the process that does not have control can only receive. The connection remains up until an event occurs that tears it down. One event, A connection descriptor, Data is passed in typed buffers just as in request/response mode. The buffer types must be recognized by the application; they must be allocated with ATMI functions as described in Chapter 2, "Writing Client Programs," and in Conversational clients must join the application via a call to name must point to the name of a service posted in the bulletin board by a conversation server. If name is not a pointer to a conversational service, the call fails with a Data can be sent at the same time the connection is being established by having data point to a buffer previously allocated by As with other ATMI functions, the behavior of the called program can be controlled by values of the New valid Conversational Mode
TPEV_SENDONLY
, notifies the receiving program that control of the connection has been passed to it and it can successfully call tpsend
(). Other events are notifications that something significant has occurred; they have the result of either bringing the conversation to a normal conclusion or precipitating a disorderly disconnection.
The Connection Descriptor
cd
, is returned when a connection is established with tpconnect
(). The cd
is used to identify subsequent message transmissions with a particular conversation. A client or conversational service can have more than one conversation active simultaneously. The maximum number is 64.
Buffer Management
tpalloc
(3c) in the BEA TUXEDO Reference Manual.
Joining an Application
tpinit()
prior to attempting to establish a connection to a service. The procedure for joining the application is described in Chapter 2, "Writing Client Programs."
Establishing a Connection
tpconnect
() is the ATMI function used to set up a conversation. The syntax is:
int
tpconnect(name
, data, len, flags)
char *name, *data;
long len, flags;-1
and tperrno
is set to the error code TPENOENT
. If the calling program has already reached the maximum number of active connections allowed, the call will fail with the error code TPELIMIT
.
tpalloc
(). The type and subtype of the buffer pointed to by data
must be a type recognized by the service being called. If no data is being sent, data
can be set to NULL
. len
is used to specify how much of the buffer to send. If the buffer is self-defining (for example, an FML buffer), len
can be set to 0
. The conversational service being called receives the data
and len
pointers via the TPSVCINFO
data structure passed to it by main()
when the service is invoked. So far, this should sound a lot like what happens when a request/response service is invoked, because it is. Differences begin to appear when we consider options for the flags
argument.
Values for the flags Argument: tpconnect()
flags
argument of tpconnect
(). Four of the values are identical to their use in tpcall
() and are described in "Values for the flags Argument: tpcall()" in Chapter 2, "Writing Client Programs." They are:
TPNOTRAN TPNOBLOCK
TPNOTIME TPSIGRSTRTflags
options are:
TPSENDONLY
flags
member of its TPSVCINFO
structure; TPSVCINFO->flags == TPRECVONLY
. TPSENDONLY
and TPRECVONLY
are mutually exclusive; one or the other must be specified.
TPRECVONLY
flags
member of its TPSVCINFO
structure; TPSVCINFO->flags == TPSENDONLY.
TPSENDONLY
and TPRECVONLY
are mutually exclusive; one or the other must be specified.
As mentioned above, on successful completion tpconnect
() returns a connection descriptor that is used in all subsequent calls of the conversation. Your call to tpconnect
() should be coded something like that shown in Listing 4-1.
Listing 4-1 Establishing a Conversational Connection
#include atmi.h
#define FAIL -1
int cd1; /* Connection Descriptor */
main()
{
if ((cd = tpconnect("AUDITC",NULL,0,TPSENDONLY)) == -1) {
error routine
}
}
After the conversational connection is set up, communication between the client (or initiator) and the service is accomplished with send/receive calls. The connection is half-duplex. That means communication can be in only one direction at a time. The process that has control of the connection can send; the process that does not have control can receive. Initially, control is decided by the originator and is specified by the TPSENDONLY
or TPRECVONLY
flag value of the tpconnect()
call; TPSENDONLY
means control is retained by the originator, TPRECVONLY
means control is given to the called service. After tpconnect
() returns successfully, data is sent across the open connection with the tpsend
() function.
The syntax of tpsend
() is:
int
tpsend(cd
,data
,len
,flags
,revent
)
intcd
;
char *data
;
longlen
;
longflags
;
long *revent
;
cd is the connection descriptor returned by tpconnect()
that identifies the connection over which to send the data. *data
and len
are, respectively, a pointer to a buffer created by tpalloc()
, and the length of the data to be sent. The same rules apply to data
and len
that have been outlined earlier: The buffer must be of a type recognized by the program that receives it and length can be 0
if the buffer is self-defining. There is no requirement that data be sent. If the data pointer is NULL
, len
is ignored.
There are four valid values for the flags
argument of tpsend
(). Three of them:
TPNOBLOCK
TPNOTIME
TPSIGRSTRT
have the same meaning described in Chapter 2 (in "Values for the flags Argument: tpcall()" section). The fourth value is like one that is used in tpconnect
(), but has added significance in this function.
TPRECVONLY
tpsend
() calls at the moment and to pass control of the connection over to the other side of the connection. When the called program receives the data, it also receives a TPEV_SENDONLY
event at the address pointed to by revent
.
It is not a requirement that control be passed each time the tpsend
() call is made. The process authorized to make tpsend
() calls on the connection can make as many calls as necessary before turning over control of the connection. In fact, the logic of the conversational program may be such that one side of the conversation retains control of the connection throughout the life of the conversation.
Listing 4-2 shows tpsend
() used in a code fragment.
Listing 4-2 Sending Data in Conversational Mode
if (tpsend(cd,line,0,TPRECVONLY,revent) == -1) {
(void)userlog("%s: tpsend failed tperrno %d",
argv[0],tperrno);
(void)tpabort(0);
(void)tpterm();
exit(1);
}
The function used to receive data sent over an open connection is tprecv
(). The syntax is:
int
tprecv(cd
,data
,len
,flags
,revent
)
intcd
;
char **data
;
long *len
;
longflags
;
long *revent
;
cd
is the connection descriptor. If the function is being issued from a subordinate program (that is, not the originator of the connection), cd
is in the TPSVCINFO structure for the program. If tprecv() is being issued by the originator, cd
is the descriptor returned by tpconnect(). When the call is made, *data
is a pointer to the address of a previously tpalloc'd buffer and len
is a pointer to the size of the buffer. len, data
, and *data
are not allowed to be NULL
. The call fails and tperrno is set to TPEINVAL.
Upon successful return, *data points to the data received and len contains the size of the buffer. If len is greater than the total size of the buffer before the call to tprecv(), it indicates the buffer's new size. If len is 0, no data was received.
If an event exists for cd, tprecv
() returns a -1
and tperrno is set to TPEEVENT. The event type is returned in revent. With events TPESVCSUCC, TPESVCFAIL, and TPESENDONLY, data can be received. These three events are all normal completions of the tprecv() call, so it is not correct to assume the -1
return value means the call has failed. A more complete discussion of events can be found in "Events and Their Significance" later in this chapter.
tprecv
() has four valid flags. Three of them are described in Chapter 2 (in the "Values for the flags Argument: tpcall()" section). They are:
TPNOCHANGE
The fourth valid flag value is When the flag is set, tprecv() does not wait for data to arrive. If data is available, fine; tprecv() gets the data and returns. If data is not available, the call fails and tperrno is set to TPEBLOCK. When the flag is not set, tprecv() waits and does not return until data arrives or a timeout occurs.
Listing 4-3 shows a fragment of code using TPNOBLOCK
.
tprecv
().
Listing 4-3
Receiving Data in Conversation
if (tprecv(cd,line,len,TPNOCHANGE,revent) != -1) {
(void)userlog("%s: tprecv failed tperrno %d revent %ld",
argv[0],tperrno,revent);
(void)tpabort(0);
(void)tpterm();
exit(1);
}
There are three ways in which the connection can be taken down in an orderly fashion and the conversation ended normally. Figure 4-1 and Figure 4-2 show two scenarios that help to illustrate how conversations are ended where global transactions are not involved. The third approach of ending a conversation where a transaction is involved is shown in Chapter 5, "Global Transactions in BEA TUXEDO System."
Figure 4-1 shows a simple A to B conversation. The connection is set up initially with a call to tpconnect
() with the TPSENDONLY
flag set. In due course, A turns control of the connection over to B by calling tpsend
() with the TPRECVONLY
flag set. This generates a TPEV_SENDONLY
event. The next call by B to tprecv
() returns a -1
, tperrno
is set to TPEEVENT
, and revent shows the event TPEV_SENDONLY
. B knows from the TPEV_SENDONLY
event that it now controls the connection. Subsequently, B calls tpreturn
() with rval
set to TPSUCCESS
. This generates a TPEV_SVCSUCC
event for A. The call to tpreturn
() also brings down the connection. When A calls tprecv
() and learns of the event, it recognizes that the conversation has been terminated. Data can be received on this call to tprecv
() even if the event is TPEV_SVCFAIL
. In this illustration, A can be either a client or a server, B can only be a server.
Figure 4-1 Simple SENDONLY Connection and Return
Figure 4-2 shows a hierarchy of connections. The scenario applies to a service in a conversation, B, that has initiated a connection to a second service, C. In other words, there are two active connections, A to B, and B to C. If B is in control of both connections, a call to tpreturn
() has the following effect: the call will fail, a TPEV_SVCERR
event will be posted on all open connections, and the connections will be closed in a disorderly manner. The proper sequence is for B to call tpsend
() with the TPRECVONLY
flag set on the connection to C, turning control of the B-C connection over to C. C can then call tpreturn
() with rval
set to TPSUCCESS
, TPFAIL
or TPEXIT
, as appropriate. B can then call tpreturn
(), posting an event (either TPEV_SVCSUCC
or TPEV_SVCFAIL
) for A. Both connections are terminated normally.
Figure 4-2 Connection Hierarchy
It is an error to end a conversation with connections still open. Either tpcommit
() or tpreturn
() will fail in a disorderly manner.
To summarize the ways a conversation can be ended in an orderly manner:
tpreturn
(). This is illustrated in Figure 4-1 above.
tpreturn
(). The subordinate must have control of the connection and must make the call to tpreturn
() before the originator does. This is illustrated in Figure 4-2 above.
In each case, the subordinate has control and calls There are five events recognized in conversational communication. All five can be posted for tpreturn
().
Events and Their Significance
tprecv
(), three of the five can be posted for tpsend
(). Table 4-1 summarizes them.
The The syntax is simple:
The preferred way of bringing down a connection is for the subordinate to call There is nothing that prevents a conversational service from making request/response calls if it needs to communicate with another service. In the examples of connection hierarchies shown in Figure 4-2 above, the calls from B to C could have been made with There are some parameters in the configuration file that pertain only to conversational processing. As noted in Chapter 1 (in the "Configuration File" section), the BEA TUXEDO system administrator normally is responsible for setting up the production version of the configuration file for the application, but you may need to set some parameters in your own development configuration.
Here are the parameters you need to know about:
Disorderly Disconnection
tpdiscon
() function has an innocent sound to it, as though it was the logical opposite of tpconnect
(), but it is really the equivalent of pulling the plug on the connection. It can be called only by the initiator of a conversation.
int
tpdiscon(cd)
int cd;cd
is the connection descriptor returned by tpconnect
().
tpdiscon
() generates a TPEV_DISCONIMM event for the service at the other end of the connection, and the cd
is no longer valid. If a transaction is in progress, it is aborted. Data may be lost. If tpdiscon
() is called from a service that was not the originator of the connection identified by cd
, it fails with an error code of TPEBADDESC.
tpreturn
().
Request/Response Calls and Conversations
tpcall
() or tpacall
() instead of tpconnect
(). Remember, however, that conversational services are not permitted to make calls to tpforward
().
Configuration Parameters
MAXCONV
RESOURCES
section for all machines in the configuration and can be overridden in the MACHINES
section for each machine. It is quite probable that for an application under development the default is adequate.
CONV = { Y/N }
SERVERS
section. Connections can only be made to servers that have this value set to Y
. If it is set to N
or left unspecified, a tpconnect() call to a service of the server will fail.
MIN
and MAX
SERVERS
section that specify the minimum and maximum number of occurrences of the server to be started by tmboot
(1). If not specified, MIN
defaults to 1 and MAX
defaults to MIN
. The same parameters are available for use with request/response servers. However, conversational servers are automatically spawned as needed. So if you set MIN = 1
and MAX = 10
, for example, tmboot
starts one initially. When a tpconnect
() call is made to a service offered by that server, the system starts up a second copy. As each copy is called a new one is spawned up to a limit of 10.
MAXSERVERS
MAX
values for all conversational servers. You probably will not need to worry about this for an application under development, but it could be something that needs attention when the application reaches the production stage. The parameter is in the RESOURCES
section.
The utilities described in Chapters 2 and 3, buildclient(1) and buildserver(1), are used for building conversational clients and servers.
Conversational servers must be built only with conversational services; that is, mixing of conversational services and request/response services in the same server is not allowed.
Conversational services and request/response services can not use the same name.