Contents
Discovering
a Web Service Reference from INVOKE ? when a COPY exists
Discovering
New Services – Part 1, Discovering Operations
Discovering
New Services – Part 2, Discovering Message Formats
Add
Service Reference Information
Creating
a Web Service Description
Exchanging
Data with SOA Objects
Dealing
with WSDL Conversion Errors
A web service is invoked with a statement like
INVOKE MyJSv-JSPG1(IJSPG1.*) REPLY(OJSPG1.*);
Like a CALL statement, an INVOKE statement requires a preceding COPY (containing DEFINE) statement defining the input and output message formats, so this statement will be preceded by
COPY MyJSv-JSPG1;
The name of this service, MyJSv-JSPG1, suggests that this is a program JSPG1 that we have developed ourselves, and so we’ll have this copy book. However most web services that we invoke will have been developed by others, probably people we don’t know and with languages other than MANASYS Jazz. Unless we wrote the service, we need to discover the message formats and create the Jazz COPY book. Many web services provide descriptive information so that requesting programs can discover these formats. SOAP (WSDL) web services, and REST(JSON) services that follow the Swagger (Open UI) standards, have message formats that can probably be discovered from information available at their URL. MANASYS Jazz has features built in that can discover this information and create the COPY book containing the Jazz-format message description.
We start by writing
INVOKE
?;
in our program and clicking [Check].
When Jazz reaches the INVOKE ?; statement the “Add Service Reference” dialog results, displaying the form below. The combo box, and list of operations at that service, will be blank if this is the first time that you’ve tried to discover a web service, and if you haven’t created any web service provider programs of your own. Otherwise the Combo box is populated with the services that you already know about, including the services that you have created yourself. Here I’ve selected the second entry, which are the JSON services that I’ve created.
At this URL there are several operations: each operation is a separate program. MyJSv is one of our own services (the service reference name MyJSv is a giveaway), so the required COPY book describing the service will already exist in our Jazz Copybooks folder. We click JSPG1 and INVOKE ?; is replaced with
COPY MyJSv-JSPG1;
INVOKE MyJSv-JSPG1(IJSPG1.*) REPLY(OJSPG1.*);
We can now write logic before the INVOKE statement to set values in the input record, IJSPG1, and after it to handle the results returned in OJSPG1.
But how do we discover new web services that we haven’t developed ourselves? Discovering New Services tells you about this. However if this is your first contact with web services you might like to read Jazz Web Service Definitions first to get an understanding of the structure of a COPY book describing a web service.
What if the service is not one that we developed ourselves? We will need a description of the service so that we can create the COPY book describing it and so know what fields required in the input message and what we’ll get back in the output message. But if the service was developed by somebody else that we’ve never met, using technology different to ours, then how do we get this description? Fortunately, many web services provide a description of their interfaces in a standard form, so with any luck all we need to know is the service’s description URL, and its type, JSON or WSDL.
For example, here is a public SOAP API that we created for some testing: -
http://www.jazzsoftware.co.nz/SOATesting/Service1.svc.
Its description can be found at http://www.jazzsoftware.co.nz/SOATesting/Service1.svc@WSDL
And here is a public sample REST API
Petstore 2.0: http://petstore.swagger.io/v2/
Its description can be found at https://petstore.swagger.io/v2/swagger.json
We start with a Jazz program that includes INVOKE ?; and click [Check]. On reaching this statement the Add Service Reference dialog is displayed, but instead of selecting an existing service we copy/paste or type a new URL into the combo, give a service reference name, and type.
Service reference names cannot be longer than 5 characters. This restriction results because Jazz may use an IBM CICS utility to create record layouts which requires the service name to be 6 or fewer characters, after prefixing the name with “I” or “O”. The TYPE must be either JSON or WSDL.
WSDL (SOAP) services will usually respond with WSDL when the service URL is followed by ?WSDL, so to discover the operations at http://www.jazzsoftware.co.nz/SOATesting/Service1.svc we give this URL, and click [Get Svc]. Jazz adds the ?WSDL for us, and responds: -
For JSON (REST) services there is no similar standard relating the service URL to the description URL, so we may need to enter the description URL directly. If we give a URL that does not end with .json, Jazz will add this extension by default. Here we’ve entered the description URL for the petstore: -
As with MyJSv, we will click on one of the operations for INVOKE ? to be replaced with something like
COPY MyJSv-JSPG1;
INVOKE MyJSv-JSPG1(IJSPG1.*) REPLY(OJSPG1.*);
But at the moment all we have is a list of operations (programs), and we don’t have any information about the message formats, so when we click one of the operations, Jazz can’t find the appropriate copy book. It reacts by displaying the GetWebServiceDescription dialog. Here we’ve clicked User of service PtStr: -
The left button, [Get json from Service], attempts to download json data describing the message formats from the service URL. [Browse for json] initiates a Windows Explorer dialog to locate a suitable file that already exists locally. If a file is located by either button the [Create Jazz] button is enabled. Click this and the web service description is converted from .json to a Jazz format definition: in this case named PtStr-user.jzc.
The same form appears with a wsdl service. Here we’ve clicked operation SOATest1 of the WSDL service JZTs2: -
Apart from working with
.wsdl instead of .json the function is as described above.
MySvce-JSPG1 is a Jazz service definition created by Jazz when program JSPG1 was generated, and it illustrates the main features of Jazz service definitions. Definitions derived from external WSDL or JSON web services will not have COPY statements, nor use LIKE definitions and ASSIGN properties initially, but it is often worthwhile to edit the definition created for you to use these and other Jazz language features, as this will allow you to reduce the amount of procedural code that you have to write.
*# Last Updated by JAZZUSR at 13/02/2020
2:26:00 PM
*# You may edit this definition:
right-click the 'WEBSERVICE' keyword of the PROGRAM statement.
COPY Employee;
COPY TYPES;
DEFINE MyJSv-JSPG1 SYSTEM DATA([Not in COBOL
INPUT VARCHAR(6) VALUE 'IJSPG1',
OUTPUT VARCHAR(6) VALUE 'OJSPG1',
MType CHAR(4) VALUE 'JSON',
URL VARCHAR(35) VALUE 'http://localhost:9003/cics/services');
DEFINE IJSPG1 SERVICE INPUT DATA(
JZ-Employee-Skip SMALLINT
VALUE 0,
JZ-Employee GROUP,
EMPNO CHAR(6) ASSIGN EMPLOYEE.EMPNO, [KEY
WORKDEPT CHAR(3) ASSIGN EMPLOYEE.WORKDEPT,
[DKEY
END GROUP);
DEFINE OJSPG1 SERVICE OUTPUT DATA(
ERROR VARCHAR(80),
JZ-Employee-ReadTo SMALLINT
VALUE 0,
JZ-Employee-NbrReturned SMALLINT
VALUE 0,
JZ-Employee-ReturnCode LIKE
Types.ReturnCode,
JZ-Employee (1) GROUP,
EMPNO LIKE EMPLOYEE.EMPNO ASSIGN
EMPLOYEE.EMPNO,
FIRSTNME LIKE EMPLOYEE.FIRSTNME ASSIGN
EMPLOYEE.FIRSTNME,
MIDINIT LIKE EMPLOYEE.MIDINIT ASSIGN
EMPLOYEE.MIDINIT,
LASTNAME LIKE EMPLOYEE.LASTNAME ASSIGN
EMPLOYEE.LASTNAME,
WORKDEPT LIKE EMPLOYEE.WORKDEPT ASSIGN
EMPLOYEE.WORKDEPT,
PHONENO LIKE EMPLOYEE.PHONENO ASSIGN
EMPLOYEE.PHONENO,
HIREDATE LIKE EMPLOYEE.HIREDATE ASSIGN
EMPLOYEE.HIREDATE,
JOB LIKE EMPLOYEE.JOB ASSIGN
EMPLOYEE.JOB,
EDLEVEL LIKE EMPLOYEE.EDLEVEL ASSIGN
EMPLOYEE.EDLEVEL,
SEX LIKE EMPLOYEE.SEX ASSIGN
EMPLOYEE.SEX,
BIRTHDATE LIKE EMPLOYEE.BIRTHDATE ASSIGN
EMPLOYEE.BIRTHDATE,
SALARY LIKE EMPLOYEE.SALARY ASSIGN
EMPLOYEE.SALARY,
BONUS LIKE EMPLOYEE.BONUS ASSIGN
EMPLOYEE.BONUS,
COMM LIKE EMPLOYEE.COMM ASSIGN
EMPLOYEE.COMM,
CURRENCY LIKE EMPLOYEE.CURRENCY ASSIGN
EMPLOYEE.CURRENCY,
END GROUP);
1. COPY statements. This section won’t be found in derived definitions.
COPY Employee;
COPY TYPES;
It enables LIKE definitions
and ASSIGN properties, such as
FIRSTNME LIKE EMPLOYEE.FIRSTNME
ASSIGN EMPLOYEE.FIRSTNME,
2. Initial DEFINE statement.
DEFINE MyJSv-JSPG1 SYSTEM DATA([Not in COBOL
Jazz COPY books must include a DEFINE statement with the same name as the COPY name, otherwise an error will be
reported. This is the main function of
this definition, although it also contains some information that may be used by
MANASYS Jazz as you edit the program. It
is a SYSTEM definition which means that it
won’t be generated into the COBOL program.
If you want actual fields in your program with this information then you
can define fields within another definition, for example
DEFINE SundryData DATA(
INPUT LIKE MyJSv-JSPG1.INPUT);
3. Input Definition. The INPUT record is the program name prefixed with ”I”. Program JSPG1 is an enquiry program, returning an EMPLOYEE record based on either the primary key or an alternate key.
4. Output Definition. The OUTPUT record is the program name prefixed with “O”.
The Add Service Reference dialog opens with a combo that displays the list of previously-discovered services. Data for this is saved in WebServices.jzw, an XML document that is saved in your Jazz CopyLib. As each new service is discovered, another line is added to this: -
<?xml
version="1.0" encoding="utf-8"?>
<!--WebServices.jzw
documents the web services used in your project-->
<!--Do
not manually update this file-->
<References>
<LastUpdated>12/04/2020 2:18:06
PM</LastUpdated>
<LastUpdatedBy>JAZZUSR</LastUpdatedBy>
<Reference
SURL="http://localhost:9003/cics/services" RefName="MyJSv"
Type="JSON" />
<Reference
SURL="http://localhost:5482/cics/services" RefName="MyWSv"
Type="WSDL" />
<Reference DURL="https://petstore.swagger.io/v2/swagger.json"
SURL="http://petstore.swagger.io/v2" RefName="PtStr"
Type="JSON" />
<Reference
DURL="http://www.jazzsoftware.co.nz/SOATesting/Service1.svc?wsdl"
SURL="http://www.jazzsoftware.co.nz/SOATesting/Service1.svc" RefName="JZTs2"
Type="WSDL" />
</References>
The services like petstore and SOATest which have been discovered from an external site, the service reference
Also, data for each specific service is saved. For the petstore service, this is saved as PtStr.jzw: -
<Reference
Updateable="False">
<!--This documents web service PtStr at
https://petstore.swagger.io/v2/swagger.json-->
<!--Do not manually update this file-->
<ServiceReferenceName>PtStr</ServiceReferenceName>
<URI>https://petstore.swagger.io/v2/swagger.json</URI>
<Type>JSON</Type>
<Created>12/04/2020 11:54:46
AM</Created>
<CreatedBy>JAZZUSR</CreatedBy>
<Operation
Name="/pet/{petId}/uploadImage" />
<Operation Name="/pet" />
<Operation
Name="/pet/findByStatus" />
<Operation Name="/pet/findByTags"
/>
<Operation Name="/pet/{petId}"
/>
<Operation Name="/store/order"
/>
<Operation
Name="/store/order/{orderId}" />
<Operation
Name="/store/inventory" />
<Operation
Name="/user/createWithArray" />
<Operation Name="/user/createWithList"
/>
<Operation
Name="/user/{username}" />
<Operation Name="/user/login"
/>
<Operation Name="/user/logout"
/>
<Operation Name="/user" />
</Reference>
Everything below is from the old UG chapter, and needs to be reviewed
Resolving
INVOKE ?; into code like that
above is easy if the COPY book exists, but what if it doesn’t? If
·
There
is no COPY book for the selected operation, or
·
You
right-click the operation name, or
·
You
click [Refresh]
then Jazz opens a form from which you can create the copy book from WSDL or JSON
You click one of the top
four buttons to locate WSDL or JSON describing the service. Provided that this is found and it follows
the appropriate standards a Jazz COPY statement like JZTst-SOATest1 will be
created and inserted into the program. If you click [Edit Jazz Description]
without locating any WSDL or JSON, then an empty definition will be created.
You can edit the Jazz definition like any other COPY statement, and you can
re-create the definition by clicking [Refresh] or right-clicking the selected
operation.
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.
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.
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: -
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 SYSTEM
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. Type SYSTEM means that it is
available to Jazz, but not generated into COBOL.
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);
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).
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.
description of the service it will attempt to read this descriptive information and create a Jazz description from it. But there’s a lot that can go wrong, and this process may fail: -
INVOKE JZTst-SOATest1(?);
#402 S Error parsing WSDL. Jazz COPY not
generated
#126 S Refresh the service definition to
re-create JZTst-SOATest1
#263 E No definition found for
JZTst-SOATest1 but COPY required or present
#263 E No definition found for
JZTst-SOATest1
#246 E DEFINE xxx PARAMETERS not found.
Parameters not checked
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.