Invoking a Web Service. 

Prerequisite reading: Service Oriented Architectures and Web Services: Introduction.

 

In a previous chapter, Providing Web Services, we learned about writing a program that can be invoked externally as a web service, and we tested it with a Windows program, SOAPUI.  In this chapter we learn how to write requester programs, programs that invoke an existing web service much as SOAPUI did for our GetTimA and GetTimB progarms in Providing Web Services.  Our first requester program will invoke the test service SOATest1 provided at http://www.jazzsoftware.co.nz/SOATesting/Service1.svc that we reproduced with our zOS services GetTimA and GetTimB.

 

SOATest1 is a very basic web service, essentially a “Hello World” kind of program.  It is open to anybody, from anywhere in the world, it requires no authentication, and looks up no database: the only data that it uses is the name it is given in its input message, and the time from the clock on its server.  When we click the link Test Data and Web Services on the JazzSoftware web site we see

Enter your name and click [Respond:]. The web page responds like this: -

 

To do this it has invoked a web service that simply returns the current time. Like a traditional “Hello World” program this has almost no real function, it doesn’t look up a database, nor require any authentication, it simply responds. I like to have slightly more logic than returning “Hello World” so it has been programmed to return the time so that each response will be slightly different.

 

Requester programs may themselves be web service programs, or they may be classical CICS programs displaying and responding to a 3270 screen.  We’ll start with a classical CICS program.

A Classical CICS Program to Invoke SOATest1

Now, let’s see how we can write a CICS/3270 program with Jazz that does the same as our web page. The point of this is to prove that we’ve set up everything correctly and so we can invoke a web service and get its response. First we start by writing a simple CICS program, pretty much like the enquiry programs that we wrote earlier but even simpler because we’re not going to look up a file.  We start by creating a new basic CICS program like this: -

 

Click [Finish].  Jazz has created this program: -

*# Last Updated by robertb at 26/11/2013 2:56:56 p.m.

PROGRAM WST1 CICS INSCREEN(WST1S) TRANSID(TRW1) COMMAREA(WST1C) ;

* This very basic form has no logic, it is up to you to create the screen and program that you want.

* An empty screen will have been generated

* A commarea with field "Function" will have been generated

We add a couple of fields into our program: -

DEFINE Wrk  DATA(

        Name CHAR (20),

        Result CHAR (70));

 

Then we click the [Screen] button and drag Wrk on to it so that the two fields that we want are on the screen.  We click [Process] to save the screen and create it on our zOS test system: -

 

Now we temporarily “complete” our CICS program by adding code to process the screen input and post a result. We’ll simply assign “Result goes here” to the result field: -

# Last Updated by robertb at 26/11/2013 2:56:56 p.m.

PROGRAM WST1 CICS INSCREEN(WST1S) TRANSID(TRW1) COMMAREA(WST1C) ;

DEFINE Wrk  DATA(

    Name VARCHAR (20),

    Result VARCHAR (70));

ACCEPT (WST1S.Name);

wrk.result = 'Result for ' && WST1S.Name && ' goes here';

SEND inscreen;

 

If you’re like me you’ll want to test your temporary program before developing it further to make it invoke the web service.  To do this,

1.                   Click [Process] to create the program in the zOS system.

2.                   Enter CEDA commands like these into your CICS test system: -

DEF PROGRAM(WST1) GROUP(MANAJAZZ)

DEF MAPSET(WST1S) GROUP(MANAJAZZ)  

DEF TRANS(TRW1) PROGRAM(WST1) GROUP(MANAJAZZ)

3.                   Enter transaction TRW1 to run the program

 

Web Service Discovery

Now it starts getting interesting as we replace the temporary assignment statement

wrk.result = 'Result for ' && WST1S.Name && ' goes here';

with something to actually get the result from a web service.  At the heart of our Jazz program will be an INVOKE statement where we link to the web service, pass it the value of Name and get back the value of Result. We’ll replace the assignment statement with something like

            INVOKE SOATest1(Name) REPLY(Result).

 

In order to be able to use a statement like this Jazz needs to know where to find the service, what parameters to pass to it, what parameters it will get back. With each parameter Jazz will need to know what format to use.   We need a DEFINE statement that describes the format of the parameters.  How do we do this for a web service, which could have been created by somebody unknown on the other side of the world?  Actually it’s easy:  give Jazz the URI of the web service, and then everything will be done for you!  The test services that Jazz has provided for you are at http://www.jazzsoftware.co.nz/SOATesting/Service1.svc    

 

We start by replacing the temporary assignment .result = 'Result for ' && WST1S.Name && ' goes here'; with: -

INVOKE ?;

 

