This chapter covers topics related to buffers in TUXEDO System. Major sections of the chapter are:
Messages and data passed between clients and services in TUXEDO System are contained in buffers. The nine message buffer types defined in the software as delivered are:
FML types depend on being able to find field table names in a file located through the FLDTBLDIR and FIELDTBLS environment variables.
VIEW, X_C_TYPE, and X_COMMON types depend on being able to find binary view descriptions in a file located through the VIEWDIR and VIEWFILES environment variables.
The correct place for these variables to be assigned is the *MACHINES ENVFILE. Assigning or overriding them in the *SERVERS ENVFILE can make them not available to processes that require them.
The message buffer types are defined in tm_typesw,
which is described in the tuxtypes(5) manual page. You will see in
later sections of this chapter how it might be to your advantage
to change the tm_typesw so it contains only buffer
types specifically needed by a given server.
The following parameters, while optional, need to be
considered.
MAXBUFTYPE in the *RESOURCES section specifies the maximum
number of buffer types that can be accommodated in the buffer
type table of the bulletin board. The default is 16 entries.
Since the number provided with the System/T software is nine, the
default should be adequate. However, if you need to define
several new buffer types for your application, you may need to
increase this parameter.
MAXBUFSTYPE in the *RESOURCES section specifies the maximum
number of buffer subtypes that can be accommodated in the buffer
subtype table in the bulletin board. The default is 32. Buffer
subtypes are used to specify specific views (structures); they
are allowable only for VIEW (X_C_TYPE, X_COMMON) type buffers. If
your application uses VIEW type buffers, 32 subtypes is not
excessively high. Also, buffer types you define specifically for
your application can have subtypes. In such cases, this parameter
may need to be increased.
The BUFTYPE parameter occurs in both the *SERVICES and the *ROUTING section. The format of the parameter is the same in both sections. The value for the parameter may be the same in both sections or it may be different. For the present, we will describe how the parameter is specified. In the section on "Data Dependent Routing" later in this chapter, we will describe the way the value may differ in the *ROUTING section.
The syntax of the BUFTYPE parameter is the same in both the *SERVICES and *ROUTING sections. Here is the syntax:
BUFTYPE="type1[:subtype1[,subtype2...]];type2[:subtype3[,...]]]..."
The parameter provides a list of buffer types and subtypes accepted by the associated service. The following limitations apply:
total parameter length | maximum 256 characters |
type/subtype combinations | maximum 32 |
length of type name | maximum 8 characters |
length of subtype name | maximum 16 characters |
Each service (and client) has access to one type switch, tm_typesw. It can be the one delivered with the software (found in libtux.a after the system is built), or it can be a modified version of that switch (and we'll see how that is done in "Defining Your Own Buffer Types" later in this chapter). Valid types are limited to those named in the configuration file for this service. If BUFTYPE="ALL" is specified, it means that the service supports all buffer types named in its tm_typesw. Omitting the parameter is the equivalent of specifying "ALL."
In the case of the VIEW type, valid subtypes are
specific structures for which a binary view description exists or
"*", meaning that the service accepts all subtypes
defined in its VIEWFILES.
This section and the next ("Data Conversion") may cross the border into system programming. While system programming may not be among the duties of the System/T administrator, it is included in this guide because it is certainly part of putting the application together.
The responsibility for the code that manipulates buffers, allocating and freeing the space, sending and receiving messages, belongs to the application programmers. As such, it is documented in the BEA TUXEDO Programmer's Guide For applications where the default buffer types do not meet the needs of the application, other buffer types can be defined and new routines written to be incorporated into the buffer type switch.
To accomplish the definition of other buffer types, the following steps must be followed:
If your application is using static libraries, then a customized buffer type switch will make it necessary for you to build a custom server to link in your new type switch. See buildwsh(1), TMQUEUE(5), or TMQFORWARD(5) for details.
We will deal with the steps listed above to define a new buffer type in a shared object environment in the sections that follow, but first let's look at the buffer switch that comes with the System/T software. Figure 1 shows the switch delivered with the system.
#include <stdio.h> #include <tmtypes.h> /* Initialization of the buffer type switch */ static struct tmtype_sw_t tm_typesw[] = { { "CARRAY", /* type */ "", /* subtype */ 0 /* dfltsize */ }, { "STRING", /* type */ "", /* subtype */ 512, /* dfltsize */ NULL, /* initbuf */ NULL, /* reinitbuf */ NULL, /* uninitbuf */ _strpresend, /* presend */ NULL, /* postsend */ NULL, /* postrecv */ _strencdec, /* encdec */ NULL, /* route */ NULL, /* filter */ NULL /* format */ }, { "FML", /* type */ "", /* subtype */ 1024, /* dfltsize */ _finit, /* initbuf */ _freinit, /* reinitbuf */ _funinit, /* uninitbuf */ _fpresend, /* presend */ _fpostsend, /* postsend */ _fpostrecv, /* postrecv */ _fencdec, /* encdec */ _froute, /* route */ _ffilter, /* filter */ _fformat /* format */ }, { "FML32", /* type */ "", /* subtype */ 1024, /* dfltsize */ _finit32, /* initbuf */ _freinit32, /* reinitbuf */ _funinit32, /* uninitbuf */ _fpresend32, /* presend */ _fpostsend32, /* postsend */ _fpostrecv32, /* postrecv */ _fencdec32, /* encdec */ _froute32, /* route */ _ffilter32, /* filter */ _fformat32 /* format */ }, { "VIEW", /* type */ "*", /* subtype */ 1024, /* dfltsize */ _vinit, /* initbuf */ _vreinit, /* reinitbuf */ NULL, /* uninitbuf */ _vpresend, /* presend */ NULL, /* postsend */ NULL, /* postrecv */ _vencdec, /* encdec */ _vroute, /* route */ _vfilter, /* filter */ _vformat /* format */ }, { "VIEW32", /* type */ "*", /* subtype */ 1024, /* dfltsize */ _vinit32, /* initbuf */ _vreinit32, /* reinitbuf */ NULL, /* uninitbuf */ _vpresend32, /* presend */ NULL, /* postsend */ NULL, /* postrecv */ _vencdec32, /* encdec */ _vroute32, /* route */ _vfilter32, /* filter */ _vformat32 /* format */ }, { "X_OCTET", /* type */ "", /* subtype */ 0, /* dfltsize */ }, { "'X','_','C','_','T','Y','P','E'", /* type */ "*", /* subtype */ 1024, /* dfltsize */ _vinit, /* initbuf */ _vreinit, /* reinitbuf */ NULL, /* uninitbuf */ _vpresend, /* presend */ NULL, /* postsend */ NULL, /* postrecv */ _vencdec, /* encdec */ _vroute, /* route */ _vfilter, /* filter */ _vformat /* format */ }, { "'X','_','C','O','M','M','O','N'", /* type */ "*", /* subtype */ 1024, /* dfltsize */ _vinit, /* initbuf */ _vreinit, /* reinitbuf */ NULL, /* uninitbuf */ _vpresend, /* presend */ NULL, /* postsend */ NULL, /* postrecv */ _vencdec, /* encdec */ _vroute, /* route */ _vfilter, /* filter */ _vformat /* format */ }, { "" } };
It might help to understand what is shown in Figure 1 if we also include the declaration of the buffer type structure. That is shown in Figure 2.
/* The following definitions are in $TUXDIR/include/tmtypes.h */ #define TMTYPELEN 8 #define TMSTYPELEN 16 struct tmtype_sw_t { char type[TMTYPELEN]; /* type of buffer */ char subtype[TMSTYPELEN]; /* sub-type of buffer */ long dfltsize; /* default size of buffer */ /* buffer initialization function pointer */ int (_TMDLLENTRY *initbuf) _((char _TM_FAR *, long)); /* buffer re-initialization function pointer */ int (_TMDLLENTRY *reinitbuf) _((char _TM_FAR *, long)); /* buffer un-initialization function pointer */ int (_TMDLLENTRY *uninitbuf) _((char _TM_FAR *, long)); /* pre-send buffer manipulation func pointer */ long (_TMDLLENTRY *presend) _((char _TM_FAR *, long, long)); /* post-send buffer manipulation func pointer */ void (_TMDLLENTRY *postsend) _((char _TM_FAR *, long, long)); /* post-receive buffer manipulation func pointer*/ long (_TMDLLENTRY *postrecv) _((char _TM_FAR *, long, long)); /* encode/decode function pointer */ long (_TMDLLENTRY *encdec) _((int, char _TM_FAR *, long, char _TM_FAR *, long)); /* routing function pointer */ int (_TMDLLENTRY *route) _((char _TM_FAR *, char _TM_FAR *, char _TM_FAR *, long, char _TM_FAR *)); /* buffer filtering function pointer */ int (_TMDLLENTRY *filter) _((char _TM_FAR *, long, char _TM_FAR *, long)); /* buffer formatting function pointer */ int (_TMDLLENTRY *format) _((char _TM_FAR *, long, char _TM_FAR *, char _TM_FAR *, long)); /* this space reserved for future expansion */ void (_TMDLLENTRY *reserved[10]) _((void)); };
Figure 1 shows the initialization of the buffer type switch. The nine default buffer types are shown, followed by a field for naming a subtype. Except for the VIEW (and equivalently X_C_TYPE and X_COMMON) type, subtype is null. The subtype for VIEW is given as "*", which means that the default VIEW type puts no constraints on subtypes; all subtypes of type VIEW are processed in the same manner.
The next field gives the default (minimum) size of the buffer. For the CARRAY (and equivalently X_OCTET) type this is given as 0, which means that the routine that uses a CARRAY buffer type must tpalloc() enough space for the expected CARRAY.
For the other types, System/T tpallocs the space shown in the dfltsize field of the entry (unless the size argument of tpalloc specifies a larger size).
The remaining eight fields of entries in the buffer type switch contain the names of switch element routines. These routines are described in the buffer(3I) page in the BEA TUXEDO Reference Manual: Section 3c. The names give you a clue to what the routine does (the comments in Figure 2 also help); for example, _fpresend on the FML type is a pointer to a routine that manipulates the buffer before sending it. If no presend manipulation is needed, a NULL pointer may be specified. NULL means no special handling is required, and the default action should be taken. See buffer(3) for the details.
It is particularly important that you notice the NULL entry at
the end of the switch. Any changes that are made must always
leave the NULL entry at the end of the array.
Presumably if the application is defining new buffer types it is because of some special processing need. For example, let's assume the application has a recurring need to compress data before sending a buffer to the next process. The application could write a presend routine. The declaration for the presend routine is shown in Figure 3.
long presend(ptr, dlen, mdlen) char *ptr; long dlen, mdlen;
ptr is a pointer to the application data buffer, dlen is the data's length as passed into the routine. mdlen is the size of the buffer in which the data resides.
The data compression that takes place within your presend routine is the responsibility of the system programmer for your application.
On completion the routine should return the new, hopefully shorter length of the data to be sent (in the same buffer), or a -1 to indicate failure.
The name given to your version of the presend routine can be any identifier accepted by the C compiler. For example, we'll pretend that we've named it _mypresend.
Your _mypresend compression routine will probably
mean that a corresponding _mypostrecv routine will be
needed to decompress the data at the receiving end. Follow the
template shown in the buffer(3) manual page.
After the new switch element routines have been written and successfully compiled, the new buffer type must be added to the buffer type switch. The recommended way of doing this is to make a copy of $TUXDIR/lib/tmtypesw.c. This is the source code version of the default buffer type switch. Give your copy a name with a .c suffix; for example, mytypesw.c. Edit your copy to add the new type. The type name can be up to 8 characters in length. Subtype can be null ("") or a string of up to 16 characters. Enter the names of your new switch element routines in the appropriate locations, including the extern declarations. Figure 4 contains an example.
#include <stdio.h> #include <tmtypes.h> /* Customized the buffer type switch */ static struct tmtype_sw_t tm_typesw[] = { { "SOUND", /* type */ "", /* subtype */ 50000, /* dfltsize */ snd_init, /* initbuf */ snd_init, /* reinitbuf */ NULL, /* uninitbuf */ snd_cmprs, /* presend */ snd_uncmprs, /* postsend */ snd_uncmprs /* postrecv */ }, { "FML", /* type */ "", /* subtype */ 1024, /* dfltsize */ _finit, /* initbuf */ _freinit, /* reinitbuf */ _funinit, /* uninitbuf */ _fpresend, /* presend */ _fpostsend, /* postsend */ _fpostrecv, /* postrecv */ _fencdec, /* encdec */ _froute, /* route */ _ffilter, /* filter */ _fformat /* format */ }, { "" } };
In the example in Figure 4, we have added a new type, SOUND, and removed the entries for VIEW, X_OCTET, X_COMMON, and X_C_TYPE. Removing these entries was done to demonstrate that you can remove any of the entries in the default switch that are not needed. Note that the array still ends with the NULL entry.
An alternative to defining a new buffer type is to redefine an
existing type. Suppose, for the sake of argument, that the data
compression for which you defined the buffer type MYTYPE was done
on strings. You could substitute your new switch element
routines, _mypresend and _mypostrecv for
the two _dfltblen routines in type STRING.
The buffer type switch is stored in a shared object to simplify installation. Note that some platforms use the term "shared library" or "dynamic link library," but the functionality is similar. This section describes how to make all TUXEDO processes in your application aware of the modified buffer type switch. This includes application servers and clients, as well as TUXEDO-provided servers and utilities.
The first step is to copy and modify $TUXDIR/lib/tmtypesw.c, as described above. If additional functions are required, they could be stored either in tmtypesw.c or in a separate C source file.
The second step is to compile with the appropriate flags for shared objects. On UnixWare, the command would be:
$ cc -I$TUXDIR/include -KPIC -c tmtypesw.c
The third step is to link together all object files to produce a shared object. On UnixWare, the command would be:
$ cc -G -h libbuft.so.50 tmtypesw.o -o libbuft.so.50
The final step is to copy libtux.so.50 from the current directory to a directory where it will be visible to applications, and processed ahead of the default TUXEDO-supplied shared object. $APPDIR or $TUXDIR/lib are the recommended installation directories.
On various platforms the buffer type switch shared object has different names to conform to operating system conventions:
Unix (most SVR4) libbuft.so.50 HP-UX libbuft.sl Sun OS libbuft.so.5.0 Windows (16 bit) wbuft.dll Windows (32 bit) wbuft32.dll OS/2 (16 bit) obuft.dll OS/2 (32 bit) obuft.dll
Consult your platform's manual pages and software development
documentation for instructions on how to build a shared object
library. It is possible to statically link a new buffer type
switch in every client and server process, but this is more
error-prone and not as efficient.
In Windows, assuming you have modified tmtypesw.c as described above, the commands that make the modified buffer type switch available to your application are as follows:
For MS Visual C++
CL -AL -I..\sysinclu -I..\include -Aw -G2swx -Zp -D_TM_WIN \ -D_TMDLL -Od -c TMTYPESW.C LINK /CO /ALIGN:16 TMTYPESW.OBJ, WBUFT.DLL, NUL, WTUXWS /SE:250 /NOD \ /NOE LIBW LDLLCEW, WBUFT.DEF RC /30 /T /K WBUFT.DLL
In an earlier chapter (Chapter 4, ``TUXEDO System Networks'') the TYPE parameter of the *MACHINES section was discussed. If you recall, the purpose of the TYPE parameter is to group together machines that have the same form of data representation (and use the same compiler) so that data conversion is done on messages going between machines of different TYPEs. For the default buffer types, data conversion between unlike machines is transparent to the user (and to the administrator and programmer, for that matter).
If your application has defined new buffer types to be used for messages that move between machines that have different data representation schemes, you also have to write new encode/decode routines to be incorporated into the buffer type switch.
Here is how you should proceed when writing your own data conversion routines:
The encode/decode routines are called only when System/T
determines that data is being sent between two machines that are
not of the same TYPE.
Routing criteria can be specified for service requests using FML buffers or VIEW (X_C_TYPE, X_COMMON) buffers. The information in the *ROUTING section of UBBCONFIG gives the following information:
The format of the entry line is:
CRITERION [parameters]
where CRITERION is the name associated with the ROUTING
parameter in the *SERVICES section. The same CRITERION can
not appear more than once for the same BUFTYPE. Parameters are
FIELD, RANGES, and BUFTYPE.
The FIELD parameter names the field used to determine routing.
The field must be either an FML field or a VIEW field. If it is
an FML field, it must be one in an FML field table available to
the server through the FLDTBLDIR and FIELDTBLS variables (which
should be set in an ENVFILE). If it is a VIEW field, it must be
one in a binary view file available to the server through the
VIEWDIR and VIEWFILES variables (which should be set in an
ENVFILE).
The RANGES parameter is used to direct the service request to a particular server group based on the value in the designated routing field. The parameter takes a string of up to 256 characters. The string must be enclosed in double quotes.
The format of the parameter is as follows:
RANGES = "range1:group1,range2:group2,range3:group3..."
The RANGES parameter value follows these rules:
Here are two examples:
for a criterion named ROUTE_1 routing is based on a numeric field named router; for a range from any negative number to positive 3 the target is DBG1, for 4 to 6 the target is DBG2, for 7 and above the target is DBG3. The entry would look like this:
ROUTE_1 FIELD=router RANGES="MIN-3:DBG1,4-6:DBG2,7-MAX:DBG3"
ROUTE_2 FIELD=lastname RANGES="'AAA'-'FFF':SVR8,'JONES':SVR7,'*':SVR9"
In FML, any field can have multiple occurrences. For routing purposes, if the buffer type is FML, the FIELD is always understood to be the first (zeroth) occurrence of the field.
In VIEWS, a field may be an array. For routing
purposes, if the buffer type is VIEW and the FIELD type
is an array, it is assumed that the reference is always
to the x[0] element.
The BUFTYPE parameter for the *ROUTING section associates a buffer type specification with the CRITERION of the entry. The value of the BUFTYPE parameter must be an FML, VIEW, X_COMMON, or X_C_TYPE buffer specified in the *SERVICES section for the service that uses this routing statement. However, the same CRITERION value can be specified more than once in the *ROUTING section with different BUFTYPE values each time. In other words, the list of buffer types specified in the *SERVICES section can be split over multiple BUFTYPE parameters in the *ROUTING section. This might well be the case where the buffer type is VIEW; FIELD and RANGES might change for different subtypes.
If multiple buffer types (or subtypes) are specified, the data types of the routing fields must be the same in all cases.
Note: In the current release, data dependent routing is done only for FML and VIEW (X_COMMON, X_C_TYPE) buffer types. It is not done for STRING or CARRAY (X_OCTET) buffer types or for application-written buffer types.
Figure 5 shows the data dependent routing specifications for bankapp.
# *ROUTING # ACCOUNT_ID FIELD=ACCOUNT_ID BUFTYPE="FML" RANGES="10000-59999:BANKB1, 60000-109999:BANKB2, *:*" BRANCH_ID FIELD=BRANCH_ID BUFTYPE="FML" RANGES="1-5:BANKB1, 6-10:BANKB2, *:*" b_id FIELD=b_id BUFTYPE="VIEW:aud" RANGES="1-5:BANKB1, 6-10:BANKB2, *:*"