从 PHP 调用书库应用程序
使 PHP 可以访问 COMMAREA
在 COBOL 程序中定义 COMMAREA 并生成 ADATA 之后,下一步就是创建用于表示 COMMAREA 的 Java™ 类。这些类使 PHP 脚本可以访问 COMMAREA。
这些 Java 类仅需生成一次:只要 CA1S 可以使用这些类,就可以编写任意脚本通过它们与 COBOL 程序交互。然而,如果修改 COBOL 程序导致改变了 COMMAREA,那么就要重新生成 ADATA 和 Java 类,并根据需要修改 PHP 脚本。
下面的步骤需要使用 Java SDK,并假设在系统 PATH 上可以使用 java 和 javac 命令。您可以在工作台上执行它们,或直接在 CICS 系统上执行它们。
1. 生成 COMMAREA 类的源代码
java -cp jzos_recgen.jar com.ibm.jzos.recordgen.cobol.RecordClassGenerator genCache=false
adataFile=LIBRARY symbol=DFHCOMMAREA class=Library_Commarea package=library outputDir=.
|
我们看看以上命令的各部分的作用:
java -cp jzos_recgen.jar com.ibm.jzos.recordgen.cobol.RecordClassGenerator
|
这将调用 JZOS 记录生成器,这是一个包含在 jzos_recgen.jar 文件中的 Java 程序。这个文件包含在 CA1S 中,在 alphaWorks 的 IBM JZOS Batch Toolkit for z/OS SDKs 站点上可以找到最新的版本(见 参考资料)。
 |
