Evolving a heritage application using Java
In this chapter, we take a COBOL heritage application and describe ways that you can enhance it using the JCICS API. The stages of evolution are:
1. Implement a Web interface to coexist alongside the original 3270 display, which opens up the underlying business logic to a generation of Internet clients.
2. Fully implement the back-end business logic using Java, which introduces a pluggable data storage component for future scalability.
3. Implement a DB2 back-end, and migrate the application data from its existing VSAM setup.
4. Implement a Web service interface alongside the other interfaces into the program
You might find this section useful as both an approach for migrating heritage applications to JCICS and as a reference to example implementations of Web, file, and DB2 access using the JCICS API.
7.1 The heritage Trader application
The COBOL Trader sample is a 3270 application that allows you to buy and sell shares from a group of companies in real time. Written in COBOL, it has a CICS BMS interface and uses VSAM file access for data storage. The application is pseudo-conversational, which means that a chain of related non-conversational CICS transactions is used to convey the impression of a “conversation” to you as you go through a sequence of windows that constitute a business transaction. Figure 7-1 shows a breakdown of the heritage application.
The application consists of two modules:
TRADERPL: The 3270 presentation logic. Invokes TRADERBL using an EXEC CICS LINK passing a COMMAREA structure for input and output.
TRADERBL: The business logic. Data is persisted in VSAM files.
Figure 7-1 Breakdown of the heritage application
7.1.1 Installing the Trader application
The COBOL Trader application requires the following resources:
Files
Programs
Mapset
CICS definitions
Files
The Trader application uses two VSAM files:
COMPFILE: Stores the list of companies and associated share prices. You can create it using the supplied JCL TRADERCOJCL.TXT, which requires as input the file TRADERCODATA.TXT.
CUSTFILE: Stores the list of users and their share holding. You can create it using the supplied JCL TRADERCUJCL.TXT.
Programs
Two COBOL programs are used, TRADERPL and TRADERBL, which you must compile and put in a data set in your CICS region’s DFHRPL concatenation. The programs are supplied as the files TRADERPL.TXT and TRADERBL.TXT, respectively.
Mapset
Trader uses the mapset NEWTRAD, which comprises the maps T001, T002, T003, T004, T005, and T006. The mapset is supplied in the file NEWTRAD.TXT, which you must assemble and put in your CICS region’s DFHRPL concatenation.
CICS definitions
Define these CICS resources in the TRADER group.
MAPSET(NEWTRAD)
PROGRAM(TRADERBL)
LANGUAGE(COBOL)
PROGRAM(TRADERPL)
LANGUAGE(COBOL)
TRANSACTION(TRAD)
PROGRAM(TRADERPL)
FILE(COMPFILE)
DSNAME(DATASET.CONTAINING.COMPFILE)
RLSACCESS(Yes)
STRINGS(5)
ADD(Yes)
BROWSE(Yes)
DELETE(Yes)
READ(Yes)
UPDATE(Yes)
FILE(CUSTFILE)
DSNAME(DATASET.CONTAINING.CUSTFILE)
RLSACCESS(Yes)
STRINGS(5)
ADD(Yes)
BROWSE(Yes)
DELETE(Yes)
READ(Yes)
UPDATE(Yes)
For further information about creating the resource definitions for the Trader application, refer to the supplied file TRADERRDO.TXT, which contains the output of a CSD extract for group TRADER.
7.2 Other Extensions to the Trader application
There are other extensions to the Trader application that we wanted to try, but we did not have the time for. Two of these in particular are worthy of mention. They are both alternative ways of driving the Trader application, which we describe in the next sections.
7.2.1 Using WMQ Classes to drive the Trader application
It is useful to drive CICS transactions from messages using WebSphere MQ Series queues, which you can do easily with the MQ Series Java classes. All that is required is the following:
1. Define the application and transaction to CICS using the supplied CEDA transaction.
2. Ensure that the WebSphere MQ CICS adapter is installed in your CICS system. See WebSphere MQ for z/OS System Setup Guide for details.
3. Ensure that the JVM environment that is specified in CICS includes the appropriate CLASSPATH and LIBPATH entries.
4. Initiate the transaction from WebSphere MQ
More information about this and the CICS MQ adapter in general are in the CICS Transaction Server documentation. From CICS TS 3., 2 the MQ Adapter is owned and shipped by CICS.
7.2.2 Using the CICS Common Client Interface (CCI)
CICS TS 3.2 ships with support for the Common Client Interface (CCI), which is defined by the J2EE Connector Architecture Specification, Version 1.0 (JCA). The CCI connector for CICS helps you to build Enterprise JavaBean (EJB) server components that expose existing CICS programs.
This feature is particularly useful if you want to access CICS programs from WebSphere.
The CICS CCI provides a simple way for the Java client to access CICS programs. The CICS CCI has much in common with the CCI that is provided with CICS Transaction Gateway (CICS TG) but has the added benefit that the CICS CCI runs in a CICS region. CCI programs that are written to run outside of CICS, such as those that use the CICS TG CCI, can easily be migrated to the CICS CCI.
More information about CCI and JCA is in the CICS TS documentation, and in the JCA specification, which is available at this Web site:
7.3 Adding a JCICS Web interface
The Trader application is comprised of a presentation component and a business component with requests that are transmitted between them using a COMMAREA. Given an understanding of the COMMAREA structure, you can write and run additional interfaces alongside, or instead of, the original 3270 display, which Figure 7-2 on page 139 illustrates.
Figure 7-2 A Web interface alongside the heritage application
Following this approach, you implement a Web interface to the Trader application using the JCICS Web API.
7.3.1 Wrapping the COMMAREA
To transmit data requests to the COBOL business component, the JCICS program needs an understanding of the COMMAREA data. The COMMAREA is an array of bytes, the data of which is formatted according to a COBOL data structure. Given this information, you write a wrapper class in Java, which allows you to set and get the various values that are contained within the COMMAREA.
There are several methods that we can use to construct a wrapper class, and we introduce three of them in this book. In this section, we introduce:
The first method in the next section, to construct a java class manually
Because the three methods generate different wrapper classes, we chose the first one for our Trader example. The biggest difference between the three generated classes are the function names in the class. It is easy to change from one method to another.
Example
As a starting point, we discuss wrapping the first two lines of the COMMAREA, as shown in Example 7-1.
Example 7-1 The first two lines of the COMMAREA from TRADERBL
01 COMMAREA-BUFFER.
03 REQUEST-TYPE PIC X(15).
The structure of the wrapper is a Java class that contains:
A byte[] for the COMMAREA data
A getter and setter method for the byte[]
Variables that hold the index and length of each value in the COMMAREA
Getter and setter methods for each value
A utility method for formatting values
Example 7-2 The structure of the wrapper class
public class CommareaWrapper {
 
// Values in the COMMAREA
private final int[] COMMAREA_BUFFER = {0, 372}; // (1)
private final int[] REQUEST_TYPE = {0, 15};
 
// Variable to hold the binary data in the COMMAREA
private byte[] fData;
 
/* Instantiate the wrapper object and initialize its values */
public CommareaWrapper() {
fData = new byte[COMMAREA_BUFFER[1]];
setNewValue("", 0, fData.length); // (2)
}
 
/* Set the binary data of the COMMAREA */
public void setByteArray(byte[] data) { fData = data; }
 
/* Get the binary data of the COMMAREA */
public byte[] getByteArray() { return fData; }
 
/* Get the value of REQUEST-TYPE */
public String getRequestType() {
return new String(fData, REQUEST_TYPE[0], REQUEST_TYPE[1]);
}
 
/* Set the value of REQUEST-TYPE */
public void setRequestType(String newValue) {
setNewValue(newValue, REQUEST_TYPE[0], REQUEST_TYPE[1]);
}
 
/*
* Inserts a value into the main byte[] and pads with spaces if necessary.
* Note: values will be left justified
*/
private void setNewValue(String newValue, int index, int length) { ... }
}
{0, 372} implies index=0 & length=372.
Resets all values in the array to the space character.
You have a fully usable wrapper. To add further COMMAREA values, create a new variable to hold its index and length settings and the corresponding getter and setter methods. Generally the index is set to the index of the previous value plus its size, but sometimes it can also overlay a previous value depending on the COMMAREA structure.
Character-based values
The code so far applies to character-based non-numeric values. For character-based numeric values, you must implement an alternative setNewValue(), which right justifies and pads with zeros if required.
 
Important: COBOL character types can be flexible, so pay close attention during the creation of the wrapper. Feel free to modify and create new versions of the setNewValue() method as required.
Binary-based values
COBOL types that are binary based (COMP1, COMP3, and so on) need further processing.
The suggested approach is to write a getter method that converts the binary data into a Java primitive number (short, int, long) and gives it to the user. The setter method does the opposite and takes a Java primitive number, which it converts to the corresponding COBOL binary representation and inserts it into the byte[].
Arrays
The COBOL statement OCCURS X TIMES refers to an array structure of size X. To allow for values that exist in arrays, the getter and setter methods must take an extra parameter, the index in the array:
public String getValue(int arrayIndex)
public void setValue(String newValue, int arrayIndex)
As shown in Example 7-3, the code for finding the index of a value in the array then becomes
index = value-index + (value-length * array-index),
Example 7-3 getter and setter methods for an array value
/* Get the value of ARRAY_VAL. Note: arrayIndex starts at 0 */
public String getArrayVal(int arrayIndex) {
return new String(fData, ARRAY_VAL[0] + (ARRAY_VAL[1] * arrayIndex), ARRAY_VAL[1]);
}
 
/* Set the value of ARRAY_VAL. Note: arrayIndex starts at 0 */
public void setCompanyNameTab(String newValue, int arrayIndex) {
setNewValue(newValue, ARRAY_VAL[0] + (ARRAY_VAL[1] * arrayIndex), ARRAY_VAL[1]);
}
 
Wrapping COBOL arrays: COBOL arrays can get quite complex, but it is still possible to wrapper them using indexing techniques similar to Example 7-3.
7.3.2 Wrapping the COMMAREA using JZOS
From version 2.2.1, JZOS supports automatic generation of record classes from COBOL copybooks and Assembler DSECTs. We use this function to generate our wrapper class.
Installing JZOS to z/OS
To install JZOS to z/OS:
1. Download the latest version JZOS package from the IBM AlphaWorks Web site.
2. Install it on our z/OS image. See the JZOS Batch Launcher and Toolkit Installation and User's Guide, SA23-2245-00.
After installation the following directories are created.
Name
Location
JZOS_HOME
/u/cicsrs5/jzos
Sample
CICSRS5.JZOS.SAMPJCL
Loadlib
CICSRS5.JZOS.LOADLIB
During installation, one directory is created in zFS. This directory contains the DLL and jars that are needed for JZOS, we specify this directory in <JZOS_HOME>
Changing JVM profile for JZOS
To change the JVM profile for JZOS:
1. Add <JZOS_HOME> to our LIBPATH_SUFFIX in JVM profile.
2. Add <JZOS_HOME>/ibmjzos.jar to our CLASSPATH_SUFFIX in JVM profile.
Example 7-4 shows the JVM profile for JZOS.
Example 7-4 JVM profile for JZOS
LIBPATH_SUFFIX=/u/cicsrs5/jzos:
CLASSPATH_SUFFIX=/u/cicsrs5/jzos/ibmjzos.jar:
Generating the wrapper class
To generate the wrapper class:
1. Copy COMMAREA COBOL copybook to Sample directory. There is sample JCL named COBGEN that is used to generate record classes from COBOL copybooks.
2. Edit COBGEN following the guideline in comments.
3. Tailor the procedure and job for your installation:
a. Modify the Job card per your installation's requirements.
b. Customize the JCL SET variables.
c. Edit JAVA_HOME to point to the location of the Java SDK.
d. Edit JZOSAW_HOME to point to the JZOS alphaWorks® directory.
e. Modify MAINARGS DD arguments to RecordClassGenerator, as shown in Example 7-5 on page 143.
Example 7-5 MAINARGS DD
//MAINARGS DD *
com.ibm.jzos.recordgen.cobol.RecordClassGenerator
bufoffset=false
package=com.cobol.records
outputDir=/u/cicsrs5
 
