Invoking the
Library program from PHP
Making the COMMAREA available
to PHP
After the COMMAREA is defined in the COBOL program and
the ADATA has been generated, the next step is to create Java™
classes that represent the COMMAREA. These classes are used to make
the COMMAREA accessible from PHP scripts.
You will only need to
generate the Java classes once: as soon as the classes are available to
CA1S, any number of scripts may be written to use them to interact with the
COBOL program. However, if the COBOL program is modified such that the
shape of the COMMAREA changes, you will need to regenerate the ADATA and
the Java classes, and adapt the PHP scripts as required.
The
following steps require a Java SDK and assume that the java and javac commands are available
on the system PATH. You execute them on your workstation or directly on the
CICS system.
1. Generate the
source of the COMMAREA classes
java -cp jzos_recgen.jar com.ibm.jzos.recordgen.cobol.RecordClassGenerator genCache=false
adataFile=LIBRARY symbol=DFHCOMMAREA class=Library_Commarea package=library outputDir=.
|
Let's describe the purpose of each part of the above command:
java -cp jzos_recgen.jar com.ibm.jzos.recordgen.cobol.RecordClassGenerator
|
This
invokes the JZOS Record Generator, which is a Java program contained in the
file jzos_recgen.jar. This file in included in CA1S, and the most recent
version is available on the IBM JZOS Batch Toolkit for z/OS SDKs site at
alphaWorks (see
Resources).
This option
ensures that the generated Java code does not attempt to cache the value of
COMMAREA fields. This option is required for CA1S to interact
correctly with the generated
classes.
This
specifies the path to the ADATA file obtained in the previous
section.
This specifies the name of the COMMAREA for which to
generate the
class.
This
specifies the name of the resulting Java class.
This specifies the
package of the resulting Java class.
This specifies the path to
which the Java class will be written on the file system.
2. Generate the Java source for the class
representing COBOL constants
In the sample library application,
the constants used in the COBOL program (for the operation names and
response codes) are defined in a data structure in the WORKING-STORAGE
section. This means that a Java class representing these constants can be
generated and used from PHP in a similar way to the class representing the
COMMAREA, in this case to obtain the values of the defined constants where
needed.
java -cp jzos_recgen.jar com.ibm.jzos.recordgen.cobol.RecordClassGenerator genCache=false
adataFile=LIBRARY symbol=LIBRARY-CONSTANTS class=Library_Constants package=library
outputDir=.
|
The
advantage of this approach is that PHP reflection can be used to examine
the available constant names, and the PHP programmer need not refer
directly to the values defined in the COBOL. Keep in mind
that this can be done for the LIBRARY application because of the way the
constants have been defined, and may not always be possible; for example,
where hard-coded values are embedded in the COBOL source.
In the PHP
scripts provided with this article, a mixed approach is adopted, where some
of these constants are used in their literal forms (for example, the
operation names), and some are extracted using the generated Java class
(the numeric response codes).
3. Compile the .java source files to create Java class
files
The generated Java source will be in the outputDir specified above, in subdirectory library (which corresponds to the package name).
Use javac to compile the source files as
follows:
javac -cp jzos_recgen.jar library/*
|
4. Make the class files available to
CA1S The library directory now contains the compiled classes.
For CA1S to be able to use them, they must be available on the Java
classpath. If you generated the classes on your workstation, you will need
to transfer the directory to the server (for example, using FTP).
The Java
classpath is determined by the CLASSPATH_SUFFIX attribute of the JVMPROFILE
used by CA1S. The default JVMPROFILE in CA1S (named CA1SJVMP) is already
configured to include the directory ca1s/work/classes/ in the
CLASSPATH_SUFFIX:
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
|
Therefore,
if you are using JVMPROFILE CA1SJVMP, you may
copy the library directory to ca1s/work/classes/. Alternatively, you may change your
CLASSPATH_SUFFIX to include a directory or JAR file that contains the
library directory.
Note that the directory structure representing the
package name must be preserved under the classpath location. So, if you are
using ca1s/work/classes/, copy the library
directory itself such that the classes are under ca1s/work/classes/library
(and not directly under ca1s/work/classes/).
Finally, phase out your JVMs to ensure
the new classes are picked up by CA1S.
Accessing the program from PHP code
The code
The invocation of a
COMMAREA program consists of 3 steps:
- Preparing the COMMAREA before linking to the program.
- Linking to the program.
- Retrieving the result of the link from the COMMAREA.
To illustrate, Listing 3 shows a script that invokes the library program to
obtain the list of books, then prints them out:
Listing 3. Invoking a CICS Commarea program from PHP
<?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/>";
}
?>
|
 |
Why do I just see garbage
characters when I access a script in my browser?
This can happen if the script is encoded in the wrong code-page. By
default, the runtime for PHP in CA1S is configured to expect scripts in
the UTF-8 encoding. Therefore, if you transfer scripts between a
Windows®
or Linux® workstation and your CICS server, be sure to set "binary" mode,
so that the scripts are not converted to an EBCDIC code-page during the
transfer. For more information on encoding concerns and configuration
options in CA1S, see the CA1S documentation.
|
|
This script is provided in the sample code download as library/scripts/library.php. If you transfer this script to your CICS
system to ca1s/work/scripts/library.php and
access it in your browser, you should see a list of books
like:
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...
|
Let's
examine the three steps in the code in detail.
Preparing the COMMAREA
java_import('library.Library_Commarea');
|
The
function java_import(), which is built in to the
runtime for PHP in CA1S, loads up a Java class and makes it available for
use within PHP. In the code above, we load the Java class representing the
library COMMAREA class that we generated
previously.
$COMMAREA = new Library_Commarea();
|
Then,
we create an instance of this class. This instance will be used as a
container for the input and output data of the
invocation.
$COMMAREA->setLibRequestType('LIST');
|
Finally,
on the third line, we set some input data on the COMMAREA. The method setLibRequestType is specific to the Java class
representing the COMMAREA of the COBOL program used in this tutorial: it
defines the operation we want to perform against the library, in this case
obtain a LIST of all the books. See Investigating the COMMAREA using
reflection to learn how you can use PHP to discover all the methods
on the Java
class.
Linking to the program
$program = new CICSProgram('LIBRARY');
|
First,
we create an instance of the built-in class CICSProgram, which represents the CICS program with which we
want
to interact. The name of the CICS program is specified as the constructor
argument, in this case "LIBRARY." If the program were to be renamed, this
argument would need to be changed.
$program->link($COMMAREA);
|
The
link method on the CICSProgram class triggers
the execution of the CICS program. If a single COMMAREA argument is
supplied, as is the case here, it will be used by the CICS program as a
container for both input and output data. You may also supply two separate
COMMAREA arguments, in which case the first argument will be expected to
contain input data, and any output data will be written to the second. It
is also possible to supply no arguments, which is useful in cases where the
CICS program has no input and output.
If the link fails or
the linked program terminates abnormally, the link method will throw a
CICSException. The getMessage() method on the exception object reveals details of
the failure, including the type of the underlying Java exception. For
example, if an invalid program name is specified as the argument to the
CICSProgram constructor, the code above would
print:
Error: com.ibm.cics.server.InvalidProgramIdException: CICS PGMIDERR Condition
|
Retrieving data from the COMMAREA
$totalBooks = $COMMAREA->getLibItemCount();
|
After
link() successfully returns, the COMMAREA
contains the output of the invocation. In step 3 of the PHP script above,
we use various methods specific to the Library_Commarea class to obtain the total number of books, then
to iterate over the list of books, printing the title and author of each
one.
Investigating the
Commarea using PHP reflection Setting input data on a COMMAREA
before a link and retrieving output data from it afterwards requires
knowledge of the setter and getter methods on the COMMAREA class. If you
don't have a list of these methods at hand, it is simple to obtain one with
PHP reflection. For example, the following script uses the PHP
reflection classes (see Resources) to obtain the list of methods available
of the class Library_Commarea:
Listing 4. Using reflection, part 1
<?php
java_import('library.Library_Commarea');
$COMMAREA = new Library_Commarea();
$rc = new ReflectionObject($COMMAREA);
foreach ($rc->getMethods() as $method) {
echo $method->getName() . '<br/>';
}
?>
|
The
output of the script in Listing 4
is:
Library_Commarea
__tostring
setLibItemCount
getLibReturnCode
setLibRequestType
setLibReturnCode
getByteBuffer
getLibRequestType
getLibBookItem
getLibItemCount
|
Aside
from method Library_Commarea() (which is the
constructor) and getByteBuffer() (which provides
access to the raw byte array holding the COMMAREA data), all the other
methods are getters and setters for COMMAREA fields. Those used in the
initial script are marked in bold.
Some getters return complex data
structures, such as the getLibBookItem() method
above, which returns an object representing a book. The same technique can
be used on these data structures to discover which fields can be accessed.
For example, here we use the PHP function get_class_methods() (see Resources) to establish which fields
can be accessed on a
book:
Listing 5. Using reflection, part 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));
?>
|
The
output of the script in Listing 5
is:
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
)
|
Troubleshooting the classpath with
phpinfo()
If you see warnings or errors suggesting that the
COMMAREA class cannot be found, check your Java classpath to ensure it
includes a directory or JAR file containing the library directory (which contains the generated
classes).
This can be done directly from php code using the PHP
function phpinfo():
Accessing
that script in a browser will display lots of information, including the
full classpath:
Figure 1. Checking your Java
classpath with phpinfo()
classpath.png
More generally, phpinfo() is a useful tool for investigating many aspects of the
PHP environment.
|