Then we click [Check], causing Jazz to re-check our program. When Jazz reaches the INVOKE ?; statement the “Add Service Reference” dialog results.  We enter the URI, which in this case is http://www.jazzsoftware.co.nz/SOATesting/Service1.svc, and enter the Service Reference Name.  Web service reference names cannot be longer than 5 characters[1].

 

Assuming that Jazz finds some WSDL at the URI (there’ll be error messages if not),  when you click [Get Service] Jazz responds with: -

and you can click [+] to expand the tree view to find the operations available at this web service.  If you right-click either [Get Service] or [Refresh] you will see this form: -

 

Here Jazz has located WSDL at the URI that you gave, and it is about to create Jazz-format DEFINE statements describing the data, and submit a job to create the objects (.wsdl and .wsbind files in the HFS) that you need for your CICS system to invoke this service.  This form functions like the usual Jazz Process form allowing you to interrupt the process at each of its stages.  Normally you’ll simply click [Submit], and all three stages will run and a zOS job will be submitted.

 

Even if you click [Close] Jazz will continue with the first step, reading the WSDL and creating Jazz data definitions from it.  As it creates these it may encounter elements that lack some of the information required by COBOL (and hence by Jazz) such as the lengths of character strings. Let me explain.

Exchanging Data with SOA Objects

The point of standards-based SOA is to facilitate the use of services across a network by heterogeneous programs. From a Jazz (=COBOL) program we can use a service provided by VB, ASP.NET, C#, Java, ….  as well as services provided by other COBOL programs. In theory we shouldn’t need to know what technology the service uses, or supply any further information, all we need to know where to find the interface.

 

Yet even with our first Requester test this theory starts to be compromised. In the world of Java and ASP.NET a string variable can hold a unicode string of any length and WSDL often describes a field simply as “String”, with no maximum length.  That’s the case here, where SOATest1 receives one string, and replies with another, but the strings can be any length.  You could enter “Very long name, going on and on until I got tired of typing” into the textbox on the test site, and the ASP.NET test program would respond

            “SOATest1. Hello Very long name, going on and on until I got tired of typing. The NZ date/time is 2/02/2015 12:10:48 p.m.”

 

However the mainframe world of COBOL and PL/I has no direct equivalent of an ASP.NET string.  CHAR and VARCHAR variables have a maximum length, and we therefore need to know what the maximum lengths of the string fields are so that the Jazz program can define fields as (for example) VARCHAR(20), generating a COBOL definition

000585      03 Name.

000590         49 JZL-Name              PIC S9999 COMP-5 SYNC.

000595         49 JZD-Name              PIC X(20).

 

If the web service originates in the mainframe world of fixed-length strings then the WSDL describing the service may include a maximum length, but in other cases Jazz needs to supply a maximum length value.  For Build 13.2 Jazz simply follows the IBM practice and uses a default of 255, but a future release will prompt you for these values.

Planned Feature: selecting Maximum String Lengths.

The first time that you select the service you will be prompted to enter them. For example: -

 

After getting the maximum length of Name for the input message, the dialog will ask you for the length of the reply and the name for the output message.  Fortunately the values will be saved and you won’t need to enter them again.  If the service has several operations (our test service has two, SOATest1 and WSDLTest) you’ll give these limits for each operation in turn.

 

In later exercises you’ll see how to manage tables (arrays) which, like strings, may be of indeterminate size, and other data types that don’t easily convert between COBOL and WSDL data types.

 

We won’t have to repeat this every time that you want to access this web service. Jazz remembers that we’ve accessed a service from this URI, and next time you write INVOKE ?; in a program the combo box will already be populated, and the maximum lengths of Name and Reply will be filled in.  Also, as you and other members of your project team access other web services the URI’s of these will be added to the combo box so that it will contain all the web service names used within your project.

Selecting an operation

Web services may provide several operations, so next we have to select which one we want.  If we have just read the WSDL the operation list may look like this: -

 

Click the [+] and Service1 expands: -

 

Click this [+] and you see the actual Operations offered at this URI: -

 

NOW we can select the one we want.  We click on “SOATest1”. 

 

If we selected a service that was processed earlier then the display will omit these higher levels and look like this, so we don’t need to expand these extra levels.  We still have to select the operation that we want: -

 

Continuing our Program

We wrote INVOKE ?; and clicked [Check], causing Jazz to initiate a dialog where we discovered a web service and retrieved its interface specification.  We return to our program to find that Jazz has replaced the “?” and changed our program to this: -

*# Last Updated by IBMUSER at 18/11/2015 3:22:28 p.m.

PROGRAM WST1 CICS INSCREEN(WST1S) TRANSID(TRW1) COMMAREA(WST1C) ;

DEFINE Wrk  DATA(

    Name VARCHAR (20),

    Result VARCHAR (70));

ACCEPT (WST1S.Name);

    COPY JZTst-SOATest1;

INVOKE JZTst-SOATest1(?);

#263 E No definition found for JZTst-SOATest1: COPY inserted. Click CHECK again

SEND inscreen;

We do as the message requests and click [Check] again, and our program becomes

*# Last Updated by IBMUSER at 18/11/2015 3:22:28 p.m.

PROGRAM WST1 CICS INSCREEN(WST1S) TRANSID(TRW1) COMMAREA(WST1C) ;

DEFINE Wrk  DATA(

    Name VARCHAR (20),

    Result VARCHAR (70));

ACCEPT (WST1S.Name);

COPY JZTst-SOATest1;

INVOKE JZTst-SOATest1(ISOATest1.Name [IsVarchar]);

SEND inscreen;

 

Note these points: -

1.                   The service name that we use combines the service reference name, “JZTst”, with the operation name “SOATest1”.

2.                   Jazz creates a list of all Web Services used in a project – you’ll find it stored in the Jazz library as WebServices.jzw, and the list appears in various dialogs.  When you use the INVOKE ?; dialog in another program the prompt will offer the list of previously-searched URIs.  If you select the same or another operation from this URI it will also be qualified as “JZTst”.

3.                   The dialog will have added an appropriate interface definition into our Jazz Program: see the COPY statement that has been inserted just ahead of the INVOKE. This is a normal COPY statement except for one thing.  You can right-click the COPY statement to see what it looks like, but any changes you make to it will be ignored.  This is an externally defined record layout, and the only way you can update it is by refreshing it, which you do by clicking [Refresh] in the Web Services Discovery dialog.

4.                   You’ll see that the record definition looks something like this: -

*# Last Updated by IBMUSER at 18/11/2015 3:26:47 p.m.

*# Definition created from Web Service JZTst-SOATest1

*# Do not manually edit this definition

DEFINE JZTst-SOATest1 SERVICE DATA(

    INPUT VARCHAR(30) VALUE 'ISOATest1',

    OUTPUT VARCHAR(30) VALUE 'OSOATest1');

DEFINE ISOATest1 SERVICE DATA(

    Name  VARCHAR(255) OPTIONAL minOccurs 0);

DEFINE OSOATest1 SERVICE DATA(

    SOATest1Result  VARCHAR(255) OPTIONAL minOccurs 0,

    Name  VARCHAR(255) OPTIONAL minOccurs 0);

Note that this is a Jazz-format definition, it is not XML. SERVICE tells Jazz everything that it needs to know in order to generate XML documents and SOAP messages when appropriate, and COBOL definitions when appropriate, and to invoke the web services correctly.

5.                   Three definitions are inserted into our program for each web service operation: -

1.                   The first provides a definition of the web service operation, naming the input and output messages.

2.                   The 2nd uses the operation name prefixed with “I” to define the input message.  “Input” and “Output” means from the perspective of the service: we invoke JZTst-SOATest1 passing it a name to it, so the message ISOATest1 is input to the web service, but output from our program.

3.                   Similarly the 3rd definition defines the output message.

6.                   INVOKE ?; becomes INVOKE JZTst-SOATest1(?); When we click [Check] again this becomes INVOKE JZTst-SOATest1(I-SOATst-SOATest1.Name [IsVarchar]);

This is Jazz’s way of showing you that when you invoke SOATest1 you pass it an input message containing a single field, of type VARCHAR. However this may not be the actual field that we want to pass to SOATst-SOATest1. Currently our program receives the data from the screen in field WST1S.Name, and this is assigned to field WRK.Name by the ACCEPT statement, so there is no data in the parameter field yet. We either have to write an assignment statement,

I-SOATst-SOATest1.Name = WRK.Name;

or name the actual field containing the data in the parameter list. The 2nd alternative is neater, so we’ll change the INVOKE statement to

                        INVOKE SOATst-SOATest1(Wrk.Name);

Handling the Reply

We’ve now written our program to the point where it will send data to the web service, but what happens to the reply? We need to provide something to handle the service’s response. This could hardly be simpler: we just add a REPLY option.  Our INVOKE statement becomes

            INVOKE JZTst-SOATest1(Wrk.Name) REPLY(?);

We click [Check] to process the program again. As with the input data list Jazz responds with the defined parameters: -

INVOKE JZTst-SOATest1(Wrk.Name) REPLY(OSOATest1.Result [IsVarchar],

OSOATest1.Name [IsVarchar]);

As before we could write assignment statements to move these fields to the actual program fields that we want, but it’s easier to simply change these name these fields in Wrk:-

INVOKE SOATst-SOATest1(Wrk.Name) REPLY(Wrk.Result,  Wrk,Name);

 

That’s it: our program is now complete, and we are ready to compile and test it.  Here is the complete program: -
*# Last Updated by IBMUSER at 18/11/2015 3:22:28 p.m.

PROGRAM WST1 CICS INSCREEN(WST1S) TRANSID(TRW1) COMMAREA(WST1C) ;

DEFINE Wrk  DATA(

    Name VARCHAR (20),

    Result VARCHAR (70));

ACCEPT (WST1S.Name);

COPY JZTst-SOATest1;

INVOKE JZTst-SOATest1(Wrk.name) REPLY(wrk.result, wrk.name);

SEND inscreen;

 

Click [Process] and Jazz will submit this job to be compiled. We’ve already used CEDA to define program WST1, map WST1S, and transaction TRW1 into our test CICS system, so there’s only minimal CICS set-up needed.

1.                   CEMT SET PROGRAM(WST1) NEWCOPY

2.                   CEMT PERFORM PIPELINE(MNJZREQR) SCAN

(we defined MNZREQR as well as the provider pipeline MNJZPROV earlier.  See JazzUGSOA2.htm).

Dealing with WSDL Conversion Errors

When you discover a web service you may find that one or more of the operations aren’t converted to Jazz definitions.  If you see a message like this: -

 

There is no problem if you don’t want to use this operation, but if you want to use JazzWSDLTest then you need to create a correct Jazz definition for it.   This diagram shows what’s going on when you get or refresh a service definition: -

 

While Jazz is trying to convert the WSDL directly into a Jazz definition, the mainframe job is converting the same WSDL into COBOL definitions.  These are mostly ignored by Jazz, but if Jazz has failed to convert the WSDL we can convert them into Jazz.

 

When the message “… except for xxxx” appears the button [DFHWS2LS Layout] also appears.   Wait until the mainframe job has finished, then click this.  If there is more than one “xxxx” then choose which one you want to convert.   Jazz then retrieves the relevant COBOL layouts and converts them back into Jazz.

Conclusion

Where did the complexity go? In Service Oriented Architectures and Web Services: Introduction the standards that we had to follow seemed overwhelming, but our first CICS program invoking a web service seems quite trivial. Admittedly the system programmer had quite a bit of set-up to do if this was the first CICS project using web services, and getting the very first CICS ó Web service program running can be a real challenge.  Once that is mastered however, with the techniques that have been discussed in this chapter you can write Jazz programs that use web services, and that provide web services to develop SOA systems of any complexity. You can get data from remote services and use it in your mainframe programs, you can provide mainframe data through web services to remote web pages and Windows programs, and you can develop distributed systems in which part of the data and logic is managed in the CICS environment, and the display and other parts of the logic is managed on a web server or client computer. For example, you can now write any of the Jazz programs that you wrote before with displaying the data on web pages rather than 3270-type terminals.

 

Our Jazz SOA programs have become even simpler than the corresponding CICS/3270 programs, but this is a bit deceptive. This reduced complexity has not disappeared, it has merely been moved from Jazz to Java or ASP.NET, as the remote objects have incorporated the validation logic. And without the benefit of the data definitions that have provided much of the power of Jazz’s validation logic, we have actually made more work for ourselves. Even when we use the ACCEPT statement to check that data returning from a web service is correct, the interplay between web service and service request can be much more complex than the validation logic implicit in ACCEPT with a 3270 screen named with INSCREEN. 

 

To remind you: when we were working with 3270-type displays we could write

            PROGRAM …. INSCREEN(screen) …

            DEFINE Record1 DATA(

                Field …)

and then create a screen by dragging fields to it. Jazz ensured that the screen field was appropriate for the dragged field, and when we wrote

            ACCEPT (field)

Jazz would ensure that the field was validated according to the rules implied by its definition.

 

It would be great if we could write a program using INSCREEN, ACCEPT, and SEND much as we did before, but instead of being restricted to 3270 screens we could be using web pages or Windows. Then we’d get the best of both: the flexibility and power of web services, with the programming efficiency of displays built with an awareness of the data structure and rules. That’s the plan, but Jazz hasn’t implemented this yet.

 

 this means that Jazz has been unable to convert the WSDL for this operation into a valid Jazz definition.  If you select it anyway then Jazz will insert invalid code. 

 

 



[1] This restriction results because Jazz uses the IBM CICS utility DFHWS2LS to create record layouts.  This requires the service name to be 6 or fewer characters, after prefixing the name with “I” or “O”.