Notes about Example 7-5:
 – Package is the package name of the generated java class.
 – outputDir is the directory where we want to put the generated java class.
Details about how to use this JCL are in the JZOS Cobol Record Generator Users Guide.
4. Submit the JCL, which produces a Java class based on the provided COMMAREA.
Updating generated wrapper class
The generated wrapper class does not have the method setBytebuffer(byte[] buffer), so we must manually add it.
Add setBytebuffer(), and change all of the getxxx() functions, as shown in Example 7-6.
Example 7-6 setBytebuffer() and getxxx()
public void setBytebuffer(byte[] buffer){
this._byteBuffer = buffer;
}
/*old getxxx function
public String getRequestType() {
if (requestType == null) {
requestType = REQUEST_TYPE.getString(_byteBuffer);
}
return requestType;
}
*/
public String getRequestType() {
requestType = REQUEST_TYPE.getString(_byteBuffer);
return requestType;
}
Now we have our wrapper class. It is much easier to use this method than to manually write a class because we do not need to know what data types are used in the copybook. JZOS manages all of them for us.
This is a manual process because we must update the generated java class and add the JZOS library and class to our JVM profile. Let us try another method of generating the wrapper class, J2C in RD/z.
7.3.3 Wrapping the COMMAREA using J2C in RD/z
The full name of J2C is J2EE Connector, which is a tool that you can use to create J2EE Connector artifacts that you can use to create enterprise applications. It helps you create a class or set of classes that map to COBOL, C, or PL/I data structures. In our example, we only use it to create a java data binding class. J2C is part of RD/z and is available to all users.
CICS/IMS Java Data Binding wizard
The CICS/IMS Java Data Binding wizard guides you step-by-step to generate a wrapper class:
1. Switch to the J2EE perspective.
2. To start the J2C dynamic wizard, from the menu bar, select File  New  Other  J2C.
3. Select CICS/IMS Java Data Binding, as shown in Figure 7-3.
Figure 7-3 Select CICS/IMS Java Data Binding
4. Click Next.
5. On the Specify data import configuration properties page, specify the data types that your binding class uses and the location of the COBOL file, as shown in Figure 7-4 on page 145.
Figure 7-4 Select mapping type and specify COBOL file
 
Mapping types that you can use:
COBOL to Java
C to Java
COBOL MPO to Java (For output data bindings only)
C MPO to Java (For output data bindings only)
PL/I to Java
PL/I MPO to Java (For output data bindings only)
J2C support COBOL, C and PL/I (We choose COBOL to Java here)
6. Click Next. Choose the platform and data structure that you want to map, as shown in Figure 7-5 on page 146.
Figure 7-5 select platform and communication data structure
 
Important: Make sure that you choose z/OS platform here. the default value is win-32.
7. Click Next. Select the Project and Package Names, as shown in Figure 7-6 on page 147.
Figure 7-6 Select project name and package name
8. Click Finish to create the wrapper class COMMAREABUFFER.java.
 
Important: This wrapper class depends on marshall.jar and J2EE.jar, so you must upload these two jar to your zFS, and add them into the CLASSPATH_SUFFIX of JVM profile.
You can also generate java classes for accessing a VSAM file. To do this, return to Figure 7-5 on page 146, and select CUSTOMER-IO-BUFFER to generate a java class for accessing file CUSTFILE. Alternately, select COMPARY-IO-BUFFER to generate a java class for accessing file COMPFILE.
We created the required wrapper class, which includes all of the gettter and settter methods, so no manual code is needed. A few button clicks and the class is generated. J2C is the easiest option of the three methods that are available.
7.3.4 Understanding COMMAREA request formats
Having written the CommareaWrapper, we now must understand how to format the requests to send to TRADERBL. Observing the link commands in TRADERPL, the requests can be broken down to the following settings in the COMMAREA structure:
Get list of companies:
 – REQUEST-TYPE = “Get_Company”
Get a company quote:
 – REQUEST-TYPE = “Share_Value”
 – RETURN-VALUE = “00”
 – USER-ID = User ID of current user
 – COMPANY-NAME = Company selected for previous menu
Buy shares:
 – REQUEST-TYPE = “Buy_Sell”
 – RETURN-VALUE = “00”
 – USER-ID = User ID of current user
 – COMPANY-NAME = Company selected for previous menu
 – UPDATE-BUY-SELL = “1”
Sell shares:
 – REQUEST-TYPE = “Buy_Sell”
 – RETURN-VALUE = “00”
 – USER-ID = User ID of current user
 – COMPANY-NAME = Company selected for previous menu
 – UPDATE-BUY-SELL = “2”
If you set these values in the CommareaWrapper and link to TRADERBL the data returned is exactly the same as that for TRADERPL.
7.3.5 A test Web application
To test the CommareaWrapper class, we now write a simple JCICS Web program to interface with the COBOL application TRADERBL. The program retrieves a list of companies and displays them in a Web page.
SimpleTraderPL
Using the JCICS Web API create a Java class called SimpleTraderPL. As with all JCICS programs, it needs a public static void main(CommAreaHolder commAreaHolder) method, which in this case gets called by the CICS Web transaction CWBA.
The flow of the program is:
1. Build the COMMAREA data.
2. Link to TRADERBL passing the COMMAREA.
3. Build the HTML from the returned COMMAREA data.
4. Send the HTTP response.
Building the COMMAREA is a matter of creating a CommareaWrapper object instance and setting the requestType value to "Get_Company", as shown in 7.3.4, “Understanding COMMAREA request formats” on page 148. To alleviate any potential errors that might occur if this value is referenced in multiple places, it is stored as a static variable GET_COMPANY_REQ in the class TraderConstants.
Next the request is sent using a CICS program link to TRADERBL. A call to getByteArray() extracts the byte[] from CommareaWrapper and passes it to the JCICS link() command. Any COMMAREA data that TRADERBL changes is reflected back in this byte[].
 
Program names are case sensitive: Program names specified for the JCICS link() command are case sensitive and must be presented in uppercase.
If the link command fails for any reason, the exception data is outputted to Java’s stderr. See 8.3.4, “JVM stdout and stderr” on page 206, for more information.
The program now has the list of companies and begins to build the HTML response. Dynamic HTML content is built using documents that allow a user to insert HTML line-by-line or in one big chunk. In JCICS, a document object is instantiated and HTML added using the appendText() method. In SimpleTraderPL, company names are extracted from the COMPANY-NAME-TAB array value in the CommareaWrapper and inserted into the HTML. Lastly, the document is sent back in the HTTP response. If anything fails, an attempt is made to send back a HTTP 500 error response.
 
HTTP response values: The parameters to the HttpResponse.sendDocument() method supply HTTP response values, such as status code, status response, and client code page.
Example 7-7 shows the syntax for the SimpleTraderPLmain() method.
Example 7-7 The SimpleTraderPL main() method
public static void main(CommAreaHolder commAreaHolder) {
final CommareaWrapper commareaWrapper = new CommareaWrapper();
 
// Prepare COMMAREA for request to COBOL program TRADERBL
commareaWrapper.setRequestType(TraderConstants.GET_COMPANY_REQ);
 
// Send the data request to TRADERBL
final Program program = new Program();
 
HttpResponse response = null;
Document document = null;
 
try {
// Link to TRADERBL
program.setName("TRADERBL");
program.link(commareaWrapper.getByteArray());
 
// Build the HTML response
response = new HttpResponse();
document = new Document();
 
document.appendText("<H1>SimpleTraderPL</H1>");
document.appendText("<P>List of companies:</P>");
document.appendText("<UL>");
document.appendText("<LI>" + commareaWrapper.getCompanyNameTab(0) + "</LI>");
document.appendText("<LI>" + commareaWrapper.getCompanyNameTab(1) + "</LI>");
document.appendText("<LI>" + commareaWrapper.getCompanyNameTab(2) + "</LI>");
document.appendText("<LI>" + commareaWrapper.getCompanyNameTab(3) + "</LI>");
document.appendText("</UL>");
 
// Send the response
response.sendDocument(document, (short) 200, "OK", "iso-8859-1"); // (1)
}
catch (Exception e) { sendErrorResponse(); e.printStackTrace(); }
}
 
private static void sendErrorResponse() {
try {
final Document doc = new Document();
doc.appendFromTemplate("Internal Error");
new HttpResponse().sendDocument(doc, (short) 500, "Internal Error","iso-8859-1");
}
catch (Exception e) { System.err.println("Exception: " + e); e.printStackTrace(); }
}
200 is the HTTP status code for OK.
iso-8859-1 is the name of the Latin-1 (ascii) character set.
Further features of documents are used in “Displaying the next page” on page 156.
Installing the Trader into CICS
The source code and compiled classes for this example are in Trader-1.jar. Follow the instructions in Chapter 3, “Setting up CICS to run Java applications” on page 37, and make sure this jar file is added to the CICS Java class path.
You must define two new CICS resources alongside the existing definitions in the TRADER group:
PROGRAM(TRADERPS)
CONCURRENCY(Threadsafe)
JVM(Yes)
JVMCLASS(com.ibm.itso.sg245275.trader.SimpleTraderPL)
TCPIPSERVICE(TCPIPS)
URM(DFHWBADX)
PORTNUMBER(<portnumber>)
PROTOCOL(Http)
TRANSACTION(CWXN)
AUTHENTICATE(No)
For the TCPIPSERVICE definition, make sure that you choose a port number that is available and not already open. Also ensure the group DFHWEB is installed to pick up the necessary Web transactions.
 
Note: The DFHWEB group is in DFHLIST, so if you have this in your CICS startup, the Web transactions are installed by default.
Running SimpleTraderPL
To run SimpleTraderPL:
1. Open your favorite Web browser, and type in a URL similar to the following:
2. Ensure that the port number matches up to that in your TCPIPSERVICE definition and that the program name at the end is traderps.
 
Note: The program name at the end of the URL is not case sensitive.
The browser now displays the list of companies, as shown in Figure 7-7.
Figure 7-7 SimpleTraderPL’s browser display
Congratulations. You implemented your first JCICS Web interface.
7.3.6 Designing the HTML interface
After successfully running the sample, you can begin developing JCICS Web applications. But before jumping in feet first with the coding, let us take a look at the Web site’s navigation design.
The BMS display
The 3270 BMS display allowed users to traverse through menus while making navigation decisions along the way. From each menu, they can always get back to the previous one. A graphical representation of the navigation paths is shown in Figure 7-8 on page 152.
Figure 7-8 Navigation of the Trader application
The HTML design
The HTML pages navigation follows a similar design to the 3270 display, although there are a couple of changes that you can make to increase usability. An explicit login page is no longer needed because CICS has the option of handling Web security implicitly for you. For more information about Web security, see 7.3.9, “Web security” on page 160.
Utilizing the flexibility of HTML navigation, the company selection and share trading options pages are combined to provide a single interface for showing the companies and actions that you can perform on them. For further convenience the company quotes page contains direct links to the share buying and selling pages for that company. As a usability enhancement, the buy and sell pages also display some of the company information that is available from the company quotes page.
Figure 7-9 shows the new navigation design.
Figure 7-9 Web interface navigation
7.3.7 Implementing the design
With the design complete, you can now implement the code. For the purpose of maintainability, the HTML Web pages are stored in PDS members and accessed using the CICS document’s API, which is fully supported in JCICS. Documents allow you to build dynamic HTML using CICS templates and symbols.
 
