Chinaunix首页 | 论坛 | 博客
  • 博客访问: 91763753
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类: DB2/Informix

2008-03-28 16:29:23

作者:Claus Samuelsen   出处:IBM DW中国 
 

 

 


 


级别: 初级

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 个步骤分别是:

  1. 连接到 MQ 管理器。
  2. 打开一个队列。
  3. 在队列上编写或读取消息。
  4. 关闭队列。
  5. 断开与 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 小组工作。

阅读(774) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~