Using Tuxedo with PowerBuilder

by Paco Gómez
02/22/2005

Abstract

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.

The Example

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.

Architecture

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

Tuxedo Functions Declaration

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
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:

  • C “int” and “long” numbers correspond to “Long” data type in PB.
  • C pointers (to any type) are represented as “Long” data in PB.
  • Reference arguments must be declared with the “ref” keyword in PB.
  • When passing a string to an external function by reference, all memory management is done in PB. The string variable must be long enough to hold the returned value.
  • If the string uses ANSI encoding, the ALIAS must be qualified with the “ansi” keyword.

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"

Declaring Aliases for FML32 Calls

The FML32 Fadd32 and 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, Fadd32String and 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.

Calling Tuxedo Functions

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 tpcall function.

For convenience, the constants representing FML32 fields ( IN_STR, IN_LONG, OUT_LONG, and OUT_STR) are explicitly declared in the Global Variables section in PB, with the values generated by the mkfldhdr32 command.

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 Fget32String function.

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

Conclusion

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!

References

Paco Gómez is senior principal systems engineer at BEA Systems where he has been helping customers architecting solutions since 1999.