Note: PDS members have a fixed width, so HTML files that are uploaded using FTP are checked to ensure that long lines are not truncated.
TraderPL
The JCICS Web program TraderPL is a single point of entry to all Web requests. Parameters received through HTTP GET and POST requests control the response of the program.
The main flow of logic through the program is:
1. Get HTTP input fields.
2. Perform buying and selling.
3. Get request details.
4. Display next page.
Getting HTTP input fields
For convenience, the program actions are stored as static strings. These values must match the form field values that are specified in the HTML. Example 7-8 shows action constants.
Example 7-8 Action constants
private static final String PERFORM_BUY = "performbuy";
private static final String PERFORM_SELL = "performsell";
private static final String SHOW_LIST = "showlist";
private static final String SHOW_DETAILS = "Details"; // (1)
private static final String SHOW_BUY = "Buy";
private static final String SHOW_SELL = "Sell";
private static final String LOGOFF = "Logoff";
Note on Example 7-8:
The HTML equivalent for this is:
<INPUT type='submit' name='action' value='Details'>
If the action is to buy or sell shares, then after this is performed the user is redirected to the company details page. When no parameter is supplied, the default action is to show the company list page. Alternately, if an unknown action is specified, then a message is sent back to the user.
Example 7-9 shows the main flow of logic in TraderPL.
Example 7-9 The main flow of logic in TraderPL
private String fCompanyName;
private String fNumShares;
private String fMessage;
 
public TraderPL() {
// Get HTTP data
final HttpRequest request = HttpRequest.getHttpRequestInstance();
String action = null;
try {
action = request.getFormField("action");
fCompanyName = request.getFormField("companyname");
fNumShares = request.getFormField("numshares"); // Used when buying/selling shares
}
catch (InvalidRequestException e) {}
// If action is not set then default to show company list screen
if (action == null) action = SHOW_LIST;
// Buy/sell shares and then redirect to show company details screen
else if (PERFORM_BUY.equals(action)) {
performBuy();
action = SHOW_DETAILS;
}
else if (PERFORM_SELL.equals(action)) {
performSell();
action = SHOW_DETAILS;
}
// Show desired page
if (SHOW_LIST.equals(action)) showList();
else if (SHOW_DETAILS.equals(action)) showDetails();
else if (SHOW_BUY.equals(action)) showBuy();
else if (SHOW_SELL.equals(action)) showSell();
else if (LOGOFF.equals(action)) logoff();
else {
// Notify the user of the unknown action
fMessage = "Unknown action: '" + action + "'"; // (1)
 
// Invalid page requested so show company list page instead
showList();
}
}
This message is output as part of the HTTP response.
 
Bypassing case-sensitive HTTP parameters: By default, HTTP parameters are case sensitive. To bypass this your Java program can use the String.toLowerCase() function for string comparisons.
Perform buy/sell
The performBuy() and performSell() methods are virtually identical. The only difference is the value for UPDATE-BUY-SELL in the COMMAREA, as shown in 7.3.4, “Understanding COMMAREA request formats” on page 148.
The method structure is:
1. Build request COMMAREA.
2. Send request.
3. Check return value.
A non-zero return value generates a message that is returned in the HTTP response. For convenience, we added static constants for the return values and their corresponding messages to the TraderConstants class, which we first mentioned in “SimpleTraderPL” on page 148. It also has a utility method for passing a return code and getting back its corresponding message.
Example 7-10 on page 155 contains the syntax for the performBuy() method.
Example 7-10 The performBuy() method
private void performBuy() {
// Prepare commarea for request to COBOL program Traderbl
fCommareaWrapper.setRequestType(TraderConstants.BUY_SELL_REQ);
fCommareaWrapper.setReturnValue(TraderConstants.CLEAN_RETURN);
fCommareaWrapper.setCompanyName(fCompanyName);
fCommareaWrapper.setNoOfSharesDec(fNumShares);
fCommareaWrapper.setUpdateBuySell(TraderConstants.SUBTYPE_BUY);
// Send the data request to TRADERBL
sendLinkRequest();
 
// If the link failed then a message was set by sendLinkRequest.
// If it worked then check the return value from TRADERBL
if (fMessage == null)
fMessage = TraderConstants.getMessage(fCommareaWrapper.getReturnValue());
}
Get request details
All of the show*() functions follow a common design:
1. Build request COMMAREA.
2. Send request.
3. Check return value.
4. Build symbol list.
5. Send HTTP response.
 
Get_Company: The Get_Company request to TRADERBL does not produce a return value and so is not checked.
The appropriate values are set in the CommareaWrapper and the request sent to TRADERBL. When the request returns, the symbol list is built using a series of name / value pairs (see CICS Application Programming Guide, SC34-6231, for more information about symbols). As you can see in Example 7-11, values are extracted from the CommareaWrapper and inserted into the symbol list. For any numerical values that are displayed on pages, such as the company quotes page, a custom utility method strip() is used to remove any leading zeros for the code of this method.
Example 7-11 The showList() method
private void showList() {
// Prepare COMMAREA for request to COBOL program Traderbl
fCommareaWrapper.setRequestType(TraderConstants.GET_COMPANY_REQ);
// Send the data request to TRADERBL
sendLinkRequest();
 
// Return value is not checked as none are returned from a GET_COMPANY_REQ request
// Set appropriate symbol values
final String symbolList =
"company1=" + fCommareaWrapper.getCompanyNameTab(0) + "&" +
"company2=" + fCommareaWrapper.getCompanyNameTab(1) + "&" +
"company3=" + fCommareaWrapper.getCompanyNameTab(2) + "&" +
"company4=" + fCommareaWrapper.getCompanyNameTab(3) + "&" +
"message=" + (fMessage != null ? fMessage : ""); // (1)
// Send the response
sendResponse(symbolList, "TRADCOML"); // (2)
}
If a message was generated, its symbol value is set or else it is left blank.
TRADCOML is a DOCTEMPLATE definition that needs to exist in CICS. For more information, see “Installing TraderPL” on page 157.
The sendLinkRequest() method, shown in Example 7-12, is identical to the code in “SimpleTraderPL” on page 148 other than a single addition. Most requests to TRADERBL require a user ID, and because there is no longer an explicit login page, as discussed in 7.3.6, “Designing the HTML interface” on page 151, the user ID is retrieved using the JCICS method Task.getTask().getUSERID().
Example 7-12 The sendLinkRequest() method
private void sendLinkRequest() {
final Program program = new Program();
program.setName("TRADERBL");
try {
// Set the UserId value in the COMMAREA
fCommareaWrapper.setUserId(Task.getTask().getUSERID());
 
program.link(fCommareaWrapper.getByteArray());
}
catch (Exception e) { System.err.println(e); e.printStackTrace(); fMessage = "Error - " + e; }
}
 
Note: If security is disabled, the user ID defaults to CICSUSER. See 7.3.9, “Web security” on page 160 for enabling security in Web applications.
Displaying the next page
The sendResponse() method completes the HTTP request by building the document object, adding the symbol list to it, appending the corresponding DOCTEMPLATE, and sending the HTTP response. If anything goes wrong while setting up the document object, then the exception data is outputted to Java’s stderr, and an error message is sent back to the user in the HTTP response. An error occurs if the template it is trying to append was not installed in CICS.
 
Tip: Use CEMT INQUIRE DOCTEMPLATE to ensure your DOCTEMPLATE was installed.
As discussed in “SimpleTraderPL” on page 148, the same parameter options are used in the HttpResponse.sendDocument() method, which Example 7-13 shows.
Example 7-13 The sendResponse() method
private void sendResponse(String symbolList, String template) {
final HttpResponse response = new HttpResponse();
Document document = null;
boolean appendTemplateWorked = false;
try {
// Create a new document and set the symbol list
document = new Document();
document.setSymbolList(new SymbolList(symbolList)); // (1)
 
// Append the template
// Note this needs to have been defined in cics as a DOCTEMPLATE resource
document.appendTemplate(template);
// If we get here then the append succeeded
appendTemplateWorked = true;
}
catch (Exception e) { sendErrorResponse(); e.printStackTrace(); }
 
// If the append failed then send back a message using the document object
if (!appendTemplateWorked && document != null) {
try {
document.appendText("Problem loading template. See Java stderr for more info");
}
catch (Exception e) {}
}
try { response.sendDocument(document, (short) 200, "OK", "iso-8859-1"); }
catch (Exception e) { sendErrorResponse(); e.printStackTrace(); } // (2)
}
To set the symbol list, the API specifies that it must be wrapped using a SymbolList object.
The sendErrorResponse() method is the same as Example 7-7 on page 149.
 
Note: Because the buy and sell HTML pages are so similar, they use a common template. Values that are specific to both are substituted in using symbols.
For more information about documents and templates, see CICS Application Programming Guide, SC34-6231.
7.3.8 Setting up TraderPL
In this section, we tell you how to install and run TraderPL.
Installing TraderPL
Similar to the source code and compiled classes for this example are in Trader-1.jar. Following the instructions in Chapter 3, “Setting up CICS to run Java applications” on page 37, make sure this jar file is added to the CICS Java classpath.
The new CICS resources that you must define alongside the existing definitions in the TRADER group are:
PROGRAM(TRADERPJ)
CONCURRENCY(Threadsafe)
JVM(Yes)
JVMCLASS(com.ibm.itso.sg245275.trader.TraderPL)
DOCTEMPLATE(TRADBYSL)
TEMPLATENAME(TRADBYSL)
DDNAME(DFHHTML)
MEMBERNAME(TRADBYSL)
DOCTEMPLATE(TRADCOMD)
TEMPLATENAME(TRADCOMD)
DDNAME(DFHHTML)
MEMBERNAME(TRADCOMD)
DOCTEMPLATE(TRADCOML)
TEMPLATENAME(TRADCOML)
DDNAME(DFHHTML)
MEMBERNAME(TRADCOML)
DOCTEMPLATE(TRADLOGF)
TEMPLATENAME(TRADLOGF)
DDNAME(DFHHTML)
MEMBERNAME(TRADLOGF)
Make sure that the corresponding HTML templates exist in CICSSYSF.APPL.TEMPLATE.
 
Note: DFHHTML is the DD card for template data sets in the startup JCL. In our example, it is set to CICSSYSF.APPL.TEMPLATE.
To polish up the look of the Web pages, a CICS hosted image is added to the HTML that uses the ImageLoader program to load the file RBHOME.gif. Ensure that the relevant DOCTEMPLATE was installed and that the image exists in the template data set (it needs to be uploaded as a binary file). Notice that for images, the attributes APPENDCRLF(No) and TYPE(Binary) must be set in the DOCTEMPLATE definition:
DOCTEMPLATE(RBHOME)
TEMPLATENAME(RBHOME)
DDNAME(DFHHTML)
MEMBERNAME(RBHOME)
APPENDCRLF(No)
TYPE(Binary)
 
