Leverage legacy systems with a blend of XML, XSL, and Java

Combine XML, XSL, and Java to interface with mainframe systems

No matter which way you try it, interfacing a mainframe from an application server or servlet is never fun. Among other hurdles to overcome, the mainframe could exist on a different platform or use a different character set. Think you can simply access the data directly and rebuild your business logic? Perhaps, if your database is not hierarchical and you enjoy reinventing the wheel. However, a few tricks using XML, XSL, and Java can make it easier than you think.

XML describes the structure of the data you are sending. Add XSL to complete some simple transformations. Use Java to encapsulate it with a few simple classes. You will then leverage a host of legacy system transactions written decades ago and format them into an XML document. Once your data is in an XML format, manipulating it becomes a much less daunting task, and development becomes easier. In addition, the solution you employ is realtime, which circumvents the frustrations and problems that arise from working with an extract file or batch interface.

Note: the complete source code for this article can be downloaded as a zip file from Resources.

The versatile XML

While many may only think of XML in terms of marking up structured documents, you can use XML to describe anything that has a particular structure. For the purposes of this article, we shall assume that our legacy system stores data in a hierarchical database that may be accessed through COBOL programs residing on the system. Why not access the database directly to retrieve the information? While the hierarchical database offers some advantages for the mainframe system, directly accessing it requires extensive knowledge of the database. Usually, the underlying database features significant complexity, which makes traversing it exceedingly difficult, if not downright impossible. Since the COBOL programs are already available for that purpose, there remains no reason to undertake such a complicated task.

You must know the structure of the transaction you will interface in order to invoke the COBOL program. To obtain that knowledge, refer to the program copybook, which contains the directions for use. Those directions include information about the length of the field, name, left or right justification, etc. Think of those descriptors as a set of fields that are described by attributes. Those descriptors provide directions on how to format the request and how to parse the returning data from the subsequent call.

Using XML, you can now implement that copybook metadata to generate your copybook markup language. Below, this short, simplistic example of an XML document containing our COBOL program’s metadata retrieves a name when given a social security number:

<transaction>
      <configuration>
            <name>CobolNameProgram</name>
            <requestlength>30</requestlength>
            <replylength>200</replylength>
      </configuration>
      <request>
            <field identifier="SSN", justification="R", length="12"/>
            <field identifier="username", justification="L", length="8"/>
      </request>
      <reply>
            <field identifier="firstname", length="15"/>
            <field identifier="lastname", length="25"/>
      </reply>
</transaction>

The name describes the name of the COBOL program you invoke, and the lengths of the corresponding request and reply messages. The attributes of each field you will encounter describe the name, field length, justification, or any item you need to know to process this request.

The Java engine

Referencing the structure above, you can write the code to hook up your values into your request’s structure and parse the incoming reply with another document containing the name/value pairs that correspond to the field identifiers. That sample incoming document may look like this after some initial processing:

<CobolNameProgram>
      <field name="SSN", value="123456789"/>
      <field name="username", value="mickey"/>
</CobolNameProgram>

To format the request, the code simply needs to search for and match the identifier for each element, and write the value according to the attributes’ rules by iterating through the fields.

XSL style

Developers use the XSL language for manipulating XML from one structure into another. It can be implemented for transforming XML into HTML or other XML document structures (for more information on how to manipulate XML documents with XSL, see Resources).

You will use XSL to format an incoming document structure to the name/value-pair structure described above, which the Java code processes into the COBOL structure. XSL will also format the data returned from the legacy system — once the Java code transforms it back into XML — into a usable structure for the function or application using this API. XSL allows you to build Java code that will be insulated from changes to the external format of both the calling application and the legacy system. I will describe more on that advantage later.

The Xerces parser allows for easy XML manipulation within Java (for more information regarding the Xerces parser, see Resources). Assume that an application using your API will pass the data your request needs as an XML document. It will feature whatever structure that the application requires. We need not concern ourselves with it since our application can apply XSL to style the structure into our usable name/value-pair configuration described above. That resulting name/value-pair structure can then be passed to the Java code for manipulation into the COBOL structure.

After you obtain the name/values pairs containing the data necessary for the request, and the metadata describing the structure of the COBOL transaction, you can write the Java code that manipulates the request:

