这里的内容
本文中使用的缩略语
在i5/OS上,通过CL命令和编程接口可以操作Queue objects有:
External Object Type | System(MI) Object Type/Subtype | |
Data Queue | *DTAQ | hex 0A01 |
User Queue | *USRQ | hex 0A02 |
此外,每个i5/OS作业还在时时刻刻地使用着一个Queue Object,即包含在PCS (Process Control Space)中的QMIRQ,一个作业在每次发出I/O操作请求后, 由QMIRQ接收I/O操作的结束通知。
Queue object的主要属性:
每个Queue message包含两个部分,message prefix和message text;message prefix中包含 着message text长度以及键值数据。值得注意的是,尽管在入列时,我们可以指定小于 最大queue message长度的message text,但入列后message text实际占用的存储还是等于 Queue Message最大长度。这就意味着,如果为了兼容多种长度相差很多的数据格式,而指 定冗余的queue message最大长度来存放多种数据格式的数据,将导致存储的浪费。
应用程序请求入列一个queue object时,必须提供message prefix和message text。当 queue object中没有可用存储空间时,依赖于queue object的是否可扩展属性,queue object 被扩展或引发一个"queue full"异常(MCH3802)。
由一个queue object 出列 message时,如果queue object中没有符合条件的meesage时, 应用程序可以选择是否等待,如果选择等待,可以指定永远等待或指定超时值,等待超时 时,会引发 dequeue time-out 异常(MCH5801)。
使用QRCVDTAQ 出列 data queue时,当等待超时,不会引发异常。要判断 是否超时,只能在对QRCVDTAQ调用后,依据存放message text数据的内容判断 是出列到了message,还是出列超时。
i5/OS向用户暴露的DTAQ创建接口是API QUSCRTUQ,该API允许用户 创建System Domain或User Domain的USRQ。参考 5.1.2. USRQ的Domain属性。 然而,作为一个System Domain object,创建的USRQ仅仅在Security Level 30及以下才可以被用户程序通过 MI指令访问。
CRTDTAQ DTAQ(LSBIN/Q13) MAXLEN(64)
STRJRNOBJ OBJ(LSBIN/Q13) OBJTYPE(*DTAQ) JRN(JRN01) IMAGES(*AFTER)
/* 入列 */
CALL PGM(QSNDDTAQ) PARM('Q13' 'LSBIN' X'00010F' 'AAAAAAAAAA')
CALL PGM(QSNDDTAQ) PARM('Q13' 'LSBIN' X'00020F' 'BBBBBBB CCCCCCC')
序号 | 入列时间 | Message Text |
1 | 2009-05-18-11.27.58.619412 | 'AAAAAAAAAA' |
2 | 2009-05-18-11.28.23.033237 | 'BBBBBBB CCCCCCC' |
- 出列DTAQ Q13 CALL PGM(QRCVDTAQ) PARM('Q13' 'LSBIN' X'00020F' '' X'00001D')
/* 查看journal code为Q(DTAQ操作)的journa entires */
DSPJRN JRN(LSBIN/JRN01) JRNCDE((Q))
11075 Q QS Q13 LSBIN REDLIGHT 11:27:58 11076 Q QS Q13 LSBIN REDLIGHT 11:28:23 11077 Q QR Q13 LSBIN REDLIGHT 12:25:22
Display Journal Entry Object . . . . . . . : Q13 Library . . . . . . : LSBIN Member . . . . . . . : Incomplete data . . : No Minimized entry data : No Sequence . . . . . . : 11075 Code . . . . . . . . : Q - Data queue operation Type . . . . . . . . : QS - Send data queue entry, no key Entry specific data Column *...+....1....+....2....+....3....+....4....+....5 00001 ' K 1. N AA' 00051 'AAAAAAAA' Display Journal Entry Object . . . . . . . : Q13 Library . . . . . . : LSBIN Member . . . . . . . : Incomplete data . . : No Minimized entry data : No Sequence . . . . . . : 11076 Code . . . . . . . . : Q - Data queue operation Type . . . . . . . . : QS - Send data queue entry, no key Entry specific data Column *...+....1....+....2....+....3....+....4....+....5 00001 ' K E! ! N BB' 00051 'BBBBB CCCCCCC '
Display Journal Entry Details Journal . . . . . . : JRN01 Library . . . . . . : LSBIN Sequence . . . . . . : 11075 Code . . . . . . . . : Q - Data queue operation Type . . . . . . . . : QS - Send data queue entry, no key Object . . . . . . . : Q13 Library . . . . . . : LSBIN Member . . . . . . . : Flag . . . . . . . . : 0 Date . . . . . . . . : 09/05/18 Time . . . . . . . . : 11:27:58 Ref Constraint . . . : No Count/RRN . . . . . : 0 Job . . . . . . . . : 254281/LJL/REDLIGHT Commit cycle ID . . : 0 User profile . . . . : LJL Program . . . . . . : QCMD Ignore APY/RMV . . . : No Library . . . . . : *OMITTED Trigger . . . . . . : No ASP device . . . . : *OMITTED
Display Journal Entry Details Journal . . . . . . : JRN01 Library . . . . . . : LSBIN Sequence . . . . . . : 11077 Code . . . . . . . . : Q - Data queue operation Type . . . . . . . . : QR - Rcv data queue entry, no key Object . . . . . . . : Q13 Library . . . . . . : LSBIN Member . . . . . . . : Flag . . . . . . . . : 0 Date . . . . . . . . : 09/05/18 Time . . . . . . . . : 12:25:22 Ref Constraint . . . : No Count/RRN . . . . . : 0 Job . . . . . . . . : 254281/LJL/REDLIGHT Commit cycle ID . . : 0 User profile . . . . : LJL Program . . . . . . : QCMD Ignore APY/RMV . . . : No Library . . . . . : *OMITTED Trigger . . . . . . : No ASP device . . . . : *OMITTED
操作 | Data Queue | User Queue |
创建 | CL - CRTDTAQ | API - QUSCRTUQ |
删除 | CL - DLTDTAQ | CL - DLTUSRQ; API - QUSDLTUQ |
入列 | API - QSNDDTAQ | MI - ENQ |
出列 | API - QRCVDTAQ | MI - DEQ |
清空 | API - QCLRDTAQ | ~ |
获取queue属性 | API - QMHQRDQD | MI - MATQAT |
获取messages | API - QMHRDQM | MI - MATQMSG |
下面就1000次入列操作分别对以下3种情况进行时间统计,时间单位为1/1000000秒:
操作 | 时间(us,micro-seconds) | 命令/源码 |
case 1 | 4000 | Appendix 1. Enqueue USRQ Q11 for 1000 times |
case 2 | 151000 | Appendix 2. Enqueue DTAQ Q12(not journaled) for 1000 times |
case 3 | 40599000 | Appendix 3. Enqueue DTAQ Q12(journaled) for 1000 times |
尽管以上统计仅仅是对两个具有相同queue属性的DTAQ,USRQ的入列操作得出的,不具备普遍意义。 但足以说明对DTAQ与USRQ操作的效率差异。而以上统计中,启用日志 前后,入列 DTAQ的效率对比,说明了对DTAQ启用日志带来的效率损失。 对于严重依赖DTAQ完成大数据量IPC的应用系统,应该将这种效率损失计入考虑。
使一个DTAQ包含入列作业信息,需要在使用CRTDTAQ命令时指定SENDERID(*YES)参数,如:
CRTDTAQ DTAQ(LSBIN/Q14) MAXLEN(64) SENDERID(*YES) TEXT('with sender''s info')
那么所谓sender ID在queue message中是什么样子呢? 入列上面创建的DTAQ Q14,然后使用CL命令 i5/OS Programmer's Toolkit的 DSPQMSG 就可以看到:
CALL PGM(QSNDDTAQ) PARM('Q14' 'LSBIN' X'00011F' 'ABCDE *^_^*') DSPQMSG Q(LSBIN/Q14) QTYPE(*DTAQ)
Queue Message System: 810 Queue . . . . . . . : Q14 Attribute . . . . . : Library . . . . . : LSBIN Owner . . . . . . . : LJL Object ASP number . : 1 Object Domain . . . : System Type . . . . . . . . : *DTAQ Key length . . . . . : 0 Key length . . . . . : 100 Number . . . . . . . : 1 -------------------Character data------------------- Column *...+....1....+....2....+....3....+....4....+....5 000001 'REDLIGHT LJL 254281LJL ABCDE *^_^* ' 000051 ' '
CRTDTAQ DTAQ(Q21) MAXLEN(64) SEQ(*FIFO) TEXT('FIFO, entry length=64') CRTDTAQ DTAQ(Q22) MAXLEN(64) SEQ(*LIFO) TEXT('LIFO, entry length=64')
CALL PGM(QSNDDTAQ) PARM('Q22' 'LSBIN' X'00003F' 'abc')
CALL PGM(QSNDDTAQ) PARM('Q22' 'LSBIN' X'00003F' 'def')
DSPQMSG Q22
/* 结果 */
Time Message
Opt Number enqueued Key
1 2009-05-19-16.36.49.641679
2 2009-05-19-16.36.40.429570
CRTDTAQ DTAQ(Q23) MAXLEN(64) SEQ(*KEYED) KEYLEN(8) TEXT('keyed, key length = 8')
CALL PGM(QSNDDTAQ) PARM('Q23' 'LSBIN' X'00003F' 'abc' X'008F' '00000005')
CALL PGM(QSNDDTAQ) PARM('Q23' 'LSBIN' X'00003F' 'def' X'008F' '00000002')
CALL PGM(QSNDDTAQ) PARM('Q23' 'LSBIN' X'00003F' 'ghi' X'008F' '00000003')
DSPQMSG Q(Q23)
/* 结果 */
Time Message
Opt Number enqueued Key
1 2009-05-19-16.48.14.771519 00000002
2 2009-05-19-16.48.38.547735 00000003
3 2009-05-19-16.48.04.499240 00000005
CRTDTAQ DTAQ(Q24) MAXLEN(64) SENDERID(*YES)
DLTDTAQ LSBIN/Q21
call qsnddtaq parm( 'Q21' /* char(10),DTAQ名称 */ 'LSBIN' /* char(10),库名 */ X'00003F' /* pkd(5,0),入列message text长度 */ 'ABC' /* char(*),message text */ )
call qsnddtaq parm( 'Q23' /* char(10),DTAQ名称 */ 'LSBIN' /* char(10),库名 */ X'00003F' /* pkd(5,0),入列message text长度 */ 'abc' /* char(*),message text */ x'008F' /* pkd(3,0),键值长度 */ '00000001' /* char(*),键值内容 */ )
/* * @file r101.rpgle * enqueue DTAQ Q23(keyed, key length=8, entry length=64) */ /* prototype of API QSNDDTAQ */ d qsnddtaq pr extpgm('QSNDDTAQ') d qname 10a d qlib 10a d entry_len 5p 0 d entry 64a options(*varsize) d key_len 3p 0 d key 8a options(*varsize) d qname s 10a inz('Q23') d qlib s 10a inz('LSBIN') d entry_len s 5p 0 inz(64) d entry s 64a d key_len s 3p 0 inz(8) d key s 8a /free entry = 'abc'; key = '00000002'; qsnddtaq(qname : qlib : entry_len : entry : key_len : key ); *inlr = *on; /end-free /* eof */
/************************************************/ /* @FILE CL101.CLP */ /* DEQUEUE FIFO DTAQ Q21 */ /************************************************/ PGM DCL VAR(&QNAM) TYPE(*CHAR) LEN(10) VALUE('Q21') DCL VAR(&QLIB) TYPE(*CHAR) LEN(10) VALUE('LSBIN') DCL VAR(&ENTLEN) TYPE(*DEC) LEN(5 0) /* length + of dequeued data */ DCL VAR(&ENTRY) TYPE(*CHAR) LEN(64) DCL VAR(&TIMEOUT) TYPE(*DEC) LEN(5 0) VALUE(-1) + /* dequeue infinittely */ DCL VAR(&MSG) TYPE(*CHAR) LEN(32) VALUE('entry + dequeued:') CALL PGM(QRCVDTAQ) PARM(&QNAM &QLIB &ENTLEN + &ENTRY &TIMEOUT) CHGVAR VAR(%SST(&MSG 17 16)) VALUE(&ENTRY) SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA(&MSG) ENDPGM /* EOF - CL101.CLP */
OPM CL程序cl105.clp 以指定超时值等待方式出列FIFO DTAQ Q21
/************************************************/ /* @FILE CL101.CLP */ /* DEQUEUE FIFO DTAQ Q21 WITH TIMEOUT */ /************************************************/ PGM DCL VAR(&QNAM) TYPE(*CHAR) LEN(10) VALUE('Q21') DCL VAR(&QLIB) TYPE(*CHAR) LEN(10) VALUE('LSBIN') DCL VAR(&ENTLEN) TYPE(*DEC) LEN(5 0) /* length + of dequeued data */ DCL VAR(&ENTRY) TYPE(*CHAR) LEN(64) DCL VAR(&TIMEOUT) TYPE(*DEC) LEN(5 0) VALUE(5) + /* time-out value = 5s */ DCL VAR(&MSG) TYPE(*CHAR) LEN(32) VALUE('entry + dequeued:') CALL PGM(QRCVDTAQ) PARM(&QNAM &QLIB &ENTLEN + &ENTRY &TIMEOUT) IF COND(&ENTLEN *EQ 0) THEN(DO) CHGVAR VAR(&MSG) VALUE('Dequeue timed-out') GOTO CMDLBL(SENDMSG) ENDDO CHGVAR VAR(%SST(&MSG 17 16)) VALUE(&ENTRY) SENDMSG: SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA(&MSG) ENDPGM /* EOF - CL101.CLP */
/************************************************/ /* @FILE CL102.CLP */ /* DEQUEUE KEYED DTAQ Q23 */ /************************************************/ PGM DCL VAR(&QNAM) TYPE(*CHAR) LEN(10) VALUE('Q23') DCL VAR(&QLIB) TYPE(*CHAR) LEN(10) VALUE('LSBIN') DCL VAR(&ENTLEN) TYPE(*DEC) LEN(5 0) DCL VAR(&ENTRY) TYPE(*CHAR) LEN(64) DCL VAR(&TIMEOUT) TYPE(*DEC) LEN(5 0) VALUE(-1) + /* dequeue infinittely */ DCL VAR(&KEYORDER) TYPE(*CHAR) LEN(2) + VALUE('GT') /* dequeue queue entries + whose key value aregreater than variable + &KEY */ DCL VAR(&KEYLEN) TYPE(*DEC) LEN(3 0) VALUE(8) DCL VAR(&KEY) TYPE(*CHAR) LEN(8) VALUE('00000001') DCL VAR(&SNDINFOLEN) TYPE(*DEC) LEN(3 0) + VALUE(0) /* no sender info */ DCL VAR(&SNDINFO) TYPE(*CHAR) LEN(1) DCL VAR(&MSG) TYPE(*CHAR) LEN(96) + VALUE('key: ; message text: ') CALL PGM(QRCVDTAQ) PARM(&QNAM &QLIB &ENTLEN + &ENTRY &TIMEOUT &KEYORDER &KEYLEN &KEY + &SNDINFOLEN &SNDINFO) CHGVAR VAR(%SST(&MSG 6 8)) VALUE(&KEY) CHGVAR VAR(%SST(&MSG 30 64)) VALUE(&ENTRY) SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA(&MSG) ENDPGM /* EOF - CL102.CLP */
/* * @file r102.rpgle * * DTAQ Q23,类型为基于键值出列,entry长度64,key长度8, * 不含发送作业标识信息。 * * 出列DTAQ Q23中键值大于'00000001'的entry,并保留DTAQ中的entry。 */ /* structure Qus_EC_t */ d qusec_t ds 256 qualified d bytes_in 10i 0 d bytes_out 10i 0 d exid 7a d reserved 1a d ex_data 240a /* prototype of API QRCVDTAQ */ d qrcvdtaq pr extpgm('QRCVDTAQ') d qname 10a d qlib 10a d entry_len 5p 0 d entry 64a options(*varsize) d timeout 5p 0 d key_order 2a options(*nopass) /* optional parameter group 1 */ d key_len 3p 0 options(*nopass) d key 8a options(*nopass:*varsize) /* input/output parameter */ d sender_info... d _len 3p 0 options(*nopass) d sender_info 1a options(*nopass:*varsize) d remove_msg 10a options(*nopass) /* optional parameter group 2 */ d receiver_len 5p 0 options(*nopass) d ec likeds(qusec_t) d options(*nopass) d qname s 10a inz('Q23') d qlib s 10a inz('LSBIN') d entry_len s 5p 0 d entry s 64a d timeout s 5p 0 inz(-1) /* dequeue infinitely */ d key_order s 2a inz('GT') /* key order: >= parameter @key */ d key_len s 3p 0 inz(8) d key s 8a inz('00000001') d sender_info... d _len s 3p 0 inz(0) /* no sender info */ d sender_info s 1a d remove_msg s 10a inz('*NO') /* do NOT remove message from DTAQ */ d receiver_len s 5p 0 inz(64) d ec ds likeds(qusec_t) d msg s 16a /free ec.bytes_in = 256; qrcvdtaq( qname : qlib : entry_len : entry : timeout : key_order : key_len : key : sender_info_len : sender_info : remove_msg : receiver_len : ec ); msg = %subst(entry : 1 : 16); dsply key '*EXT' msg; *inlr = *on; /end-free /* eof - r102.rpgle */
CALL PGM(QCLRDTAQ) PARM('Q21' 'LSBIN')
/* * @file r103.rpgle * * retrieve attribtutes of a DTAQ * Keyed DTAQ Q23 with entry length 64, key length 8, * does not include sender's ID */ h dftactgrp(*no) /* receiver structure in format RDQD0100 */ d rdqd0100_t ds 112 qualified d bytes_... d returned 10i 0 /* length of attribute data returned */ d bytes_... d available 10i 0 /* length DTAQ attribute data */ d entry_len 10i 0 /* DTAQ entry length */ d key_len 10i 0 /* DTAQ key length */ d sequence 1a /* DTAQ type: */ d /* - 'F', FIIO */ d /* - 'K', Keyed */ d /* - 'L', LIFO */ d include_... d sender_info 1a /* include sender info or not: Y/N */ d force_to_stg 1a /* force to storage: Y/N */ d text 50a /* text description */ d ddm_dtaq 1a /* is q DDM DTAQ: */ d /* - '0', no */ d /* - '1', yes */ d auto_reclaim 1a /* automatic reclaim storage */ d /* - '0', no */ d /* - '1', yes */ d reserved 1a d num_messages 10i 0 /* messages currently on the DTAQ */ d entries_... d allocated 10i 0 /* entries currently allocated */ d qname 10a d qlib 10a d max_messages 10i 0 /* maximum number of messages */ d init_... d messages 10i 0 /* initial number of messages */ /* prototype of API QMHQRDQD */ d qmhqrdqd pr extpgm('QMHQRDQD') d dtaq_attr likeds(rdqd0100_t) /* receiver data */ d receiver_len 10i 0 /* length of receiver data */ d format_name 8a /* format name */ d /* - RDQD0100, normal DTAQ */ d /* - RDQD0200, DDM DTAQ */ d qname_lib 20a /* DTAQ name/library, e.g. */ d /* 'Q23 LSBIN ' */ d do_report pr d dtaq_attr ds likeds(rdqd0100_t) d len s 10i 0 inz(112) d format s 8a inz('RDQD0100') d qname_lib s 20a inz('Q23 LSBIN') /free qmhqrdqd( dtaq_attr : len : format : qname_lib ); do_report(); *inlr = *on; /end-free /* eof - main procedure */ p do_report b /* structure Qus_EC_t */ d qusec_t ds 256 qualified d bytes_in 10i 0 d bytes_out 10i 0 d exid 7a d reserved 1a d ex_data 240a /* prototype of QMHSNDPM */ d qmhsndpm pr extpgm('QMHSNDPM') d msgid 7a d msgf 20a d msgdata 128a options(*varsize) d msgdata_len 10i 0 d msgtype 10a d callstack_... d entry 10a d callstack_... d counter 10i 0 d msgkey 4a d ec likeds(qusec_t) d msgid s 7a inz('CPF9898') d msgf s 20a inz('QCPFMSG QSYS') d msgdata s 128a d msgdata_len s 10i 0 d msgtype s 10a inz('*INFO') d callstack_... d entry s 10a inz('*PGMBDY') d callstack_... d counter s 10i 0 inz(1) d msgkey s 4a d ec ds likeds(qusec_t) d msg s 128a /free ec.bytes_in = 256; // report DTAQ entry length msgdata = 'Entry length: ' + %char(dtaq_attr.entry_len); msgdata_len = %len(%trim(msgdata)); qmhsndpm( msgid : msgf : msgdata : msgdata_len : msgtype : callstack_entry : callstack_counter : msgkey : ec ); // report DTAQ key length msgdata = 'Key length: ' + %char(dtaq_attr.key_len); msgdata_len = %len(%trim(msgdata)); qmhsndpm( msgid : msgf : msgdata : msgdata_len : msgtype : callstack_entry : callstack_counter : msgkey : ec ); // report more DTAQ attributes // ... ... /end-free p do_report e /* eof - r103.rpgle */
更多示例请参考 DSPQD 源码 qattr.mi 中的internal entry point display-ddmq-attr(line 361),和 display-dtaq-attr(line 396)。
在使用QMHRDQM时需要注意的是:
QUSCRTUQ参数如下所示:
关于API QUSCRTUQ的详细文档请参考 Create User Queue (QUSCRTUQ) API
MAX_MSGS = INIT_MSGS + EXT_MSGS * NUM_EXTENDS /* 例如: INIT_MSGS = 1 EXT_MSGS = 2 NUM_EXTENDS = 1 则 MAX_MSGS = 1 + 2 * 1 = 3 即USRQ可容纳的最大消息数为3,当试图向USRQ入列第4条消息时 将引发MCH3802(hex 2602)异常。 */
QALWUSRDMN System Value | Destination Library | Optional Domain Parameter | Domain of Created Object |
*ALL | Any | *DEFAULT | User domain |
*ALL | Any | *SYSTEM | System domain |
*ALL | Any | *USER | User domain |
QTEMP | QTEMP | *DEFAULT | User domain |
QTEMP | QTEMP | *SYSTEM | System domain |
QTEMP | QTEMP | *USER | User domain |
Does not contain library name | Library name | *DEFAULT | System domain |
Does not contain library name | Library name | *SYSTEM | System domain |
Does not contain library name | Library name | *USER | None; error is returned |
Number of queue extensions INPUT; BINARY(4) The maximum number of extensions allowed for the user queue. A value of -1 indicates that the maximum number of extensions will be chosen by the machine. If this parameter is not specified, 0 is assumed.
/* 创建USRQ,指定Number of queue extensions参数为-1 */ CALL PGM(QUSCRTUQ) PARM( 'Q34 LSBIN' /* USRQ name */ 'QQ' /* extended attribute */ 'K' /* queue type */ X'00000008' /* key length */ X'00000040' /* entry length */ X'00000001' /* initial number of messages */ X'00000002' /* number of messages per extension */ '*EXCLUDE' /* public authority */ '`Number of queue extensions`parameter = -1' /* text description */ '*YES' /* replace */ X'0000002000000000000000000000000000000000000000000000000000000000' /* error code structure */ '*USER' /* object domain */ '*YES' /* contains poniter */ X'FFFFFFFF' /* number of queue extensions */ '0') /* auto-reclaim storage */ /* 使用DSPQD命令检查USRQ的`Choose maximum number of extends`属性 和`Maximum number of extends`属性 */ DSPQD Q(Q34) QTYPE(*USRQ) /* 结果如下 */ Queue attributes: Message content . . . . . : Contains scalar data only Queue type . . . . . . . . : Keyed Queue overflow action . . : Extend queue Choose maximum number of extends . . . . . . . . : Machine /* 由机器决定最大扩展次数 */ Reclaim storage . . . . . : 0 ... ... Maximum number of extends . : 87380 /* 机器决定的最大扩展次数 */
/* 创建USRQ,不指定Number of queue extensions参数 */ CALL PGM(QUSCRTUQ) PARM('Q34 LSBIN' 'QQ' 'K' X'00000008' X'00000040' X'00000001' X'00000002' '*EXCLUDE' 'Do NOT specify `Number of queue extensions` parameter') /* 使用DSPQD命令检查USRQ的`Choose maximum number of extends`属性 和`Maximum number of extends`属性 */ DSPQD Q(Q34) QTYPE(*USRQ) /* 结果如下 */ Queue attributes: Message content . . . . . : Contains scalar data only Queue type . . . . . . . . : Keyed Queue overflow action . . : Extend queue Choose maximum number of extends . . . . . . . . : Machine /* 由机器决定最大扩展次数 */ Reclaim storage . . . . . : 0 ... ... Maximum number of extends . : 87380 /* 机器决定的最大扩展次数 */
/* CL命令DLTUSRQ */ DLTUSRQ USRQ(LSBIN/Q31) /* API QUSCLTUQ */ CALL PGM(QUSDLTUQ) PARM('Q34 LSBIN' X'0000002000000000000000000000000000000000000000000000000000000000')
/* * @file r104.rpgle * * enqueue USRQ Q31 */ h dftactgrp(*no) /* my protoype */ d i_main pr extpgm('R104') d p_key 8a d p_text 64a d q s * d option ds 34 qualified d type 2a inz(x'0A02') /* USRQ's MI object type: hex 0A02 */ d name 30a inz('Q31') /* USRQ name */ d auth 2a inz(x'0000') /* prototype of MI instruction RSLVSP */ d rslvsp pr extproc('_RSLVSP2') d syp * d option * value /* prototype of MI instruction ENQ */ d enq pr extproc('_ENQ') d q * d prefix * value d text * value d prefix ds qualified /* message prefix */ d text_len 10i 0 /* length of message text */ d key 8a inz('00000001') /* key data */ d text s 64a inz('hi, i''m rpg program R104:p') /* message text */ d i_main pi d p_key 8a d p_text 64a /free rslvsp(q : %addr(option)); prefix.key = p_key; text = p_text; enq(q : %addr(prefix) : %addr(text)); *inlr = *on; /end-free /* eof - r104.rpgle */
与r104.rpgle实现相同功能的MI程序 enq31.mi
/* * @file enq31.mi * * enqueue USRQ Q31(keyed, key length = 8, entry lenght=64) * * usage example: CALL ENQ31 PARM('00000003' 'ABCDE...') */ dcl sysptr q auto init("Q34", ctx("LSBIN"), type(q, 02)) ; dcl dd message-prefix char(9) auto ; dcl dd prefix-msglen bin(4) def(message-prefix) pos(1); dcl dd prefix-key char(5) def(message-prefix) pos(5) ; dcl spcptr .key parm ; dcl spcptr .text parm ; dcl ol pl-main(.key, .text) parm ext ; dcl dd key char(8) bas(.key) ; dcl dd text char(64) bas(.text) ; dcl dd msg char(64) auto bdry(16) ; dcl spcptr msg-ptr auto init(msg) ; entry *(pl-main) ext; /* enq */ cpynv prefix-msglen, 64; cpybla prefix-key, key ; cpybla msg, text ; enq q, message-prefix, msg-ptr ; rtx *; pend; /* eof - enq31.mi */
其中operand 1 message prefix 格式如下:
Offset Dec Hex Field Name Data Type and Length 0 0 Timestamp of enqueue of message Char(8) ++ 8 8 Dequeue wait time-out value Char(8) + (ignored if branch options specified) 16 10 Size of message dequeued Bin(4) ++ (The maximum allowable size of a queue message is 64 K bytes.) 20 14 Access state modification option indicator Char(1) + and message selection criteria 20 14 Access state modification option when entering Dequeue wait Bit 0 + 0 = Access state is not modified 1 = Access state is modified 20 14 Access state modification option when leaving Dequeue wait Bit 1 + 0 = Access state is not modified 1 = Access state is modified 20 14 Multiprogramming level option Bit 2 + 0 = Leave current MPL set at Dequeue wait 1 = Remain in current MPL set at Dequeue wait 20 14 Time-out option Bit 3 + 0 = Wait for specified time, then signal time-out exception 1 = Wait indefinitely 20 14 Actual key to input key relationship Bits 4-7 + (for keyed queue) 0010 = Greater than 0100 = Less than 0110 = Not equal 1000 = Equal 1010 = Greater than or equal 1100 = Less than or equal 21 15 Search key (ignored for FIFO/LIFO queues but Char(key length) + must be present for FIFO/LIFO queues with nonzero key length values) * Message key Char(key length) ++
关于DEQ的详细文档,请参考Dequeue (DEQ)
dcl sysptr q auto init("Q31", ctx("LSBIN"), type(q, 02)) ; dcl dd prefix char(37) auto ; dcl spcptr prefix-ptr auto init(prefix) ; dcl spc prefix-t bas(prefix-ptr) ; dcl dd time-enqueued char(8) dir ; /* output */ dcl dd timeout char(8) dir ; dcl dd text-len bin(4) dir ; /* output */ dcl dd access-state-option char(1) dir ; dcl dd search-key char(8) ; dcl dd returned-key char(8) ; /* output */ dcl dd text char(64) auto bdry(16) ; dcl spcptr text-ptr auto init(text) ; entry * ext ; brk "DEQB" ; /* binary: 11000010 */ /* dequeue messages whose keys are greater than search-key */ cpybla access-state-option, x"1202" ; cpybla search-key, "00000001" ; deq(b) prefix, text-ptr, q / neq(not-found), eq(found) ; found: /* when a queue message dequeued, control flow will be branched here */ not-found: /* when no queue message dequeued, control flow will be branched here */ brk "DEQI" ; dcl dd ind-found char(1) auto ; dcl dd ind-not-found char(1) auto ; deq(i) prefix, text-ptr, q / neq(ind-not-found), eq(ind-found) ; /* when a queue message is dequeued, ind-found = '1', ind-not-found = '0' */ /* when no queue message is dequeued, ind-found = '0', ind-not-found = '1' */ rtx * ; pend ;
要无限期等待需要将DEQ指令的操作数1(message prefix)的输入参数 Access state modification option when entering Dequeue wait的 bit 3 Time-out option置为1(bit 0指最高位),如:
dcl dd prefix char(37) auto ; dcl spcptr prefix-ptr auto init(prefix) ; dcl spc prefix-t bas(prefix-ptr) ; dcl dd time-enqueued char(8) dir ; /* output */ dcl dd timeout char(8) dir ; dcl dd text-len bin(4) dir ; /* output */ dcl dd access-state-option char(1) dir ; /* set 'Time-out option' */ setbts access-state-option, 3;
dcl dd prefix char(37) auto ; dcl spcptr prefix-ptr auto init(prefix) ; dcl spc prefix-t bas(prefix-ptr) ; dcl dd time-enqueued char(8) dir ; /* output */ dcl dd timeout char(8) dir ; dcl dd text-len bin(4) dir ; /* output */ dcl dd access-state-option char(1) dir ; dcl dd seconds bin(4) auto init(7) ; /* 7 seconds */ mult(s) seconds, 125000 ; cpybtrls timeout, seconds, 17 ; /* copy seconds to timeout aligning from the left; shift 17 bits to the right */ /* clear 'Time-out option' */ clrbts access-state-option, 3;
注意系统时钟使用8字节表示,其中高49位每增1表示8个1/1000000秒。
CALL CLRUSRQ PARM(usrq_name)
i5/OS的每个Output Queue(简称OUTQ)可以可选地关联一个Data Queue,以便当OUTQ 上的一个spooled file到达READY状态时,向DTAQ上发送一个通知message。 将一个DTAQ关联到一个OUTQ,可以通过在使用CRTOUTQ或CHGOUTQ命令时,指定DTAQ参数; 或者通过添加一个系统级别的环境变量QIBM_NOTIFY_CRTSPLF。这种机制为用户应用 程序管理OUTQ上的spooled file提供了便利。
这里我们通过实验检查由通知DTAQ上可以得到哪些信息。额外地,作为补充说明, 附录 Appendix 5 A Joblog Saver Program 中的示例程序,将OUTQ QEZJOBLOG中的spooled file实时地存入数据文件,并在 保存后删除。
创建DTAQ
需要注意的是:
CRTDTAQ DTAQ(LSBIN/SPLF)
MAXLEN(144) /* to receive type '02' notifications */
SEQ(*FIFO)
SIZE(*MAX2GB)
TEXT('Notification Queue for Spooled Files')
添加环境变量QIBM_NOTIFY_CRTSPLF
如果希望环境变量只影响当前交互作业,使用参数LEVEL(*JOB);如果 希望环境变量影响当前系统,使用参数LEVEL(*SYS)。
ADDENVVAR ENVVAR(QIBM_NOTIFY_CRTSPLF)
VALUE('*DTAQ LSBIN/SPLF')
LEVEL(*SYS) /* take system wide effects */
检查DTAQ中得到的通知数据
随便编译一个RPG程序,以便生成一个spooled file; 使用 DSPQMSG 命令,分别查看spooled file通知entry的字符形式的十六进制形式内容,
/* DSPQMSG */ DSPQMSG Q(LSBIN/SPLF) /* character format */ -------------------Character data------------------- Column *...+....1....+....2....+....3....+....4....+....5 000001 '*SPOOL 02REDLIGHT LJL 254395AAA ' 000051 ' QPRINT QGPL REDLIGHT LJL 254395 ' 000101 ' S65FAA4B 1090518164906 ' /* hexadecimal format */ ------------------Hexadecimal data------------------ Column * . . . + . . . . 1 . . . . + . . . . 2 . . . . + 000001 '5CE2D7D6D6D340404040F0F2D9C5C4D3C9C7C8E34040D3D1D3' 000026 '40404040404040F2F5F4F3F9F5C1C1C1404040404040400000' 000051 '0001D8D7D9C9D5E340404040D8C7D7D3404040404040D9C5C4' 000076 'D3C9C7C8E34040D3D1D340404040404040F2F5F4F3F9F54040' 000101 '4040404040404040000000000000000000000003E2F6F5C6C1' 000126 'C1F4C24040F1F0F9F0F5F1F8F1F6F4F9F0F600 '
偏移(Decimal) | 偏移(Hexadecimal) | 数据类型/长度 | 含义 | 以上示例中的值 |
0 | 0 | char(10) | 功能码。总是'*SPOOL' | '*SPOOL ' |
10 | A | char(2) | 记录类别。'02' | '02' |
12 | C | char(26) | 作业标识。10字节作业名称,10字节USRPRF名称,和6字节作业号 | 'REDLIGHT LJL 254395' |
38 | 26 | char(10) | Spooled file名称 | 'AAA ' |
48 | 30 | binary(4) | Spooled file序号 | x'00000001' |
52 | 34 | char(20) | Output queue名称 | 'QPRINT QGPL ' |
72 | 48 | char(26) | 作业标识 | 'REDLIGHT LJL 254395' |
98 | 62 | char(10) | 用户数据 | ' ' |
108 | 6C | binary(4) | 保留 | x'00000000' |
112 | char(8) | 产出spooled file的线程ID | x'0000000000000003' | |
120 | 78 | char(10) | 产出spooled file的机器名 | 'S65FAA4B ' |
130 | 82 | char(7) | Spooled file创建日期。格式为CYYMMDD | '1090518' |
137 | 89 | char(6) | Spooled file创建时间 | '164906' |
143 | 8F | char(1) | 保留 | x'00' |
作为接收通知的用户应用程序,在出列到以上通知信息后,要使用CL命令(如CPYSPLF, DLTSPLF)或Spooled File API(如Open Spooled File (QSPOPNSP) API)操作目的Spooled File时, 可以通过通知信息中以下参数唯一确定一个Spooled File:
通常使用前三个参数即可。
下面的示例中,假设的客户程序与服务程序通过一个Keyed DTAQ通讯, DTAQ的键值表示消息的优先级,客户程序使用字符形式的'00'至'99'标识自己发送给 服务程序的消息的优先级,'00'为优先级最高,'99'为最低。服务程序出列 DTAQ中键值大于等于'00'的所有消息,优先级高的(键值小的)消息将先被出列并处理。
示例中涉及的组件:
这里的内容:
CRTDTAQ DTAQ(Q27) MAXLEN(16) /* entry length = 16 */ SEQ(*KEYED) /* DTAQ type: keyed */ KEYLEN(2) /* key length = 2 */ TEXT('for priority-based IPC')
/* * @file r105.rpgle * * enqueue DTAQ Q27 * */ h dftactgrp(*no) d i_main pr extpgm('R105') d priority 2a d delay 2p 0 d msg 14a /* prototype of API QSNDDTAQ */ d qsnddtaq pr extpgm('QSNDDTAQ') d qname 10a d qlib 10a d entry_len 5p 0 d entry 16a options(*varsize) d key_len 3p 0 options(*nopass) d key 2a options(*nopass:*varsize) d qname s 10a inz('Q27') d qlib s 10a inz('LSBIN') d entry_len s 5p 0 inz(16) d key_len s 3p 0 inz(2) /* queue entry format, used both by the client and the server */ d q_format ds qualified d dly_time 2p 0 /* how long to */ d msg 14a /* greeting words */ d i_main pi d priority 2a d delay 2p 0 d msg 14a /free q_format.dly_time = delay; q_format.msg = msg; qsnddtaq( qname : qlib : entry_len : q_format : key_len : priority ); *inlr = *on; /end-free /* eof - r105.rpgle */
调用示例:
/* 调用R105,参数如下: - priority: '01' - delay: 15, hex 015F - msg: 'morning :p' */ CALL PGM(R105) PARM('01' X'015F' 'morning :p') /* 使用DSPQMSG命令查看入列到DTAQ的message */ DSPQMSG Q(Q27) QTYPE(*DTAQ) /* /* 键值数据 */ -------------------Character data------------------- Column *...+....1....+....2....+....3....+....4....+....5 000001 '01 ' /* message text的 hex 表示 */ ------------------Hexadecimal data------------------ Column * . . . + . . . . 1 . . . . + . . . . 2 . . . . + 000001 '015F94969995899587407A9740404040 ' */
/* * @file r106.rpgle * * deq R106 * * queue message format * - delay, pkd(2,0), delay time * - msg, char(14), message from the client */ /* structure Qus_EC_t */ d qusec_t ds 256 qualified d bytes_in 10i 0 d bytes_out 10i 0 d exid 7a d reserved 1a d ex_data 240a /* prototype of API QRCVDTAQ */ d qrcvdtaq pr extpgm('QRCVDTAQ') d qname 10a d qlib 10a d entry_len 5p 0 d entry 64a options(*varsize) d timeout 5p 0 d key_order 2a options(*nopass) /* optional parameter group 1 */ d key_len 3p 0 options(*nopass) d key 8a options(*nopass:*varsize) /* input/output parameter */ d sender_info... d _len 3p 0 options(*nopass) d sender_info 1a options(*nopass:*varsize) d remove_msg 10a options(*nopass) /* optional parameter group 2 */ d receiver_len 5p 0 options(*nopass) d ec likeds(qusec_t) d options(*nopass) /* queue entry format, used both by the client and the server */ d q_format ds qualified d dly_time 2p 0 /* how long to */ d msg 14a /* greeting words */ d qname s 10a inz('Q27') d qlib s 10a inz('LSBIN') d entry_len s 5p 0 inz(16) d timeout s 5p 0 inz(-1) /* dequeue infinitely */ d key_order s 2a inz('GE') /* key order: >= parameter @key */ d key_len s 3p 0 inz(2) d key s 2a inz('00') d sender_info... d _len s 3p 0 inz(0) /* no sender info */ d sender_info s 1a d priority s 16a d qcmdexc pr extpgm('QCMDEXC') d cmd 64a options(*varsize) d cmdlen 15p 5 d dlyjob_cmd s 64a d len s 15p 5 d QUIT_CMDS c 'q Q qu QU quit QUITT' d KEY_ARG c '01' /free dow '1'; key = KEY_ARG; // deq DTAQ Q27 qrcvdtaq( qname : qlib : entry_len : q_format : timeout : key_order : key_len : key : sender_info_len : sender_info ); // should i quit? if %scan( %trim(q_format.msg) : QUIT_CMDS ) > 0; priority = 'Priority: 100'; q_format.msg = 'see you :)'; dsply priority '' q_format.msg; leave; endif; // delay job dlyjob_cmd = 'DLYJOB ' + %char(q_format.dly_time); len = %len(%trim(dlyjob_cmd)); qcmdexc(dlyjob_cmd : len); // display message priority = 'Priority: ' + key; dsply priority '' q_format.msg; enddo; *inlr = *on; /end-free /* eof - r106.rpgle */
现在执行服务程序和客户程序
/* 提交服务作业 */ SBMJOB CMD(CALL R106) /* 调用客户程序4次 */ CALL PGM(R105) PARM('99' X'015F' 'see you') CALL PGM(R105) PARM('03' X'001F' 'Third') CALL PGM(R105) PARM('02' X'001F' 'Second') CALL PGM(R105) PARM('01' X'001F' 'First') /* 到message queue QSYSOPR里检查并回复服务程序的消息 */ First Reply . . : a Second Reply . . : a Third Reply . . : a
要解决上面的两个问题,可以使用queue object模拟同步调用。 这种通讯模型的具体的实现方法有多种,而基本的共性是,请求发起程序 在通过入列queue object发送完请求后,通过出列另一个 queue object等待请求处理程序完成请求处理,并反馈处理结果。
作为示例,我们实现以下的一种模拟同步调用模型
以下介绍中,实现一个基于CL命令形式的客户程序ISQL,和服务它的 服务程序ISQLSVR。ISQL向请求DTAQ ISQL入列以UUID为键值的请求entry, entry内容为待执行的SQL语句;入列请求后, ISQL由响应DTAQ ISQLR以等待方式出列ISQLSVR的响应;ISQLSVR由请求DTAQ ISQL出列ISQL的 请求,执行SQL语句,使用出列时得到请求entry键值中包含的UUID作 为键值,将SQL code作为响应信息入列到响应DTAQ ISQLR;ISQL依据出列 到的SQL code,提示用户SQL语句执行是否成功。
ISQL示例中的组件如下:
这里的内容:
Offset | Parameter Name | Data Type | Meaning |
0 | SQL statement | char(256) | SQL statement string |
256 | -- end -- |
DTAQ ISQLR
Offset | Parameter Name | Data Type | Meaning |
0 | SQL code | bin(4) | SQL code |
4 | -- end -- |
创建DTAQ的CL命令
CRTDTAQ DTAQ(LSBIN/ISQL) MAXLEN(256) SEQ(*KEYED) KEYLEN(16) TEXT('Request Data Queue for ISQL') CRTDTAQ DTAQ(LSBIN/ISQLR) MAXLEN(4) SEQ(*KEYED) KEYLEN(16) TEXT('Reply Data Queue for ISQL')
/* @FILE ISQL.CMD */ CMD PROMPT('Run SQL statement') PARM KWD(SQL) TYPE(*CHAR) LEN(256) MIN(1) + INLPMTLEN(50) PROMPT('SQL statement') /* EOF */
编译CL命令ISQL
CRTCMD CMD(LSBIN/ISQL) PGM(*LIBL/ISQLCPP) SRCFILE(LS2008/DEC) SRCMBR(*CMD) TEXT('i can sql')
/* * @file isqlcpp.rpgle * * commmand processing program for CL command ISQL */ h dftactgrp(*no) d i_main pr extpgm('ISQLCPP') d sql 256a d sqlcod 10i 0 options(*nopass) /* structure Qus_EC_t */ d qusec_t ds 256 qualified d bytes_in 10i 0 d bytes_out 10i 0 d exid 7a d reserved 1a d ex_data 240a /* input checking procedure */ d input_check pr n /* * do_request * - send SQL request to server program ISQLSVR * - receive the SQLCOD returned by ISQLSVR * - report result message to the user * * @pre sql * @return sqlcod */ d do_request pr 10i 0 /* send message to interactive user */ d sendmsg pr d msg 256a d i_main pi d sql 256a d sqlcod 10i 0 options(*nopass) d msg s 256a d rtn s 10i 0 /free if input_check(); rtn = do_request(); endif; sendmsg(msg); // return SQLCOD to the caller when parameter sqlcod is specified if %parms() > 1; sqlcod = rtn; endif; *inlr = *on; /end-free /* eof - i_main() */ /* procedure input_check() */ p input_check b d input_check pi n /free if sql = *blanks; msg = 'Empty SQL statement'; return *off; endif; return *on; /end-free p input_check e /* procedure do_request() */ p do_request b d q_entry ds qualified d sql 256a d sqlcod 10i 0 d ch_sqlcod 4a overlay(sqlcod) d uuid_template ds qualified d bytes_in 10u 0 inz(32) d bytes_out 10u 0 d reserved 8a inz(x'0000000000000000') d uuid 16a d genuuid pr extproc('_GENUUID') d template * value /* prototype of API QSNDDTAQ */ d qsnddtaq pr extpgm('QSNDDTAQ') d qname 10a d qlib 10a d entry_len 5p 0 d entry 1a options(*varsize) d key_len 3p 0 options(*nopass) d key 1a options(*nopass:*varsize) /* prototype of API QRCVDTAQ */ d qrcvdtaq pr extpgm('QRCVDTAQ') d qname 10a d qlib 10a d entry_len 5p 0 d entry 1a options(*varsize) d timeout 5p 0 d key_order 2a options(*nopass) /* optional parameter group 1 */ d key_len 3p 0 options(*nopass) d key 1a options(*nopass:*varsize) /* input/output parameter */ d sender_info... d _len 3p 0 options(*nopass) d sender_info 1a options(*nopass:*varsize) d remove_msg 10a options(*nopass) /* optional parameter group 2 */ d receiver_len 5p 0 options(*nopass) d ec likeds(qusec_t) d options(*nopass) d qname s 10a d qlib s 10a inz('*LIBL') d entry_len s 5p 0 d timeout s 5p 0 inz(-1) /* dequeue infinitely */ d key_order s 2a inz('EQ') /* key order: >= parameter @key */ d key_len s 3p 0 inz(16) d key s 16a d sender_info... d _len s 3p 0 inz(0) /* no sender info */ d sender_info s 1a d ec ds likeds(qusec_t) d do_request pi 10i 0 /free // generate UUID genuuid(%addr(uuid_template)); // enqueue DTAQ ISQL entry_len = 256; qname = 'ISQL'; q_entry.sql = sql; qsnddtaq( qname : qlib : entry_len : q_entry.sql : key_len : uuid_template.uuid ); // dequeue DTAQ ISQL clear ec; ec.bytes_in = 256; qname = 'ISQLR'; entry_len = 4; qrcvdtaq( qname : qlib : entry_len : q_entry.ch_sqlcod : timeout : key_order : key_len : uuid_template.uuid : sender_info_len : sender_info ); if ec.bytes_out <> 0; msg = 'QRCVDTAQ() failed with exception ID: ' + ec.exid; return -9999; endif; // set msg according to returned SQLCOD if q_entry.sqlcod < 0; msg = 'SQL statement failed with SQLCOD ' + %char(q_entry.sqlcod); elseif q_entry.sqlcod > 0; msg = 'SQL statement succeeded with SQLCOD ' + %char(q_entry.sqlcod); else; msg = 'SQL statement succeeded'; endif; return q_entry.sqlcod; /end-free p do_request e /* procedure sendmsg() */ p sendmsg b /* prototype of QMHSNDPM */ d qmhsndpm pr extpgm('QMHSNDPM') d msgid 7a d msgf 20a d msgdata 512a options(*varsize) d msgdata_len 10i 0 d msgtype 10a d callstack_... d entry 10a d callstack_... d counter 10i 0 d msgkey 4a d ec likeds(qusec_t) d sendmsg pi d msg 256a d msgid s 7a inz('CPF9898') d msgf s 20a inz('QCPFMSG QSYS') d msgdata_len s 10i 0 d msgtype s 10a inz('*INFO') d callstack_... d entry s 10a inz('*PGMBDY') d callstack_... d counter s 10i 0 inz(1) d msgkey s 4a d ec ds likeds(qusec_t) d cmd s 512a d len s 15p 5 /free ec.bytes_in = 256; msgdata_len = %len(%trim(msg)); qmhsndpm( msgid : msgf : msg : msgdata_len : msgtype : callstack_entry : callstack_counter : msgkey : ec ); /end-free p sendmsg e /* eof - isqlcpp.rpgle */
/* * @file isqlsvr.sqlrpgle * * - dequeue SQL request from DTAQ ISQL * - process SQL statement * - reply client with SQLCOD */ /* to use activation group scope commitment control */ h dftactgrp(*no) actgrp('ISQLSVR') /* * procedure server_client * * - dequeue SQL request from DTAQ ISQL * - process SQL statement * - reply client with SQLCOD * * @return boolean, *off if the main procedure should quit. */ d serve_client pr n /* * procedure run_sql() * * @return SQLCOD */ d run_sql pr 10i 0 d sql 256a options(*varsize) /free dow serve_client(); enddo; *inlr = *on; /end-free p serve_client b /* structure Qus_EC_t */ d qusec_t ds 256 qualified d bytes_in 10i 0 d bytes_out 10i 0 d exid 7a d reserved 1a d ex_data 240a /* prototype of API QSNDDTAQ */ d qsnddtaq pr extpgm('QSNDDTAQ') d qname 10a d qlib 10a d entry_len 5p 0 d entry 1a options(*varsize) d key_len 3p 0 options(*nopass) d key 1a options(*nopass:*varsize) /* prototype of API QRCVDTAQ */ d qrcvdtaq pr extpgm('QRCVDTAQ') d qname 10a d qlib 10a d entry_len 5p 0 d entry 1a options(*varsize) d timeout 5p 0 d key_order 2a options(*nopass) /* optional parameter group 1 */ d key_len 3p 0 options(*nopass) d key 1a options(*nopass:*varsize) /* input/output parameter */ d sender_info... d _len 3p 0 options(*nopass) d sender_info 1a options(*nopass:*varsize) d remove_msg 10a options(*nopass) /* optional parameter group 2 */ d receiver_len 5p 0 options(*nopass) d ec likeds(qusec_t) d options(*nopass) d qname s 10a d qlib s 10a inz('*LIBL') d entry_len s 5p 0 d timeout s 5p 0 inz(-1) /* dequeue infinitely */ d key_order s 2a inz('GE') /* key order: >= parameter @key */ d key_len s 3p 0 inz(16) d key s 16a d sender_info... d _len s 3p 0 inz(0) /* no sender info */ d sender_info s 1a d ec ds likeds(qusec_t) d q_entry ds qualified d sql 256a d sqlcod 10i 0 d ch_sqlcod 4a overlay(sqlcod) d uuid s 16a d msg s 26a d rtn s 1a d QUIT_CMDS c 'q Q qu QU quit QUITT' d KEY_ARG c x'00000000000000000000000000000000' d serve_client pi n /free rtn = *on; // dequeue client request from DTAQ ISQL clear ec; ec.bytes_in = 256; qname = 'ISQL'; entry_len = 256; uuid = KEY_ARG; qrcvdtaq( qname : qlib : entry_len : q_entry.sql : timeout : key_order : key_len : uuid : sender_info_len : sender_info ); if ec.bytes_out <> 0; // error handling msg = 'QRCVDTAQ failed: ' + ec.exid; dsply 'Error' '' msg; return *off; endif; // should i quit? if %scan(%trim(q_entry.sql) : QUIT_CMDS) > 0; rtn = *off; else; // run sql statement q_entry.sqlcod = run_sql(q_entry.sql); endif; // enqueue reply to DTAQ ISQLR entry_len = 4; qname = 'ISQLR'; qsnddtaq( qname : qlib : entry_len : q_entry.ch_sqlcod : key_len : uuid ); return rtn; /end-free p serve_client e p run_sql b d run_sql pi 10i 0 d sql 256a options(*varsize) c/exec sql prepare stmt from :sql c/end-exec c if sqlcod < 0 c return sqlcod c endif c/exec sql execute stmt c/end-exec c return sqlcod p run_sql e /* eof - isqlsvr.sqlrpgle
CRTSQLRPGI OBJ(LSBIN/ISQLSVR) SRCFILE(LS2008/DEC) SRCMBR(*OBJ) COMMIT(*CS)
SBMJOB CMD(CALL PGM(ISQLSVR))
清空要使用SQL操作的物理文件
CLRPFM FILE(PF16)
使用ISQL命令执行删除物理文件PF16的操作
ISQL SQL('delete from pf16') /* 结果 SQL statement succeeded wich SQLCOD 100. */
ISQL SQL('insert into pf16 values(''Field 1'', ''Field 2'', ''Field 3'')') /* 结果 SQL statement succeeded. */
使用DSPJOB命令查看服务程序作业的事务控制信息
DSPJOB JOB(257356/LJL/LJL_DAILY) OPTION(*CMTCTL)
Commitment Opt Definition Text ISQLSVR Activation-group-level
Job: LJL_DAILY User: LJL Number: 257356 Commitment definition . . . . . . . . : ISQLSVR -------------Changes-------------- File Library Member Commit Rollback Pending PF16 LSBIN PF16 0 0 0 PF16 0 0 1
使用ISQL命令提交事务控制
ISQL SQL('commit') /* 结果 SQL statement succeeded. */
/* @FILE RUNSQL.CMD */ CMD PROMPT('Run SQL statement') PARM KWD(SQL) TYPE(*CHAR) LEN(256) MIN(1) + INLPMTLEN(50) PROMPT('SQL statement') PARM KWD(SQLCOD) TYPE(*CHAR) LEN(4) RTNVAL(*YES) + MIN(1) PROMPT('SQLCOD returned') /* EOF */
编译CL命令RUNSQL
CRTCMD CMD(LSBIN/RUNSQL) PGM(*LIBL/ISQLCPP) SRCFILE(LS2008/DEC) ALLOW(*BPGM *IPGM *BREXX *IREXX)
使用CL命令RUNSQL的示例OPM CL程序 cl103.clp
/* @FILE CL103.CLP */ PGM DCL VAR(&SQLCOD) TYPE(*CHAR) LEN(4) RUN_SQL: RUNSQL SQL('delete from pf16') SQLCOD(&SQLCOD) /* + clear PF16 */ RUNSQL SQL('insert into PF16 values(''A'', ''B'', + ''C'')') SQLCOD(&SQLCOD) /* insert new + records */ IF COND(%BIN(&SQLCOD) *LT 0) THEN(DO) SNDPGMMSG MSG('SQL operations failed.') + TOMSGQ(*TOPGMQ) MSGTYPE(*INFO) RUNSQL SQL('rollback') SQLCOD(&SQLCOD) /* rollbak + when insert operation failed */ GOTO CMDLBL(END) ENDDO RUNSQL SQL('commit') SQLCOD(&SQLCOD) /* commit */ END: ENDPGM /* EOF - CL103.CLP */
那么,如何基于queue object传递变长数据呢? 答案在于指针。 在 2. 对i5/OS Queue Object的介绍 中介绍Queue object的主要属性时, 已经提到,如果queue object的允许message包含指针属性为真, 则可以通过一个queue object传递MI指针。 在MI指针类型中,Space Pointer(下面简称为SPCPTR),用于描述各种存储的寻址属性 (自动存储,静态存储和堆存储)。 作为一个i5/OS MI指针, SPCPTR包含了MI指针的通用属性(如指针类型, 标记位)及SPCPTR的专有属性(如在space object中的偏移)。 SPCPT对等于i5/OS HLL(高级语言)中 指向自动存储,静态存储和堆存储的HLL指针, 如ILE RPG中的pionter类型,或C语言中的void *, char *,等等。 通过在一个允许message包含指针属性的queue object上传递SPCPTR, 可以让出列queue object的程序访问入列程序内部分配的变长数据, 从而达到基于queue object传递变长数据的目的。
包含指针的结构必须对齐到16字节
IBM没有提供创建一个允许queue message包含指针的DTAQ 的接口;因此,需要在queue object上传递指针时,我们 只能选择USRQ(user queue)。
SPCPTR并不是唯一可以由queue object传递的指针类型, 在queue object传递哪些指针类型,取决于软件的系统架构, 例如,可以通过在queue object上传递指向可执行的program object 的system pointer实现运行时刻的业务逻辑变更;如何 善用这项技术,规避负面影响,取决于作为软件设计者的你。
ISQL2组件
这里的内容:
Offset | Parameter Name | Data Type | Meaning |
0 | Pointer to SQL statement | SPCPTR | Pointer to SQL statement data |
16 | -- end -- |
DTAQ ISQLR2
Offset | Parameter Name | Data Type | Meaning |
0 | SQL code | bin(4) | SQL code |
4 | -- end -- |
创建USRQ的CL命令
/* 请求USRQ ISQL2 */ CALL PGM(QUSCRTUQ) PARM( 'ISQL2 LSBIN' /* USRQ name */ 'QQ' /* extended attribute */ 'K' /* queue type */ X'00000010' /* key length */ X'00000010' /* entry length */ X'00000010' /* initial number of messages */ X'00000010' /* number of messages per extension */ '*EXCLUDE' /* public authority */ 'ISQL request queue. key length = 16, entry length = 16' /* text description */ '*YES' /* replace */ X'0000002000000000000000000000000000000000000000000000000000000000' /* error code structure */ '*USER' /* object domain */ '*YES') /* contains poniter */ /* 响应USRQ ISQLR2 */ CALL PGM(QUSCRTUQ) PARM( 'ISQLR2 LSBIN' /* USRQ name */ 'QQ' /* extended attribute */ 'K' /* queue type */ X'00000010' /* key length */ X'00000004' /* entry length */ X'00000010' /* initial number of messages */ X'00000010' /* number of messages per extension */ '*EXCLUDE' /* public authority */ 'ISQL reply queue. key length = 16, entry length = 4') /* text description */
/* @FILE ISQL.CMD */ CMD PROMPT('Run SQL statement') PARM KWD(SQL) TYPE(*CHAR) LEN(256) MIN(1) + INLPMTLEN(50) PROMPT('SQL statement') /* EOF */
编译CL命令ISQL2
CRTCMD CMD(LSBIN/ISQL2) PGM(*LIBL/ISQLCPP2) SRCFILE(LS2008/DEC) SRCMBR(*CMD) TEXT('i can sql too :p')
/* * @file isqlcpp2.rpgle * */ h dftactgrp(*no) d sql_param_t ds qualified d sql_len 10i 0 d sql 32767a d i_main pr extpgm('ISQLCPP2') d sql_parm likeds(sql_param_t) d sqlcod 10i 0 options(*nopass) /* structure Qus_EC_t */ d qusec_t ds 256 qualified d bytes_in 10i 0 d bytes_out 10i 0 d exid 7a d reserved 1a d ex_data 240a /* * do_request * - send SQL request to server program ISQLSVR2 * - receive the SQLCOD returned by ISQLSVR2 * - report result message to the user * * @pre sql * @return sqlcod */ d do_request pr 10i 0 /* send message to interactive user */ d sendmsg pr d msg 256a /* * prototype of procedure resolve_qs * @return boolean, *on if both the request USRQ and the reply * USRQ are resolved. */ d resolve_qs pr n /* request USRQ ISQL2 */ d request_q s * /* reply USRQ ISQLR2 */ d reply_q s * d msg s 256a d rtn s 10i 0 d i_main pi d sql_parm likeds(sql_param_t) d sqlcod 10i 0 options(*nopass) /free // resolve request q, reply q if resolve_qs(); rtn = do_request(); endif; sendmsg(msg); // return SQLCOD to the caller when parameter sqlcod is specified if %parms() > 1; sqlcod = rtn; endif; *inlr = *on; /end-free /* procedure resolve_qs() */ p resolve_qs b /* prototype of MI instruction RSLVSP */ d rslvsp pr extproc('_RSLVSP2') d syp * d option * value d resolve_option ds 34 qualified d type 2a inz(x'0A02') d name 30a d auth 2a inz(x'0000') d rtn s n d resolve_qs pi n /free rtn = *on; // resolve request q, reply q monitor; // monitor MCH3401 resolve_option.name = 'ISQL2'; rslvsp(request_q : %addr(resolve_option)); resolve_option.name = 'ISQLR2'; rslvsp(reply_q : %addr(resolve_option)); on-error; msg = 'Failed to resolve request user queue' + ' or reply user queue'; rtn = *off; endmon; return rtn; /end-free p resolve_qs e /* procedure do_request() */ p do_request b d uuid_template ds qualified d bytes_in 10u 0 inz(32) d bytes_out 10u 0 d reserved 8a inz(x'0000000000000000') d uuid 16a d genuuid pr extproc('_GENUUID') d template * value /* prototype of MI instruction ENQ */ d enq pr extproc('_ENQ') d q * d prefix * value d text * value /* prototype of MI instruction DEQ */ d deq pr extproc('_DEQWAIT') d prefix * value d text * value d q * /* enq prefix */ d enq_prefix ds 20 qualified d text_len 10i 0 d key 16a /* deq prefix */ d deq_prefix ds 53 qualified d enqueue_time 8a d timeout 8a d text_len 10i 0 d acc_sts_mod 1a inz(x'D8') /* deq infinitely */ d key_in 16a d key_out 16a /* request data */ d request ds qualified d sql_len 10u 0 d reserved 8a /* align to 16 bytes boundary */ d sql_ptr * d request_ptr s * inz(%addr(request)) /* reply message from ISQLSVR2 */ d reply ds 16 qualified d sqlcod 10i 0 d do_request pi 10i 0 /free // generate UUID genuuid(%addr(uuid_template)); // enqueue SQL statement to USRQ ISQL2 enq_prefix.text_len = 16; enq_prefix.key = uuid_template.uuid; request.sql_len = sql_parm.sql_len; request.sql_ptr = %addr(sql_parm.sql); enq(request_q : %addr(enq_prefix) : %addr(request_ptr)); // dequeue SQLCOD from USRQ ISQLR2 deq_prefix.key_in = uuid_template.uuid; deq(%addr(deq_prefix) : %addr(reply) : reply_q); // set msg according to returned SQLCOD if reply.sqlcod < 0; msg = 'SQL statement failed with SQLCOD ' + %char(reply.sqlcod); elseif reply.sqlcod > 0; msg = 'SQL statement succeeded with SQLCOD ' + %char(reply.sqlcod); else; msg = 'SQL statement succeeded'; endif; return reply.sqlcod; /end-free p do_request e /* procedure sendmsg() */ p sendmsg b /* prototype of QMHSNDPM */ d qmhsndpm pr extpgm('QMHSNDPM') d msgid 7a d msgf 20a d msgdata 512a options(*varsize) d msgdata_len 10i 0 d msgtype 10a d callstack_... d entry 10a d callstack_... d counter 10i 0 d msgkey 4a d ec likeds(qusec_t) d msgid s 7a inz('CPF9898') d msgf s 20a inz('QCPFMSG QSYS') d msgdata_len s 10i 0 d msgtype s 10a inz('*INFO') d callstack_... d entry s 10a inz('*PGMBDY') d callstack_... d counter s 10i 0 inz(1) d msgkey s 4a d ec ds likeds(qusec_t) d cmd s 512a d len s 15p 5 d sendmsg pi d msg 256a /free clear ec; ec.bytes_in = 256; msgdata_len = %len(%trim(msg)); qmhsndpm( msgid : msgf : msg : msgdata_len : msgtype : callstack_entry : callstack_counter : msgkey : ec ); /end-free p sendmsg e /* eof - isqlcpp2.rpgle */
/* * @file isqlsvr.sqlrpgle * * - dequeue SQL request from USRQ ISQL2 * - process SQL statement * - reply client with SQLCOD */ /* to use activation group scope commitment control */ h dftactgrp(*no) actgrp('ISQLSVR2') /* * prototype of procedure resolve_qs * @return boolean, *on if both the request USRQ and the reply * USRQ are resolved. */ d resolve_qs pr n /* * procedure server_client * * - dequeue SQL request from USRQ ISQL2 * - process SQL statement * - reply client with SQLCOD * * @return boolean, *off if the main procedure should quit. */ d serve_client pr n /* * procedure run_sql() * * @return SQLCOD */ d run_sql pr 10i 0 d the_sql 5000a options(*varsize) /* request USRQ ISQL2 */ d request_q s * /* reply USRQ ISQLR2 */ d reply_q s * d msg s 26a /free // resolve request q and reply q if not resolve_qs(); msg = 'Failed to resolve user queues'; dsply '' '' msg; *inlr = *on; return; endif; // serves client dow serve_client(); enddo; *inlr = *on; /end-free /* procedure resolve_qs() */ p resolve_qs b /* prototype of MI instruction RSLVSP */ d rslvsp pr extproc('_RSLVSP2') d syp * d option * value d resolve_option ds 34 qualified d type 2a inz(x'0A02') d name 30a d auth 2a inz(x'0000') d rtn s n d resolve_qs pi n /free rtn = *on; // resolve request q, reply q monitor; resolve_option.name = 'ISQL2'; rslvsp(request_q : %addr(resolve_option)); resolve_option.name = 'ISQLR2'; rslvsp(reply_q : %addr(resolve_option)); on-error; msg = 'Failed to resolve request user queue' + ' or reply user queue'; rtn = *off; endmon; return rtn; /end-free p resolve_qs e p serve_client b /* prototype of MI instruction ENQ */ d enq pr extproc('_ENQ') d q * d prefix * value d text * value /* prototype of MI instruction DEQ */ d deq pr extproc('_DEQWAIT') d prefix * value d text * value d q * /* enq prefix */ d enq_prefix ds 20 qualified d text_len 10i 0 d key 16a /* deq prefix */ d deq_prefix ds 53 qualified d enqueue_time 8a d timeout 8a d text_len 10i 0 d acc_sts_mod 1a inz(x'DA') /* deq infinitely, key relation:'GE' */ d key_in 16a d key_out 16a /* request data */ d request ds qualified d based(request_ptr) d sql_len 10u 0 d reserved 8a /* align to 16 bytes boundary */ d sql_ptr * d request_ptr s * d sql_str ds based(request.sql_ptr) d qualified d string 32767a /* reply message to ISQLCPP2 */ d reply ds 16 qualified d sqlcod 10i 0 d quit_cmd s 4a d rtn s n d QUIT_CMDS c 'q Q qu QU quit QUITT' d MIN_KEY_VALUE c x'00000000000000000000000000000000' /* 16 bytes */ d sql s 32767a d serve_client pi n /free rtn = *on; // dequeue client's request from USRQ ISQL2 deq_prefix.text_len = 16; deq_prefix.key_in = MIN_KEY_VALUE; deq(%addr(deq_prefix) : %addr(request_ptr) : request_q); // run sql statement sql = %subst(sql_str.string : 1 : request.sql_len); quit_cmd = %subst(sql : 1 : 4); if %scan(%trim(quit_cmd) : QUIT_CMDS) > 0; rtn = *off; else; reply.sqlcod = run_sql(sql); endif; // enqueue reply message to USRQ ISQLR2 enq_prefix.text_len = 4; enq_prefix.key = deq_prefix.key_out; enq(reply_q : %addr(enq_prefix) : %addr(reply)); return rtn; /end-free p serve_client e p run_sql b d run_sql pi 10i 0 d the_sql 5000a options(*varsize) c/exec sql prepare stmt from :the_sql c/end-exec c if sqlcod < 0 c return sqlcod c endif c/exec sql execute stmt c/end-exec c return sqlcod p run_sql e /* eof - isqlsrv2.sqlrpgle */
CRTSQLRPGI OBJ(LSBIN/ISQLSVR2) SRCFILE(LS2008/DEC) SRCMBR(*OBJ) COMMIT(*CS)
/* @FILE RUNSQL2.CMD */ CMD PROMPT('Run SQL statement') PARM KWD(SQL) TYPE(*CHAR) LEN(5000) MIN(1) + VARY(*YES *INT4) INLPMTLEN(50) + PROMPT('SQL statement') PARM KWD(SQLCOD) TYPE(*CHAR) LEN(4) RTNVAL(*YES) + MIN(1) PROMPT('SQLCOD returned') /* EOF */
编译CL命令RUNSQL2
CRTCMD CMD(LSBIN/RUNSQL2) PGM(*LIBL/ISQLCPP2) SRCFILE(LS2008/DEC) ALLOW(*BPGM *IPGM *BREXX *IREXX)
/* @FILE CL104.CLP */ PGM DCL VAR(&SQLCOD) TYPE(*CHAR) LEN(4) RUN_SQL: RUNSQL2 SQL('delete from pf16') SQLCOD(&SQLCOD) /* + clear PF16 */ RUNSQL2 SQL('insert into PF16 values(''A'', ''B'', + ''C'')') SQLCOD(&SQLCOD) /* insert new + records */ IF COND(%BIN(&SQLCOD) *LT 0) THEN(DO) SNDPGMMSG MSG('SQL operations failed.') + TOMSGQ(*TOPGMQ) MSGTYPE(*INFO) RUNSQL2 SQL('rollback') SQLCOD(&SQLCOD) /* rollbak + when insert operation failed */ GOTO CMDLBL(END) ENDDO RUNSQL2 SQL('commit') SQLCOD(&SQLCOD) /* commit */ END: ENDPGM /* EOF - CL104.CLP */
CRTCLPGM PGM(LSBIN/CL104) SRCFILE(LS2008/DEC) LOG(*YES)
分别使用CL命令ISQL2,调用CL104操作物理文件PF16
/* 提交服务程序作业 */ SBMJOB CMD(CALL PGM(ISQLSVR2)) /* 清空PF16 */ ISQL2 SQL('delete from pf16') ISQL2 SQL('commit') /* 执行CL104 */ CALL CL104 400 - RUNSQL2 SQL('delete from pf16') SQLCOD(&SQLCOD) SQL statement succeeded wich SQLCOD 100. 600 - RUNSQL2 SQL('insert into PF16 values(''A'', ''B'', ''C'')') SQLCOD(&SQLCOD) SQL statement succeeded. 1700 - RUNSQL2 SQL('commit') SQLCOD(&SQLCOD) SQL statement succeeded. - RETURN /* RETURN due to end of CL program */ /* 通知ISQLSVR2退出 */ ISQL2 SQL('qu')
IBM没有提供由Java直接访问User Queue的接口,要在Java程序中使用User Queue需要使用主机语言编写访问User Queue的JNI方法。
作为IPC方法,Queue Object为Java程序与Java程序间,或Java程序与主机语言程序间进行进程间通讯带来了这样的好处
这里,作为示例,我们将使用Java实现 6.5. 通过i5/OS queue object传递变长数据 中的客户程序ISQL2。
Java版本的ISQL2组件如下:
这里的内容:
public class isql { static { System.loadLibrary("ISQLSVR"); } public static final String _usage = "usage info: java isql \"SQL statement to run.\""; public static void main(String[] args) { isql client = new isql(); client.run(args); } protected void run(String[] args) { if(args.length < 1) { System.out.println(_usage); return; } String msg = ""; try { // send request to ISQLSVR int sqlcod = isqlsvr.sendRequest(args[0]); // compose msg if(sqlcod < 0) msg = "SQL statement failed with SQLCOD " + sqlcod; else msg = "SQL statement succeeded with SQLCOD " + sqlcod; } catch(Exception e) { System.out.println(e.getMessage()); return; } // report execuation result System.out.println(msg); } } /* eof - isql.java */
public class isqlsvr { public static native synchronized int sendRequest( String sql ) throws Exception; } /* eof - isqlsvr.java */
编译Java源码
/* 将源码上传到IFS */ /* 启动一个QShell session */ qsh /* 确定CLASSPATH环境变量中包含当前路径,如: */ export CLASSPATH=. /* 切换到源码目录 */ /* 使用javac编译Java类 */ javac -encoding utf-8 *java /* 使用javah生成JNI方法头文件isqlsvr.h */ javah isqlsvr
查看流文件的CCSID属性可以使用CL命令wrklnk,然后在文件项上使用 8(Display attributes)选项;或使用QShell命令attr,如
attr isql.java
# include <isqlsvr.h> // libc # include <stdlib.h> # include <string.h> # include <except.h> // libmi # include <mih/rslvsp.h> # include <mih/enq.h> # include <mih/genuuid.h> // libiconv # include <iconv.h> // apis # include <qusec.h> # include <qusrjobi.h> typedef _Packed struct _deq_prefix { char time_enqueued[8]; char timeout[8]; int text_len_returned; char acc_state_mod[1]; char key_in[16]; char key_out[16]; } deq_prefix_t; // prototype of instruction DEQWAIT # pragma linkage(_DEQWAIT, builtin) void _DEQWAIT ( deq_prefix_t *, void*, _SYSPTR* ); # define REQUEST_Q "ISQL2" # define REPLY_Q "ISQLR2" # define UTF8_CCSID \ "IBMCCSID012080000000\0\0\0\0\0\0\0\0\0\0\0\0" # define ECLEN 256 # define TEXTLEN 16 # define KEYLEN 16 # define ENQ_PREFIX_LEN 20 # define DEQ_PREFIX_LEN 53 typedef _Packed struct _request { size_t sql_len; char reserved[8]; char *sql; } request_t; static request_t _r; typedef _Packed struct _reply { int sqlcod; char reserved[12]; } reply_t; void gen_uuid(char *uuid); int get_job_ccsid(); void utf8_2_ebcdic ( const char *source, size_t source_length, char *dest ); void enq_request( _SYSPTR q, const char *key, const char *sql ); int deq_reply(_SYSPTR q, const char* key); int resolve_queues( _SYSPTR *request_q, _SYSPTR *reply_q ); /* * implements isqlsvr.sendRequest() * * - resolve request q * - resolve reply q * - enq SQL */ JNIEXPORT jint JNICALL Java_isqlsvr_sendRequest ( JNIEnv *env, jclass jthis, jstring jsql ) { int r = 0; int sqlcod = 0; jclass class_exception; const char *utf8_sql = NULL; char *sql = NULL; jboolean is_copy = JNI_FALSE; size_t len = 0; char uuid[16] = {0}; _SYSPTR request_q = NULL; _SYSPTR reply_q = NULL; /* resolve qs */ if(resolve_queues(&request_q, &reply_q) != 0) { // raise exception and return # pragma convert(819) class_exception = (*env)->FindClass(env, "java/lang/Exception"); # pragma convert(0) if(class_exception != 0) (*env)->ThrowNew(env, class_exception, "Failed to resolve user queues" ); return; } // convert sql to ebcdic utf8_sql = (*env)->GetStringUTFChars(env, jsql, &is_copy); len = (*env)->GetStringUTFLength(env, jsql); sql = malloc(len * 2 + 1); utf8_2_ebcdic(utf8_sql, len, sql); (*env)->ReleaseStringUTFChars(env, jsql, utf8_sql); // enq request q gen_uuid(uuid); enq_request(request_q, uuid, sql); // deq reply q sqlcod = deq_reply(reply_q, uuid); free(sql); return sqlcod; } void gen_uuid(char *uuid) { _UUID_Template_T tmpl; memset(&tmpl, 0, 32); tmpl.bytesProv = 32; _GENUUID(&tmpl); memcpy(uuid, tmpl.uuid, 16); } int get_job_ccsid() { int ccsid = 0; Qwc_JOBI0400_t jobi; Qus_EC_t *ec = NULL; ec = (Qus_EC_t*)malloc(ECLEN); memset(ec, 0, ECLEN); ec->Bytes_Provided = ECLEN; jobi.Bytes_Avail = sizeof(Qwc_JOBI0400_t); QUSRJOBI( &jobi, sizeof(Qwc_JOBI0400_t), "JOBI0400", "* ", " ", ec ); if(ec->Bytes_Available != 0) ccsid = 37; // ascii ccsid else ccsid = jobi.Coded_Char_Set_ID; free(ec); return ccsid; } void utf8_2_ebcdic ( const char *source, size_t source_length, char *dest ) { size_t result_length = source_length * 2; char host_ccsid[32 + 1] = {0}; iconv_t cvt; sprintf(host_ccsid, "IBMCCSID%05d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", get_job_ccsid()); cvt = iconv_open(host_ccsid, UTF8_CCSID); iconv(cvt, &source, &source_length, &dest, &result_length ); iconv_close(cvt); } void enq_request( _SYSPTR q, const char *key, char *sql ) { _ENQ_Msg_Prefix_T *prefix; request_t *request = &_r; char text[16] = {0}; prefix = (_ENQ_Msg_Prefix_T*)malloc(ENQ_PREFIX_LEN); memset(prefix, 0, ENQ_PREFIX_LEN); prefix->Msg_Len = TEXTLEN; memcpy(prefix->Msg, key, KEYLEN); request->sql_len = strlen(sql); request->sql = sql; memcpy(text, &request, 16); _ENQ(&q, prefix, text); free(prefix); } int deq_reply(_SYSPTR q, const char* key) { deq_prefix_t prefix; reply_t reply; prefix.acc_state_mod[0] = 0xF8; // 1111,1000; key realtion=EQ, deq infinitely memcpy(prefix.key_in, key, KEYLEN); _DEQWAIT(&prefix, &reply, &q); return reply.sqlcod; } int resolve_queues( _SYSPTR *request_q, _SYSPTR *reply_q ) { int resolved = -1; volatile int ca = 0; # pragma exception_handler(end, ca, _C1_OBJECT_NOT_FOUND, \ _C2_ALL, _CTLA_HANDLE) *request_q = rslvsp(0x0A02, REQUEST_Q, "", 0x0000); *reply_q = rslvsp(0x0A02, REPLY_Q, "", 0x0000); resolved = 0; # pragma disable_handler end: return resolved; } /* eof - isqlsvr.c */
编译Service Program ISQLSVR
/* 编译module ISQLSVR */ CRTCMOD MODULE(LSBIN/ISQLSVR) SRCSTMF(isqlsvr.c) INCDIR('.') /* 创建*SRVPGM ISQLSVR */ CRTSRVPGM LSBIN/ISQLSVR MODULE(*LIBL/ISQLSVR) EXPORT(*ALL)
/* clear physical file PF16 */ CLRPFM PF16 /* submit job ISQLSVR2 */ SBMJOB CMD(CALL ISQLSVR2) /* 修改当前路径为isql.class, isqlsvr.class所在的IFS目录,如 */ CHGCURDIR '/home/ljl/tmp' /* run java class isql */ JAVA CLASS(isql) PARM('delete from pf16') /* 结果 SQL statement succeeded with SQLCOD 100 Java program completed */ JAVA CLASS(isql) PARM('insert into pf16 values(''20081212'', ''冬天'',''Friday'')') /* 结果 SQL statement succeeded with SQLCOD 0 Java program completed */ /* check physical file PF16 */ RUNQRY *N PF16 /* 结果 Line ....+....1....+....2....+....3....+. RQ NAME REMARK 000001 20081212 冬天 Friday ******** End of report ******** */
CALL PGM(QUSCRTUQ) PARM( 'Q11 LSBIN' /* queue name: LSBIN/Q11 */ 'USRQ' /* extended attribute */ 'F' /* queue type: keyed */ X'00000000' /* key length: 0 */ X'00000040' /* max entry length: 64 */ X'00010000' /* initial number of messages: 65536 */ X'00001000' /* extend number of messages: 4096 */ '*EXCLUDE' /* public authority */ 'FIFO USRQ: entry length = 64' /* text description */ )
入列 USRQ Q11的MI程序enq11.mi
dcl sysptr q auto ; dcl dd message-prefix char(4) auto; dcl dd prefix-msglen bin(4) def(message-prefix) pos(1); dcl dd message char(64) auto init("is usrq more faster?") ; dcl spcptr .message auto init(message) ; entry * ext; dcl dd index bin(4) auto init(0) ; cpybla rslv-option-obj-type, x"0A02"; cpyblap rslv-option-obj-name, "Q11", " "; rslvsp q, rslv-option-short, *, x'0000'; /* enq */ cpynv prefix-msglen, 64; cpynv index, 0 ; enq-loop: addn(s) index, 1 ; cmpnv(b) index, 1000 / hi(end-loop) ; enq q, message-prefix, .message; b enq-loop ; end-loop: rtx *; %include ptrres.mi ; pend; /* eof - enq11.mi */
ILE RPG程序renq11.rpgle 负责调用program enq11完成1000次入列操作,并统计用时:
/* * @file renq11.rpgle * * call PGM ENQ11 for 1000 times */ d b s z d e s z d ind s 10i 0 d dur s 15p 0 c eval b = %timestamp() c call 'ENQ11' c eval e = %timestamp() c eval dur = %diff(e : b : *ms) c 'microseconds'dsply dur c seton lr /* eof - renq11.rpgle */
调用程序RENQ11 10次,执行结果如下:
DSPLY microseconds 4000 DSPLY microseconds 4000 DSPLY microseconds 4000 DSPLY microseconds 4000 DSPLY microseconds 4000 DSPLY microseconds 4000 DSPLY microseconds 4000 DSPLY microseconds 4000 DSPLY microseconds 4000 DSPLY microseconds 4000 /* 结果: 10次调用的平均结果为4000 micro-seconds(0.004s) */
使用 i5/OS Programmer's Toolkit的 DSPQD:命令查看USRQ Q11
DSPQD Q(LSBIN/Q11) QTYPE(*USRQ)
/* DSPQD命令的部分输出 */
Current maximum number of
messages . . . . . . . . . : 65536
Current number of messages
enqueued . . . . . . . . . : 10000
Extension value . . . . . . : 4096
Key length . . . . . . . . . : 0
Maximum size of message to be
enqueued . . . . . . . . . : 64
Maximum number of extends . : 35
Current number of extends . : 0
Initial number of messages . : 65536
CRTDTAQ DTAQ(LSBIN/Q12) /* queue name */ MAXLEN(64) /* entry length: 64 */ SEQ(*FIFO) /* queue type: keyed */ SIZE(*MAX16MB 65536) /* initial number of messages: 65536 */ TEXT('FIFO DTAQ: entry length=64')
ILE RPG程序renq12.rpgle 负责完成1000次入列 DTAQ Q12的操作,并统计用时:
/* * @file renq12.rpgle * * enqueue DTAQ Q12 for 1000 times */ d b s z d e s z d ind s 10i 0 d dur s 15p 0 c eval b = %timestamp() c for ind = 1 to 1000 by 1 c call 'QSNDDTAQ' c parm 'Q12' qname 10 c parm 'LSBIN' qlib 10 c parm 64 entlen 5 0 c parm *all'1' ent 64 c endfor c eval e = %timestamp() c eval dur = %diff(e : b : *ms) c 'microseconds'dsply dur c seton lr /* eof */
调用程序RENQ12 10次,执行结果如下:
DSPLY microseconds 17000 DSPLY microseconds 15000 DSPLY microseconds 15000 DSPLY microseconds 15000 DSPLY microseconds 15000 DSPLY microseconds 15000 DSPLY microseconds 15000 DSPLY microseconds 14000 DSPLY microseconds 15000 DSPLY microseconds 15000 /* 结果: 10次调用的平均结果为15100 micro-seconds(0.0151s) */
CALL PGM(QCLRDTAQ) PARM('Q12' 'LSBIN')
对DTAQ Q12启用日志:
STRJRNOBJ OBJ(LSBIN/Q12) OBJTYPE(*DTAQ) JRN(LSBIN/JRN01)
调用 Appendix 2. Enqueue DTAQ Q12(not journaled) for 1000 times 中的ILE RPG程序RENQ12 10次,执行结果如下:
DSPLY microseconds 4066000 DSPLY microseconds 4019000 DSPLY microseconds 4145000 DSPLY microseconds 4018000 DSPLY microseconds 4007000 DSPLY microseconds 4206000 DSPLY microseconds 4031000 DSPLY microseconds 4042000 DSPLY microseconds 4023000 DSPLY microseconds 4042000 /* 结果: 10次调用的平均结果为4059900 micro-seconds(4.0599s) */
创建DTAQ
创建DTAQ JOBLOGNTF,类型为FIFO,entry长度128
CRTDTAQ DTAQ(LSBIN/JOBLOGNTF) MAXLEN(128) SIZE(*MAX2GB) AUTORCL(*YES) TEXT('i watch joblogs')
准备保存joblog的数据文件JOBLOG
CRTPF FILE(LSBIN/JOBLOG) RCDLEN(132) IGCDTA(*YES)
修改OUTQ的DTAQ属性
CHGOUTQ OUTQ(QEZJOBLOG) DTAQ(LSBIN/JOBLOGNTF)
Joblog Saver程序
编写ILE RPG程序 savjoblog.rpgle
/* * @file savjoblog.rpgle * * @remark 需要退出时,向DTAQ JOBLOGNTF入列个QUIT * e.g. CALL PGM(QSNDDTAQ) PARM('JOBLOGNTF' * 'LSBIN' X'00010F' 'QUIT') * */ h dftactgrp(*no) /* * dequeue a notification message from DTAQ JOBLOGNTF * * @return *on if okey, *off if notified to quit. */ d deq_notify pr n /* * copy joblog to PF JOBLOG; * delete spooled file. */ d wrk_with_splf pr /* Notification information */ d notify ds 128 qualified d func_code 10a d rec_type 2a d job_id 26a d splf_name 10a d splf_num 10i 0 /free dow deq_notify(); wrk_with_splf(); enddo; *inlr = *on; /end-free /* deq_notify() */ p deq_notify b /* prototype of API QRCVDTAQ */ d qrcvdtaq pr extpgm('QRCVDTAQ') d qname 10a d qlib 10a d qentrylen 5p 0 d qentry 128a d timeout 5p 0 d qname s 10a inz('JOBLOGNTF') d qlib s 10a inz('LSBIN') d qentrylen s 5p 0 inz(128) d timeout s 5p 0 inz(-1) d quit c 'QUIT' d deq_notify pi n /free qrcvdtaq( qname : qlib : qentrylen : notify : timeout ); if %subst(notify.func_code : 1 : 4) = quit; return *off; endif; return *on; /end-free p deq_notify e p wrk_with_splf b /* prototype of API QCMDEXC */ d qcmdexc pr extpgm('QCMDEXC') d cmdstr 128 options(*varsize) d cmdlen 15p 5 d cmd s 128a d len s 15p 5 d jid s 28a /free jid = %subst(notify.job_id:21:6) + '/' + %trim(%subst(notify.job_id:11:10)) + '/' + %trim(%subst(notify.job_id: 1:10)); monitor; // copy spooled file to PF JOBLOG cmd = 'CPYSPLF FILE(' + %trim(notify.splf_name) + ') TOFILE(LSBIN/JOBLOG) JOB(' + %trim(jid) + ') SPLNBR(' + %char(notify.splf_num) + ') MBROPT(*ADD)'; len = %len(%trim(cmd)); qcmdexc(cmd : len); // delete spooled file cmd = 'DLTSPLF FILE(' + %trim(notify.splf_name) + ') JOB(' + %trim(jid) + ') SPLNBR(' + %char(notify.splf_num) + ') '; len = %len(%trim(cmd)); qcmdexc(cmd : len); on-error; endmon; /end-free p wrk_with_splf e /* eof */
提交Joblog Saver作业
SBMJOB CMD(CALL PGM(LSBIN/SAVJOBLOG)) JOB(JOBLOGSVR)
/* 下面的命令将导致被提交的作业在正常退出后,记录joblog */
SBMJOB CMD(DLYJOB DLY(3)) JOB(MAKEJOBLOG) LOG(*JOBD *JOBD *SECLVL)
5722SS1 V5R2M0 020719 Job Log 810 09/05/19 14:24:35 Page 1 Job name . . . . . . . . . . : MAKEJOBLOG User . . . . . . : LJL Number . . . . . . . . . . . : 254693 Job description . . . . . . : LJL_DAILY Library . . . . . : QGPL MSGID TYPE SEV DATE TIME FROM PGM LIBRARY INST TO PGM LIBRARY INST CPF1124 Information 00 09/05/19 14:24:32.615208 QWTPIIPP QSYS 05D4 *EXT *N Message . . . . : Job 254693/LJL/MAKEJOBLOG started on 09/05/19 at 14:24:32 in subsystem QBATCH in QSYS. Job entered system on 09/05/19 at 14:24:32. CPI1125 Information 00 09/05/19 14:24:32.619792 QWTPCRJA QSYS 0108 *EXT *N Message . . . . : Job 254693/LJL/MAKEJOBLOG submitted. Cause . . . . . : Job 254693/LJL/MAKEJOBLOG submitted to job queue QBATCH in QGPL from job 254687/LJL/GREENTEA. Job 254693/LJL/MAKEJOBLOG was started using the Submit Job (SBMJOB) command with the following job attributes: JOBPTY(5) OUTPTY(5) PRTTXT() RTGDTA(QCMDB) SYSLIBL(QSYS QSYS2 QHLPSYS QUSRSYS) CURLIB(LSBIN) INLLIBL(QGPL QTEMP LSBIN LJLCP ATS XTXRUN MN I5TOOLKIT) LOG(4 00 *SECLVL) /* .... ... */
通知joblog saver作业退出
当savjoblog.rpgle在DTAQ JOBLOGNTF上 接收一条内容为‘QUIT'的entry时,认为被通知退出。因此通知joblog saver作业退出只需要:
CALL PGM(QSNDDTAQ) PARM('JOBLOGNTF' 'LSBIN' X'00010F' 'QUIT')
在我维护的开源项目 i5/OS Programmer's Toolkit 中,多半代码是MI的。 在此,不得不感谢 Mr. Leif Svalgaard,他在 midrange.com 的文章为我学习MI提供了莫大帮助。 还有几个我常去的网站,相信大家也常常去看,比如:
我的邮件: junleili-cn@users.sourceforge.net,欢迎同行们的意见与建议 :)