Tip: You can use the ImageLoader program to host images in CICS by adding the following statement to your HTML:
<IMG src='imageldr?filename=<filename>&filetype=<filetype>' border='0'>
In the statement:
file name: Case sensitive name of the DOCTEMPLATE definition for the image file.
file type: Image type of the file, for example, gif, jpg, bmp.
Running TraderPL
Similar to “Running SimpleTraderPL” on page 150, bring up a Web browser and type in the URL:
Ensure that the port number matches up to that in your TCPIPSERVICE definition and that the program name at the end is traderpj, as shown in Figure 7-10 on page 159.
Figure 7-10 The company selection screen
If an error occurs, for example, if TRADERBL is not installed, then a message is displayed, as shown in Figure 7-11 on page 160.
Figure 7-11 The message received if TRADERBL was not installed
Congratulations. You implemented a Web interface to your existing COBOL heritage application.
 
Tip: To prove that there are no smoke and mirrors involved, try to run the Web interface and the original 3270 display side-by-side, and watch the figures get updated in both.
7.3.9 Web security
Enabling security in the TraderPL program does not require any additional coding because CICS takes all of the pain out of it by handling it for us. All you must do is set the AUTHENTICATE attribute to Basic in the TCPIPSERVICE definition.
 
Note: For information about basic authentication and other forms of Web security, see CICS Internet Guide, SC34-6245.
Now when TRADERPJ is accessed over the Web, you are prompted with a login box. Ensure that you enter a valid user name and password combination, as shown in Figure 7-12 on page 161.
Figure 7-12 Log in to run TraderPL
The JCICS call Task.getTask().getUSERID() picks up this user ID, and TRADERPJ continues seamlessly with security enabled.
Because there is no explicit way of closing an authenticated Web session in CICS, the browser window must be closed to perform a logoff.
 
Note: For enhanced levels of Web security, refer to CICS RACF Security Guide, SC34-6249.
7.4 Migrating TRADERBL to JCICS
A Web front-end introduces an application to a world-wide client base. Originally, the COBOL heritage application was probably written for an intended user base of no more than a few hundred people, which through the Web can become millions. Knowing this, it might be the case that the back-end code might not handle as well when scaled up to this factor. In anticipation of this, the approach is to re-implement the back-end using JCICS, and separate out the logic and data access functionality into two components. Then, if needed, the data access is migrated to a DB2 back end or even a workload balanced solution. Figure 7-13 on page 162 illustrates the migration of the back end to JCICS.
Figure 7-13 Migrating the back-end to JCICS
7.4.1 Mapping COBOL to Java
Instead of re-architecting the code in a pure object-oriented (OO) setup, the alternative is to re-code it with a procedural Java equivalent, which means to write Java in a procedural and not OO manner. In this way, if a problem occurs in the code, you can be debug it through comparison with the original working COBOL version.
 
Note: Converting COBOL to an object-oriented Java setup is possible, but it requires a complete understanding of the code, which might not be a trivial task because if there is a lot of code, there is also a lot to understand.
Fundamentally, COBOL was designed to be written in a human understandable form, so reading the code is just like reading an instruction manual. By migrating the back-end logic to Java, you get an understanding of how the languages can be mapped.
 
Working storage
The first section in TRADERBL is the working storage. You can categorize the various COBOL data structures in it as:
Error messages
Input/output buffers
Constants
Conversion fields
Miscellaneous
Error messages
Error messages feed debug information to the user. Example 7-14 on page 163 shows the COMPANY-NOT-FOUND message. Conveniently these map directly to Java static constants, as shown in Example 7-15 on page 163.
Example 7-14 The company not found message
03 COMPANY-NOT-FOUND-MSG.
05 FILLER PIC X(25) VALUE 'COMPANY #CCCCCCCCCCCCCCCC'.
05 FILLER PIC X(25) VALUE 'CCC NOT FOUND '.
Input/output buffers
The following data areas are used to read and write data using CICS API:
CUSTOMER-IO-BUFFER
COMPANY-IO-BUFFER
COMMAREA-BUFFER
Using the same approach as 7.3.1, “Wrapping the COMMAREA” on page 139, the classes CustomerIOWrapper and CompanyIOWrapper are written, and the class CommareaWrapper is reused from SimpleTraderPL. These become instance variables in TraderBL, as shown in Example 7-15.
Constants
Various constants are defined to represent return values for actions in the code. We used the same values in TraderPL, and we reuse them here to minimize code and to alleviate any potential errors when passing the values between TraderPL and TraderBL. We first mentioned the TraderConstants class in “SimpleTraderPL” on page 148.
Conversion fields
One of the powerful features of COBOL is its ability to convert between and format multiple data types. The conversion fields in TRADERBL, a sample of which is shown in Example 7-24 on page 169, show how you can redefine data areas to allow convenient conversions between character-based numeric types.
To replicate this behavior in Java, you can do one of these:
Wrap the conversion fields using the approach in “Wrapping the COMMAREA” on page 139, and implement specific conversion and formatting rules in the various getters and setters.
Utilize the powerful conversion and formatting facilities that are available to Java.
To reduce the number of wrapper classes, and fundamentally the amount of code, TraderBL uses the second option and inserts explicit Java code to perform the conversions. See Example 7-25 on page 169 for an example of this.
Miscellaneous
What remains are helper values that you can easily map to Java types and instantiate as local variables when needed. The tracing fields are not implemented because Java exception data provides enough information about problems.
Putting it together
Following a similar naming convention to TraderPL, the Java code for the back-end is in the class TraderBL. Example 7-15 shows the Java version of the working storage.
Example 7-15 TraderBL’s equivalent to WORKING-STORAGE
private static final String COMPANY_NOT_FOUND_MSG =
"COMPANY #CCCCCCCCCCCCCCCCCCC NOT FOUND"; // (1)
 
private static final String REQUEST_NOT_FOUND_MSG =
"REQUEST CODE OF #RRRRRRRRRRRRRR INVALID";
 
private static final String SUB_FUNCTION_NOT_FOUND_MSG =
"FUNCTION BUY/SELL CALLED WITH AN INVALID SUBCODE";
 
private static final String OVERFLOW_MSG =
"OVERFLOW WHEN CALCULATING SHARE VALUE";
 
private static final String TOO_MANY_SHARES_MSG =
"CUSTOMER TRIED TO SELL MORE SHARES THAN THEY OWN";
 
private static final String NO_SHARES_MSG =
"CUSTOMER HAS NO SHARES TO SELL IN SELECTED COMPANY";
 
private static final String VALIDATE_MSG =
"VALIDATING COMPANY #CCCCCCCCCCCCCCCCCCC";
 
private static final String TOO_MANY_MSG =
"TOO MANY SHARES REQUESTED, MAX OWNERSHIP IS 9999";
 
private final CommareaWrapper fCommareaWrapper = new CommareaWrapper();
private final CustomerIOWrapper fCustomerIOWrapper = new CustomerIOWrapper();
private final CompanyIOWrapper fCompanyIOWrapper = new CompanyIOWrapper();
A further expansion is to externalize these strings and provide internationalized versions that can be returned depending on the language preference in the HTTP header.
Abstracting the data access
With the wrappers written, we have everything that we need to design the data access interface. There are five sections in the COBOL program that use file access for retrieving data. They are replaced with a call to an object that implements the Java interface TraderDataAccess. This interface, shown in Example 7-16, provides an individual method for each type of data access required in the program.
Because Java does not have an equivalent to EIBRESP, the methods return the type int, which contains the response value. If a class that implements the interface does not directly support these values, it maps them back to ensure compatibility with TraderBL. The response values that we use in TraderBL are included as static constants, and we use DFHRESP_UNKNOWN for values that are not listed.
We discuss an implementation of this interface for VSAM data access in 7.4.2, “Using TraderBL with VSAM” on page 169.
Example 7-16 The TraderDataAccess interface
public interface TraderDataAccess {
public static final int DFHRESP_UNKNOWN = -1;
public static final int DFHRESP_NORMAL = 0;
public static final int DFHRESP_NOTFND = 13;
public int getCompanyNames(CommareaWrapper wrapper, String key);
public int readCustfile(CustomerIOWrapper wrapper, String key);
public int readCustfileForUpdate(CustomerIOWrapper wrapper, String key);
public int writeCustfile(CustomerIOWrapper wrapper, String key);
public int readCompfile(CompanyIOWrapper wrapper, String key);
public int rewriteCustfile(CustomerIOWrapper wrapper); // (1)
}
Does not need a key because the file is already opened from a previous readCustfileForUpdate() call.
Main section
The main section is the equivalent of public static void main() in a Java class, which gets executed when the program starts. Because TraderBL is written procedurally to reflect the COBOL code, this is the likely place to put the MAINLINE section. Observing the recommended approach2.3.2, “Continuous JVM” on page 21, the code instead is placed in TraderBL’s constructor and the object instantiated from this method, which minimizes the need for static variables.
To allow for pluggable data access, a class that implements the TraderDataAccess interface is passed to the object constructor and the supplied COMMAREA data.
Example 7-17 shows the syntax to instantiate the TraderBL object in the main method.
Example 7-17 Instantiating the TraderBL object in the main method
public static void main(CommAreaHolder commAreaHolder) {
new TraderBL(commAreaHolder, new TraderVSAMAccess()); // (1)
}
For more information about TraderVSAMAccess, see “Using TraderBL with VSAM” on page 169.
Mainline section
In TRADERBL, the MAINLINE section evaluates the request that is passed in the COMMAREA and executes the relevant code section before exiting the program.
Writeq-TS
At the start of MAINLINE, and in many places throughout the code, messages are sent to a TSQ by moving text to a variable and then calling WRITEQ-TS. In the Java implementation, this is collapsed to a single call to writeMessage() and the message passed as a parameter. Similar to WRITEQ-TS, the text is formatted and pre-pended with a time stamp. Instead of having separate sections for doing this they are included in-line instead.
 
Note: Not all EXEC CICS calls have a JCICS equivalent. In some cases, such as EXEC CICS ASKTIME, you must implement a Java equivalent.
For convenience, the message is outputted to Java’s stdout instead of a TSQ, which allows an unlimited length log to be used. If you still want the output in a TSQ, this is possible using the fully supported temporary storage JCICS API.
Example 7-18 The writeMessage() method
private void writeMessage(String comment) {
// Get a time stamp
final String timeStamp = new SimpleDateFormat("h:mm:ss a").format(new Date());
// If they exist, replace fields in the comment with COMMAREA values
comment =
comment.replaceFirst("#CCCCCCCCCCCCCCCCCCC", fCommareaWrapper.getCompanyName());
 
comment =
comment.replaceFirst("#RRRRRRRRRRRRRR", fCommareaWrapper.getRequestType());
 
comment =
comment.replaceFirst("#UUUUUUUUUUUUUU",
fCommareaWrapper.getUserId().substring(0, 15));
comment =
comment.replaceFirst("#R", fCommareaWrapper.getReturnValue());
// Remove spaces from the comment
comment.trim();
 
// Original wrote to TSQ, for Java we'll use stdout
System.out.println(timeStamp + " TraderBL: " + comment);
}
 
Tip: Use tail -f <filename> to continually display Java stdout or stderr in a ssh or telnet terminal.
Evaluate statement
Evaluate statements in COBOL allow for multiple outcomes to be coded dependant on a variable’s value, as shown in Example 7-19.
Example 7-19 A COBOL evaluate statement
EVALUATE REQUEST-TYPE
WHEN GET-COMPANY-REQ
PERFORM GET-COMPANY
WHEN SHARE-VALUE-REQ
PERFORM GET-SHARE-VALUE
END-EVALUATE.
The Java equivalent to evaluate statements are switch statements. The only difference is that switch statements are limited to integer-based or character-based comparisons and so cannot be used with string-based values. Instead, you can use a series of if-then-else statements, which we show in Example 7-20 on page 167.
 
