Configuring System/T Typed Buffers

This chapter covers topics related to buffers in TUXEDO System. Major sections of the chapter are:

TUXEDO System/T Buffer Types

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:

STRING
a null-terminated character array.
CARRAY
an array of uninterpreted binary data that is neither encoded nor decoded during transmission; it may contain null characters.
FML
a special TUXEDO System type in which each field carries its own definition. FML is used by servers provided in the sample application, bankapp, and by the System/T-provided client programs, mio(1) and ud(1).
FML32
similar to the FML type, except that 32-bit identifiers and offsets are used internally. There is modestly more overhead than with FML buffers. This permits FML's limits of 64K per buffer and 8K total field identifiers to be exceeded.
VIEW
a C-structure. The VIEW type is the only one provided by the TUXEDO System that can have subtypes; each subtype is a particular structure. The VIEW structure may be one mapped from an FML buffer by means of viewc(1).
VIEW32
similar to the view type, except that 32-bit FML field identifiers are associated with VIEW32 structure elements.
X_OCTET
equivalent to CARRAY.
X_C_TYPE
equivalent to VIEW.
X_COMMON
equivalent to VIEW but this buffer type is used for both COBOL and C; only short, long, and character fields should be used.

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.

Configuration File Parameters for Buffers

The following parameters, while optional, need to be considered.

MAXBUFTYPE in *RESOURCES

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 *RESOURCES

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.

BUFTYPE in *SERVICES and *ROUTING

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

BUFTYPE in *SERVICES

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.

Defining Your Own Buffer Types

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:

  1. Code any switch element routines that may be required.
  2. Add your new types and the names of your buffer management modules to tm_typesw.
  3. Build a new shared object containing your updated buffer type switch and associated functions.
  4. Install your new shared object so that all servers, clients, and TUXEDO-provided executables will dynamically load it at runtime.

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.

Figure 1: Default Buffer Type Switch


#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.

Figure 2: Buffer Type Structure


/* 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.

Coding Switch Element Routines

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.

Figure 3: The Semantics of the Presend Switch Element


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.

Adding a New Buffer Type to tm_typesw

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.

Figure 4: Adding a New Type to the Buffer Switch


#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.

Compiling and Linking Your New tm_typesw

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.

Compiling and Linking Your New tm_typesw for Windows

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

Data Conversion

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:

  1. Use the semantics of the _tmencdec routine shown in the buffer(3) manual page. buffer(3) That is, code your routine so that it uses the same arguments and returns the same values on success or failure as _tmencdec.
  2. Follow the procedure shown above for building servers that have services that use your new buffer type.

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.

Specifying Data Dependent Routing

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.

FIELD

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).

RANGES

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"
  • for a criterion named ROUTE_2 routing is based on a string type field named lastname; for a range of AAA to FFF the target is SVR8, for a range of JONES the target is SVR7, for any other value the target is SVR9. The entry would look like this:
    
    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.

    BUFTYPE in *ROUTING

    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.

    Example

    Figure 5 shows the data dependent routing specifications for bankapp.

    Figure 5: *ROUTING Section, 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,
    			*:*"