Chinaunix首页 | 论坛 | 博客
  • 博客访问: 304385
  • 博文数量: 40
  • 博客积分: 1
  • 博客等级: 民兵
  • 技术积分: 670
  • 用 户 组: 普通用户
  • 注册时间: 2011-07-31 11:19
个人简介

从事银行核心系统设计开发的程序猿

文章存档

2019年(1)

2018年(4)

2017年(11)

2016年(6)

2015年(18)

分类: 信息化

2016-04-05 02:34:06

*异常处理


在核心系统开发和运行中,异常处理是特别重要的一环。异常的规划,对于系统的表现能力和查错能力有着巨大的作用,仔细的设计是十分值得的。
之前的报文格式中,提到Firebird的返回码,是10位STRING。如果交易正常,返回码为'OK',如果出错,则为错误代码。如果为AS400系统级错误,则为F+系统错误代码,如FCPF3282。如果是应用错误,则为E+应用错误代码,如ECNBRNORL。另外,在返回报文中,还返回出错的程序源文件名,以及如果是系统错误,要在日志中记录出错的代码行号,访问的物理文件或者逻辑文件名等备查。


应用部分的出错信息处理比较简单。Firebird没有使用MSGF,而是普通的PF定义错误代码表,里面使用&1到&9用于文本的替换。应用的错误代码根据模块适用范围,定义为E+[一级模块]+[二级模块]+错误代码,通用的错误代码模块名可以省略。例如,EALEX表示“&1中&2记录已经存在”,ECNCMATDC表示“账户的余额性质&1和余额方向&2不符”。在RPGLE程序中报应用错误的示例如下。

  1. /FREE
  2. CHAIN K_CNTLMF ACNTLMF.RACNTLMF R_CNTLMF1;
  3. IF %FOUND(ACNTLMF); //柜员已存在报错
  4. PMG.BKMSID = 'EALEX';
  5. A_MSDS(1) = C_MGTLMF;
  6. A_MSDS(2) = C_MGTLNO + TLADF1.BKTLNO;
  7. EXSR #ERR;
  8. ENDIF;
  9. /END-FREE
  10. **********************************************************************
  11. ** #ERR 取错误信息
  12. **********************************************************************
  13. C #ERR BEGSR
  14. **
  15. C EVAL PMG.MSFLNM = PG_SRCMBR
  16. C IF PMG.BKMSDS = *BLANKS
  17. C CALL 'SCNCMMG'
  18. C PARM PMG
  19. C PARM A_MSDS
  20. C ENDIF
  21. C EXSR #EXIT
  22. **
  23. C ENDSR
  24. **********************************************************************
其中PMG结构保存了错误代码,报错文件名,错误描述,在#ERR这个公用过程中,调用了SCNCMMG程序,以A_MSDS数组对&1到&9进行了替换,结果放在PMG.BKMSDS中,层层传递到最外层主控程序。PMG.MSFLNM使用的是PSDS结构,这个在接下来会有介绍。还记得V7R1中的新增内置函数%SCANRPL吗,用于错误信息替换正合适,V6R1就只能自己写一大段处理程序了。



相对于应用报错的写法,RPGLE程序中系统错误的捕获和处理就有技巧得多。要做到使得应用程序简单,又不缩减错误信息,还要不遗漏所有出错场景处理,并不容易。在RPGLE程序中,处理异常通常有4种方法,有置出错指示器,(E)加%ERROR内置函数,设置错误处理过程,MONITOR和ON-ERROR语句组。前两种是以往系统常见的写法,但是应用程序就不好看,大段的系统错误处理把正常逻辑都淹没了。因此,Firebird选用了对应用最透明的错误处理过程写法,达到绝大多数情况无需应用关注即可捕获异常组织错误信息,如果有特殊处理,则可结合(E)加%ERROR使用,满足特殊要求。下面分别详细介绍程序错误和文件错误的错误处理过程。