Tip: Good practice for Java string comparisons is to call equals on the constant and not the variable (for example, CONSTANT.equals(astring)), which protects against null pointer exceptions if astring is set to null.
Perform <section>
Depending on the request type, different actions are performed in the evaluate statement, as shown in Example 7-19. The PERFORM GET-COMPANY call executes code that resides in the GET-COMPANY section, which in Java equates to calling a method with return type void. So any PERFORM <section> calls in the COBOL code are converted such that PERFORM GET-COMPANY becomes getCompany().
 
Note: COBOL allows the hyphen character (-) in identifiers, while Java does not. The Java convention is to use inner capitalization, so GET-COMPANY in COBOL becomes getCompany() in Java.
Putting it together
Following the directions that we discussed so far, the MAINLINE code in TraderBL becomes the code in Example 7-20.
Example 7-20 The TraderBL constructor
private final TraderDataAccess fTraderDataAccess; // (1)
 
public TraderBL(CommAreaHolder commAreaHolder, TraderDataAccess traderDataAccess) {
// Set the traderDataAccess reference
fTraderDataAccess = traderDataAccess;
 
writeComment("Entry");
// Get the commarea byte[] and put in wrapper
fCommareaWrapper.setByteArray(commAreaHolder.value);
 
// Evaluate the request type
final String requestType = fCommareaWrapper.getRequestType();
if (TraderConstants.GET_COMPANY_REQ.equals(requestType)) getCompany();
else if (TraderConstants.SHARE_VALUE_REQ.equals(requestType)) getShareValue();
else if (TraderConstants.BUY_SELL_REQ.equals(requestType)) buySell();
else {
fCommareaWrapper.setReturnValue(TraderConstants.UNKNOWN_REQUEST);
writeComment(REQUEST_NOT_FOUND_MSG);
}
writeComment("Exit");
 
// EXEC CICS RETURN END-EXEC // (2)
}
Notes on Example 7-20:
This holds a reference to the pluggable data access component passed in during object construction.
There is no JCICS equivalent to EXEC CICS RETURN. Java programs that run in CICS must always run to the end and terminate cleanly to allow for appropriate cleanup.
 
Get company
The GET-COMPANY section is the first section to include file access functionality. It also has a few other commands worth mentioning.
Move
To move a value into a variable is to set its value. In Java, this is the same as using the = operator. Pay close attention to the code because COBOL implicitly converts values according to the data type that they are inserted into. In Java, you must do this explicitly. Also, double check the value that you set because sometimes it is not obvious which wrapper the variable is in, and you might check the working-storage section for clarification.
Perform varying
Although not used in the Java version of getCompany() because file access is abstracted out to a TraderDataAccess implementation, we still want to mention the PERFORM VARYING command. This command is a flexible statement that is essentially the super set of the Java iteration statements, that is, both for and while loops can be represented with the PERFORM statement. The main identifier, as to whether a for loop or a do while loop are used, is the WITH TEST BEFORE and WITH TEST AFTER modifiers, respectively. The default action is to use WITH TEST BEFORE.
The Java version
With the use of the pluggable data access component, the Java getCompany() method becomes substantially smaller than the COBOL version.
Example 7-21 The getCompany() method
private void getCompany() {
fCommareaWrapper.setCompanyName("");
fTraderDataAccess.getCompanyNames(fCommareaWrapper);
}
Calculate shares bought
The CALCULATE-SHARES-BOUGHT section, shown in Example 7-22, takes the number of shares that are bought and adds it to the amount of shares owned. Both of these numbers are character-based numerics. By looking at a specific character in the total value, it is determined whether there is an overflow.
Example 7-22 The calculate-shares-bought section
CALCULATE-SHARES-BOUGHT SECTION.
ADD NO-OF-SHARES-DEC TO DEC-NO-SHARES GIVING SHARES-WORK1
EVALUATE SHARES-OVERFLOW
WHEN 0
MOVE SHARES-NORMAL TO NO-OF-SHARES-DEC
MOVE SHARES-NORMAL TO DEC-NO-SHARES
PERFORM UPDATE-BUY-SELL-FIELDS
WHEN OTHER
MOVE INVALID-BUY TO RETURN-VALUE
MOVE TOO-MANY-MSG TO COMMENT-FIELD
PERFORM WRITEQ-TS
END-EVALUATE
.
CALCULATE-SHARES-BOUGHT-EXIT.
EXIT.
As we mentioned in “Conversion fields” on page 163, COBOL can convert and format values implicitly based on their definitions. Building on this, it also allows basic arithmetic to be performed on values that differ in type, which means that character-based numbers can be added or subtracted to binary-based numbers without the user implementing additional conversion code.
In Java, the share values are stored internally as strings, so numeric addition is not directly possible. Instead, the numbers are converted to integers, using the Integer.parseInt() method and then added together. To determine if the value overflowed, the integer total is evaluated using an if statement, which gives the calculateSharesBought() method shown in Example 7-23.
Example 7-23 The calculateSharesBought() method
private void calculateSharesBought() {
int sharesWork =
Integer.parseInt(fCommareaWrapper.getNoOfSharesDec()) +
Integer.parseInt(fCustomerIOWrapper.getDecNoShares());
if (sharesWork <= 9999) {
fCommareaWrapper.setNoOfSharesDec("" + sharesWork); // (1)
fCustomerIOWrapper.setDecNoShares("" + sharesWork);
updateBuySellFields();
}
else {
fCommareaWrapper.setReturnValue(TraderConstants.INVALID_SALE_OR_BUY);
writeMessage(TOO_MANY_MSG);
}
}
To insert the values into the wrappers they are converted to strings first. The Java compiler implicitly converts ““ + sharesWork to new String(sharesWork).
Calculating share value
Calculating share value is when values are moved between different data types to perform arithmetic operations and number formatting. The code in Example 7-24 allows a number to be inserted into NUM-VALUE, which replaces any leading spaces with the 0 character. When CHAR-VALUE is referenced, it picks up this number formatting.
Example 7-24 COBOL values used for conversions
03 CONVERSION-FIELDS.
05 CHAR-VALUE.
07 CHAR-INT-PART PIC X(09).
07 FILLER PIC X VALUE '.'.
07 CHAR-DEC-PART PIC X(02).
05 NUM-VALUE REDEFINES CHAR-VALUE.
07 NUM-INT-PART PIC 9(09).
07 GUB92 PIC X.
07 NUM-DEC-PART PIC 9(02).
Because arithmetic in Java occurs using binary-based numbers, the DecimalFormat class is used to format it similar to the COBOL code, as shown in Example 7-25. The result is placed in the COMMAREA wrapper.
Example 7-25 Formatting a number as a string
final DecimalFormat decimalFormat = new DecimalFormat("000000000.00");
fCommareaWrapper.setTotalShareValue(decimalFormat.format(totalShareValue));
Housekeeping
It is most likely that TRADERBL is an application that evolved over many years. Through writing its JCICS equivalent, notice that various statements were commented out, and sections, such as BUY-SELL-UPDATE, are no longer used by the presentation layer. This allows for a little spring cleaning, and so redundant code is removed along the way.
7.4.2 Using TraderBL with VSAM
The result of 7.4.1, “Mapping COBOL to Java” on page 162 gives us a JCICS version of TRADERBL that has a pluggable interface to access the client and company data. To access the VSAM data, the class TraderVSAMAccess is used, which implements the TraderDataAccess interface, thus giving direct access to the existing VSAM data using the JCICS file access API.
The VSAM data access component
Example 7-16 on page 164 shows the methods to implement. Each method takes, as a parameter, a wrapper data area within which to return the results and a key for accessing a specific record. Similar to the REWRITE-CUSTFILE COBOL code, rewriteCustfile() does not take a key because the file was already opened using a readCustfileForUpdate() call. Example 7-26 shows CICS file access code from the READ-COMPFILE section in TRADERBL.
Example 7-26 File access code in the read-compfile section
EXEC CICS READ
FILE('COMPFILE')
INTO(COMPANY-IO-BUFFER)
LENGTH(LENGTH OF COMPANY-IO-BUFFER)
RIDFLD(COMPANY-NAME OF COMMAREA-BUFFER)
NOHANDLE
END-EXEC
Because TraderVSAMAccess is explicitly written to access the files CUSTFILE and COMPFILE, the file names are stored inside the class as static constants. The appropriate value is then set on the JCICS KSDS object to access the data, as shown in Example 7-27.
Example 7-27 The readCompfile() method
public int readCompfile(CompanyIOWrapper wrapper, String key) {
int result = DFHRESP_NORMAL;
 
final RecordHolder recordHolder = new RecordHolder();
final KSDS ksds = new KSDS();
 
ksds.setName(COMPFILE);
 
try {
ksds.read(key.getBytes(), recordHolder);
wrapper.setByteArray(recordHolder.value);
}
catch (RecordNotFoundException e) { result = DFHRESP_NOTFND; }
catch (Exception e) {
result = DFHRESP_UNKNOWN;
System.err.println("Exception: " + e);
e.printStackTrace();
}
 
return result;
}
All of the methods in TraderVSAMAccess are implemented similar to this using the JCICS file API. Observe that, as specified in the design, no business logic is performed in this class; instead, it is solely used to provide access to the VSAM data files.
7.4.3 Setting up TraderBL
In this section, we tell you how to install and run TraderPL with TraderBL as the back-end.
Installing TraderPL with TraderBL as the back end
The source code and compiled classes for this example are in Trader-2.jar. To install TraderPL with TraderBL as the back end:
1. Following the instructions in Chapter 3, “Setting up CICS to run Java applications” on page 37, and make sure that this jar file replaces Trader-1.jar in the CICS Java classpath.
2. In CICS, run the command CEMT SET JVMPOOL PHASE to phase out any JVMs that still might reference the old code.
3. Re-define the existing TRADERBL CICS resource definition such that it has the following properties:
 – PROGRAM(TRADERBL)
CONCURRENCY(Threadsafe)
JVM(Yes)
JVMCLASS(com.ibm.itso.sg245275.trader.TraderBL)
4. Run CEMT SET PROGRAM(TRADERBL) NEWCOPY to pick up the new program definition.
 