为什么运行 JZOS 记录生成器时遇到 Java 异常?
在使用 FTP 获取 ADATA 之前,记得运行命令 quote site rdw 和 bin。
要了解更多信息,请阅读 JZOS 文档 “Running the COBOL RecordClassGenerator”,即 CA1S 包中的 “JZOS Cobol Record Generator Users Guide.pdf”。
|
|
这个选项确保生成的 Java 代码不尝试缓存 COMMAREA 字段的值。CA1S 需要用这个选项与生成的类正确地进行交互。
这指定前一小节中的 ADATA 文件的路径。
这指定需要为它生成类的 COMMAREA 的名称。
这指定生成的 Java 类的名称。
这指定生成的 Java 类所在的包。
这指定在文件系统上写入 Java 类的路径。
2. 为表示 COBOL 常量的类生成 Java 源代码
在示例书库应用程序中,COBOL 程序使用的常量(对于操作名和响应代码)是在 WORKING-STORAGE 部分的数据结构中定义的。这意味着可以生成表示这些常量的 Java 类,并以类似于表示 COMMAREA 类的方式从 PHP 使用它们。在这个例子中,是为了在需要时获取已定义常量的值。
java -cp jzos_recgen.jar com.ibm.jzos.recordgen.cobol.RecordClassGenerator genCache=false
adataFile=LIBRARY symbol=LIBRARY-CONSTANTS class=Library_Constants package=library
outputDir=.
|
这种方法的优势是可以使用 PHP 反射检查可用的常量名,因此 PHP 程序员不需要直接引用在 COBOL 中定义的值。记住,可以对 LIBRARY 应用程序执行该操作,这是由定义常量的方式决定的,并且不能保证始终可用;例如,在 COBOL 源代码中嵌入了硬编码的值。
在本文提供的 PHP 脚本中,采用一种混合的方法,其中一些常量以字面的形式使用(比如操作名),另一些常量则使用生成的 Java 类提取(数字响应代码)。
3. 通过编译 .java 源文件创建 Java 类文件
生成的 Java 源文件将储存在前面指定的 outputDir 的子目录中(与包名对应)。使用 javac 按以下方式编译源文件:
javac -cp jzos_recgen.jar library/*
|
4. 使 CA1S 可以使用类文件 现在,library 目录已经包含已编译的类。这些类必须在 Java 类路径中,CA1S 才能使用它们。如果您在工作台中生成这些类,则需要将目录转移到服务器(例如使用 FTP)。
Java
类路径由 CA1S 使用的 JVMPROFILE 的 CLASSPATH_SUFFIX 属性决定。CA1S 中默认的 JVMPROFILE(名为 CA1SJVMP)已经配置为在 CLASSPATH_SUFFIX 中包含 ca1s/work/classes/ 目录:
CLASSPATH_SUFFIX=/u/p8build/ca1s/config/ini:\
/u/p8build/ca1s/p8/jars/p8api.jar:\
/u/p8build/ca1s/p8/jars/p8.jar:\
/u/p8build/ca1s/p8/jars/p8cics.jar:\
/u/p8build/ca1s/work/classes:\
/usr/lpp/db2910/classes/db2jcc.jar:\
/usr/lpp/db2910/classes/db2jcc_javax.jar:\
/usr/lpp/db2910/classes/db2jcc_license_cisuz.jar
|
因此,如果您使用 JVMPROFILE CA1SJVMP,那么需要将 library 目录复制到 ca1s/work/classes/。或者更改 CLASSPATH_SUFFIX,使其包含一个包含 library 目录的目录或 JAR 文件。
注意,表示包名的目录结构必须位于类路径位置之下。因此,如果您使用 ca1s/work/classes/,则需要复制 library 目录,使类位于 ca1s/work/classes/library 之下(而不是 ca1s/work/classes/)。
最后,退出 JVM 以确保 CA1S 能够使用新的类。
从 PHP 代码访问书库程序
代码
调用 COMMAREA 程序包含 3 个步骤:
- 在链接到程序之前准备好 COMMAREA。
- 链接到程序。
- 从 COMMAREA 获取链接的结果。
为了进行演示,清单 3 的脚本通过调用书库程序获取图书列表,然后打印它们:
清单 3. 从 PHP 调用 CICS Commarea 程序
<?php
// Step 1: create and prepare the COMMAREA instance
java_import('library.Library_Commarea');
$COMMAREA = new Library_Commarea();
$COMMAREA->setLibRequestType('LIST');
// Step 2: create the program instance and invoke it using the COMMAREA
$program = new CICSProgram('LIBRARY');
try {
$program->link($COMMAREA);
} catch (CICSException $e) {
echo 'Error: ' . $e->getMessage();
return;
}
// Step 3: retrieve the result of the link from the COMMAREA
$totalBooks = $COMMAREA->getLibItemCount();
echo "Total number of books: $totalBooks <br/>";
for ($i=0; $i<$totalBooks; $i++) {
$book = $COMMAREA->getLibBookItem($i);
$title = $book->getBookTitle();
$author = $book->getBookAuthor();
echo "Book $i is '$title' by '$author'. <br/>";
}
?>
|
 |
为何在浏览器中访问脚本时看到的是乱码?
如果在错误的编码页面中编码脚本将发生这种错误。默认情况下,CA1S 中的 PHP 运行时被配置为使用 UTF-8 编码。因此,如果您在 Windows® 或 Linux® 工作台和 CICS 服务器之间传输脚本,一定要设置为 “binary” 模式,这样脚本就不会在传输期间被转换为 EBCDIC 编码页面。要更多地了解 CA1S 中的编码注意事项和配置选项,请阅读 CA1S 文档。
|
|
在示例脚本下载中提供的脚本为 library/scripts/library.php。如果您将这个脚本传输到 CICS 系统的 ca1s/work/scripts/library.php 中,并从浏览器访问它,您将看到一个图书列表:
Total number of books: 18
Book 0 is 'PHP for Beginners ' by 'Rob Nicholson '.
Book 1 is 'Project Management ' by 'A N IBMer '.
Book 2 is 'Easy Z Specification' by 'Jonathan '.
Book 3 is 'REST Protocol Design' by 'Zoe, Ant & Rob '.
etc...
|
现在,我们仔细查看代码中包含的 3 个步骤。
准备 COMMAREA
java_import('library.Library_Commarea');
|
在 CA1S 中为 PHP 运行时内置的 java_import() 函数将加载一个 Java 类,从而使这个类可以在 PHP 中使用。在上面的代码中,我们将加载表示前面创建的书库 COMMAREA 类的 Java 类。
$COMMAREA = new Library_Commarea();
|
然后,创建这个类的一个实例。这个实例被用作调用的输入和输出数据的容器。
$COMMAREA->setLibRequestType('LIST');
|
最后,在第三行代码中,我们在 COMMAREA 上设置一些输入数据。setLibRequestType 方法特定于表示本教程的 COBOL 程序的 COMMAREA 的 Java 类:它定义我们希望对书库执行的操作,在这个例子中,是获取一个包含所有图书的列表(LIST)。查看 使用反射研究 COMMAREA,了解如何使用 PHP 发现关于 Java 类的所有方法。
链接到程序
$program = new CICSProgram('LIBRARY');
|
首先,我们创建内置类 CICSProgram 的一个实例,它表示我们将要与之交互的 CICS 程序。CICS 程序的名称指定为构造器的参数,在这个例子中为 “LIBRARY”。如果重命名程序,那么也要更改这个参数。
$program->link($COMMAREA);
|
CICSProgram 类的 link 方法触发 CICS 程序的执行。如果提供单一的 COMMAREA 参数(如本例),它将被 CICS 程序用作输入和输出数据的容器。您也可能提供两个不同的 COMMAREA 参数,对于这种情况,第一个参数用于包含输入数据,第二个参数用于包含输出数据。此外,也可以不提供参数,当 CICS 程序没有输入和输出时,这种做法很有用。
如果链接失败,或被链接的程序异常终止,那么 link 方法将抛出一个 CICSException。异常对象中的 getMessage() 方法描述失败细节,包括基本 Java 异常的类型。例如,如果将一个无效程序名指定为 CICSProgram 构造器的参数,以上代码将输出:
Error: com.ibm.cics.server.InvalidProgramIdException: CICS PGMIDERR Condition
|
Retrieving data from the COMMAREA
$totalBooks = $COMMAREA->getLibItemCount();
|
在 link() 成功返回之后,COMMAREA 就包含调用的输出。在上面的 PHP 脚本的步骤 3 中,我们使用各种特定于 Library_Commarea 类的方法来获取图书的总数,然后遍历图书列表,并输出每本书的标题和作者。
使用 PHP 反射研究 COMMAREA 如果要在链接之前在 COMMAREA 上设置输入数据,随后从 COMMAREA 获取输出数据,那么需要了解 COMMAREA 类的 setter 和 getter 方法。如果您没有这些方法,可以通过 PHP 反射轻松获得。 例如,下面的脚本使用 PHP 反射类(见 参考资料)获取在 Library_Commarea 类中可用的方法列表:
清单 4. 使用反射,第 1 部分
<?php
java_import('library.Library_Commarea');
$COMMAREA = new Library_Commarea();
$rc = new ReflectionObject($COMMAREA);
foreach ($rc->getMethods() as $method) {
echo $method->getName() . '<br/>';
}
?>
|
清单 4 中的脚本的输出为:
Library_Commarea
__tostring
setLibItemCount
getLibReturnCode
setLibRequestType
setLibReturnCode
getByteBuffer
getLibRequestType
getLibBookItem
getLibItemCount
|
除了 Library_Commarea() 方法(构造器)和 getByteBuffer() 方法(提供对包含 COMMAREA 数据的原始字节数组的访问)之外,其他方法都是 COMMAREA 字段的 getter 和 setter 方法。在最初脚本中使用的方法用粗体表示。
一些 getter 方法返回复杂的数据结构,比如以上的 getLibBookItem() 方法,它返回一个表示一本书的对象。可以将这种技术用于这些数据结构中,找到可以访问的字段。例如,在这里我们使用 PHP 函数 get_class_methods()(见 参考资料)确定一本书的哪些字段是可以访问的:
清单 5. 使用反射,第 2 部分
<?php
// Step 1: create and prepare the COMMAREA instance
java_import('library.Library_Commarea');
$COMMAREA = new Library_Commarea();
$COMMAREA->setLibRequestType('LIST');
// Step 2: create the program instance and invoke the program using the COMMAREA
$program = new CICSProgram('LIBRARY');
$program->link($COMMAREA);
// Investigate the fields available on a book object
$book = $COMMAREA->getLibBookItem(0);
print_r(get_class_methods($book));
?>
|
清单 5 中的脚本的输出为:
Array
(
[0] => LibBookItem
[1] => getBookTitle
[2] => setBookItemRef
[3] => isBookOnloan
[4] => getBookAuthor
[5] => isBookUnlent
[6] => getByteBuffer
[7] => setBookAuthor
[8] => setBookBorrower
[9] => getBookLoanStatus
[10] => getByteBufferOffset
[11] => getBookBorrower
[12] => __tostring
[13] => getFiller_1
[14] => getBookItemRef
[15] => setBookTitle
[16] => setFiller_1
[17] => setBookLoanStatus
)
|
使用 phpinfo() 诊断类路径
如果您看到表示找不到 COMMAREA 类的警告或错误,请检查 Java 类路径是否正确,它必须具有一个包含 library 目录(包含生成的类)的 JAR 文件或目录。
这可以使用 PHP phpinfo() 函数直接完成:
在浏览器中访问这个脚本能够显示很多信息,包括完整的类路径:
图 1. 使用 phpinfo() 检查 Java 类路径
classpath.png
phpinfo() 的最常见用法是检查 PHP 环境,它是一个非常有用的工具。
|