The purpose of the remainder of this chapter is to describe the environment in which you will be writing code for a BEA TUXEDO System/T application.
In addition to the C code that expresses the logic of your application, you will be using the Application-Transaction Monitor Interface (ATMI), which refers to the interface between the BEA TUXEDO System/T transaction monitor and your application. The ATMI primitives are C language functions that resemble UNIX system calls, but they have the specific purpose of implementing the communication among application modules running under the control of the BEA TUXEDO System/T transaction monitor, including all the associated resources you need.
As you might remember from the BEA TUXEDO Product Overview the BEA
TUXEDO System uses an enhanced client-server architecture.
Chapters 2 through 7 of this book describe how the ATMI
primitives are used in writing and debugging clients and
services. This chapter provides some of the context within which
you will be doing that work.
A client process takes user input and sends it as a service
request to a server process that offers the requested service.
A client process uses one ATMI primitive to join an application, allocates a message buffer by using another ATMI primitive, and uses still others to send the buffer to a server and receive the reply.
The operation of a basic client process can be summarized by the pseudo-code shown in Figure 1.
main() { allocate a TPINIT buffer place initial client identification in buffer enroll as a client of the System/T application allocate buffer do while true { place user input in buffer send service request receive reply pass reply to the user } leave application }
Most of the statements in Figure 1 are implemented with ATMI primitives. Placing user input in a buffer and passing the reply to the user are implemented with C language functions.
When client programs are ready to test, you use the buildclient(1)
command to compile and link edit them.
A client may send and receive any number of service requests
before leaving the application. These can be sent as a series of
request/response calls or, if it is important to carry state
information from one call to the next, a connection to a
conversational server can be set up. The logic within the client
program is about the same, but different ATMI primitives are
used.
Servers are processes that provide one or more services. They
continually check their message queue for service requests and
dispatch them to the appropriate service subroutines.
Applications combine their service subroutines with the main() that System/T provides in order to build server processes. This system supplied main() is a set of predefined functions. It performs server initialization and termination and allocates buffers to receive and dispatch incoming requests to service routines. All of this processing is transparent to the application.
Server and a service subroutine interaction can be summarized by the pseudo-code shown in Figure 2.
After some initialization a server allocates a buffer, waits until a request message is put on its message queue, dequeues the request, and dispatches it to a service subroutine for processing. If a reply is needed, the reply is considered part of request processing.
The conversational paradigm is somewhat different. Pseudo-code is shown in Figure 3.
The BEA TUXEDO System/T-supplied main() contains the code
needed to enroll as a server, advertise services, allocate
buffers, and dequeue request messages. The ATMI primitives are
used in service subroutines that process requests. When they are
ready to compile and test, service subroutines are link edited
with the server main() by means of the buildserver(1)
command to form an executable server.
The serially reusable architecture of servers is particularly significant if the operation requested by the user is logically divisible into several services, or several iterations of the same service. Such operations can be overlapped by having a server assume the role of a client and hand off part of the task to another server as part of fulfilling the original client's request. In such a capacity the server becomes a requester. Both clients and servers can be requesters. In fact, a client can only be a requester. The coding model for such a system is easily accomplished with the routines that are provided by ATMI.
A request/response server can also forward a request to
another server. This is different from becoming a requester. In
this case, the server does not assume the role of client since no
reply is expected by the server that forwards a request. The
reply is expected by the original client.
The Application-Transaction Monitor Interface is a reasonably compact set of primitives used to open and close resources, begin and end transactions, allocate and free buffers, and provide the communication between clients and servers. Table 1 summarizes them. Each routine is documented on its own page in the BEA TUXEDO Reference Manual: Section 3c
Group | Name | Operation |
---|---|---|
Application Interface | tpinit() | join an application |
tpterm() | leave an application | |
Buffer Management Interface | tpalloc() | allocate a buffer |
tprealloc() | re-size a buffer | |
tpfree() | free a buffer | |
tptypes() | get buffer type | |
Request/Response Communication Interface | tpcall() | send a request, wait for answer |
tpacall() | send request asynchronously | |
tpgetrply() | get reply after asynchronous call outstanding reply | |
tpcancel() | cancel communications handle for | |
tpgprio() | get priority of last request | |
tpsprio() | set priority of next request | |
Conversational Interface | tpconnect() | begin a conversation |
tpdiscon | end a conversation | |
tpsend() | send data in conversation | |
tprecv() | receive data in conversation | |
Unsolicited Notification Interface | tpnotify() | notify by client id |
tpbroadcast() | notify by name | |
tpsetunsol() | set unsolicited message handling routine | |
tpgetunsol() | get unsolicited message | |
tpchkunsol() | check for unsolicited messages | |
Transaction Management Interface | tpbegin() | begin a transaction |
tpcommit() | commit the current transaction | |
tpabort() | abort the current transaction | |
tpgetlev() | check if in transaction mode | |
Service Routine Template | tpservice() | start a service |
tpreturn() | end service routine | |
tpforward() | forward request and end service routine | |
Dynamic Adv Interface | tpadvertise() | advertise a service name |
tpunadvertise() | unadvertise a service name | |
Resource Mgr Interface | tpopen() | open a resource manager |
tpclose() | close a resource manager | |
Event Broker/Event Monitor Interface | tppost() | post an event |
tpsubscribe() | subscribe to an event | |
tpunsubscribe() | unsubscribe to an event |
In addition to ATMI's transaction management verbs, System/T also supports X/Open's TX Interface for defining and managing transactions. Because X/Open used ATMI's transaction demarcation verbs as the base for the TX Interface, the syntax and semantics of the TX Interface are quite similar to ATMI.
Table 2 introduces the routines in the TX Interface and highlights the main differences with their corresponding ATMI routines. For maximum portability, the TX routines can be used in place of the ATMI routines shown in Table 2.
TX Verbs | Corresponding ATMI Verbs | Main Differences |
---|---|---|
tx_begin | tpbegin | Timeout value not passed as argument to tx_begin. See tx_set_transaction_timeout. |
tx_close | tpclose | None |
tx_commit | tpcommit | tx_commit can optionally start a new transaction before it returns. This is known as a "chained" transaction. |
tx_info | tpgetlev | tx_info returns the settings of transaction characteristics set via the three tx_set_* routines. |
tx_open | tpopen | None |
tx_rollback | tpabort | tx_rollback supports chained transactions. |
tx_set_commit_return | tpscmt | None |
tx_set_transaction_control | None | Defines whether the application is using chained or unchained transactions. |
tx_set_transaction_timeout | tpbegin | Transaction timeout parameter separated from tx_begin. |
The TX interface requires that tx_open() be called before using any other TX verbs. Thus, even if a client or a server is not accessing an XA-compliant resource manager, it must call tx_open() before it can use tx_begin(), tx_commit(), and tx_rollback() to define transactions.
Figure 4 contains an example of how the TX Interface can be used to support chained transactions. Note that tx_begin() must be used to start the first of a series of chained transactions. Also, note that before calling tx_close(), the application must switch to unchained transactions so that the last tx_commit() or tx_rollback() does not start a new transaction.
tx_open(); tx_set_transaction_control(TX_CHAINED); tx_set_transaction_timeout(120); tx_begin(); do_forever { do work as part of transaction; if (no more work exists) tx_set_transaction_control(TX_UNCHAINED); if (work done was successful) tx_commit(); else tx_rollback(); if (no more work exists) break; } tx_close();
Messages are passed to servers in typed buffers. Why ``typed?'' Well, different types of data require different software to initialize the buffer, send and receive the data and perhaps encode and decode it, if the buffer is passed between heterogeneous machines. Buffers are designated as being of a specific type so the routines appropriate to the buffer and its contents can be invoked. These issues are typically not of concern to application developers, but more details can be found in buffer(3), tuxtypes(5), and typesw(5).
System/T provides nine buffer types for messages: STRING, CARRAY, VIEW, VIEW32, FML, FML32, X_OCTET, X_COMMON, and X_C_TYPE. Applications can define additional types as needed. Consult the manual pages referred to above and the BEA TUXEDO Administrator's Guide.
The STRING buffer type is used when the data is an array of characters that terminates with the null character.
The data in a CARRAY buffer is an undefined array of characters, any of which can be null. The CARRAY is not self-describing and the length must always be provided when transmitting this buffer type. The X_OCTET buffer type is equivalent to CARRAY.
The VIEW type is a C structure that the application defines and for which there has to be a view description file. Buffers of the VIEW type must have subtypes, which designate individual data structures. The X_C_TYPE buffer type is equivalent to VIEW. The X_COMMON buffer type is similar to VIEW but is used for both COBOL and C programs so field types should be limited to short, long, and string. The VIEW32 buffer type is similar to VIEW but allows for larger character fields, more fields, and larger overall buffers.
An FML buffer is a proprietary BEA TUXEDO System type of self-defining buffer where each data field carries its own identifier, an occurrence number, and possibly a length indicator. This type provides great flexibility at the expense of some processing overhead in that all data manipulation is done via FML function calls rather than native C statements.
The FML32 data type is similar to FML but allows for larger
character fields, more fields, and larger overall buffers.
If you are using the VIEW or FML buffer
types, some preliminary work is required to create view
description files or field table files. In the case of VIEWs,
a description file must exist and must be available to client and
server processes that use a data structure described in the VIEW.
For FML buffers, a field table file containing
descriptions of all fields that may be in the buffer must be
available.
There are two kinds of VIEW buffers. One is based
on an FML buffer. The other VIEW buffer is
independent; it is simply a C structure. Both types are described
in view description files and compiled with viewc(1), the BEA
TUXEDO System view compiler. We're going to talk first about the
FML variety.
BEA TUXEDO System FML is a family of functions, some of which convert an FML buffer into a C structure or vice versa. The C structure that is derived from the fielded buffer is referred to as an FML VIEW. The reason for converting FML buffers to C structures and back again is that while FML buffers provide data independence and convenience, they do involve processing overhead because they must be manipulated using FML function calls. C structures, while not providing flexibility, offer the performance required for lengthy manipulations on buffer data. If enough manipulation of the data is called for, you can improve the performance of your programs if you transfer fielded buffer data to C structures, operate on the data using normal C functions, and then put the data back into the FML buffer for storage or message transmission.
There are slight differences between a view description of an FML-based view and one that is independent of FML. Figure 5 shows a view description file with all of the available data types. The file is myview.v and the structure is based on an FML buffer. Note that the CARRAY1 field has a count of 2 occurrences and has the "C" count flag to indicate that an additional count element should be created in the structure so the application can indicate how many of the occurrences are actually being used. It also has the "L" length flag such that there is a length element (which occurs twice, once for each occurrence of the field) indicating how many of the characters the application has populated.
VIEW MYVIEW $ /* View structure */ #type cname fbname count flag size null float float1 FLOAT1 1 - - 0.0 double double1 DOUBLE1 1 - - 0.0 long long1 LONG1 1 - - 0 short short1 SHORT1 1 - - 0 int int1 INT1 1 - - 0 dec_t dec1 DEC1 1 - 9,16 0 char char1 CHAR1 1 - - '\0' string string1 STRING1 1 - 20 '\0' carray carray1 CARRAY1 2 CL 20 '\0' END
Field table files are always required when using FML records, including the use of FML-dependent VIEWS. A field table file maps the logical name of a field in an FML buffer to a field identifier that uniquely identifies the field.
An example that could be used with the view shown in Figure 5 is shown in Figure 6.
# name number type flags comments FLOAT1 110 float - - DOUBLE1 111 double - - LONG1 112 long - - SHORT1 113 short - - INT1 114 long - - DEC1 115 string - - CHAR1 116 char - - STRING1 117 string - - CARRAY1 118 carray - -
Figure 7 shows the view description file, similar to the example in Figure 5, but for a VIEW independent from FML.
$ /* View data structure */ VIEW MYVIEW #type cname fbname count flag size null float float1 - 1 - - - double double1 - 1 - - - long long1 - 1 - - - short short1 - 1 - - - int int1 - 1 - - - dec_t dec1 - 1 - 9,16 - char char1 - 1 - - - string string1 - 1 - 20 - carray carray1 - 2 CL 20 - END
Note that in this view description, the format is similar to
the FML-dependent view, except that the columns fbname
and null in the file are ignored by the view compiler.
These columns are not relevant when an FML buffer does
not stand behind the view, but it is necessary to place some
value (a dash, for example) in these columns to serve as a
placeholder.
The C float and double fields correspond to COBOL COMP-1 and COMP-2 respectively.
The field types long and short correspond to S9(9) COMP-5 and S9(4) COMP-5 respectively in COBOL. (The use of COMP-5 is for use with MicroFocus COBOL so that the COBOL integer fields match the data format of the corresponding C fields; the data type for VS COBOL II would simply be COMP.)
The dec_t type maps to a COBOL COMP-3 packed decimal field. Packed decimals exist in the COBOL environment as two decimal digits packed into each byte with the low-order half byte used to store the sign. The length of a packed decimal may be 1 to 9 bytes with storage available for 1 to 17 digits and a sign. The dec_t field type is supported within the VIEW definition for the conversion of packed decimals between the C and the COBOL environments. The dec_t field is defined in a VIEW with a size of two numbers separated by a comma. The number to the left of the comma is the total number of bytes that the decimal occupies in COBOL. The number to the right is the number of digits to the right of the decimal point in COBOL. The formula for conversion to the COBOL declaration is:
dec_t(m, n) <=> S9(2*m-(n+1),n)COMP-3
For example, say a size of 6,4 is specified in the VIEW. There are 4 digits to the right of the decimal point, 7 digits to the left, and the last half byte stores the sign. The COBOL application programmer would represent this as 9(7)V9(4), with the V representing the decimal point between the number of digits to each side. Note that there is no dec_t type supported in FML; if FML-dependent VIEWs are used, then the field must be mapped to a C type in the VIEW file (for instance, the packed decimal can be mapped to an FML string field and the mapping functions do the conversion between the formats).
A decimal field can be initialized and accessed in C using the
functions described in the decimal(3c) manual pages.
View description files are source files. To use the view in a program, you need a header file that defines the structures in the view. You can create a header file from the myview.v view description file by invoking the view compiler, viewc(1). viewc creates two files. One is the header file and the other is the binary version of the source description file, myview.V. This binary file must be in the environment when a VIEW buffer is allocated. For an FML-dependent VIEW, the compiler is invoked as follows:
viewc myview.v
The header file it creates from the myview.v description file is shown in Figure 8.
struct MYVIEW { float float1; double double1; long long1; short short1; int int1; dec_t dec1; char char1; char string1[20]; unsigned short L_carray1[2]; /* length array of carray1 */ short C_carray1; /* count of carray1 */ char carray1[2][20]; };
To compile a view description of an independent view, use the -n option on the command line as follows:
viewc -n myview.v
The header file created is the same with or without the -n option. Header files for views must be brought into client programs and service subroutines with #include statements.
For use with VIEW32, the viewc32 command
should be used.
To create a field header file from the field table file, you use the mkfldhdr(1) command. For example:
mkfldhdr myview.flds
creates a file called myview.flds.h that can be #include'd in a service routine or client program so you can refer to fields by their symbolic names. The myview.flds.h header file produced by mkfldhdr from this field table file is shown in Figure 9.
/* fname fldid */ /* ----- ----- */ #define FLOAT1 ((FLDID)24686) /* number: 110 type: float */ #define DOUBLE1 ((FLDID)32879) /* number: 111 type: double */ #define LONG1 ((FLDID)8304) /* number: 112 type: long */ #define SHORT1 ((FLDID)113) /* number: 113 type: short */ #define INT1 ((FLDID)8306) /* number: 114 type: long */ #define DEC1 ((FLDID)41075) /* number: 115 type: string */ #define CHAR1 ((FLDID)16500) /* number: 116 type: char */ #define STRING1 ((FLDID)41077) /* number: 117 type: string */ #define CARRAY1 ((FLDID)49270) /* number: 118 type: carray */
For use with FML32, the mkfldhdr32
command should be used.
If you are using FML or VIEW typed buffers, #include the header files generated from their field table files or view description files as described above.
In addition, all System/T application programs must #include the atmi.h header file.
If you are using FML buffers, #include
the fml.h header file in your programs.
Environment variables needed either for clients or service routines associated with a server can be set in ENVFILEs that are specified in the configuration file. The environment variables that might have to be set for field tables and view descriptions, for example, are summarized in Table 3.
Variable | Contains | Used By |
---|---|---|
FIELDTBLS | comma-separated list of field table file names | client and server processes using FML buffers |
FLDTBLDIR | colon-separated list of directories to be used to find field table files with relative file names | client and server processes using FML buffers |
VIEWFILES | comma separated list of binary view description files | client and server processes using VIEW buffers |
VIEWDIR | colon-separated list of directories to be used to find binary view description files | client and server processes using VIEW buffers |
For the FML32 and VIEW32 record types, the environment variables are suffixed with "32", that is, FLDTBLDIR32, FIELDTBLS32, VIEWFILES32, and VIEWDIR32.
The CC and CFLAGS environment variables are used by the buildclient(1) and buildserver(1) commands. You may want to set them in your environment to make compilation of clients and servers more convenient. Set CC to the command that invokes the C compiler. It defaults to cc. Set CFLAGS to the link edit flags you may want to use on the compile command line. Setting this variable is optional.
The location of the BEA TUXEDO System binary files must be
known to your application. It is the convention to install the
BEA TUXEDO System software under a root directory whose location
is specified in the TUXDIR environment variable. $TUXDIR/bin
must be included in your PATH in order for your
application to locate the executables for BEA TUXEDO System
commands.
The configuration file specifies the configuration of an application to System/T. For a BEA TUXEDO System/T application in production, it is the responsibility of the System/T administrator to set up a configuration file that defines the application. In the development environment, the responsibility may be delegated to application programmers to create their own.
If you are faced with the task of creating a configuration file, here are some suggestions:
The configuration file is an ASCII file. To make it usable,
you have to run tmloadcf(1)
to convert it to a binary file. The TUXCONFIG environment
variable must be set to the pathname for the binary file, and
exported.
The bulletin board is the BEA TUXEDO System/T name for a group of data structures in a segment of shared memory that is allocated from information stored in TUXCONFIG when the application is booted. Both client and server processes attach to the bulletin board. Part of the bulletin board associates service names with the queue address of servers that advertise that service. Clients send their requests to the name of the service they want to invoke, rather than to a specific address.
All processes that are part of a System/T application share
this IPC resource.
Execute the tmboot(1) command to bring up an application. The command gets the IPC resources needed by the application, starts administrative processes and the application servers.
When it is time to bring the application down, execute the tmshutdown(1) command. tmshutdown stops the servers and releases the IPC resources used by the application, except any that might be used by the database resource manager.