Note: Before you deploy your newly developed back end on a live system, thoroughly test it against sample or replica data.
Running TraderBL
Because there is no change to the front-end code, open a Web browser, and type in the URL:
http://wtsc66.itso.ibm.com:5275/cics/cwba/traderpj
Ensure that the port number matches that specified in your TCPIPSERVICE definition and that the program name at the end is traderpj.
You will see the same results, as shown in Figure 7-10 on page 159. This time, however, the back-end code being driven is that of the Java class TraderBL. To prove this, look at Java’s stdout to observe the various messages output during the run. Example 7-28 shows the message’s output for a getCompany() call.
Example 7-28 Messages for a getCompany() call
12:07:49 PM TraderBL: Entry
12:07:49 PM TraderBL: Entry to GET-SHARE-VALUE
12:07:49 PM TraderBL: Reading record from customer file
12:07:49 PM TraderBL: Reading record from company file
12:07:49 PM TraderBL: Building return commarea
12:07:49 PM TraderBL: Exit
Now try the same thing with the original 3270 front end. Nothing appears to have changed, but by watching the Java stdout messages you can see that the TraderBL implementation of the business logic is driven under the covers instead.
Congratulations. You migrated your COBOL heritage application to a JCICS environment with a scalable back end.
 
 
7.5 Moving to a DB2 back end
With the Web interface and JCICS back end in place, the next stage is to replace the existing VSAM data setup with a DB2 implementation. The pluggable design that we implemented in 7.4, “Migrating TRADERBL to JCICS” on page 161, combined with an understanding of DB2 JDBC, aids this in being a trivial task. Figure 7-14 illustrates the implementation of a DB2 back end.
Figure 7-14 Implementing a DB2 back-end
7.5.1 Data migration
The client and company data in the VSAM files are stored as flat data; however, databases generally store elements in structured columns.
Database tables
The data in the VSAM file COMPDATA is broken down into the columns shown in Table 7-1.
Table 7-1 The TRADER_COMPANY table
Column name
Column type
Length
Nulls
name
Character
20
No
unitprice
Double
8
No
price1dayago
Double
8
No
price2daysago
Double
8
No
price3daysago
Double
8
No
price4daysago
Double
8
No
price5daysago
Double
8
No
price6daysago
Double
8
No
price7daysago
Double
8
No
sellcommission
Double
8
No
buycommission
Double
8
No
Similarly, the data in the VSAM file CUSTDATA is stored using the columns in Table 7-2.
Table 7-2 The TRADER_CUSTOMER table
Column name
Column type
Length
Nulls
username
Character
8
No
companyname
Character
20
No
sharesheld
Decimal
19,0
No
The TRADER_CUSTOMER table contains the minimum fields that are required to perform share transactions.
Data migration SQL
To simplify the data migration in this example, the company information is manually inserted into the appropriate table using the SQL statements in Example 7-29. The client information is not migrated, and so all share holdings are reset to zero. For a real-life migration, one might consider the CICS VSAM Transparency tool for transferring the data. See the following Web site for more information about this tool.
Example 7-29 SQL statements for inserting the data into TRADER_COMPANY
INSERT INTO TRADER_COMPANY VALUES('Casey_Import_Export',
79, 77, 78, 72, 70, 65, 63, 59, 100, 0);
INSERT INTO TRADER_COMPANY VALUES('Glass_and_Luget_Plc',
19, 22, 25, 20, 16, 20, 22, 17, 200, 0);
INSERT INTO TRADER_COMPANY VALUES('Headworth_Electrical',
124, 131, 133, 133, 133, 137, 138, 141, 300, 0);
INSERT INTO TRADER_COMPANY VALUES('IBM',
163, 163, 162, 160, 161, 159, 156, 157, 400, 0);
7.5.2 Changing the JVM profile for DB2
To change the JVM profile for DB2:
1. Add the DB2 library into our LIBPATH_SUFFIX in JVM profile.
2. Add the DB2 JDBC driver classes into our CLASSPATH_SUFFIX in JVM profile.
Example 7-30 shows the JVM profile for DB2V9
Example 7-30 JVM profile for DB2V9
LIBPATH_SUFFIX=/usr/lpp/db2910/lib
CLASSPATH_SUFFIX= /usr/lpp/db2910/classes/db2jcc.jar:
/usr/lpp/db2910/classes/db2jcc_javax.jar:
/usr/lpp/db2910/classes/db2jcc_license_cisuz.jar:
For detailed information about what is required to support Java programs in the CICS DB2 environment, refer to CICS Transaction Server for z/OS DB2 Guide, SC34-6837.
 
Important: In this example, we use DB2V9, so we needed to migrate from using the JDBC/SQLJ Driver for OS/390 and z/OS to the IBM Data Server Driver for JDBC and SQLJ. If you are using the JDBC/SQLJ Driver for OS/390 and z/OS, read Chapter 9 in DB2 Application programming Guide and Reference for Java, SC18-9842.
7.5.3 Using TraderBL with DB2
With the pluggable architecture that we discussed in “Abstracting the data access” on page 164, to move TraderBL to a DB2 back end, we must create a Java class that implements the TraderDataAccess interface. The class, called TraderDB2Access, provides implementations of the methods shown in Example 7-16 on page 164, which performs DB2 calls for accessing the data.
Opening and closing the database connection
A common function that the methods in TraderDB2Access share is opening and closing a DB2 connection. The code for this is put into two utility methods, as shown in Example 7-31.
Example 7-31 The openConnection() and closeConnection() methods
private Connection fConnection = null; // (1)
 
private int openConnection() {
int result = DFHRESP_NORMAL;
 
try {
final String jdbcUrl = "jdbc:default:connection"; // (2)
fConnection = DriverManager.getConnection(jdbcUrl);
fConnection.setAutoCommit(false);
}
catch (Exception e) {
result = DFHRESP_UNKNOWN;
System.err.print("Exception: " + e);
e.printStackTrace();
}
 
return result;
}
 
private int closeConnection() {
int result = DFHRESP_NORMAL;
 
try {
fConnection.close();
fConnection = null;
}
catch (Exception e) {
result = DFHRESP_UNKNOWN;
System.err.print("Exception: " + e);
e.printStackTrace();
}
 
return result;
}
Holds a reference to the connection object, which is used for database access.
The default JDBC connection is used.
We have not used a DataSource for accessing DB2. If you want to know about DataSources and how they are used to connect to DB2, refer to the manual: DB2 V9 Application Programming Guide and Reference for Java.
 
Note: We used JDBC for the DB2 access here, but you must really consider using SQLJ programs as the preferred method for accessing DB2 from CICS. SQLJ programs must be bound into plans, and it is here that the SQL validation takes place compared to JDBC where the SQL is dynamically validated and bound at execution time. If you require the flexibility, then you must use JDBC; otherwise, SQLJ must be your first choice. More information can be found in the CICS DB2 Guide and DB2 Application Programming Guide and Reference for Java.
The SQL statements
SQL statements are needed to extract and update information in the database tables, as shown in Example 7-32. For convenience, the SQL is stored as static variables in TraderDB2Access. Use the prepared statement functionality to update the values dynamically.
Example 7-32 SQL to extract and update the data
private static final String SQL_GET_COMPANY_NAMES =
"SELECT name FROM trader_company";
 
private static final String SQL_GET_COMPANY_DATA =
"SELECT * FROM trader_company WHERE name=?"; // (1)
 
private static final String SQL_GET_CUSTOMER_DATA =
"SELECT sharesheld FROM trader_customer WHERE username=? AND companyname=?";
 
private static final String SQL_UPDATE_CUSTOMER_DATA =
"UPDATE trader_customer SET sharesheld=? WHERE username=? AND companyname=?";
 
private static final String SQL_INSERT_CUSTOMER_DATA =
"INSERT INTO trader_customer(username, companyname, sharesheld) VALUES(?,?,?)";
Note on Example 7-32:
Values get inserted into the place of the question mark symbol (?).
Running a query
All the methods in TraderDB2Access follow the similar process pattern:
1. Open a database connection.
2. Prepare the SQL statement.
3. Run the query.
4. Extract the results, and insert them into supplied data wrapper, if required.
5. Close the database connection.
6. Return the response code.
One of the methods, readCompfile(), which implements this structure, is shown in Example 7-33.
Note that when you insert values into the CompanyIOWrapper in the readCompfile() method, you must format them first because the original COMPANY-IO-BUFFER did not enforce formatting; instead, the format was picked up from the way that the data was stored in the VSAM files. This means that you must format the data up front using a DecimalFormat object to provide data consistency. If COMPANY-IO-BUFFER specified the format of these values, it does not need to be done here; instead, they are automatically converted upon insertion.
Example 7-33 The readCompfile() method
public int readCompfile(CompanyIOWrapper wrapper, String key) {
int result = openConnection();;
 
// If everything's ok then get the data
if (result == DFHRESP_NORMAL) {
PreparedStatement prepStmt = null;
ResultSet resultSet = null;
 
try {
prepStmt = fConnection.prepareStatement(SQL_GET_COMPANY_DATA);
prepStmt.setString(1, key);
resultSet = prepStmt.executeQuery(); // (1)
 
if (resultSet.next()) {
final DecimalFormat df1 = new DecimalFormat("00000.00");
final DecimalFormat df2 = new DecimalFormat("000");
 
wrapper.setShareValue(df1.format(resultSet.getDouble("unitprice")));
wrapper.setValue1(df1.format(resultSet.getDouble("price1dayago")));
wrapper.setValue2(df1.format(resultSet.getDouble("price2daysago")));
wrapper.setValue3(df1.format(resultSet.getDouble("price3daysago")));
wrapper.setValue4(df1.format(resultSet.getDouble("price4daysago")));
wrapper.setValue5(df1.format(resultSet.getDouble("price5daysago")));
wrapper.setValue6(df1.format(resultSet.getDouble("price6daysago")));
wrapper.setValue7(df1.format(resultSet.getDouble("price7daysago")));
wrapper.setCommissionSell(df2.format(resultSet.getDouble("sellcommission")));
wrapper.setCommissionBuy(df2.format(resultSet.getDouble("buycommission")));
}
else result = DFHRESP_NOTFND; // (2)
}
catch (SQLException e) {
result = DFHRESP_NOTFND;
System.err.print("Exception: " + e);
e.printStackTrace();
}
finally {
try {
resultSet.close();
prepStmt.close();
} catch (Exception e) {}
}
}
 
closeConnection();
 
return result;
}
To retrieve information, we use the executeQuery(). To update data, you must use the executeUpdate() method.
If no results were found, then set the response value accordingly.
Plugging in TraderDB2Access
The final step in the migration is to replace the code in TraderBL that instantiates a TraderVSAMAccess object with that of a TraderDB2Access object. The swap works seamlessly because both classes implement the TraderDataAccess interface. Example 7-34 shows the new main() method in TraderBL.
Example 7-34 The new main() method in TraderBL
public static void main(CommAreaHolder commAreaHolder) {
new TraderBL(commAreaHolder, new TraderDB2Access());
}
You are now ready to try out the DB2 back end.
7.5.4 Setting up TraderBL with DB2
In this section, we tell you how to install and run TraderPL with TraderBL using DB2 for data access.
Installing TraderBL with DB2
The source code and compiled classes for this example are in Trader-3.jar. To install TraderBL with DB2:
1. Following the instructions in Chapter 3, “Setting up CICS to run Java applications” on page 37, make sure that this jar file replaces Trader-2.jar in the CICS Java classpath.
2. In CICS, run the command CEMT SET JVMPOOL PHASE to phase out any JVMs that still might reference the old code.
3. Do not forget to set up DB2, as discussed in 7.5.1, “Data migration” on page 172.
The new CICS resources that must be defined alongside the existing definitions in the TRADER group are:
 – DB2CONNECTOIN(TRADER) DB2ID(D9E1)
 – DB2ENTRY(TRADER)
PLAN(DSNJDBC)
 – DB2TRAN(TRADCOBL)
ENTRY(TRADER)
TRANSID(TRAD)
 – DB2TRAN(TRADJAVA)
ENTRY(TRADER)
TRANSID(CWBA)
4. Run CEMT SET PROGRAM(TRADERBL) NEWCOPY to pick up the new program definition that uses the TraderDB2Access object.
 