package JavaWorldExample;
import org.apache.xerces.parsers.*;
import org.xml.sax.*;
import java.io.*;
import org.w3c.dom.*;
import com.lotus.xsl.*;
public class TransformMessage {
  public TransformMessage() {
  }
  byte [] formatMessage(Document doc) {
  int txFieldLength = 0;
  byte [] txFieldLengthBytes;
  int txFieldIndex = 0;
  byte [] txField;
  StringBuffer txInputBuffer = new StringBuffer();
  String value = "";
  String szLen = "";
  try {
  // Apply XSL to document to style into name/value format
  StringWriter writer = new StringWriter();
  XSLProcessor processor = new XSLProcessor();
  processor.reset();
  processor.process( new XSLTInputSource(doc),
                     new XSLTInputSource(new FileReader("c://input//CommonFormat.xsl")),
                     new XSLTResultTarget(writer) );
  String  formattedxml = writer.toString();
  //Get the element list and root node
  DOMParser lparser = new DOMParser();
  lparser.parse(new InputSource(new StringReader(writer.toString())));
  doc = lparser.getDocument();
  NodeList transNlist = doc.getElementsByTagName("GetName");
  Element dataNode =  (Element) transNlist.item(0);
  
  // Get the transaction metadata
  DOMParser parser = new DOMParser();
  parser.parse(new InputSource(new FileReader("c://input//Samplemetadata.xml")));
  Document metadoc = parser.getDocument();
  // Apply the metadata and rules to the message
  NodeList nlist = metadoc.getElementsByTagName("FIELD");
    for (int i = 0; i < nlist.getLength(); i++) {
    txFieldIndex = 0;
        //get the field length from the meta data
        if ( nlist.item(i).getAttributes().getNamedItem("LTH") == null)
          szLen="0";
        else
          szLen = nlist.item(i).getAttributes().getNamedItem("LTH").getNodeValue().trim();
        //calculate the length of current field
        txFieldLength = Integer.parseInt(szLen);
        //create transaction-field byte array
        txField = new byte [txFieldLength];
        // find the field in the application XML Document & get the attributes
        NodeList dataNlist = dataNode.getElementsByTagName(nlist.item(i).getAttributes().getNamedItem("NAME").getNodeValue().trim());
        if (dataNlist.getLength() > 0) {
          value = new String (dataNlist.item(0).getAttributes().getNamedItem("VALUE").getNodeValue().trim());
        }
        //add the actual value of field
        txFieldIndex = CommonUtilities.buildRI(txField, value.getBytes(), txFieldIndex, value.length());
        //Determine the justification of the field
        if ( nlist.item(i).getAttributes().getNamedItem("JUST").getNodeValue().equals("L")) {
        //Left Justify
          for(int k=0; k < (txFieldLength-txFieldIndex); k++) {
          txField[k+txFieldIndex]=(byte)(' ');
          }
        }
        else { //Right Justify
         CommonUtilities.rightJustify(txField);
       }
       //add field to input buffer
       txInputBuffer.append(new String(txField));
    }// end for all fields
    }
    catch (Exception e) {
    e.printStackTrace();
    }
    return txInputBuffer.toString().getBytes();
  } // End function
} //End class TransformMessage

I’ve described all that you need to accomplish to set up your outgoing structure. To format the return, a like strategy is utilized. Data manipulation transforms the response from a byte array or similar structure into an XML document. Essentially, the code matches the fields with the value from the array and creates name/value pairs that are suitable for a simplistic XML document. Once that is complete, XSL styles the information in any way desired. It is also important that you set up some sort of transmission method to the environment in which you execute the COBOL program. As the focus of this article is the server-side application, I do not delve into that discussion here.

Framework advantages

It may be helpful at this point to provide an illustration of the process as it has evolved to this point.

Figure 1. Legacy transaction framework

While it may not be readily apparent, this layout offers several distinct advantages:

  • Since XSL is applied to the incoming document as well as the outgoing document, the Java engine, once written, is insulated from changes in the incoming message structure. The entire DTD, or schema, of the incoming message could be changed completely. A programmer would only have to modify the XSL within this framework that styles the document into your name/value-pair format. In addition, if you store the XSL in some sort of database or flat file that is looked up, you can modify it without even recompiling our code.
  • If the mainframe format changes, you could alter the XML metadata to reflect those shifts, providing the same benefits as above.
  • The transmission method is separate from the data manipulation. You could plug in another transmission method with minimal effort should the need arise or a better method come along.
  • The existing legacy programs are used as is — you don’t need to modify the mainframe.

Conclusion

XML, XSL, and Java work hand in hand to solve the complex problem of mainframe transaction access. Implementing XSL styling creates supplementary layers in the framework that allow the solution to configure changes from the application server or the mainframe programs without recompiling the Java code that performs the mapping. XML allows us to describe the structure of the COBOL transactions, providing directions to the Java engine on how to build the request or parse the response. I used this framework successfully in the last two e-commerce projects I participated in, and I will not hesitate to implement it again in the future.

Michael Koch is senior consultant for Software
Architects in Cincinnati, Ohio. A Sun-certified Java Programmer,
Michael has been working on projects interfacing with legacy
systems for the last two years, using a variety of middleware
technologies. Previously, Michael worked as a DSP/VC++ programmer
writing code for high-definition television exciters. Michael
graduated with high honors in computer science from the University
of Cincinnati.

Source: www.infoworld.com