程序错误的捕获和报错,在RPGLE中使用的是PSDS自动结构,以及*PSSR异常处理过程。即在遇到错误时,系统自动会设置PSDS结构内容,并调用*PSSR过程。

  1. **程序状态结构
  2. DRPGPSDS SDS
  3. D PG_MAINPROC 1 10A
  4. D PG_STATUS 11 15A
  5. D PG_PRVSTAT 16 20A
  6. D PG_SRCLINE 21 28A
  7. D PG_ROUTINE 29 36A
  8. D PG_PARMNUM 37 39S 0
  9. D PG_MSID 40 46A
  10. D PG_PGMLIB 81 90A
  11. D PG_EXCPDT 91 170A
  12. D PG_LSERRFL 175 184A
  13. D PG_JOBDATE 191 198A
  14. D PG_FLINFO 209 243A
  15. D PG_JOBNAME 244 253A
  16. D PG_JOBUSER 254 263A
  17. D PG_JOBNUM 264 269S 0
  18. D PG_RUNDATE 276 281S 0
  19. D PG_RUNTIME 282 287S 0
  20. D PG_SRCFILE 304 313A
  21. D PG_SRCLIB 314 323A
  22. D PG_SRCMBR 324 333A
  23. D PG_PGMNAME 334 343A
  24. D PG_MODNAME 344 353A
  25. D PG_SRCLNADD 354 355B 0
  26. D PG_FLILNADD 356 357B 0
  27. D PG_USRPRF 358 367A
  28. D PG_EXTERR 368 371I 0
  29. **********************************************************************
  30. **特殊变量
  31. **是否执行过错误处理程序
  32. DPG_ERYNFG S LIKE(DICT.@@YNFG) INZ(YNFG_NO)
  33. **********************************************************************
  34. ** *PSSR程序异常处理
  35. **********************************************************************
  36. C *PSSR BEGSR
  37. **
  38. C IF PG_ERYNFG = YNFG_NO
  39. C EVAL PG_ERYNFG = YNFG_YES
  40. C EVAL PMG.MSFLNM = PG_SRCMBR
  41. C EVAL PMG.MSCDLN = PG_SRCLINE
  42. C EVAL PMG.BKMSID = 'F' + PG_MSID
  43. C EVAL PMG.BKMSDS = PG_EXCPDT
  44. C EVAL PMG.OTMSDS = PG_STATUS+' '+PG_PGMLIB+' '+
  45. C PG_PGMNAME
  46. C EXSR #EXIT
  47. C ENDIF
  48. **
  49. C ENDSR
  50. **********************************************************************
注意这里为了简化,PSDS没有定义为QUALIFIED。PG_ERYNFG的作用,是为了防止错误处理程序的多次重入(避免错误程序中又抛出系统异常,导致无限递归)。
*PSSR过程中,通过结合PSDS结构,直接设置了系统异常错误代码(前面加了'F'),出错源文件名,出错源码行号,异常信息,以及附加信息,做到了报错信息组织的全自动化。



文件错误的捕获和报错,相对就复杂一些。在RPGLE中使用的是INFDS自动结构,以及INFSR异常处理过程。即在遇到错误时,系统根据F表定义文件时指定的名字,自动会设置INFDS结构内容,并调用INFSR过程。但是,这里有特例,如果在F表时就有错误(比如自动打开的文件不存在),那么仍然会执行系统默认异常处理而造成MSGW,这时候就需要外层主控进行捕获处理了。示例代码节选如下。

  1. FCCNTLCA IF E K DISK INFSR(#FLEX) INFDS(S_CNTLCA)
  2. F QUALIFIED
  3. FACNTLTC UF A E K DISK COMMIT QUALIFIED
  4. F INFSR(#FLEX) INFDS(S_CNTLTC)
  5. **********************************************************************
  6. **记录被锁错误信息
  7. DC_MGLOCK C CONST('记录被锁')
  8. **文件状态结构
  9. DFILESDS DS BASED(FILEDSP)
  10. D FL_FILE 1 8A
  11. D FL_OPNIND 9 9A
  12. D FL_EOFIND 10 10A
  13. D FL_STATUS 11 15S 0
  14. D FL_OPCODE 16 21A
  15. D FL_ROUTINE 22 29A
  16. D FL_SRCLINE 30 37A
  17. D FL_RECORD 38 45A
  18. D FL_MSID 46 52A
  19. D FL_SRCLNADD 77 78B 0
  20. D FL_ODPTYPE 81 82A
  21. D FL_FILENAME 83 92A
  22. D FL_LIBRARY 93 102A
  23. D FL_SPLFILE 103 112A
  24. D FL_SPLLIB 113 122A
  25. D FL_RCDLEN 125 126I 0
  26. D FL_KEYLEN 127 128I 0
  27. D FL_MEMBER 129 138A
  28. D FL_TYPE 147 148I 0
  29. D FL_RCDNUM 156 159I 0
  30. D FL_SPLNUM 160 163I 0
  31. D FL_OVERFLOW 188 189I 0
  32. D FL_BASEDMBRS 211 212I 0
  33. D FL_OPENID 214 215B 0
  34. D FL_RCDFMTLEN 216 217I 0
  35. D FL_CCSID 218 219I 0
  36. D FL_FBSIZE 367 370I 0
  37. D FL_KEYNUM 387 388I 0
  38. D FL_FBKEYLEN 393 394I 0
  39. D FL_MBRNUM 395 396I 0
  40. D FL_RRN 397 400I 0
  41. D FL_KEY 401 2400A
  42. **文件状态结构指针
  43. DFILEDSP S *
  44. **
  45. DS_CNTLCA DS LIKEDS(FILESDS)
  46. DR_CNTLCA DS LIKEREC(CCNTLCA.RCCNTLCA:*INPUT)
  47. DK_CNTLCA DS LIKEREC(CCNTLCA.RCCNTLCA:*KEY)
  48. **
  49. DS_CNTLTC DS LIKEDS(FILESDS)
  50. DR_CNTLTC1 DS LIKEREC(ACNTLTC.RACNTLTC:*INPUT)
  51. DR_CNTLTC2 DS LIKEREC(ACNTLTC.RACNTLTC:*OUTPUT)
  52. DK_CNTLTC DS LIKEREC(ACNTLTC.RACNTLTC:*KEY)
  53. **
  54. /FREE
  55. CLEAR R_CNTLCA; //取角色表信息
  56. CLEAR K_CNTLCA;
  57. FILEDSP = %ADDR(S_CNTLCA);
  58. K_CNTLCA.BKCHGN = TLADF3.ARR(V_RECD1).BKCHGN ;
  59. CHAIN %KDS(K_CNTLCA) CCNTLCA.RCCNTLCA R_CNTLCA ;
  60. IF NOT %FOUND(CCNTLCA);
  61. PMG.BKMSID = 'ENTRD';
  62. A_MSDS(1) = C_MGTLCA;
  63. A_MSDS(2) = C_MGCHGN + TLADF3.ARR(V_RECD1).BKCHGN;
  64. EXSR #ERR;
  65. ENDIF;
  66. /END-FREE
  67. **********************************************************************
  68. ** #FLEX文件异常处理
  69. **********************************************************************
  70. C #FLEX BEGSR
  71. **
  72. C EVAL PMG.MSFLNM = PG_SRCMBR
  73. C EVAL PMG.MSCDLN = FL_SRCLINE
  74. C EVAL PMG.BKMSID = 'F' + FL_MSID
  75. C IF FL_STATUS = 1218
  76. C CALL 'GETOBJTXT'
  77. C PARM FL_LIBRARY V_FLEXLIB 10
  78. C PARM FL_FILENAME V_FLEXOBJ 10
  79. C PARM '*FILE' V_FLEXTYP 7
  80. C PARM *BLANKS V_FLEXTEXT 50
  81. C EVAL PMG.BKMSDS = %TRIM(V_FLEXTEXT) +
  82. C %TRIM(FL_FILENAME) + '.' +
  83. C %TRIM(FL_MEMBER) + C_MGLOCK +
  84. C ',' + PG_EXCPDT
  85. C ELSE
  86. C EVAL PMG.BKMSDS = PG_EXCPDT
  87. C ENDIF
  88. C EVAL PMG.OTMSDS = PG_FLINFO
  89. C EXSR #EXIT
  90. **
  91. C ENDSR
  92. **********************************************************************
这段就需要解释一下了。首先,F表定义的多个文件,INFSR可以共用一个,但INFDS系统要求是不能共享的,必须每个文件定义一份。为了#FLEX的通用写法,因此对FILESDS指定了一个指针FILEDSP,每次在文件操作前,程序中像这样FILEDSP = %ADDR(S_CNTLCA)设置指针指向要操作文件对应的FILESDS结构。然后如果后续的文件操作出错,FILEDSP指向被自动填充的文件结构,系统执行#FLEX过程,类似程序异常一样做到了报错信息组织的全自动化。这里特别注意对于错误状态1218,即文件记录被锁定超时,抛出的错误带经过GETOBJTXT转换的PF中文注释,形如“柜员角色表CCNTLCA.CCNTLCA记录被锁”,对渠道端错误信息展示更友好。
  1. /*BEGIN***************************************************************/
  2. /*程序名称:GETOBJTXT */
  3. /*功能描述: */
  4. /* */
  5. /*设计人员:PACMAN 开发人员:PACMAN */
  6. /*设计日期:2014-11-27 开发日期:2014-11-27 */
  7. /*-------------------------------------------------------------------*/
  8. /*维护人员: */
  9. /*维护日期: */
  10. /*维护内容: */
  11. /* */
  12. /*END*****************************************************************/
  13. PGM PARM(&LIBNM &OBJNM &OBJTP &TEXT)
  14. INCLUDE SRCMBR(CLHD) SRCFILE(DSCPPGM)
  15. DCL VAR(&LIBNM) TYPE(*CHAR) LEN(10)
  16. DCL VAR(&OBJNM) TYPE(*CHAR) LEN(10)
  17. DCL VAR(&OBJTP) TYPE(*CHAR) LEN(7)
  18. DCL VAR(&TEXT) TYPE(*CHAR) LEN(50)
  19. IF COND(&LIBNM *EQ ' ') THEN(DO)
  20. CHGVAR VAR(&LIBNM) VALUE('*LIBL')
  21. ENDDO
  22. RTVOBJD OBJ(&LIBNM/&OBJNM) OBJTYPE(&OBJTP) TEXT(&TEXT)
  23. MONMSG MSGID(CPF0000)
  24. ENDPGM: ENDPGM

在真正的组件和交易程序中,PSDS,FILESDS结构都封装到/COPY的PGDS中,#ERR,*PSSR,#FLEX都封装到/COPY的PGCM中,应用程序篇幅就简化了很多。


通过*PSSR和#FLEX解决了大部分问题,但最后还遗留了一点会造成MSGW的未能捕获的异常。这种情况显然也不能放过,就需要更高级的主控程序处理了。联机交易主控程序MONSVR会扫描监视真正执行的JOBSVR,结束掉MSGW的JOBSVR并记录出错信息,然后重新启动一个新的代替。MONSVR代码片段如下。

  1. /*保存最大启动进程数*/
  2.   arr[0]=num;
  3.   freenum=num;

  4.     /*监测子进程是否正常*/
  5.     for (i=arr[0];i>=1;i--)
  6.     {
  7.       if (arr[i]!=0)
  8.       {
  9.         char reason[3009];

  10.         memset(reason, 0, sizeof(reason));
  11.         rc=checkJob(arr[i], reason);
  12.         if (rc==1)
  13.         {
  14.           if (i<=num && new_reg_ver!=0)
  15.             writeLog("WARNING", JOBNAME, __FILE__, __LINE__,
  16.                      "任务S%05d%03d %d不存在", port, i, arr[i]);
  17.           else
  18.             debugLog(svrdebug, JOBNAME, __FILE__, __LINE__,
  19.                      "任务S%05d%03d %d已退出", port, i, arr[i]);
  20.           arr[i]=0;
  21.         }
  22.         else if (rc==2)
  23.         {
  24.           writeLog("INFO", JOBNAME, __FILE__, __LINE__,
  25.                    "任务S%05d%03d %d已出错:%s", port, i, arr[i],
  26.                    reason);
  27.           if (!svrdebug)
  28.           {
  29.             endJob(arr[i]);
  30.             arr[i]=0;
  31.           }
  32.         }
  33.         else if (rc==3)
  34.             freenum++;
  35.       }
  36.       if (i==arr[0] && arr[i]==0)
  37.         arr[0]--;
  38.     }

  39.     /*保存最大启动进程数*/
  40.     arr[0] = arr[0]>num ? arr[0] : num;
  41.     /*检验进程是否存在并启动*/
  42.     for (i=1;i<=num;i++)
  43.     {
  44.       if (arr[i]==0)
  45.       {
  46.         sprintf(jobname,"S%05d%03d",port,i);
  47.         sprintf(argstr, "%d %d %d %d %d %d", port, savedate,
  48.                 reg_ver, reg_num, i, bakmode);
  49.         pid=spawn(pathstr, 1, spawn_fdmap, &inherit, spawn_argv,
  50.                   spawn_envp);
  51.         if (pid<0)
  52.         {
  53.           writeLog("WARNING", JOBNAME, __FILE__, __LINE__, "重启%s出错%d",
  54.                    jobname, errno);
  55.           arr[i]=0;
  56.         }
  57.         else
  58.         {
  59.           arr[i]=pid;
  60.           debugLog(svrdebug, JOBNAME, __FILE__, __LINE__,
  61.                    "重启%s成功%d", jobname, pid);
  62.         }
  63.       }
  64.     }
其中重要的是这么几点,checkJob(arr[i], reason)用于检查某个pid进程的状态,如果MSGW则获取详细出错信息到reason字符串中。endJob(arr[i])用于结束一个pid进程。spawn()函数产生一个进程执行指定的程序。通过writeLog(),MONSVR将出错的进程和错误信息写到主控日志备查。下面是checkJob和endJob的代码。
  1. /*BEGIN***************************************************************/
  2. /*程序名称:JOBCTL */
  3. /*功能描述:任务相关的调用封装 */
  4. /* */
  5. /*设计人员:PACMAN 开发人员:PACMAN */
  6. /*设计日期:2011-11-11 开发日期:2011-11-11 */
  7. /*-------------------------------------------------------------------*/
  8. /*维护人员: */
  9. /*维护日期: */
  10. /*维护内容: */
  11. /* */
  12. /*END*****************************************************************/

  13. #include <string.h>
  14. #include <sys/types.h>
  15. #include <qp0wpid.h>

  16. #include "dscppgm/cpyrgt_h"

  17. extern void ENDJOBCL(char *jobnb, char *usrnm, char*jobnm);
  18. extern void GETJOBCL(char *jobnb, char *usrnm, char*jobnm, char *rtcd,
  19.                      char *reason);

  20. /*返回1表示未找到,返回2表示MSGW*/
  21. int checkJob(pid_t pid, char *reason)
  22. {
  23.   QP0W_Job_ID_T jobinfo;
  24.   int ret;
  25.   char rtcd[1];

  26.   ret=Qp0wGetJobID(pid, &jobinfo);
  27.   if (ret!=0)
  28.     return 1;
  29.   GETJOBCL(jobinfo.jobnumber, jobinfo.username, jobinfo.jobname, rtcd,
  30.            reason);
  31.   return rtcd[0]-'0';
  32. }

  33. int endJob(pid_t pid)
  34. {
  35.   QP0W_Job_ID_T jobinfo;
  36.   int ret;

  37.   ret=Qp0wGetJobID(pid, &jobinfo);
  38.   if (ret!=0)
  39.     return ret;
  40.   ENDJOBCL(jobinfo.jobnumber, jobinfo.username, jobinfo.jobname);
  41.   return 0;
  42. }

  43. int getJobname(pid_t pid, char *name)
  44. {
  45.   QP0W_Job_ID_T jobinfo;
  46.   int ret;

  47.   ret=Qp0wGetJobID(pid, &jobinfo);
  48.   if (ret!=0)
  49.     return ret;
  50.   strncpy(name, jobinfo.jobname, 10);
  51.   name[10]='\0';
  52.   return 0;
  53. }
里面使用了GETJOBCL和ENDJOBCL这两个CL程序。代码如下。
  1. /*BEGIN***************************************************************/
  2. /*程序名称:GETJOBCL */
  3. /*功能描述:获取指定任务的运行状态 */
  4. /* RTCD 返回状态 */
  5. /* '0' 正常 */
  6. /* '1' 未找到 */
  7. /* '2' MSGW */
  8. /* '3' TIMW或TIMA */
  9. /* REASON MSGW原因,前7位为MSGID后3000字节为原因 */
  10. /* */
  11. /*设计人员:PACMAN 开发人员:PACMAN */
  12. /*设计日期:2011-11-11 开发日期:2011-11-11 */
  13. /*-------------------------------------------------------------------*/
  14. /*维护人员: */
  15. /*维护日期: */
  16. /*维护内容: */
  17. /* */
  18. /*END*****************************************************************/
  19. PGM PARM(&JOBNUM &USRNAM &JOBN &RTCD &REASON)
  20. INCLUDE SRCMBR(CLHD) SRCFILE(DSCPPGM)
  21. DCL VAR(&JOBNUM) TYPE(*CHAR) LEN(6)
  22. DCL VAR(&USRNAM) TYPE(*CHAR) LEN(10)
  23. DCL VAR(&JOBN) TYPE(*CHAR) LEN(10)
  24. DCL VAR(&RTCD) TYPE(*CHAR) LEN(1)
  25. DCL VAR(&JOBINF) TYPE(*CHAR) LEN(225)
  26. DCL VAR(&JOBST) TYPE(*CHAR) LEN(10)
  27. DCL VAR(&JOBACTST) TYPE(*CHAR) LEN(4)
  28. DCL VAR(&MSGKEY) TYPE(*CHAR) LEN(4)
  29. DCL VAR(&MSGQUE) TYPE(*CHAR) LEN(10)
  30. DCL VAR(&MSGLIB) TYPE(*CHAR) LEN(10)
  31. DCL VAR(&MSGASP) TYPE(*CHAR) LEN(10)
  32. DCL VAR(&JOBNAM) TYPE(*CHAR) LEN(26)
  33. DCL VAR(&SENDER) TYPE(*CHAR) LEN(80)
  34. DCL VAR(&REASON) TYPE(*CHAR) LEN(3008)
  35. DCL VAR(&MSGID) TYPE(*CHAR) LEN(7)
  36. DCL VAR(&MSGHLP) TYPE(*CHAR) LEN(3000)
  37. CHGVAR VAR(&RTCD) VALUE('0')
  38. CHGVAR VAR(&JOBNAM) VALUE(&JOBN *CAT &USRNAM *CAT +
  39. &JOBNUM)
  40. CHGVAR VAR(&SENDER) VALUE(&JOBNAM)
  41. CALL PGM(QUSRJOBI) PARM(&JOBINF X'000000D1' +
  42. 'JOBI0200' &JOBNAM ' ')
  43. MONMSG MSGID(CPF3C51 FPF3C52 CPF3C53 CPF3C54 +
  44. CPF3C55) EXEC(DO)
  45. CHGVAR VAR(&RTCD) VALUE('1')
  46. GOTO CMDLBL(END)
  47. ENDDO
  48. CHGVAR VAR(&JOBST) VALUE(%SST(&JOBINF 51 10))
  49. CHGVAR VAR(&JOBACTST) VALUE(%SST(&JOBINF 108 4))
  50. IF COND(&JOBACTST *EQ 'MSGW') THEN(DO)
  51. CHGVAR VAR(&RTCD) VALUE('2')
  52. CHGVAR VAR(&MSGKEY) VALUE(%SST(&JOBINF 192 4))
  53. CHGVAR VAR(&MSGQUE) VALUE(%SST(&JOBINF 196 10))
  54. CHGVAR VAR(&MSGLIB) VALUE(%SST(&JOBINF 206 10))
  55. CHGVAR VAR(&MSGASP) VALUE(%SST(&JOBINF 216 10))
  56. RCVMSG MSGQ(&MSGQUE) MSGTYPE(*INQ) RMV(*NO) +
  57. MSGKEY(&MSGKEY) SECLVL(&MSGHLP) +
  58. MSGID(&MSGID) +
  59. SENDER(&SENDER) SENDERFMT(*SHORT)
  60. MONMSG MSGID(CPF0000) EXEC(DO)
  61. GOTO CMDLBL(END)
  62. ENDDO
  63. CHGVAR VAR(&REASON) VALUE(&MSGID *CAT ':' *CAT +
  64. &MSGHLP)
  65. GOTO CMDLBL(END)
  66. ENDDO
  67. IF COND((&JOBACTST *EQ 'TIMW') *OR (&JOBACTST +
  68. *EQ 'TIMA')) THEN(DO)
  69. CHGVAR VAR(&RTCD) VALUE('3')
  70. ENDDO
  71. END:
  72. ENDPGM


  1. /*BEGIN***************************************************************/
  2. /*程序名称:ENDJOBCL */
  3. /*功能描述:结束指定任务 */
  4. /* */
  5. /*设计人员:PACMAN 开发人员:PACMAN */
  6. /*设计日期:2011-11-11 开发日期:2011-11-11 */
  7. /*-------------------------------------------------------------------*/
  8. /*维护人员: */
  9. /*维护日期: */
  10. /*维护内容: */
  11. /* */
  12. /*END*****************************************************************/
  13. PGM PARM(&JOBNB &USRNM &JOBNM)
  14. INCLUDE SRCMBR(CLHD) SRCFILE(DSCPPGM)
  15. DCL VAR(&JOBNB) TYPE(*CHAR) LEN(6)
  16. DCL VAR(&USRNM) TYPE(*CHAR) LEN(10)
  17. DCL VAR(&JOBNM) TYPE(*CHAR) LEN(10)
  18. ENDJOB JOB(&JOBNB/&USRNM/&JOBNM) OPTION(*IMMED)
  19. MONMSG MSGID(CPF1321)
  20. ENDPGM
批处理的主控也是类似,使用GETJOBCL和ENDJOBCL进行批量任务分段JOB的监视和错误登记,这里就不再赘述了。


通过主控,RPGLE程序层层把关,以及统一的错误处理程序,Firebird减轻了应用开发人员的错误处理负担,并且让错误信息更为友好,尽可能的保存了错误的丰富信息,这对于系统的维护可以带来很大的帮助。而这些,正是系统设计时就需要重视并在整体架构里考虑,才能做好的。


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