级别: 初级
Claus Samuelsen, 高级 IT 专家, IBM
2005 年 1 月 01 日
扩展 IBM® Informix® 4GL 应用程序,以便与 IBM WebSphere® MQ 消息传递系统无缝地通信。
不同应用程序之间的通信正变得越来越重要。无论您是要实现完善的面向服务的架构(Service Oriented Architechture),还是要在两个系统之间传输数据,IBM WebSphere MQ 都可以作为数据交换的坚实基础。虽然 IBM Informix 4GL 拥有以数据库为中心(database-centric)的第四代编程语言,并且具备使用数据库和构建业务逻辑所需的所有功能,但是它不能处理外部任务。幸运的是,Informix 4GL 有结合 C-函数的能力,因而扩展 4GL 并使其通过 WebSphere MQ 发送和接收消息就变得比较容易。本文将逐步描述如何做到这一点。文中还包含完整有效的源代码,以帮助您学习。
WebSphere MQ Message Queue Interface (MQI) 是一种功能丰富的编程接口。程序员可以通过它访问 WebSphere MQ 消息传递平台的所有功能,同时可以对消息和消息的行为方式进行详细的控制。MQI 支持多种编程语言,但在扩展 Informix 4GL 的时候,必须使用 C 语言。
MQI 由一些 CALL 和 STRUCTURE 组成,应用程序通过这些 CALL 访问 MQ 管理器,而使用 STRUCTURE 来与队列管理器来回传递消息。最基本的 CALL 有:
- MQCONN 和 MQDISC,用于建立和断开与 MQ Manager 的连接。
- MQOPEN 和 MQCLOSE,用于打开和关闭队列。
- MQPUT 和 MQGET,用于编写和读取消息。
MQI 还包含下面这些 CALL,但它们在本文中没有用到:
- MQINQ,用于获取队列的属性。
- MQSET,用于设置或更改队列的属性。
- MQBEGIN、MQCMT 和 MQBACK,用于通过 MQ 管理器管理事务处理。
在后面的例子中,所有在 4GL 代码中调用的 C 函数都被命名为 "fgl_mq...",例如 fgl_mqconnect 和 fgl_mqclose。与 MQ 服务器进行通信要采取以下 5 个步骤,按顺序,这 5 个步骤分别是:
- 连接到 MQ 管理器。
- 打开一个队列。
- 在队列上编写或读取消息。
- 关闭队列。
- 断开与 MQ 管理器的连接。
当设计 fgl_mq 函数时,处理 MQ 状态和错误的方式很重要。MQ Interface 使用了一个完成代码(completion code)和一个原因代码(reason code)。由于很多函数必须同时返回一个值和一个代码,因此这里必须进行特殊的考虑。返回两个或更多的值也是可以的,但这样的话就必须限制函数只能通过 CALL 语句来执行。
CALL fgl_mqconnect() RETURNING mq_status, connect_handle
|
例如,为了得到编码的最大灵活性,同时又能够在 LET 语句或表达式中使用函数,那么 fgl_mq 函数必须只返回一个值。因此,这里要实现一个特殊的状态函数,即 fgl_mqstatus。fgl_mqstatus 函数应该可以在任何其他 fgl_mq 函数之后立即调用。所以通常的 4GL 代码看上去将如下所示:
LET connect_handle = fgl_mqconnect()
IF fgl_mqstatus() <> 0 THEN
error handling code
END IF
|
所有的 MQI CALL 返回一个 32 位整数格式的完成码。fgl_mq 函数使用一个静态 long 变量来保存最近的完成码。如果 fgl_mq 函数顺利运行,就会返回一个值为 0 的状态。如果用不适合的参数调用 fgl_mq 函数,则这个静态变量还会被设置为错误(!=0)。当检测到一个错误时,就会设置一个原因代码,同时还会设置一个简短的错误消息。函数 fgl_mqreason 和 fgl_mqerrmsg 用于获取原因代码和简短的错误文本。
static long mqStatus;
int fgl_mqstatus(int nargs) /* number of parameters (ignored) */
{
ibm_lib4gl_returnInt(mqStatus);
return(1); /* number of values pushed */
}
|
函数 fgl_mqstatus 用于获取任何 MQ 接口函数的返回状态码。应该在任何 fgl_mq 服务函数之后立即调用这个函数。如果 fgl_mqstatus 返回的值为 0,那么 MQ 调用就成功了。如果这个值不是 0,则有错误发生。该函数调用还可以返回两种正值,它们不是指错误,而是指警告。如果返回的值是 1,则表示警告。在消息被截断时就会出现这种情况。当返回状态码 100 时,则意味着没有发现消息。每当出现错误或警告,都应该调用 fgl_mqreason,以便获得错误或警告的原因。函数 fgl_mqerrmsg 可用于获得错误或警告的文本描述。为了使用这个函数,必须安装 SupportPac MA0K。
int fgl_mqstatus(int nargs)
{
ibm_lib4gl_returnInt4(mqStatus);
return(1);
}
int fgl_mqreason(int nargs)
{
ibm_lib4gl_returnInt4(mqReason);
return(1);
}
int fgl_mqerrmsg(int nargs)
{
if (nargs == 1)
ibm_lib4gl_popInt4(&mqReason);
if (mqReason > 0)
MQReasonCodeMsg(mqReason, mqErrmsg);
ibm_lib4gl_returnString(mqErrmsg);
return(1);
}
|
由 MQI CALL 设置的所有原因代码都是整数。如果原因代码为负,则该原因代码已经被 fgl_mq 函数本身进行了设置,例如在内存分配失败时就会出现这种情况。
任何 4GL 应用程序在进行其他 MQI 调用之前,都必须首先建立到某个队列管理器的连接。当应用程序成功地建立连接时,队列管理器返回一个连接句柄。这是一个标识符,应用程序每次发出 MQI 调用时,都必须指定该句柄。一个应用程序每次只能连接到一个队列管理器,所以(对于特定的应用程序)每次只有一个连接句柄是合法的。当应用程序连接到某一队列管理器时,队列管理器将处理应用程序发出的所有 MQI 调用,直到应用程序断开与队列管理器的连接。
连接句柄是一个 32 位的整数。在 C 中,连接句柄由一个 long 型变量实现,而用于包含句柄的 4GL 变量则必须定义为 INTEGER。
int fgl_mqconnect(int nargs)
{
char QMName[MQ_Q_MGR_NAME_LENGTH]; /* 队列管理器 name */
MQHCONN Hcon; /* connection handle */
if (nargs > 0)
ibm_lib4gl_popString(QMName,MQ_Q_MGR_NAME_LENGTH);
else
QMName[0] = '\0'; /* use default 队列管理器 */
MQCONN(QMName, /* 队列管理器 */
&Hcon, /* connection handle */
&mqStatus, /* completion code */
&mqReason); /* reason code */
if ((mqStatus != MQCC_OK) || (mqReason != MQRC_NONE))
{
ibm_lib4gl_returnInt4(0);
return(1);
}
ibm_lib4gl_returnInt4(Hcon);
return(1);
}
|
fgl_mqconnect 带一个参数,即 MQ Manager 的名称。如果没有提供参数,则会建立到默认 MQ Manager 的连接。变量 mqStatus 和 mqReason 是静态变量(请参阅前面关于错误处理的小节)。
DEFINE connect_handle INT
CALL fgl_mqconnect('queueManager') RETURNING connect_handle
IF fgl_mqstatus() <> 0 THEN
error handling
END IF
|
当打开一个队列时,必须告诉 MQM 队列的使用方式 —— 浏览、读或写。还可以同时以读和写的方式打开一个队列。打开队列的模式由提供给打开语句的参数决定。为了方便起见,这里提供了两个打开函数:fgl_mqopen4read 和 fgl_open4write。还可以使用标准打开函数 fgl_mqopen。由于没有默认队列,因此在打开一个队列时必须提供队列名称。
如果打开请求成功,队列管理器将返回一个对象句柄(队列句柄)。4GL 应用程序在发出 put 或 get 调用时,必须指定这个句柄,一起指定的还有连接句柄。该对象句柄确保请求能够在适当的队列上得到处理。
对象句柄是 32 位的整数。在 C 中,对象句柄以 long 型变量实现,而用于包含该句柄的 4GL 变量则必须定义为 INTEGER。
int fgl_mqopen4write(int nargs)
{
MQHCONN Hcon; /* connection handle */
MQOD queue = {MQOD_DEFAULT}; /* Object Descriptor */
MQHOBJ Hqueue; /* object handle */
MQLONG O_options; /* MQOPEN options */
if (nargs < 2)
{
sprintf(mqErrmsg, "MQ-ERR: MQOPEN missing connection handle or queue_name");
mqStatus = -1;
mqReason = -1;
ibm_lib4gl_returnInt4(0);
return(1);
}
else
{
ibm_lib4gl_popString(queue.ObjectName, MQ_Q_NAME_LENGTH);
ibm_lib4gl_popInt4(&Hcon);
}
O_options = MQOO_OUTPUT /* open queue for output */
| MQOO_FAIL_IF_QUIESCING /* but not if MQM stopping */
; /* = 0x2010 = 8208 decimal */
MQOPEN(Hcon, /* connection handle */
&queue, /* object descriptor for queue */
O_options, /* open options */
&Hqueue, /* object handle */
&mqStatus, /* MQOPEN completion code */
&mqReason); /* reason code */
/* report reason, if any; stop if failed */
if ((mqStatus != MQCC_OK) || (mqReason != MQRC_NONE))
{
ibm_lib4gl_returnInt4(0);
return(1);
}
ibm_lib4gl_returnInt4(Hqueue);
return(1);
}
|
DEFINE connect_handle INT
DEFINE queue_handle INT
LET connect_handle = fgl_mqconnect('queueManager')
IF fgl_mqstatus() <> 0 THEN
error handling
END IF
LET queue_handle = fgl_mqopen4write(connect_handle)
IF fgl_mqstatus() <> 0 THEN
error handling
END IF
|
以写的方式打开队列之后,4GL 应用程序可以将消息放在该队列上。为此,它使用另一个 MQI 调用,调用时必须指定一些参数和数据结构。这些参数和数据结构定义了与要放入队列的消息有关的所有信息,这些信息包括消息类型、目的地、设置的选项,等等。消息数据(即应用程序所发送消息中特定于应用程序的内容)是在一个缓冲区中定义的,而这个缓冲区则是在调用 MQI 时指定的。当队列管理器处理该调用时,它添加了一个消息描述符(message descriptor),其中包含确保能够正确传递消息所需的信息。消息描述符的格式是由 MQSeries 定义的,消息数据(也就是在应用程序代码中放入到消息数据缓冲区的内容)则由应用程序定义。
int fgl_mqwrite(int nargs)
{
MQHCONN Hcon; /* connection handle */
MQHOBJ Hqueue; /* object handle */
char *buffer; /* message buffer */
MQLONG messlen; /* message length */
MQMD md = {MQMD_DEFAULT}; /* Message Descriptor */
MQPMO pmo = {MQPMO_DEFAULT}; /* put message options */
if (nargs != 4)
{
sprintf(mqErrmsg, "MQ-ERR: MQWRITE missing connection handle, queue_handle, message or msg_size");
mqStatus = -1;
mqReason = -1;
return(0);
}
else
{
ibm_lib4gl_popInt4(&messlen);
if (messlen > 0)
{
messlen++;
buffer = (char *)malloc(messlen);
if (buffer == NULL)
{
sprintf(mqErrmsg, "MQ-ERR: MQWRITE could not allocate %d bytes memory\n", messlen);
mqStatus = -1;
mqReason = -2;
return(0);
}
}
ibm_lib4gl_popString(buffer,messlen);
ibm_lib4gl_popInt4(&Hqueue);
ibm_lib4gl_popInt4(&Hcon);
memcpy(md.Format, /* character string format */
MQFMT_STRING, (size_t)MQ_FORMAT_LENGTH);
memcpy(md.MsgId, /* reset MsgId to get a new one */
MQMI_NONE, sizeof(md.MsgId) );
memcpy(md.CorrelId, /* reset CorrelId to get a new one */
MQCI_NONE, sizeof(md.CorrelId) );
MQPUT(Hcon, /* connection handle */
Hqueue, /* object handle */
&md, /* message descriptor */
&pmo, /* default options (datagram) */
messlen, /* message length incl. null term. */
buffer, /* message buffer */
&mqStatus, /* completion code */
&mqReason); /* reason code */
free(buffer);
return(0);
}
}
|
DEFINE connect_handle INT
DEFINE queue_handle INT
DEFINE msg CHAR(80)
# Snipped code for connect and open queue - see example 3
DECLARE cust_curs FOR select fname || ' ' || lname from customer
FOREACH cust_curs INTO msg
CALL fgl_mqwrite(connect_handle, queue_handle, msg, LENGTH(msg))
IF fgl_mqstatus() <> 0 THEN
error handling
END IF
END FOREACH
|
当程序断开与队列管理器的连接时,队列将自动关闭。但是,关闭已打开的对象是一种良好的编程习惯。
int fgl_mqclose(int nargs)
{
MQHCONN Hcon; /* connection handle */
MQHOBJ Hqueue; /* object handle */
MQLONG C_options; /* MQCLOSE options */
if (nargs != 2)
{
sprintf(mqErrmsg, "MQ-ERR: MQCLOSE missing connection handle or queue_name");
mqStatus = -1;
mqReason = -1;
return(0);
}
else
{
ibm_lib4gl_popInt4(&Hqueue);
ibm_lib4gl_popInt4(&Hcon);
}
C_options = MQCO_NONE; /* no close options */
MQCLOSE(Hcon, /* connection handle */
&Hqueue, /* object handle */
C_options,
&mqStatus, /* completion code */
&mqReason); /* reason code */
ibm_lib4gl_returnInt4(mqStatus);
return(1);
}
|
最后一步是删除 4GL 应用程序与 MQ Manager 之间的连接。
int fgl_mqdisconnect(int nargs)
{
MQHCONN Hcon; /* connection handle */
if (nargs == 1)
ibm_lib4gl_popInt4(&Hcon);
else
{
sprintf(mqErrmsg, "MQ-ERR: MQDISCONNECT missing connection handle");
mqStatus = -1;
mqReason = -1;
return(0);
}
MQDISC(&Hcon,
&mqStatus,
&mqReason);
ibm_lib4gl_returnInt4(mqStatus);
return(1);
}
|
当使用 r4gl(或 fglpc)编译 4GL 模块时,所得到的程序由一个 runner(即执行模块)执行。标准的 runner 是 fglgo,它只支持符合 4GL 标准的内建函数。但是,如果程序调用一个 C 函数,那么必须将该函数链接到 runner 中。
为构建一个新的 runner,首先必须编辑 fgiusr.c 文件。 fgiusr.c 文件包括 runner 中包含的 C 函数的列表,还包括一个表,这个表描述了 4GL 调用与 C 函数之间的映射,另外,这个文件还包含每个函数所带参数的数量。如果参数的数量是可变的,则参数的最大数量是以负数的形式给出的。
#include "fgicfunc.h"
int fgl_mqstatus();
int fgl_mqreason();
int fgl_mqerrmsg();
int fgl_mqconnect();
int fgl_mqdisconnect();
int fgl_mqopen();
int fgl_mqopen4write();
int fgl_mqopen4read();
int fgl_mqclose();
int fgl_mqwrite();
int fgl_mqread();
int fgl_mqrecieve();
int fgl_mqwriteonce();
cfunc_t usrcfuncs[] =
{
{"fgl_mqstatus", fgl_mqstatus, 0},
{"fgl_mqreason", fgl_mqreason, 0},
{"fgl_mqerrmsg", fgl_mqerrmsg, -1},
{"fgl_mqconnect", fgl_mqconnect, -1},
{"fgl_mqdisconnect", fgl_mqdisconnect, 1},
{"fgl_mqopen", fgl_mqopen, 3},
{"fgl_mqopen4write", fgl_mqopen4write, 2},
{"fgl_mqopen4read", fgl_mqopen4read, 2},
{"fgl_mqclose", fgl_mqclose, 2},
{"fgl_mqwrite", fgl_mqwrite, 4},
{"fgl_mqread", fgl_mqread, -4},
{"fgl_mqrecieve", fgl_mqrecieve, 3},
{"fgl_mqwriteonce", fgl_mqwriteonce, -3},
{0, 0, 0}
};
|
发出下列命令来编译 fgl_mq 函数并构建一个新的 runner。要记住更改路径。
$ cfglgo -I/usr/mqm/inc fgiusr.c mqfunc.c /usr/mqm/lib/libmqm.a -o fglmq
|
这个新的 runner 名为 fglmq 。
描述 |
名字 |
大小 |
下载方法 |
All the fgl_mq functions |
4glmqi.tar.gz |
5 KB |
|HTTP |
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文 。
|
|
|
Claus Samuelsen 在 IBM Software Group 的 Software Innovation 小组工作。 | |