Attention: Make sure that the status of DB2CONNECTION is connected after you install DB2CONNECTION. Use the CEMT INQUIRE DB2CONNECTION command to do this.
Running TraderBL
To run TraderBL:
1. Because there is no change to the front-end code, open a Web browser, and type in the URL:
2. Ensure that the port number matches the port number that is specified in your TCPIPSERVICE definition and that the program name at the end is traderpj. You will see the same results as shown in Figure 7-10 on page 159.
3. Use the Web interface to buy some shares, and open up your favorite DB2 query tool.
4. Input and run the following SQL:
SELECT * FROM trader_customer;
The resulting data shows the shares that you just bought. Example 7-35 shows sample output.
Example 7-35 Query results
---------+---------+---------+---------+---------+---------
USERNAME COMPANYNAME SHARESHELD
---------+---------+---------+---------+---------+---------
CICSUSER Glass_and_Luget_Plc 42.
CICSUSER Casey_Import_Export 100.
5. Do the same thing with the original 3270 display to see that the back-end is fully implemented using DB2.
Congratulations. You migrated your JCICS Trader application to a DB2 back-end.
7.6 Adding a Web services interface
CICS Web services was introduced in CICS TS Version 3.1 and further enhanced in CICS TS Version 3.2. CICS SupportPac CA1P contains samples, tutorials, and step-by-step instructions about the function. Now, with the Web interface and JCICS back-end in place, the next stage is to provide a CICS Web services function to the Trader application.
7.6.1 Building the TraderBL Web service provider
The CICS Web services assistant is a CICS TS tool that generates the necessary Web service artifacts. The following exercise gives step-by-step instructions on how to configure CICS as a Web service requester using the CICS supplied tooling. IBM Rational Developer for System z product (RDz) provides an alternative tool. We do not describe that tool in this chapter.
To expose an existing CICS program as a Web service:
1. Run the CICS Web services assistant to generate the required files:
a. Create the zFS directories.
b. Extract the existing program interface.
c. Run the program DFHLS2WS.
d. Customize the service location in the WSDL.
2. Set up the CICS infrastructure:
a. Create a PIPELINE resource definition.
b. Create a TCPIPSERVICE resource definition.
3. Deploy the Web service provider:
a. Deploy the Web service binding file.
b. Publish the WSDL to clients.
c. Test the Web service in Rational Developer for System z (RDz).
Running the CICS Web services assistant to generate the required files
CICS Transaction Server Version 3 provides the Web services assistant to generate Web services definitions (WSDL) from supplied program language structures and generate language structures from supplied WSDL documents. This assistant is actually two utility programs that run in z/OS batch. Both programs use the IBM Java SDK.
Creating the zFS directories
To expose a CICS program as a Web service over HTTP, a TCPIPSERVICE and a PIPELINE resource that is configured with a SOAP message handler are required. The PIPELINE resource definition points to zFS directories and also points to a pipeline configuration file.
More than one WEBSERVICE can share a single PIPELINE. We must, however, define a second PIPELINE for outbound requests because the pipeline configuration file referenced by a PIPELINE resource cannot be configured for both provider (inbound) and requester (outbound) services.
The set up requires configuration of files and directories on the zFS. The following directories are created:
/u/cicsrs6/pipelines
/u/cicsrs6/trader/shelf
/u/cicsrs6/trader/wsbind/provider
/u/cicsrs6/trader/wsbind/requester
/u/cicsrs6/trader/wsdl
Extracting the existing program interface
We are generating a new WSDL file from an existing program and using the DFHLS2WS utility (Language Structure to Web Service). As input, the DFHLS2WS utility must import the language copybooks that match the program’s COMMAREA for request and response. The DFHLSWS utility can call a program using the new CICS Transaction Server Version 3 CHANNEL interface to support greater than 32 KB messages, but we are not using that facility.
The interface to program TraderBL is a COMMAREA. However, the DFHLS2WS utility does not support the REDEFINES and INDEX statements; therefore, to use DFHLS2WS, we must create a new copybook that has only those elements that relate to the program function. Example 7-36 shows the structure.
Example 7-36 The COMMAREA to TraderBL
01 COMMAREA-BUFFER.
03 REQUEST-TYPE PIC X(15).
03 RETURN-VALUE PIC X(02).
03 USERID PIC X(60).
03 USER-PASSWORD PIC X(10).
03 COMPANY-NAME PIC X(20).
03 CORRELID PIC X(32).
03 UNIT-SHARE-VALUES.
05 UNIT-SHARE-PRICE PIC X(08).
05 UNIT-VALUE-7-DAYS PIC X(08).
05 UNIT-VALUE-6-DAYS PIC X(08).
05 UNIT-VALUE-5-DAYS PIC X(08).
05 UNIT-VALUE-4-DAYS PIC X(08).
05 UNIT-VALUE-3-DAYS PIC X(08).
05 UNIT-VALUE-2-DAYS PIC X(08).
05 UNIT-VALUE-1-DAYS PIC X(08).
03 COMMISSION-COST-SELL PIC X(03).
03 COMMISSION-COST-BUY PIC X(03).
03 SHARES.
05 NO-OF-SHARES PIC X(04).
03 TOTAL-SHARE-VALUE PIC X(12).
03 BUY-SELL1 PIC X(04).
03 BUY-SELL-PRICE1 PIC X(08).
03 BUY-SELL2 PIC X(04).
03 BUY-SELL-PRICE2 PIC X(08).
03 BUY-SELL3 PIC X(04).
03 BUY-SELL-PRICE3 PIC X(08).
03 BUY-SELL4 PIC X(04).
03 BUY-SELL-PRICE4 PIC X(08).
03 ALARM-CHANGE PIC X(03).
03 UPDATE-BUY-SELL PIC X(01).
03 FILLER PIC X(15).
03 COMPANY-NAME-BUFFER.
05 COMPANY-NAME-TAB OCCURS 4 TIMES PIC X(20).
Running the program DFHLS2WS
The DFHLS2WS utility takes program language structures and generates WSbind and WSDL files, as shown in Figure 7-15 on page 181.
Figure 7-15 DFHLS2WS processing
The CICS supplied sample, JCL, to run DFHLS2WS is available on the zFS in the file /usr/lpp/cicsts/cicsts32/samples/webservices/JCL/LS2WS. We customized the JCL, as shown in Example 7-37.
Example 7-37 The DFHLS2WS JCL
//LS2WS EXEC DFHLS2WS,
// TMPFILE='ls2ws',
// JAVADIR='/usr/lpp/java/J5.0',
// PATHPREF=' ',
// USSDIR='cicsts32'
//INPUT.SYSUT1 DD *
LOGFILE=/u/cicsrs6/trader/wsbind/dfhls2ws.log
PDSLIB=//CICSRS1.TRADER.COPYLIB
REQMEM=TRADERCM
RESPMEM=TRADERCM
LANG=COBOL
PGMNAME=TRADERBL
MAPPING-LEVEL=2.1
#TRANSACTION=CPIH
#CCSID=037
URI=trader/traderbl
PGMINT=COMMAREA
WSBIND=/u/cicsrs6/trader/wsbind/provider/traderbl.wsbind
WSDL=/u/cicsrs6/trader/wsdl/traderbl.wsdl
*/
We create the WSbind file and WSDL file for Trader Web service. Based on the input parameters:
The WSBind file is placed in/u/cicsrs6/trader/wsbind/provider and is called traderbl.wsbind.
The WSDL file is placed in /u/cicsrs6/trader/wsdl and is called traderbl.wsdl.
Customizing the service location in the WSDL
As generated, the <service> element in the WSDL file is as shown in Example 7-38.
Example 7-38 Traderbl WSDL
<service name="TRADERBLService">
<port binding="tns:TRADERBLHTTPSoapBinding" name="TRADERBLPort">
<soap:address location="http://my-server:my-port/trader/traderbl"/>
</port>
</service>
Make the following changes, which are needed before the Web service description can be published to clients:
Replace my-service with wtsc66.itso.ibm.com
Replace my-port with 5276
Setting up the CICS infrastructure
A PIPELINE resource definition contains a reference to a shelf directory, a WSDir directory containing WSBind files, and a pipeline configuration file. If a value is provided for the WSDir directory, then CICS searches that directory, looking for WSBind files, and dynamically installs WEBSERVICE and URIMAP resources. If no value is specified, you must manually define these resources. The pipeline configuration file is in XML format and describes the message handlers that act on Web service requests and responses as they pass through the pipeline.
Creating a PIPELINE resource definition
We use the CICS supplied PIPELINE configuration file for a SOAP 1.1 handler. The directory contains the CICS supplied pipeline configuration file: /usr/lpp/cicsts/cicsts32/samples/pipelines/
1. Copy file basicsoap11provider.xml to directory u/cicsrs6/pipelines.
2. Create the pipeline resource definition, as shown in Figure 7-16 on page 183.
OVERTYPE TO MODIFY CICS RELEASE = 0650
CEDA ALter PIpeline( TRPIPE01 )
PIpeline : TRPIPE01
Group : TRADER
Description ==> Basic SOAP 1.1 provider pipeline
STatus ==> Enabled Enabled | Disabled
Respwait ==> Deft Default | 0-9999
Configfile ==> /u/cicsrs6/pipelines/basicsoap11provider.xml
(Mixed Case) ==>
==>
==>
==>
SHelf ==> /u/cicsrs6/trader/shelf
(Mixed Case) ==>
==>
==>
==>
Wsdir : /u/cicsrs6/trader/wsbind/provider
+ (Mixed Case) :
SYSID=PAA1 APPLID=SCSCPAA1
PF 1 HELP 2 COM 3 END 6 CRSR 7 SBH 8 SFH 9 MSG 10 SB 11 SF 12 CNCL
Figure 7-16 Pipeline definition attributes
Creating a TCPIPSERVICE resource definition
To access a Web Service that is exposed in CICS, we must tell CICS to listen on a port. The port number that CICS is listening on is defined in a TCPIPService definition. We define a TCPIPService Resource definition in CICS, as shown in Figure 7-17, to tell CICS to receive the inbound HTTP requests on a specific port.
OVERTYPE TO MODIFY CICS RELEASE = 0650
CEDA ALter TCpipservice( SOAPPORT )
TCpipservice : SOAPPORT
GROup : TRADER
DEscription ==> CICS WEB SERVICE PORT
Urm ==> NONE
POrtnumber ==> 05276 1-65535
STatus ==> Open Open | Closed
PROtocol ==> Http IIop | Http | Eci | User | IPic
TRansaction ==> CWXN
Backlog ==> 00001 0-32767
TSqprefix ==>
Ipaddress ==>
SOcketclose ==> No No | 0-240000 (HHMMSS)
Maxdatalen ==> 000032 3-524288
SECURITY
SSl ==> No Yes | No | Clientauth
CErtificate ==>
+ (Mixed Case)
SYSID=PAA1 APPLID=SCSCPAA1
PF 1 HELP 2 COM 3 END 6 CRSR 7 SBH 8 SFH 9 MSG 10 SB 11 SF 12 CNCL
Figure 7-17 TCP/IP Service definition attributes
Deploying the Web service provider
To expose a CICS program as a Web service, simply installing the PIPELINE definition causes CICS to look for WSBind files in WSDir directory and to dynamically install WEBSERVICE and URIMAP resources for each Web service.
Deploying the Web service binding file
The Web service binding file that DFHLS2WS creates is deployed into the CICS region dynamically when we install a PIPELINE resource. A PIPELINE scan command is issued, either explicitly through a CEMT PERFORM PIPELINE() SCAN or automatically during a PIPELINE installation. CICS scans the pickup directory to search for Web service binding files, that is for file names with the .wsbind extension. For each binding file that is found, CICS determines whether to install WEBSERVICE and URIMAP resources. When auto-installed in this way, a WEBSERVICE and URIMAP are automatically discarded when the associated PIPELINE is discarded, which makes for easy management of Web services resources in CICS.
To install the TRPIPE01 resource that we just created, type CEDA INSTALL G(TRADER) PIPELINE(TRPIPE01), and press Enter. An “INSTALL SUCCESSFUL” message is displayed.
 
