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

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类: DB2/Informix

2008-04-03 21:08:36

扩展 IBM® Informix® 4GL 应用程序,以便与 IBM WebSphere® MQ 消息传递系统无缝地通信。
简介
不同应用程序之间的通信正变得越来越重要。无论您是要实现完善的面向服务的架构(Service Oriented Architechture),还是要在两个系统之间传输数据,IBM WebSphere MQ 都可以作为数据交换的坚实基础。虽然 IBM Informix 4GL 拥有以数据库为中心(database-centric)的第四代编程语言,并且具备使用数据库和构建业务逻辑所需的所有功能,但是它不能处理外部任务。幸运的是,Informix 4GL 有结合 C-函数的能力,因而扩展 4GL 并使其通过 WebSphere MQ 发送和接收消息就变得比较容易。本文将逐步描述如何做到这一点。文中还包含完整有效的源代码,以帮助您学习。

WebSphere Message Queue Interface (MQI) 
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 管理器管理事务处理。
编写 C 函数
在后面的例子中,所有在 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 用于获取原因代码和简短的错误文本。 

清单 1. fgl_mqstatus 函数

     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。 

清单 2. fgl_mqreason 和 fgl_mqerrmsg 函数

     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 函数本身进行了设置,例如在内存分配失败时就会出现这种情况。 

连接到 MQ Manager
任何 4GL 应用程序在进行其他 MQI 调用之前,都必须首先建立到某个队列管理器的连接。当应用程序成功地建立连接时,队列管理器返回一个连接句柄。这是一个标识符,应用程序每次发出 MQI 调用时,都必须指定该句柄。一个应用程序每次只能连接到一个队列管理器,所以(对于特定的应用程序)每次只有一个连接句柄是合法的。当应用程序连接到某一队列管理器时,队列管理器将处理应用程序发出的所有 MQI 调用,直到应用程序断开与队列管理器的连接。 

连接句柄是一个 32 位的整数。在 C 中,连接句柄由一个 long 型变量实现,而用于包含句柄的 4GL 变量则必须定义为 INTEGER。 

清单 3. fgl_mqconnect 函数

     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 是静态变量(请参阅前面关于错误处理的小节)。

例 2. 调用 fgl_mqconnect 函数

     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。 

清单 4. fgl_mqopen4write 函数

     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);
     }
  

例 3. 调用 fgl_mqopen 函数

     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 定义的,消息数据(也就是在应用程序代码中放入到消息数据缓冲区的内容)则由应用程序定义。 

清单 5. fgl_mqwrite 函数

     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);
      }
     }
  

例 4. 调用 fgl_mqwrite 函数

     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
  


关闭队列
当程序断开与队列管理器的连接时,队列将自动关闭。但是,关闭已打开的对象是一种良好的编程习惯。 

清单 6. fgl_mqclose 代码

     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);
     }
  


断开与 MQ Manager 的连接
最后一步是删除 4GL 应用程序与 MQ Manager 之间的连接。 

fgl_mqdisconnect 代码

     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);
     }
  


构建定制的 runner
当使用 r4gl(或 fglpc)编译 4GL 模块时,所得到的程序由一个 runner(即执行模块)执行。标准的 runner 是 fglgo,它只支持符合 4GL 标准的内建函数。但是,如果程序调用一个 C 函数,那么必须将该函数链接到 runner 中。 

为构建一个新的 runner,首先必须编辑 fgiusr.c 文件。fgiusr.c 文件包括 runner 中包含的 C 函数的列表,还包括一个表,这个表描述了 4GL 调用与 C 函数之间的映射,另外,这个文件还包含每个函数所带参数的数量。如果参数的数量是可变的,则参数的最大数量是以负数的形式给出的。 

清单 . fgiusr.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
http://www-900.ibm.com/developerWorks/cn/dmdd/library/techarticles/dm-0501samuelsen/4glmqi.tar.gz
阅读(549) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~