Building iSeries program calls with PCML

To build iSeries™ program calls with PCML, you must start by creating a Java™ application and a PCML source file.

Depending on your design process, you must write one or more PCML source files where you describe the interfaces to the iSeries programs that will be called by your Java application. Refer to PCML syntax for a detailed description of the language.

Then your Java application interacts with the PCML classes (in this case, the ProgramCallDocument class). The ProgramCallDocument class uses your PCML source file to pass information between your Java application and the iSeries programs. Figure 1 illustrates how Java applications interact with the PCML classes.

Figure 1. Making program calls to the server using PCML.

Flow chart that outlines the process of creating a program call with PCML Image description

When your application constructs the ProgramCallDocument object, the XML parser reads and parses the PCML source file. For more information about using an XML parser with IBM® Toolbox for Java, see XML parser and XSLT processor.

After the ProgramCallDocument class has been created, the application program uses the ProgramCallDocument class's methods to retrieve the necessary information from the server through the iSeries distributed program call (DPC) server.

To improve run-time performance, the ProgramCallDocument class can be serialized during your product build time. The ProgramCallDocument is then constructed using the serialized file. In this case, the XML parser is not used at run-time. Refer to Using serialized PCML files.

Using PCML source files

Start of changeYour Java application uses PCML by constructing a ProgramCallDocument object with a reference to the PCML source file. The ProgramCallDocument object considers the PCML source file to be a Java resource. The java application finds the PCML source file by using the Java CLASSPATHEnd of change

The following Java code constructs a ProgramCallDocument object:

    AS400 as400 = new AS400();
    ProgramCallDocument pcmlDoc = new ProgramCallDocument(as400, "myPcmlDoc");

The ProgramCallDocument object will look for your PCML source in a file called myPcmlDoc.pcml. Notice that the .pcml extension is not specified on the constructor.

If you are developing a Java application in a Java package, you can package-qualify the name of the PCML resource:

    AS400 as400 = new AS400();
    ProgramCallDocument pcmlDoc = new ProgramCallDocument(as400, "com.company.package.myPcmlDoc");

Using serialized PCML files

To improve run-time performance, you can use a serialized PCML file. A serialized PCML file contains serialized Java objects representing the PCML. The objects that are serialized are the same objects that are created when you construct the ProgramCallDocument from a source file as described above.

Using serialized PCML files improves performance because the XML parser is not needed at run-time to process the PCML tags.

The PCML can be serialized using either of the following methods:

If your PCML is in a source file named myDoc.pcml, the result of serialization is a file named myDoc.pcml.ser.

PCML source files vs. serialized PCML files

Consider the following code to construct a ProgramCallDocument:

    AS400 as400 = new AS400();
    ProgramCallDocument pcmlDoc = new ProgramCallDocument(as400, "com.mycompany.mypackage.myPcmlDoc");

The ProgramCallDocument constructor will first try to find a serialized PCML file named myPcmlDoc.pcml.ser in the com.mycompany.mypackage package in the Java CLASSPATH. If a serialized PCML file does not exist, the constructor will then try to find a PCML source file named myPcmlDoc.pcml in the com.mycompany.mypackage package in the Java CLASSPATH. If a PCML source file does not exist, an exception is thrown.

Qualified names

Your Java application uses ProgramCallDocument.setValue() to set input values for the iSeries program being called. Likewise, your application uses ProgramCallDocument.getValue() to retrieve output values from the iSeries program.

When accessing values from the ProgramCallDocument class, you must specify the fully qualified name of the document element or <data> tag. The qualified name is a concatenation of the names of all the containing tags with each name separated by a period.

For example, given the following PCML source, the qualified name for the "nbrPolygons" item is "polytest.parm1.nbrPolygons". The qualified name for accessing the "x" value for one of the points in one of the polygons is "polytest.parm1.polygon.point.x".

If any one of the elements needed to make the qualified name is unnamed, all descendants of that element do not have a qualified name. Any elements that do not have a qualified name cannot be accessed from your Java program.

<pcml version="1.0">
  <program name="polytest" path="/QSYS.lib/MYLIB.lib/POLYTEST.pgm">
    <!-- Parameter 1 contains a count of polygons along with an array of polygons -->
    <struct name="parm1" usage="inputoutput">
      <data name="nbrPolygons" type="int" length="4" init="5" />
      <!-- Each polygon contains a count of the number of points along with an array of points -->
      <struct name="polygon" count="nbrPolygons">
        <data name="nbrPoints" type="int" length="4" init="3" />
        <struct name="point" count="nbrPoints" >
          <data name="x" type="int" length="4" init="100" />
          <data name="y" type="int" length="4" init="200" />
        </struct>
      </struct>
    </struct>
  </program>
</pcml>

Accessing data in arrays

Any <data> or <struct> element can be defined as an array using the count attribute. Or, a <data> or <struct> element can be contained within another <struct> element that is defined as an array.

Furthermore, a <data> or <struct> element can be in a multidimensional array if more than one containing element has a count attribute specified.

In order for your application to set or get values defined as an array or defined within an array, you must specify the array index for each dimension of the array. The array indices are passed as an array of int values. Given the source for the array of polygons shown above, the following Java code can be used to retrieve the information about the polygons:

    ProgramCallDocument polytest; // Initialized elsewhere
    Integer nbrPolygons, nbrPoints, pointX, pointY;
    nbrPolygons = (Integer) polytest.getValue("polytest.parm1.nbrPolygons");
    System.out.println("Number of polygons:" + nbrPolygons);
    indices = new int[2];
    for (int polygon = 0; polygon < nbrPolygons.intValue(); polygon++) 
    {
        indices[0] = polygon;
        nbrPoints = (Integer) polytest.getValue("polytest.parm1.polygon.nbrPoints", indices );
        System.out.println("  Number of points:" + nbrPoints);

        for (int point = 0; point < nbrPoints.intValue(); point++) 
        {
            indices[1] = point;
            pointX = (Integer) polytest.getValue("polytest.parm1.polygon.point.x", indices );
            pointY = (Integer) polytest.getValue("polytest.parm1.polygon.point.y", indices );
            System.out.println("    X:" + pointX + " Y:" + pointY);
        }
    }

Debugging

When you use PCML to call programs with complex data structures, it is easy to have errors in your PCML that result in exceptions from the ProgramCallDocument class. If the errors are related to incorrectly describing offsets and lengths of data, the exceptions can be difficult to debug.

Use the following method from the Trace class to turn on PCML tracing:

     Trace.setTraceOn(true);      // Turn on tracing function.
     Trace.setTracePCMLOn(true);  // Turn on PCML tracing.
Note: All public methods in the PcmlMessageLog class, including tracing, were deprecated in V5R2.

The Trace setFileName() method enables you to send the following types of information to specific log files or, by default, to System.out:

/QSYS.lib/QGY.lib/QGYOLOBJ.pgm[2]
Offset : 0....... 4....... 8....... C....... 0....... 4....... 8....... C.......   
         0...4...8...C...0...4...8...C...
     0 : 0000000A 0000000A 00000001 00000068 D7F0F9F9 F0F1F1F5 F1F4F2F6 F2F5F400  
         *................P09901151426254.*
    20 : 00000410 00000001 00000000 00000000 00000000 00000000 00000000 00000000  
         *................................*
    40 : 00000000 00000000 00000000 00000000                                      
         *................                *
Reading data -- Offset: 0   Length: 4   Name: "qgyolobj.listInfo.totalRcds"   
 Byte data: 0000000A
Reading data -- Offset: 4   Length: 4   Name: "qgyolobj.listInfo.rcdsReturned"    
 Byte data: 0000000A
Reading data -- Offset: 8   Length: 4   Name: "qgyolobj.listInfo.rqsHandle"   
 Byte data: 00000001
Reading data -- Offset: c   Length: 4   Name: "qgyolobj.listInfo.rcdLength"    
 Byte data: 00000068
Reading data -- Offset: 10  Length: 1   Name: "qgyolobj.listInfo.infoComplete"    
 Byte data: D7
Reading data -- Offset: 11  Length: 7   Name: "qgyolobj.listInfo.dateCreated" 
 Byte data: F0F9F9F0F1F1F5
Reading data -- Offset: 18  Length: 6   Name: "qgyolobj.listInfo.timeCreated" 
 Byte data: F1F4F2F6F2F5
Reading data -- Offset: 1e  Length: 1   Name: "qgyolobj.listInfo.listStatus"  
 Byte data: F4
Reading data -- Offset: 1f  Length: 1   Name: "qgyolobj.listInfo.[8]" 
 Byte data: 00
Reading data -- Offset: 20  Length: 4   Name: "qgyolobj.listInfo.lengthOfInfo"    
 Byte data: 00000410
Reading data -- Offset: 24  Length: 4   Name: "qgyolobj.listInfo.firstRecord" 
 Byte data: 00000001
Reading data -- Offset: 28  Length: 40  Name: "qgyolobj.listInfo.[11]"    
 Byte data: 00000000000000000000000000000000000000000000000000000000000000000000000000000000
The above messages can be very helpful in diagnosing cases where the output data coming from the iSeries program does not match the PCML source. This can easily occur when you are using dynamic lengths and offsets.