To verify that the PIPELINE properly installed:
1. Use a CEMT INQUIRE PIPELINE command to verify that the PIPELINE was properly installed. A status of ‘Ena’ (Enabled) is displayed.
2. Tab down and place an S to the left of the pipeline entry, and press Enter. the pipeline’s details is displayed.
3. If the pipeline does not have a status of ‘Ena’, look in the MSGUSR section of the CICS joblog. The likelihood is that CICS did not find the pipeline configuration file or write to the shelf directory.
Use a CEMT INQUIRE WEBSERVICE command to verify the WEBSERVICE is installed properly.
Use CEMT INQUIRE URIMAP command to look at the URIMAP that was installed when the PIPELINE SCAN was performed.
Publishing the WSDL to clients
There are many ways to make WSDL files available for clients or development teams to discover. For simplicity, we use FTP to copy the file from the host to our workstation.
Testing the Web service in Rational Developer for System z (RDz)
To test the TraderBL Web service with Web services Explorer in RDz, RDz needs the WSDL that was downloaded earlier. We then use the Web services Explorer to invoke the TraderBL Web service in the CICS.
To test the TraderBL Web service in Rational Developer for System z:
1. Start RDz.
2. Open or change to a Resource perspective.
3. Create a General project called Trader.
4. Import traderbl.wsdl into the Trader project.
5. From the Navigator view, expand the Trader project, right-click traderbl.wsdl, and from the context menu, select Web services  Test with Web services Explorer. A Web Browser view opens.
6. Double-click the Web services Explorer tab to maximize its size.
7. In the Navigator pane of the Web services Explorer, click TRADERBLHTTPSoapBinding.
8. In the Actions pane, locate the Endpoints section, and verify that the endpoint is http://wtsc66.itso.ibm.com:5276/trader/traderbl. Click Go.
9. In the Navigator pane of the Web services Explorer, click + to expand TRADERBLHTTPSoapBinding.
10. Select TRADERBLOperation (the operation name). An Invoke a WSDL Operation view opens.
11. In the Actions pane, type Get_Company in the text box at the field request_type, and click Go. This causes RDz to invoke the CICS Web service. In the status pane, the Web service response is as shown in Figure 7-18.
Figure 7-18 RDz Web services Explorer
7.6.2 Migrating TraderPJ to a Web service requester
With TraderBL exposed as a Web service, we now migrate TraderPJ to be a Web service requester. To allow coexistence with the LINK version of program TraderPJ, we develop a new Web service requester program called TraderPW.
Invoking an existing Web service from a new or modified CICS program involves the following steps:
1. Run the CICS Web services assistant to generate the required files:
a. Run the program DFHWS2LS.
b. Develop the CICS program to invoke the Web service.
2. Set up the CICS infrastructure:
a. Create a PIPELINE resource definition.
3. Deploy the Web service requester:
a. Deploy the Web service binding file.
b. Test the CICS program to invoke the Web service.
Running the CICS Web Service assistant to generate the required files
The DFHWS2LS (Web Service to Language Structure) program is the CICS supplied batch procedure to import a WSDL file and to generate language structures for client applications to use and a WSBIND file to map data between XML and language structures at runtime. DFHWS2LS can handle DOC literal, RPC literal, or wrapped DOC literal forms of WSDL as input. The language structure can be COBOL, PL/I, C, or C++.
We have the CommareaWrapper that was migrated from the COBOL language structure previously, and because the content is the same as the generated COBOL language structure that we will generate, we generate a COBOL language structure and use CommareaWrapper instead.
Running the program DFHWS2LS
The DFHWS2LS utility takes WSDL and generates language structure and WSBind files, as shown in Figure 7-19 on page 187.
Figure 7-19 DFHWS2LS processing
We must start with a copy of the WSDL file that describes the Web service to be called. In our case, the WSDL file is located on the zFS at:
/u/cicsrs6/trader/wsdl/traderbl.wsdl
The CICS supplied sample JCL to run DFHWS2LS is available on the zFS in the file /usr/lpp/cicsts/cicsts32/samples/webservices/JCL/WS2LS.
Note there is no program name, language, or interface supplied because the WEBSERVICE resource does not call a program. Because an URI is not specified, the default is to use the value in the WSDL document, in our case it is http://wtsc66.itso.ibm.com:5276/trader/traderbl, which is invoked on the INVOKE WEBSERVICE command. The JCL was customized, as shown in Example 7-39.
Example 7-39 The DFHWS2LS JCL
//WS2LS EXEC DFHWS2LS,
// TMPFILE='ws2ls',
// JAVADIR='/usr/lpp/java/J5.0',
// PATHPREF=' ',
// USSDIR='cicsts32'
//INPUT.SYSUT1 DD *
PDSLIB=//CICSRS1.TRADER.COPYLIB
LANG=COBOL
REQMEM=TRADEI
RESPMEM=TRADEO
MAPPING-LEVEL=2.1
LOGFILE=/u/cicsrs6/trader/wsbind/dfhws2ls.log
WSBIND=/u/cicsrs6/trader/wsbind/requester/traderplRequester.wsbind
WSDL=/u/cicsrs6/trader/wsdl/traderbl.wsdl
BINDING=TRADERBLHTTPSoapBinding
/*
 
Note: Because the WS2LS job is creating two new members in the CICSRS1.TRADER.COPYLIB dataset, if the dataset is in use, the job fails.
View the generated language copybook files. Note that the names of the generated copybooks are the values of TRADEI and TRADEO with a “01” appended to them. This is allows DFHWS2LS to generate more than one copybook if the WSDL contains multiple interface definitions. In our case, we do not use the two copybooks; instead, we use the CommareaWrapper.
Developing the CICS program to invoke the Web service
This is the point where a developer writes the CICS program to invoke the Web service. In our case, we use the TraderPL program as base and write a new program TraderPW.
Example 7-40 shows interesting excerpts from TraderPL.
Example 7-40 TraderPW: interesting excepts from TraderPL
private Channel channel;
private Container container;
 
 
// Send the data request to TRADERBL
invokeWebService();
 
 
/*
* Invoke webservice to the TRADERBL program with our COMMAREA
*/
private void invokeWebService() {
final WebService webservice = new WebService();
webservice.setName("traderplRequester");
try {
// Set the UserId value in the COMMAREA
fCommareaWrapper.setUserId(Task.getTask().getUSERID());
if (channel == null ){
channel = Task.getTask().createChannel("SERVICE-CHANNEL");
container = channel.createContainer("DFHWS-DATA");
}
container.put(fCommareaWrapper.getByteArray());
webservice.invoke(channel, "TRADERBLOperation");
fCommareaWrapper.setByteArray(container.get());
}
catch (Exception e) { System.err.println(e); e.printStackTrace(); fMessage = "Error - " + e; }
}
Note that the container.put() method copies the request data into a CONTAINER in the CHANNEL (CHANNEL and CONTAINERs are a new feature in CICS TS Version 3). The CHANNEL and OPERATION are then passed as a parameter on the webservice.invoke() method.
To share HTML templates with TraderPJ and TraderPW, we add a symbol called pgmname, and the default value is set as traderpj. The default value make program TraderPJ does not need to be changed. The HTML equivalent for this is:
<!--#set var=pgmname value=traderpj -->
<FORM action='&pgmname;' method='POST'>
Example 7-41 shows the code equivalent for this.
Example 7-41 TraderPW: add program name symbol
private String pgmname;
 
pgmname = Task.getTask().getProgramName();
 
final String symbolList = "pgmname=" + pgmname + "&" +
"company1=" + fCommareaWrapper.getCompanyNameTab(0) + "&" +
"company2=" + fCommareaWrapper.getCompanyNameTab(1) + "&" +
"company3=" + fCommareaWrapper.getCompanyNameTab(2) + "&" +
"company4=" + fCommareaWrapper.getCompanyNameTab(3) + "&" +
"message=" + (fMessage != null ? fMessage : "");
Setting up the CICS infrastructure
For a CICS program to make a Web service request over HTTP, a WEBSERVICE and a PIPELINE resource are needed. A TCPIPSERVICE is not needed for a requester Web service call. The client program uses the WebService invoke method() method to make the call.
Creating a PIPELINE resource definition
The PIPELINE resource that we create refers to a configuration file (CONFIGFILE), two zFS directories, a SHELF directory, and a WSDIR directory. We created the directories in “Creating the zFS directories” on page 179.
We use the CICS supplied CONFIGFILE for a basic SOAP 1.1 requester. The directory contains the CICS supplied pipeline configuration file /usr/lpp/cicsts/cicsts32/samples/pipelines/:
1. Copy the basicsoap11requester.xml file to the directory u/cicsrs6/pipelines.
2. Create the pipeline resource definition, as shown in Figure 7-20 on page 190.
OVERTYPE TO MODIFY CICS RELEASE = 0650
CEDA ALter PIpeline( TRPIPE02 )
PIpeline : TRPIPE02
Group : TRADER
Description ==> BASIC SOAP 1.1 REQUESTER PIPELINE
STatus ==> Enabled Enabled | Disabled
Respwait ==> Deft Default | 0-9999
Configfile ==> /u/cicsrs6/pipelines/basicsoap11requester.xml
(Mixed Case) ==>
==>
==>
==>
SHelf ==> /u/cicsrs6/trader/shelf
(Mixed Case) ==>
==>
==>
==>
Wsdir : /u/cicsrs6/trader/wsbind/requester
+ (Mixed Case) :
SYSID=PAA1 APPLID=SCSCPAA1
PF 1 HELP 2 COM 3 END 6 CRSR 7 SBH 8 SFH 9 MSG 10 SB 11 SF 12 CNCL
Figure 7-20 Pipeline definition attributes
Deploying the Web service requester
Each CICS Web service request requires at least the following to be installed:
A PIPELINE resource that is configured as a requester pipeline
A WEBSERVICE resource
Installing a PIPELINE definition causes CICS to look for WSBind files in the WSDir directory, and then CICS dynamically installs a WEBSERVICE for each Web service.
Deploying the Web service binding file
To install the TRPIPE02 resource that we just created:
1. Type CEDA INSTALL G(TRADER) PIPELINE(TRPIPE02), and press Enter. An “INSTALL SUCCESSFUL” message is displayed.
2. Use the PERFORM PIPELINE() SCAN command as an alternative way to pick up the WSBind file automatically, and dynamically create the WEBSERVICE resource. A URIMAP is not created for a requester Web service call.
 
Verifying PIPELINE and WEBSERVICE installation:
Use the CEMT INQUIRE PIPELINE command to verify that the PIPELINE is installed properly.
Use the CEMT INQUIRE WEBSERVICE command to verify that the WEBSERVICE is installed properly.
Testing the CICS program to invoke the Web service
In this section, we tell you how to install and run TraderPW.
Installing TraderPW
The source code and compiled classes for this example are in Trader-2.jar. Following the instructions in Chapter 3, “Setting up CICS to run Java applications” on page 37, make sure this jar file is added to the CICS Java classpath.
The new CICS resources that must be defined alongside the existing definitions in the TRADER group are:
PROGRAM(TRADERPW)
CONCURRENCY(Threadsafe)
JVM(Yes)
JVMCLASS(com.ibm.itso.sg245275.trader.TraderPW)
Running TraderPW
Similar to “Running TraderPL” on page 158, open a Web browser and type in the URL:
Figure 7-21 is displayed.
Figure 7-21 The company selection window
 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset