by Paco Gómez
This article describes how to use BEA Tuxedo with PowerBuilder. The versions covered are BEA Tuxedo 8.1 and Sybase PowerBuilder 10.0. While Tuxedo and PowerBuilder are veteran products, the upgrade of legacy middleware to SOA is renewing the interest in integrating Tuxedo with PowerBuilder and other 4GL tools.
For example, DCE/RPC-based client/server applications can be easily upgraded using Tuxedo ATMI as the communication layer. The business logic C server code and the presentation PowerBuilder client code doesn't need to be changed; only the communication interface must be changed. This article describes how this can be accomplished.
To illustrate how to integrate PowerBuilder (PB) with Tuxedo, a simple example is provided. The example consists of a PB client application (pbclient) that calls a Tuxedo ATMI server (txserver). The client application uses an FML buffer to send a string and a long number to the server. The server converts the string to uppercase, calculates a random number n between 1 and the received number, and sends the converted string back to the client in n records inside the outbound FML buffer.
The example shows how to pass different data types between PB and Tuxedo as well as how to deal with dynamic structures in PB representing variable length data received from Tuxedo.
The source code of the Tuxedo server and the PowerBuilder client applications are included with this article.
The architecture of our example scenario is as follows. The PB client uses the Tuxedo Workstation (WS) component to invoke the Tuxedo server through the ATMI API. The PB client makes calls to the ATMI and FML32 APIs declared in PB as external functions. The calls are dynamically linked to Tuxedo/WS DLL libraries (LIBWSC.DLL and LIBFML32.DLL) at runtime. Figure 1 is a high-level diagram of the example.
Figure 1: The architecture describing the example scenario
The Tuxedo server (txserver) is a standard ATMI server using FML32 buffers for inbound and outbound data.
To use the ATMI and FML32 functions in PB, these need to be declared as Global External Functions, as shown in Figure 2.
Figure 2: Declared Global External Functions in PowerBuilder
It is very important to declare these functions with the correct signature to avoid compilation or runtime errors. The data types of the arguments must correspond with the data types as declared in the ATMI and FML32 functions definitions. Here are some typical correspondences and rules:
The PowerBuilder help documentation provides additional details about other data types.
Here is the complete listing of external function declaration:
FUNCTION int tpinit(long tpinfobuf) LIBRARY "LIBWSC.DLL" FUNCTION int gettperrno() LIBRARY "LIBWSC.DLL" FUNCTION string tpsterror(int errnum) LIBRARY "LIBWSC.DLL" FUNCTION int tpterm() LIBRARY "LIBWSC.DLL" //int tpcall(char *svc, char *idata, long ilen, char **odata, long *olen, long flags) FUNCTION int tpcall(string svc, long idata, long ilen, ref long odata, ref long olen, long flags) LIBRARY "LIBWSC.DLL" ALIAS FOR "tpcall;ansi" //char * tpalloc(char *type, char *subtype, long size) FUNCTION long tpalloc(string theType, string theSubType, long theSize) LIBRARY "LIBWSC.DLL" ALIAS FOR "tpalloc;ansi" //int Fadd32(FBFR32 *fbfr, FLDID32 fieldid, char *value, FLDLEN32 len) FUNCTION int Fadd32String(long fbfr, long fieldid, ref string value, long len) LIBRARY "LIBFML32.DLL" ALIAS FOR "Fadd32;ansi" FUNCTION int Fadd32Long(long fbfr, long fieldid, ref long value, long len) LIBRARY "LIBFML32.DLL" ALIAS FOR "Fadd32;ansi" //int Fget32(FBFR32 *fbfr, FLDID32 fieldid, FLDOCC32 oc, char *loc, FLDLEN32 *maxlen) FUNCTION int Fget32String(long fbfr, long fieldid, long oc, ref string loc, ref long maxlen) LIBRARY "LIBFML32.DLL" ALIAS FOR "Fget32;ansi" FUNCTION int Fget32Long(long fbfr, long fieldid, long oc, ref long loc, ref long maxlen) LIBRARY "LIBFML32.DLL" ALIAS FOR "Fget32;ansi" //void tpfree(char *ptr) SUBROUTINE tpfree(long buffer) LIBRARY "LIBWSC.DLL"
Fget32 functions declare as pointer to a char (
char *) the argument with the value to add or get to and from the buffer. While this is valid in C, PowerBuilder will throw a compilation error if the function argument is declared with the
ref long data type and then, for example, is called with a string as the actual parameter.
To overcome this issue we can take advantage of the PowerBuilder feature of declaring aliases for external functions. With this PB feature, we can declare an external function with a name that doesn’t need to be the same as the name exported by the DLL. In fact, we can create different aliases for the same function. Each alias declared will have a different name and a different signature to avoid compilation errors, but they all refer to the same DLL function. At runtime, PB will pass the data back and forth to the FML32 function in the right format.
In our example,
Fadd32 has two aliases,
Fadd32Long. The original
char * value argument is declared as “ref string” in the first alias and as
ref long in the second alias. When adding a string or a long value to the FML32 buffer, use one or the other to avoid any compilation or runtime issues.
The following program listing corresponds to the
clicked action of the
call txserver button in the PowerBuilder client application. The code shows how to work with the FML32 buffer and how to call the server with the
For convenience, the constants representing FML32 fields (
OUT_STR) are explicitly declared in the Global Variables section in PB, with the values generated by the
int result, n long fbuf, input_long, output_long, max_str_len, cnt, len string input_str, output_str string string_array max_str_len = 64 * 1024 output_str = space(max_str_len) input_str = sle_1.text input_long = long(sle_2.text) //this call could go in a global initialization result = tpinit(0) fbuf = tpalloc("FML32", "", 1024) result = Fadd32String(fbuf, IN_STR, input_str, 0) result = Fadd32Long(fbuf, IN_LONG, input_long, 0) result = tpcall("txserver", fbuf, 0, fbuf, len, 0) result = Fget32Long(fbuf, OUT_LONG, 0, output_long, len) messages.text = "" for n = 1 to output_long len = max_str_len result = Fget32String(fbuf, OUT_STR, (n - 1), output_str, len) string_array[n] = output_str messages.text = messages.text + string(n) + ": [" + string_array[n] + "] :: " next tpfree(fbuf) //this call could go in a global finalization result = tpterm()
This code also illustrates how Tuxedo and PowerBuilder cooperate to handle arbitrary length data, like strings or array of strings. As mentioned earlier, PowerBuilder has to manage the memory used by a string, therefore the string has to be initialized in PowerBuilder with enough space to store the data retrieved from the FML32 buffer with the
Fget32String function. In the code, the string is initialized with 64k blank spaces. The string is then passed by reference to the
max_str_len = 64 * 1024 output_str = space(max_str_len) ... Len = max_str_len result = Fget32String(fbuf, OUT_STR, (n - 1), output_str, len)
PB supports arrays of variable size. This data structure is appropriate to store an arbitrary number of fields of the same type received in a FML32 buffer. We just have to let PB handle the dynamic grow of the array, as in the example:
for n = 1 to output_long len = max_str_len result = Fget32String(fbuf, OUT_STR, (n - 1), output_str, len) string_array[n] = output_str next
This screenshot shows the PB client application running:
Figure 3. The PB client application running
Before running the client application, the following environment variables need to be set:
WSNADDR - the network address of the WSL to contact, for example: WSNADDR=//10.1.1.25:4444
TUXDIR - the Tuxedo installation directory
PATH - add %TUXDIR%\bin to the PATH variable
This article describes the main issues to consider when using Tuxedo with PowerBuilder. Tuxedo ATMI and FML APIs are declared as external functions, dynamically linked at runtime. PowerBuilder integrates seamlessly with Tuxedo when the functions are declared with the right signature and the dynamic memory is managed by PowerBuilder. Enjoy working with Tuxedo and PowerBuilder; the author certainly had a lot of fun playing around with these two veterans!
Paco Gómez is senior principal systems engineer at BEA Systems where he has been helping customers architecting solutions since 1999.