搜索文章:

首页  |  Java技术  |  Asp.net  |  Asp编程  |  VC/C++  |  Delphi  |  VB编程

让Tuxedo和PowerBuilder协同工作

这篇文章主要介绍如何让 PowerBuilder 与 BEA Tuxedo 协同工作。所适用的版本是 BEA Tuxedo 8.1 和 Sybase PowerBuilder 10.0 。尽管 Tuxedo 和 PowerBuilder 都是行业里面赫赫有名的产品,然而对于早期中间件的升级来说,面向服务的架构( SOA )重新将目光投向了 Tuxedo 与 PowerBuilder 及其他第四代语言工具( 4GL )的集成 。

  例如,基于 DCE/RPC 技术的客户端 / 服务器应用程序可以通过使用 Tuxedo ATMI 作为其通信层来轻易地实现升级。我们仅仅需要改变通信接口,而作为业务逻辑的 C 服务器代码以及用作表示的 PowerBuilder 客户端代码无需做任何改变。下面,我们来讲述如何来实现上述功能。

例子

  为了详细说明如何集成 Tuxedo 和 PowerBuilder ( PB ),我们提供了一个简单的应用例子。这个例子包括一个调用 Tuxedo ATMI 服务器( txserver )的 PB 客户端应用程序( pbclient )。该客户端应用程序使用一个叫做 FML 的缓冲区来给服务器发送一个字符串和一个长的数字;服务器将这个字符串转变为大写格式,并且从 1 到所接收到的数字之间计算出一个随机数 n ,然后将转换后的字符串重复 n 次发送回客户端,将它们存储在出站 FML 缓冲区中。

  这个例子说明了如何在 PB 和 Tuxedo 之间传递不同的数据类型,同时也解释了在 PB 这一端是如何处理动态结构的,如何表示从 Tuxedo 接收的可变长数据。

  值得注意的是,这篇文章还包含了 Tuxedo 服务器端和 PowerBuild 客户端应用程序的源代码。

架构

  上述例子场景的架构表述如下: PB 客户端使用 Tuxedo 工作站( Tuxedo Workstation , WS) 组件,通过 ATMI 接口( ATMI API )请求调用 Tuxedo 服务器。 PB 客户端频繁调用 ATMI 以及定义在 PB 内的外部函数 FML32 接口( FML32 API )。程序运行时,这些调用与 Tuxedo/WS 动态链接库( LIBWSC.DLL 和 LIBFML32.DLL )保持动态连结。图 1 是该例的高层视图。


图 1. 描述该例子场景的架构

  Tuxedo 服务器( txserver )是一个标准的 ATMI 服务器。它使用 FML32 缓冲区来存储入站数据和出站数据。

Tuxedo 函数声明

  要在 PB 中使用 ATMI 和 FML32 函数,必须首先将它们声明为全局外部函数( Global External Functions ),参见图 2 。


图 2. 在 PowerBuilder 中声明的全局外部函数

  为了避免编译或运行时错误,使用正确的签名来声明这些函数是极其重要的。参数的数据类型要与在 ATMI 和 FML32 中的函数所定义的数据类型保持一致。下面是一些典型的对应关系和规则:

     

  • C 中的“ int ”和“ long ”型数据对应于 PB 中“ Long ”数据类型。

     

     

  • C 中的指针(可指向任何类型)在 PB 中表示成“ Long ”数据类型。

     

     

  • 对于引用参数,在 PB 中必须用关键字“ ref ”来声明。

     

     

  • 当通过引用将字符串传递到外部函数时,所有的内存管理都在 PB 中进行。该字符串变量必须足够长,以便能够保存返回的值。

     

     

  • 如果该字符串使用 ANSI 标准编码,其别名必须能满足关键字“ ansi ”的条件。

     

  PowerBuilder 的帮助文档提供了额外的关于数据类型的详细说明。

  下面是外部函数声明的完整列表

 

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"

声明FML32调用的别名

  FML32 的 Fadd32 函数和 Fget32 函数声明为一个指向字符的指针( char * ),该参数带有从缓冲区加入 / 取出的值。下面这种情况尽管在 C 中合法,然而 PowerBuilder 会弹出一个编译错误:函数的参数声明为 ref long 型数据类型,然后,比如说,函数实际调用的参数是一个 string 类型。

  为了解决此类问题,我们可以充分利用 PowerBuilder 的一个功能,即为外部函数声明别名。通过借助这个功能,我们可以使用与动态链接库( DLL )导出的名称不必相同的名称来声明函数。事实上,我们可以为同一个函数创建不同的别名。每个别名需要有各自不同的名称和签名以避免编译错误,然而他们事实上引用的可以是同一个动态链接库函数( DLL )。在运行时, PB 会自我调整正确的数据格式,将数据在 FML32 函数内来回传递。

在我们的例子中, Fadd32 就有两个别名,它们分别是 Fadd32String 和 Fadd32Long 。在第一个别名中,原始的 char * 值的参数声明为“ ref string ”,而在第二个别名中,它却声明为“ ref long ”。当向 FML32 缓冲区加入一个 string 值或 long 值的时候,一定要区分开来使用,以避免任何的编译错误或运行时错误。

调用Tuxedo 函数

  当点击 PowerBuilder 客户端应用程序上的 call txserver 按钮时,会完成下列过程,下面的程序清单对应了这一过程。这些代码显示了如何使用 FML32 ,如何通过 tpcall 函数来调用服务器。

  出于方便起见,表示 FML32 域的常量( IN_STR , IN_LONG 、 OUT_LONG 和 OUT_STR )在 PB 的全局变量区进行显式声明,它们的值通过 mkfldhdr32 命令生成。

 

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

?

  上述代码同样阐明了 Tuxedo 与 PowerBuilder 协同工作来处理任意长数据,例如,字符串( strings )或者字符串数组( array of strings )。正如我们早先提到的, PowerBuilder 需要管理字符串使用的内存,因此,该字符串必须在 PowerBuilder 中使用足够的空间来初始化,以便用来存储 Fget32String 函数从 FML32 缓冲区那儿取得的数据。在上述代码中,该 字符串 使用了 64k 的空间来初始化,然后,该 字符串 通过引用传递给了 Fget32String 函数。

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 支持可变大小的数组。这样的数据结构适合于在 FML32 中存储任意数量的域值。我们必须让 PB 去处理数组的动态增长问题,请看如下例子:

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

  这个屏幕截图显示了 PB 客户端应用程序运行时的状况:


图 3. PB 客户端应用程序运行情况

  在运行客户端应用程序之前,需要设定以下的环境变量:

     

  • WSNADDR – 要联系的 WSL 的网络地址,例如: WSNADDR=//10.1.1.25:4444

     

     

  • TUXDIR - Tuxedo 安装目录

     

     

  • PATH – 将 %TUXDIR%\bin 加入到 PATH 变量中

     

结束语

  这篇文章主要描述了 PowerBuilder 和 Tuxedo 结合使用所要考虑的主要问题。 Tuxedo ATMI 和 FML API 声明为外部函数,且在运行时保持动态连结。使用正确的签名来声明函数,使用 PowerBuilder 来管理动态内存,从而实现 PowerBuilder 与 Tuxedo 的无缝集成。现在,读者可以尽情享受 Tuxedo 和 PowerBuilder 的完美结合了,就像作者本人那样,从这“两员大将”身上寻找最大的快乐。

相关文章:
© 2006   www.java-asp.net