├── .gitattributes ├── CONTRIBUTING.md ├── Example-Cobol-AirportService ├── README.md ├── Sample Output.txt ├── Sample Trace.txt └── cobol │ └── airport.cbl ├── Example-Download ├── README.md └── c │ └── hwtdload.c ├── Example-GeoServices ├── README.md ├── images │ ├── image0.png │ ├── image1.png │ ├── image2.png │ └── image3.png └── rexx │ └── RXEXEC1.rexx ├── Example-Slack ├── KeyDatabase.md ├── KeyRing.md ├── README.md ├── jcl │ └── slack.jcl └── rexx │ └── slack.rexx ├── Example-zOSMF ├── ATTLSPolicy ├── README.md └── jcl │ ├── HLTHCHK.jcl │ └── RXZOSMF.jcl ├── LICENSE ├── MAINTAINERS.md ├── README.md └── _config.yml /.gitattributes: -------------------------------------------------------------------------------- 1 | # These files are text and should be normalized (convert crlf to lf) 2 | * text=auto git-encoding=iso8859-1 working-tree-encoding=iso8859-1 zos-working-tree-encoding=ibm-1047 3 | *.sh text eol=lf git-encoding=iso8859-1 working-tree-encoding=iso8859-1 zos-working-tree-encoding=ibm-1047 4 | # Everything in image folder is binary and should not be treated as text 5 | *.jpg binary diff=hex git-encoding=utf-8 working-tree-encoding=utf-8 zos-working-tree-encoding=utf-8 6 | *.png binary diff=hex git-encoding=utf-8 working-tree-encoding=utf-8 zos-working-tree-encoding=utf-8 7 | 8 | # encode selected files as ascii 9 | .gitattributes text=auto git-encoding=iso8859-1 working-tree-encoding=iso8859-1 zos-working-tree-encoding=iso8859-1 10 | .gitignore text=auto git-encoding=iso8859-1 working-tree-encoding=iso8859-1 zos-working-tree-encoding=iso8859-1 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing In General 2 | Our project welcomes external contributions. If you have an itch, please feel 3 | free to scratch it. 4 | 5 | To contribute code or documentation, please submit a pull request. 6 | 7 | A good way to familiarize yourself with the codebase and contribution process is 8 | to look for and tackle low-hanging fruit in the [issue tracker](https://github.com/IBM/zOS-Client-Web-Enablement-Toolkit/issues). 9 | Before embarking on a more ambitious contribution, please quickly [get in touch](#communication) with us. 10 | 11 | **Note: We appreciate your effort, and want to avoid a situation where a contribution 12 | requires extensive rework (by you or by us), sits in backlog for a long time, or 13 | cannot be accepted at all!** 14 | 15 | ### Proposing new features 16 | 17 | If you would like to implement a new feature, please [raise an issue](https://github.com/IBM/zOS-Client-Web-Enablement-Toolkit/issues) 18 | before sending a pull request so the feature can be discussed. This is to avoid 19 | you wasting your valuable time working on a feature that the project developers 20 | are not interested in accepting into the code base. 21 | 22 | ### Fixing bugs 23 | 24 | If you would like to fix a bug, please [raise an issue](https://github.com/IBM/zOS-Client-Web-Enablement-Toolkit/issues) before sending a pull request so it can be tracked. 25 | 26 | ### Merge approval 27 | 28 | The project maintainers use LGTM (Looks Good To Me) in comments on the code 29 | review to indicate acceptance. A change requires LGTMs from two of the 30 | maintainers of each component affected. 31 | 32 | For a list of the maintainers, see the [MAINTAINERS.md](MAINTAINERS.md) page. 33 | 34 | ## Legal 35 | 36 | Each source file must include a license header for the Apache 37 | Software License 2.0. Using the SPDX format is the simplest approach. 38 | e.g. 39 | 40 | ``` 41 | /* 42 | Copyright All Rights Reserved. 43 | 44 | SPDX-License-Identifier: Apache-2.0 45 | */ 46 | ``` 47 | -------------------------------------------------------------------------------- /Example-Cobol-AirportService/README.md: -------------------------------------------------------------------------------- 1 | ## Example-Cobol-AirportService 2 | Airport Info is a published REST API that allows applications to pull descriptive information about an airport. The input is a three character IATA code which represents the airport. 3 | 4 | Example: http://www.airport-data.com/api/ap_info.json?iata=LAX 5 | 6 | This example is based upon an old version of the Cobol code distributed with the Toolkit in SYS1.SAMPLIB. The SAMPLIB version invoked an FAA service that is no longer available. 7 | 8 | 9 | 10 | ## Prep work 11 | 12 | Insure that the service host and port 80 is available to the z/OS system running the example. Recommend that you try to invoke the API using cURL (if it's available.) The API should return something like: 13 | 14 | ``` 15 | {"icao":"KLAX","iata":"LAX","name":"Los Angeles International Airport","location":"Los Angeles, CA","country":"United States","country_code":"US","longitude":"-118.408068","latitude":"33.942495","link":"http:\/\/www.airport-data.com\/airport\/LAX\/","status":200} 16 | ``` 17 | 18 | ## What to do if you can't get z/OS to hit http://www.airport-data.com:80 19 | 20 | Tiny Server is a freeware web server that runs in Windows and serves files. Install Tiny Server, then create a file named index.htm in C:\Program Files (x86)\Tiny Server. Cut and paste the JSON response string in to the file. Look through the example code and uncomment the lines that will direct the API to an IP address of your choosing. 21 | -------------------------------------------------------------------------------- /Example-Cobol-AirportService/Sample Output.txt: -------------------------------------------------------------------------------- 1 | *********************************************** 2 | ** HTTP Web Enablement Toolkit Sample Begins ** 3 | ** Initialize succeeded (HWTHINIT) 4 | ** Set HWTH-OPT-VERBOSE for connection 5 | ** Set HWTH-OPT-URI for connection 6 | ** Set HWTH-OPT-PORT for connection 7 | ** Set HWTH-OPT-COOKIETYPE for connection 8 | ** Connect succeeded (HWTHCONN) 9 | ** Initialize succeeded (HWTHINIT) 10 | ** Set HWTH-REQUESTMETHOD for request 11 | ** Set HWTH-OPT-URI for request 12 | ** Adding SLIST APPEND 13 | ** Set HWTH-OPT-HTTPHEADERS for request 14 | ** Set HWTH-OPT-TRANSLATE-RESPBODY for request 15 | ** Set HWTH-OPT-RESPONSEHDR-EXIT for request 16 | ** Set HWTH-OPT-RESPONSEHDR-USERDATA for request 17 | ** Set HWTH-OPT-RESPONSEBODY-EXIT for request 18 | ** Set HWTH-OPT-RESPONSEBODY-USERDATA for request 19 | ******************************************* 20 | ** Response Header Exit Receives Control ** 21 | ** HTTP Status Code: 000000200 22 | ** HTTP Reason Phrase: OK 23 | Response Header 000000001 24 | Name: Date 25 | Value: Fri, 27 Sep 2019 18:45:13 GMT 26 | ** Response Header Exit Returns ** 27 | ******************************************* 28 | ******************************************* 29 | ** Response Header Exit Receives Control ** 30 | Response Header 000000002 31 | Name: Server 32 | Value: Apache/2.2.15 (CentOS) 33 | ** Response Header Exit Returns ** 34 | ******************************************* 35 | ******************************************* 36 | ** Response Header Exit Receives Control ** 37 | Response Header 000000003 38 | Name: X-Powered-By 39 | Value: PHP/5.3.3 40 | ** Response Header Exit Returns ** 41 | ******************************************* 42 | ******************************************* 43 | ** Response Header Exit Receives Control ** 44 | Response Header 000000004 45 | Name: X-RateLimit-Limit 46 | Value: 120 47 | ** Response Header Exit Returns ** 48 | ******************************************* 49 | ******************************************* 50 | ** Response Header Exit Receives Control ** 51 | Response Header 000000005 52 | Name: X-RateLimit-Remaining 53 | Value: 118 54 | ** Response Header Exit Returns ** 55 | ******************************************* 56 | ******************************************* 57 | ** Response Header Exit Receives Control ** 58 | Response Header 000000006 59 | Name: X-RateLimit-Reset 60 | Value: 1569613312 61 | ** Response Header Exit Returns ** 62 | ******************************************* 63 | ******************************************* 64 | ** Response Header Exit Receives Control ** 65 | Response Header 000000007 66 | Name: Vary 67 | Value: Accept-Encoding 68 | ** Response Header Exit Returns ** 69 | ******************************************* 70 | ******************************************* 71 | ** Response Header Exit Receives Control ** 72 | Response Header 000000008 73 | Name: Content-Length 74 | Value: 247 75 | ** Response Header Exit Returns ** 76 | ******************************************* 77 | ******************************************* 78 | ** Response Header Exit Receives Control ** 79 | Response Header 000000009 80 | Name: Connection 81 | Value: close 82 | ** Response Header Exit Returns ** 83 | ******************************************* 84 | ******************************************* 85 | ** Response Header Exit Receives Control ** 86 | Response Header 000000010 87 | Name: Content-Type 88 | Value: application/json 89 | ** Response Header Exit Returns ** 90 | ******************************************* 91 | ******************************************* 92 | ** Response Body Exit Receives Control ** 93 | ** Response body is in JSON format 94 | ** Initializing JSON parser 95 | Parser initialized. 96 | Response body parsed successfully 97 | Extracting airport status information... 98 | Parser work area freed. 99 | ** Response Body Exit Returns ** 100 | ******************************************* 101 | ** Request succeeded (HWTHRQST) 102 | *********************************** 103 | Airport data for OMA 104 | *********************************** 105 | Airport name: Eppley Airfield Airport 106 | Airport state: Omaha, NE 107 | Airport country: United States 108 | Airport longitude: -95.894056 109 | Airport latitude: 41.303167 110 | ----------------------------------- 111 | ** Terminate succeeded (HWTHTERM) 112 | ** Disconnect succeeded (HWTHDISC) 113 | ** Terminate succeeded (HWTHTERM) 114 | ** Program Ended Successfully ** 115 | ** HTTP Web Enablement Toolkit Sample Ends ** 116 | *********************************************** 117 | 118 | -------------------------------------------------------------------------------- /Example-Cobol-AirportService/Sample Trace.txt: -------------------------------------------------------------------------------- 1 | t-Entry: iconnImpl 2 | t: Connecting to www.airport-data.com via port 80 3 | t: Attempting to connect to IP address: 192.99.41.136 4 | t-Entry: setSocketOptions 5 | t-Exit: setSocketOptions 6 | t: Connection established using socket: 0 7 | t-Entry: initTranslationTables 8 | t-Exit: initTranslationTables 9 | t-Entry: checkForTTLS 10 | t: Socket maps to TTLSRule: (none) 11 | t: ATTLS is *not* in effect 12 | t-Exit: iconnImpl 13 | t-Entry: sendrqst 14 | t-Entry: sendrqstImpl 15 | t-Entry: appendRequestLine 16 | t: No proxy is being used for the request 17 | [HWTHCKST] (no request cookies specified) 18 | [HWTHCKST] getCookieHeader() - No applicable cookies found 19 | t: No applicable cookies found 20 | t: * * * * * HTTP REQUEST HEADERS * * * * * 21 | t: GET /api/ap_info.json?iata=OMA HTTP/1.1 22 | 23 | Accept: application/json 24 | 25 | Accept-Language: en-US 26 | 27 | Host: www.airport-data.com:80 28 | 29 | 30 | 31 | t: * * * * * END HTTP REQUEST HEADERS * * * * * 32 | t-Entry: translate 33 | t-Exit: translate 34 | t-Entry: sendInlineRequest 35 | t-Entry: doSend 36 | t-Entry: ignoreSignal 37 | t: now ignoring signal: SIGPIPE 38 | t-Exit: ignoreSignal 39 | t-Entry: restoreSignal 40 | t: restoring signal: SIGPIPE 41 | t-Exit: restoreSignal 42 | t: send() 43 | t: bytes sent: 124 44 | t: total bytes: 124 45 | t: bytes remaining: 124 46 | t: send successful. 47 | t-Exit: doSend 48 | t-Exit: sendrqstImpl 49 | t-Entry: recvresp 50 | t-Entry: recvrespImpl 51 | t-Entry: initReceiveVars 52 | t: Response buffer is empty. 53 | t-Entry: getNextResponseData 54 | t: Now processing 538 additional response bytes. 55 | t-Entry: processResponseData 56 | t-Entry: translate 57 | t-Exit: translate 58 | t-Entry: parseResponseHeaders 59 | t: HTTP status = 200 60 | t: HTTP version = HTTP/1.1 61 | t: HTTP reason = OK 62 | t: Header: Date = Fri, 27 Sep 2019 18:45:13 GMT 63 | t-Entry: headerCallback 64 | t-Exit: headerCallback 65 | t: Header: Server = Apache/2.2.15 (CentOS) 66 | t-Entry: headerCallback 67 | t-Exit: headerCallback 68 | t: Header: X-Powered-By = PHP/5.3.3 69 | t-Entry: headerCallback 70 | t-Exit: headerCallback 71 | t: Header: X-RateLimit-Limit = 120 72 | t-Entry: headerCallback 73 | t-Exit: headerCallback 74 | t: Header: X-RateLimit-Remaining = 118 75 | t-Entry: headerCallback 76 | t-Exit: headerCallback 77 | t: Header: X-RateLimit-Reset = 1569613312 78 | t-Entry: headerCallback 79 | t-Exit: headerCallback 80 | t: Header: Vary = Accept-Encoding 81 | t-Entry: headerCallback 82 | t-Exit: headerCallback 83 | t: Header: Content-Length = 247 84 | t-Entry: headerCallback 85 | t-Exit: headerCallback 86 | t: Header: Connection = close 87 | t-Entry: headerCallback 88 | t-Exit: headerCallback 89 | t: Header: Content-Type = application/json 90 | t-Entry: headerCallback 91 | t-Exit: headerCallback 92 | t-Entry: processResponseDataInline 93 | t-Entry: translate 94 | t-Exit: translate 95 | t: Invoking the user specified body exit 96 | t: Client received 538 byte response: 97 | t: 98 | Response: 99 | First 40 (of 247) bytes: {"icao":"KOMA","iata":"OMA","name":"Eppl (Hex: c07f898381967f7a7fd2d6d4c17f6b7f8981a3817f7a7fd6d4c17f6b7f95 100 | 8194857f7a7fc5979793) 101 | t: Last 40 (of 247) bytes: data.com\/airport\/OMA\/","status":200}. (Hex: 8481a3814b839694e061818999979699a3e061d6d4c1e0617f6b7fa2a3 102 | 81a3a4a27f7af2f0f0d015) 103 | t-Entry: finalizeResponse 104 | t: (Explicit) connection closed by remote system. 105 | t-Entry: idiscImpl 106 | t: Closing socket: 0 107 | t-Exit: idiscImpl 108 | t-Exit: recvrespImpl 109 | t-Exit: recvresp 110 | t-Exit: sendrqst 111 | t-Entry: idisc 112 | t-Entry: idiscImpl 113 | t: Redundant socket shutdown. 114 | t: Closing socket: 0 115 | t: Redundant socket close. 116 | t-Exit: idiscImpl 117 | 118 | -------------------------------------------------------------------------------- /Example-Download/README.md: -------------------------------------------------------------------------------- 1 | ## Example-Download 2 | When a file is downloaded via the web, if you inspect the backend 3 | processing, you may find a GET request. This request returned data, 4 | most likely in binary format, and that was the data that was saved to 5 | the location specified during the download request. 6 | 7 | This sample uses the [HTTP/HTTPS enabler portion](https://www.ibm.com/docs/en/zos/2.5.0?topic=toolkit-zos-httphttps-protocol-enabler) of the toolkit to issue 8 | a GET request and store the corresponding content in either zFS file 9 | or a sequential data set. 10 | 11 | It is intended to showcase how a native z/OS application can easily 12 | download content accessible via a REST API. 13 | 14 | This sample supports TLS version 1.3 by default. 15 | 16 | ## Prep work 17 | Compile and link `hwtdload.c` 18 | 19 | **Encoding consideration**: 20 | `hwtdload.c` file was uploaded and tested using ISO8859-1 encoding 21 | 22 | **Sample compilation in UNIX:** 23 | 24 | `xlc -o hwtdload -I//'HWT.H' -qdll -qrent -qlongname -qlist -Wl,LIST=ALL,MAP,DYNAM=DLL hwtdload.c > hwtdload.listing` 25 | 26 | where `-I` points to a location that contains the IBM-supplied C header `hwthic.c` 27 | 28 | ## Invocation 29 | ### Syntax: 30 | `hwtdload -f -t [-c ] [-k [-s ]] [-v] [-x]` 31 | 32 | ### OPTIONS 33 | 34 | **required** 35 |
-f *from*, the URI *from* which the remote content should be downloaded 36 | i.e.: `https://codeload.github.com/IBM/IBM-Z-zOS/tar.gz/refs/tags/v1.0.0` 37 |
-t *to*, the location *to* which the downloaded content is to be stored 38 | * **for a zFS file**, specify absolute pathname 39 | * **for a data set**, specify name of a pre-allocated sequential data set, 40 | with the following attributes: `DSORG=PS, RECF=FB, LRECL=1024` 41 | 42 | **NOTE:** Should the content being downloaded exceed the zFS or data set capacity, the program will fail with messages indicating how much data was, and was not written. 43 | 44 | 45 | **optional** 46 |
-c *credentials*, userid and password required for the URI 47 | * **syntax:** `userId:password` 48 | 49 |
-k certificate *keystore*, a SAF *keyring* or a *keyring* created using the SystemSSL gskkyman utility 50 |
-s *stashfile*, password stashfile associated with the certificate keystore 51 |
-v *verbose*, turn on [**HWTH_OPT_VERBOSE** tracing option](https://www.ibm.com/docs/en/zos/2.5.0?topic=values-options-connections) 52 |
-x *translate text*, write to file or dataset as IBM-1047 encoded text rather than binary 53 | 54 | 55 | **sample output** 56 | ``` 57 | hwtdload -k keyring.kdb -s keyring.sth -f https://codeload.github.com/IBM/IBM-Z-zOS/tar.gz/refs/tags/v1.0.0 -t /u/user/path/to/content.tar.gz 58 | 59 | Using connect scheme: https 60 | Using host: codeload.github.com 61 | Using requestUri: /IBM/IBM-Z-zOS/tar.gz/refs/tags/v1.0.0 62 | Using file (or dataset): /u/user/path/to/content.tar.gz 63 | Using keyring: keyring.kdb 64 | Using stashfile: keyring.sth 65 | [recvexit] 52568037 bytes received 66 | [recvexit] 105135589 bytes received 67 | [recvexit] 157702605 bytes received 68 | [recvexit] 210272132 bytes received 69 | [recvexit] 262839868 bytes received 70 | [recvexit] 315408585 bytes received 71 | [recvexit] 367976588 bytes received 72 | [recvexit] 420543180 bytes received 73 | [recvexit] 473111873 bytes received 74 | File successfully downloaded to /u/user/path/to/content.tar.gz (492236945 bytes) 75 | ``` 76 | -------------------------------------------------------------------------------- /Example-GeoServices/README.md: -------------------------------------------------------------------------------- 1 | ## Example-GeoServices 2 | Geo Services is a published REST API that allows applications to find distances between two points on the map, and to get geographical information about a municipality and its zip codes, area codes, and latitude/longitude coordinates. 3 | 4 | This sample uses the HTTP/HTTPS enabler portion of the toolkit to issue the Geo Services Distance REST API to obtain distance between two cities and the JSON Parser portion to retrieve the information from the response body. 5 | 6 | [Distance REST API Reference](http://geosvc.com/docs/Ref) 7 | 8 | ![Geo Services main page](images/image0.png) 9 | 10 | The take away from the above are the following pieces: 11 | - **Uniform Resource Identifier (URI)** 12 | ``` 13 | http://api.geosvc.com/rest/{COUNTRYCODE}/{REGION}/{CITY}/distance?apikey={APIKEY}&p={CITY2}&r={REGION2}&c={COUNTRYCODE2} 14 | ``` 15 | - **Connection portion of URI** (Where and how to connect?): 16 | ``` 17 | http://api.geosvc.com 18 | ``` 19 | This is the value the program provides for the `HWTH_HANDLETYPE_HTTPREQUEST` option associated with the `ConnectionHandle`. 20 | 21 | - **Request portion of the URI** (What is the particular request, including query parameters?): 22 | ``` 23 | /rest/{COUNTRYCODE}/{REGION}/{CITY}/distance?apikey={APIKEY}&p={CITY2}&r={REGION2}&c={COUNTRYCODE2} 24 | ``` 25 | This is the value the program provides for the `HWTH_HANDLETYPE_HTTPREQUEST` option associated with the `RequestHandle`. 26 | - **HTTP Method**: GET 27 | 28 | This method corresponds to the `HWTH_HTTP_REQUEST_GET` value that is specified for the `HWTH_OPT_REQUESTMETHOD` option associated with the 'RequestHandle'. 29 | 30 | - **Response format**: JSON 31 | 32 | The exec specifies an `Accept:application/json` header using the `HWTH_OPT_HTTPHEADERS` option to request the server returns the response body in JSON. 33 | 34 | 35 | ## Prep work 36 | 37 | To run the sample, you first need to obtain a *Public Key* to use for the Geo Services REST API requests. This key is how the Geo Services server regulates the daily usage allowance per user. 38 | 39 | Launch a web browser to the [Geo Services URI](http://geosvc.com) and follow the **Sign up** link in the upper right hand corner. 40 | 41 | ![Geo Services main page](images/image1.png) 42 | 43 | Fill in an email address (hint: it's not verified) and a password and click **Get Started**. 44 | 45 | ![Geo Services main page](images/image2.png) 46 | 47 | Save the Public Key that is generated based on your sign up. 48 | 49 | ![Geo Services main page](images/image3.png) 50 | 51 | In addition, you also need to update the value of the `traceDataSetName` variable in the **RXEXEC1** exec itself. 52 | ``` 53 | traceDataSetName = 'REPLACE.ME' 54 | ``` 55 | This variable points to a pre-allocated sequential z/OS data set which will contain any trace debugging messages generated by the toolkit. The exec allocates a `MYTRACE` DD statement to point to the sequential data set and sets [`HWTH_OPT_VERBOSE_OUTPUT`](https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.ieac100/ieac1-cwe-http-options.htm) option to the `MYTRACE` DD name. 56 | 57 | The sequential data set must adhere to the following recommended attributes: 58 | * Physical sequential (DSORG=PS) 59 | * Unblocked variable or undefined record format (RECFM=V or RECFM=U) 60 | * Unspecified (or zero-valued) block size and record length, so that the default values will be set when the DD is opened 61 | * Expandable (nonzero primary and secondary extents) 62 | * Disposition of OLD (DISP=OLD), or NEW (DISP=NEW) if allocated in a DD statement in the same JCL job step that includes the EXEC for your toolkit application 63 | 64 | ## Invocation 65 | **Syntax**: 66 | ``` 67 | RXEXEC1 PublicKey City1, State1,Country1 City2,State2,Country2 -v 68 | ``` 69 | where: 70 | - *city* is the name of the city 71 | - *state* is the 2-letter state abbreviation 72 | - *country* is the 2-letter country abbreviation 73 | - *publickey* is the Public Key you saved when you signed up for Geo Services 74 | - *-v* is an optional parameter that will turn on verbose output, the output is directed to the data set specified by **traceDataSetName** variable inside exec 75 | 76 | **sample invocation in TSO:** 77 | ``` 78 | ex 'SCOUT.HWT.LAB(RXEXEC1)' '4e39f8f8910442769bb2c17293475a10 Denver,CO,US Providence,RI,US' 79 | ex 'SCOUT.HWT.LAB(RXEXEC1)' '4e39f8f8910442769bb2c17293475a10 Denver,CO,US Providence,RI,US -v' 80 | ``` 81 | 82 | **sample invocation in UNIX:** 83 | ``` 84 | ./rxexec1 4e39f8f8910442769bb2c17293475a10 Denver,CO,US Providence,RI,US 85 | ./rxexec1 4e39f8f8910442769bb2c17293475a10 Denver,CO,US Providence,RI,US -v 86 | ``` 87 | 88 | **sample output with verbose flag not specified:** 89 | ``` 90 | HTTP Web Enablement Toolkit Sample (Begin) 91 | 92 | The distance between denver, co, us and 93 | providence, ri, us is 1757 94 | Miles. 95 | 96 | HTTP Web Enablement Toolkit Sample (End) 97 | ``` 98 | -------------------------------------------------------------------------------- /Example-GeoServices/images/image0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/zOS-Client-Web-Enablement-Toolkit/5117ca15e45eb77e39f0b80a421b0d6c7e3c79e3/Example-GeoServices/images/image0.png -------------------------------------------------------------------------------- /Example-GeoServices/images/image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/zOS-Client-Web-Enablement-Toolkit/5117ca15e45eb77e39f0b80a421b0d6c7e3c79e3/Example-GeoServices/images/image1.png -------------------------------------------------------------------------------- /Example-GeoServices/images/image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/zOS-Client-Web-Enablement-Toolkit/5117ca15e45eb77e39f0b80a421b0d6c7e3c79e3/Example-GeoServices/images/image2.png -------------------------------------------------------------------------------- /Example-GeoServices/images/image3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/zOS-Client-Web-Enablement-Toolkit/5117ca15e45eb77e39f0b80a421b0d6c7e3c79e3/Example-GeoServices/images/image3.png -------------------------------------------------------------------------------- /Example-GeoServices/rexx/RXEXEC1.rexx: -------------------------------------------------------------------------------- 1 | /* REXX */ 2 | 3 | /* START OF SPECIFICATIONS *********************************************/ 4 | /* Beginning of Copyright and License */ 5 | /* */ 6 | /* Copyright 2015, 2019 IBM Corp. */ 7 | /* */ 8 | /* Licensed under the Apache License, Version 2.0 (the "License"); */ 9 | /* you may not use this file except in compliance with the License. */ 10 | /* You may obtain a copy of the License at */ 11 | /* */ 12 | /* http://www.apache.org/licenses/LICENSE-2.0 */ 13 | /* */ 14 | /* Unless required by applicable law or agreed to in writing, */ 15 | /* software distributed under the License is distributed on an */ 16 | /* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, */ 17 | /* either express or implied. See the License for the specific */ 18 | /* language governing permissions and limitations under the License. */ 19 | /* */ 20 | /* End of Copyright and License */ 21 | /***********************************************************************/ 22 | /* */ 23 | /* SCRIPT NAME=RXEXEC1 */ 24 | /* */ 25 | /* DESCRIPTIVE NAME=Sample Rexx code to use HTTP services */ 26 | /* in the z/OS Client Web Enablement Toolkit */ 27 | /* Based on HWTHXRX1 sample shipped in samplib */ 28 | /* FUNCTION: */ 29 | /* This sample how to access the geosvcs REST API interface. */ 30 | /* */ 31 | /* This script provides sample calls to these HTTP Enabler */ 32 | /* services: */ 33 | /* HWTHINIT - Initialize a connection or request instance. */ 34 | /* HWTHSET - Set various connection or request options. */ 35 | /* HWTHCONN - Connect to a web server. */ 36 | /* HWTHRQST - Make an HTTP request over an existing connection. */ 37 | /* HWTHDISC - Disconnect from a web server. */ 38 | /* HWTHTERM - Terminate a connection or request instance. */ 39 | /* */ 40 | /* OPERATION: */ 41 | /* */ 42 | /* CODE FLOW in this sample: */ 43 | /* Call HTTP_init to create a connection instance. */ 44 | /* Call HTTP_setupConnection to set all the necessary connection */ 45 | /* options prior to connecting to the web server. */ 46 | /* Call HTTP_connect to connect to the web server. */ 47 | /* Call HTTP_init to create a request instance. */ 48 | /* Call HTTP_setupRequest to set the necessary request options. */ 49 | /* Call HTTP_request to send the request over the established */ 50 | /* connection. */ 51 | /* * Stem variable ResponseHeaders. accumulates each response */ 52 | /* header received from the HTTP server */ 53 | /* * Variable ResponseBody holds any response body content */ 54 | /* received from the HTTP server. */ 55 | /* * Variables ResponseStatusCode and ResponseReasonCode hold */ 56 | /* the status code and reason received from the HTTP server. */ 57 | /* Call writeData to work with the ResponseBody returned by the */ 58 | /* web server (this body should be in JSON format, by design, */ 59 | /* allowing JSON Parser services to be used, in doing so). */ 60 | /* Call HTTP_terminate to terminate the request instance. */ 61 | /* Call HTTP_disconnect to disconnect the connection (socket) */ 62 | /* from the web server. */ 63 | /* Call HTTP_terminate to terminate the connection instance. */ 64 | /* */ 65 | /* */ 66 | /* INVOCATION: */ 67 | /* This can run in any non-reentrant REXX environment (e.g., TSO, */ 68 | /* ISPF, zOS UNIX, SYSTEM REXX) where the HWTHTTP and HWTJSON host */ 69 | /* commands are available. */ 70 | /* (the optional -v enables verbose trace) */ 71 | /* */ 72 | /* DEPENDENCIES */ 73 | /* none. */ 74 | /* */ 75 | /* NOTES: */ 76 | /* */ 77 | /* No recovery logic has been supplied in this sample. */ 78 | /* */ 79 | /* REFERENCE: */ 80 | /* See the z/OS MVS Programming: Callable Services for */ 81 | /* High-Level Languages publication for more information */ 82 | /* regarding the usage of HTTP Enabler APIs. */ 83 | /* */ 84 | /* Change Activity: */ 85 | /* $L0 xxxxxxxx HBB77B0 yyyyy PDSCW: z/OS Client Web Enablement */ 86 | /* Toolkit sample (HTTP) in REXX programming language */ 87 | /* */ 88 | /* END OF SPECIFICATIONS * * * * * * * * * * * * * * * * * * * * * * **/ 89 | 90 | /************/ 91 | /* MAIN */ 92 | /************/ 93 | 94 | /*********************/ 95 | /* Get program args */ 96 | /*********************/ 97 | VERBOSE = 0 98 | parse arg argString 99 | if GetArgs( argString ) <> 0 then 100 | exit -1 101 | 102 | /******************************************************************/ 103 | /* REST API requires: all countries must be 2 chars long; city */ 104 | /* and state to be at least 1 character */ 105 | /******************************************************************/ 106 | if (length(sCity)=0|length(sState)=0|length(sCountry)=0) then 107 | exit usage( '** Invalid start location **' ) 108 | 109 | if (length(dCity)=0|length(dState)=0|length(dCountry)=0) then 110 | exit usage( '** Invalid dest location **' ) 111 | 112 | /*********************************************/ 113 | /* Get Web Enablement Toolkit REXX constants */ 114 | /*********************************************/ 115 | call HTTP_getToolkitConstants 116 | if RESULT <> 0 then 117 | exit fatalError( '** Environment error **' ) 118 | 119 | /***************************/ 120 | /* Indicate Program start */ 121 | /***************************/ 122 | say 'HTTP Web Enablement Toolkit Sample (Begin)' 123 | programRc = -1 124 | /*****************************************/ 125 | /* Initialize request-related variables */ 126 | /*****************************************/ 127 | ConnectionHandle = '' 128 | RequestHandle = '' 129 | 130 | /******************************************/ 131 | /* Initialize response-related variables */ 132 | /******************************************/ 133 | ExpectedResponseStatus = 200 134 | ResponseStatusCode = '' 135 | ResponseReason = '' 136 | ResponseHeaders. = '' 137 | ResponseBody = '' 138 | 139 | /*******************************/ 140 | /* Obtain a connection handle */ 141 | /*******************************/ 142 | call HTTP_init HWTH_HANDLETYPE_CONNECTION 143 | if RESULT == 0 then 144 | do 145 | /**************************************************************/ 146 | /* Set the necessary options before connecting to the server */ 147 | /**************************************************************/ 148 | call HTTP_setupConnection 149 | if RESULT == 0 then 150 | do 151 | /*******************************/ 152 | /* Connect to the HTTP server */ 153 | /*******************************/ 154 | call HTTP_connect 155 | if RESULT == 0 then 156 | do 157 | /****************************/ 158 | /* Obtain a request handle */ 159 | /****************************/ 160 | call HTTP_init HWTH_HANDLETYPE_HTTPREQUEST 161 | if RESULT == 0 then 162 | do 163 | /****************************************************************/ 164 | /* Set the necessary request options before making the request */ 165 | /****************************************************************/ 166 | call HTTP_setupRequest 167 | if RESULT == 0 then 168 | do 169 | /**********************/ 170 | /* Make the request */ 171 | /**********************/ 172 | call HTTP_request 173 | if RESULT == 0 then 174 | do 175 | /*****************************************************/ 176 | /* If the response code was ok, then write the data */ 177 | /* (for the purpose of this sample, the writeData */ 178 | /* outcome is purely illustrative). */ 179 | /*****************************************************/ 180 | if ResponseStatusCode == ExpectedResponseStatus then 181 | programRc = writeData( ResponseBody ) 182 | else 183 | say '***Bad resp received: 'ResponseStatusCode'.' 184 | end /* endif request made */ 185 | 186 | /**************************/ 187 | /* Terminate the request */ 188 | /**************************/ 189 | call HTTP_terminate RequestHandle, HWTH_NOFORCE 190 | end /* endif request setup */ 191 | end /* endif request handle obtained */ 192 | 193 | /******************************/ 194 | /* Disconnect the connection */ 195 | /******************************/ 196 | call HTTP_disconnect 197 | end /* endif connected */ 198 | end /* endif connection setup */ 199 | 200 | /**********************************/ 201 | /* Release the connection handle */ 202 | /**********************************/ 203 | call HTTP_terminate ConnectionHandle, HWTH_NOFORCE 204 | end /* endif connection handle obtained */ 205 | 206 | call closeToolkitTrace traceDD 207 | 208 | /*************************/ 209 | /* Indicate Program end */ 210 | /*************************/ 211 | say 212 | say 'HTTP Web Enablement Toolkit Sample (End)' 213 | 214 | exit programRc /* end main */ 215 | 216 | 217 | /*******************************************************************/ 218 | /* Function: writeData() */ 219 | /* */ 220 | /* Use JSON Parser services to process the airport data returned */ 221 | /* by the web server. For simplicity, write results to standard */ 222 | /* out (in a real world application, this data could be displayed */ 223 | /* real-time in an application, written to storage media or */ 224 | /* displayed in some log or other media). */ 225 | /* */ 226 | /* Return 0 if all parsing activity was performed successfully, */ 227 | /* -1 if otherwise. */ 228 | /*******************************************************************/ 229 | writeData: 230 | distanceData = arg(1) 231 | parserHandle = '' 232 | isJSON = 0; 233 | jRespHead = "application/json" 234 | l = length(jRespHead) 235 | /******************************************************************/ 236 | /* Check to make sure that the data coming back is in JSON format */ 237 | /* Loop thru all of the response headers and see if any of them */ 238 | /* say Content-Type=application/json */ 239 | /******************************************************************/ 240 | if VERBOSE then 241 | say "CHECKING HEADERS" 242 | 243 | do i = 1 to ResponseHeaders.0 /* 1 to total # of headers*/ 244 | /* Header Name check */ 245 | if ResponseHeaders.i = "Content-Type" then 246 | /* Header value check */ 247 | if substr(ResponseHeaders.i.1,1,l) = jRespHead then 248 | isJSON = 1; 249 | end 250 | 251 | /* if none of the headers was JSON, then don't call parser */ 252 | if isJSON = 0 Then do 253 | return fatalError( '** Data did not come back in JSON format ** ') 254 | end 255 | 256 | /***********************************/ 257 | /* Obtain a JSON Parser instance. */ 258 | /***********************************/ 259 | call JSON_initParser 260 | if RESULT <> 0 then 261 | return fatalError( '** Pre-processing error (parser init failure) **' ) 262 | /****************************/ 263 | /* Parse the airport data. */ 264 | /****************************/ 265 | call JSON_parse distanceData 266 | if RESULT <> 0 then 267 | do 268 | call JSON_termParser 269 | return fatalError( '** Error while parsing distance data **' ) 270 | end 271 | /*****************************************/ 272 | /* Extract specific data and surface it, */ 273 | /* then release the parser instance. */ 274 | /*****************************************/ 275 | call JSON_searchAndDeserializeData 276 | call JSON_termParser 277 | return 0 /* end function */ 278 | 279 | 280 | /*****************************************************/ 281 | /* HTTP-related functions */ 282 | /* */ 283 | /* These { HTTP_xxx } functions are located together */ 284 | /* for ease of reference and are used to demonstrate */ 285 | /* how this portion of the Web Enablement Toolkit */ 286 | /* can be used. */ 287 | /*****************************************************/ 288 | 289 | /*******************************************************/ 290 | /* Function: HTTP_getToolkitConstants */ 291 | /* */ 292 | /* Access constants used by the toolkit (for return */ 293 | /* codes, etc), via the HWTCONST toolkit api. */ 294 | /* */ 295 | /* Returns: 0 if toolkit constants accessed, -1 if not */ 296 | /*******************************************************/ 297 | HTTP_getToolkitConstants: 298 | /***********************************************/ 299 | /* Ensure that the toolkit host command is */ 300 | /* available in your REXX environment (no harm */ 301 | /* done if already present). Do this before */ 302 | /* your first toolkit api invocation. Also, */ 303 | /* ensure no conflicting signal-handling in */ 304 | /* cases of running in USS environments. */ 305 | /***********************************************/ 306 | if VERBOSE then 307 | say 'Setting hwtcalls on, syscalls sigoff' 308 | call hwtcalls 'on' 309 | call syscalls 'SIGOFF' 310 | /************************************************/ 311 | /* Call the HWTCONST toolkit api. This should */ 312 | /* make all toolkit-related constants available */ 313 | /* to procedures via (expose of) HWT_CONSTANTS */ 314 | /************************************************/ 315 | if VERBOSE then 316 | say 'Including HWT Constants...' 317 | address hwthttp "hwtconst ", 318 | "ReturnCode ", 319 | "DiagArea." 320 | RexxRC = RC 321 | if HTTP_isError(RexxRC,ReturnCode) then 322 | do 323 | call HTTP_surfaceDiag 'hwtconst', RexxRC, ReturnCode, DiagArea. 324 | return fatalError( '** hwtconst (hwthttp) failure **' ) 325 | end /* endif hwtconst failure */ 326 | return 0 /* end function */ 327 | 328 | 329 | /*************************************************/ 330 | /* Function: HTTP_init */ 331 | /* */ 332 | /* Create a handle of the designated type, via */ 333 | /* the HWTHINIT toolkit api. Populate the */ 334 | /* corresponding global variable with the result */ 335 | /* */ 336 | /* Returns: 0 if successful, -1 if not */ 337 | /*************************************************/ 338 | HTTP_init: 339 | HandleType = arg(1) 340 | /***********************************/ 341 | /* Call the HWTHINIT toolkit api. */ 342 | /***********************************/ 343 | ReturnCode = -1 344 | DiagArea. = '' 345 | address hwthttp "hwthinit ", 346 | "ReturnCode ", 347 | "HandleType ", 348 | "HandleOut ", 349 | "DiagArea." 350 | RexxRC = RC 351 | if HTTP_isError(RexxRC,ReturnCode) then 352 | do 353 | call HTTP_surfaceDiag 'hwthinit', RexxRC, ReturnCode, DiagArea. 354 | return fatalError( '** hwthinit failure **' ) 355 | end 356 | if HandleType == HWTH_HANDLETYPE_CONNECTION then 357 | ConnectionHandle = HandleOut 358 | else 359 | RequestHandle = HandleOut 360 | return 0 /* end Function */ 361 | 362 | 363 | /****************************************************/ 364 | /* Function: HTTP_setupConnection */ 365 | /* */ 366 | /* Sets the necessary connection options, via the */ 367 | /* HWTHSET toolkit api. The global variable */ 368 | /* ConnectionHandle orients the api as to the scope */ 369 | /* of the option(s). */ 370 | /* */ 371 | /* Returns: 0 if successful, -1 if not */ 372 | /****************************************************/ 373 | HTTP_setupConnection: 374 | if VERBOSE then 375 | do 376 | /*****************************************************************/ 377 | /* Set the HWT_OPT_VERBOSE option, if appropriate. */ 378 | /* This option is handy when developing an application (but may */ 379 | /* be undesirable once development is complete). Inner workings */ 380 | /* of the toolkit are traced by messages written to standard */ 381 | /* output, or optionally redirected to file (by use of the */ 382 | /* HWTH_OPT_VERBOSE_OUTPUT option) */ 383 | /*****************************************************************/ 384 | say '****** Set HWTH_OPT_VERBOSE for connection ******' 385 | ReturnCode = -1 386 | DiagArea. = '' 387 | address hwthttp "hwthset ", 388 | "ReturnCode ", 389 | "ConnectionHandle ", 390 | "HWTH_OPT_VERBOSE ", 391 | "HWTH_VERBOSE_ON ", 392 | "DiagArea." 393 | RexxRC = RC 394 | if HTTP_isError(RexxRC,ReturnCode) then 395 | do 396 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 397 | return fatalError( '** hwthset (HWTH_OPT_VERBOSE) failure **' ) 398 | end /* endif hwthset failure */ 399 | 400 | call TurnOnVerboseOutput 401 | end /* endif script invocation requested (-V) VERBOSE */ 402 | 403 | /****************************************************************************/ 404 | /* Set URI for connection to the Geo Services REST API host */ 405 | /****************************************************************************/ 406 | if VERBOSE then 407 | say '****** Set HWTH_OPT_URI for connection ******' 408 | ConnectionUri = 'http://api.geosvc.com' 409 | ReturnCode = -1 410 | DiagArea. = '' 411 | address hwthttp "hwthset ", 412 | "ReturnCode ", 413 | "ConnectionHandle ", 414 | "HWTH_OPT_URI ", 415 | "ConnectionUri ", 416 | "DiagArea." 417 | RexxRC = RC 418 | if HTTP_isError(RexxRC,ReturnCode) then 419 | do 420 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 421 | return fatalError( '** hwthset (HWTH_OPT_URI) failure **' ) 422 | end /* endif hwthset failure */ 423 | /***********************************************************************/ 424 | /* Set HWTH_OPT_COOKIETYPE */ 425 | /* Enable the cookie engine for this connection. Any "eligible" */ 426 | /* stored cookies will be resent to the host on subsequent */ 427 | /* interactions automatically. */ 428 | /***********************************************************************/ 429 | if VERBOSE then 430 | say '****** Set HWTH_OPT_COOKIETYPE for session cookies ******' 431 | ReturnCode = -1 432 | DiagArea. = '' 433 | address hwthttp "hwthset ", 434 | "ReturnCode ", 435 | "ConnectionHandle ", 436 | "HWTH_OPT_COOKIETYPE ", 437 | "HWTH_COOKIETYPE_SESSION ", 438 | "DiagArea." 439 | RexxRC = RC 440 | if HTTP_isError(RexxRC,ReturnCode) then 441 | do 442 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 443 | return fatalError( '** hwthset (HWTH_OPT_COOKIETYPE) failure **' ) 444 | end /* endif hwthset failure */ 445 | if VERBOSE then 446 | say 'Connection setup successful' 447 | return 0 /* end subroutine */ 448 | 449 | 450 | /*************************************************************/ 451 | /* Function: HTTP_connect */ 452 | /* */ 453 | /* Connect to the configured domain (host) via the HWTHCONN */ 454 | /* toolkit api. */ 455 | /* */ 456 | /* Returns: 0 if successful, -1 if not */ 457 | /*************************************************************/ 458 | HTTP_connect: 459 | if VERBOSE then 460 | say 'Connect' 461 | /**********************************/ 462 | /* Call the HWTHCONN toolkit api */ 463 | /**********************************/ 464 | ReturnCode = -1 465 | DiagArea. = '' 466 | address hwthttp "hwthconn ", 467 | "ReturnCode ", 468 | "ConnectionHandle ", 469 | "DiagArea." 470 | RexxRC = RC 471 | if HTTP_isError(RexxRC,ReturnCode) then 472 | do 473 | call HTTP_surfaceDiag 'hwthconn', RexxRC, ReturnCode, DiagArea. 474 | return fatalError( '** hwthconn failure **' ) 475 | end 476 | if VERBOSE then 477 | say 'Connect (hwthconn) successful' 478 | return 0 /* end function */ 479 | 480 | 481 | /************************************************************/ 482 | /* Function: HTTP_setupRequest */ 483 | /* */ 484 | /* Sets the necessary request options. The global variable */ 485 | /* RequestHandle orients the api as to the scope of the */ 486 | /* option(s). */ 487 | /* */ 488 | /* Returns: 0 if successful, -1 if not */ 489 | /************************************************************/ 490 | HTTP_setupRequest: 491 | if VERBOSE then 492 | say '****** Set HWTH_OPT_REQUESTMETHOD for request ******' 493 | /**************************************************************/ 494 | /* Set HTTP Request method. */ 495 | /* A GET request method is used to get data from the server. */ 496 | /**************************************************************/ 497 | ReturnCode = -1 498 | DiagArea. = '' 499 | address hwthttp "hwthset ", 500 | "ReturnCode ", 501 | "RequestHandle ", 502 | "HWTH_OPT_REQUESTMETHOD ", 503 | "HWTH_HTTP_REQUEST_GET", 504 | "DiagArea." 505 | RexxRC = RC 506 | if HTTP_isError(RexxRC,ReturnCode) then 507 | do 508 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 509 | return fatalError 'hwthset (HWTH_OPT_REQUESTMETHOD) failure **' ) 510 | end /* endif hwthset failure */ 511 | /*****************************************************************/ 512 | /* Set the request URI */ 513 | /* Set the URN URI that identifies a resource by name that is */ 514 | /* the target of our request. */ 515 | /*****************************************************************/ 516 | if VERBOSE then 517 | say'****** Set HWTH_OPT_URI for request ******' 518 | /* Add the request path portion of the request */ 519 | requestPath = '/rest/'||sCountry||'/'||sState||'/'||sCity||'/distance' 520 | 521 | /* Add the apikey to the query parms portion of the request */ 522 | queryParms = '?apikey='apikey 523 | 524 | 525 | /* Add the destination to the query parms portion of the request */ 526 | queryParms = queryParms||'&p='||dCity||'&r='||dState 527 | queryParms = queryParms||'&c='dCountry 528 | 529 | requestPath = requestPath||queryParms 530 | 531 | if VERBOSE then 532 | say 'request URI:' requestPath 533 | 534 | ReturnCode = -1 535 | DiagArea. = '' 536 | 537 | address hwthttp "hwthset ", 538 | "ReturnCode ", 539 | "RequestHandle ", 540 | "HWTH_OPT_URI ", 541 | "requestPath ", 542 | "DiagArea." 543 | RexxRC = RC 544 | if HTTP_isError(RexxRC,ReturnCode) then 545 | do 546 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 547 | return fatalError( '** hwthset (HWTH_OPT_URI) failure **' ) 548 | end /* endif hwthset failure */ 549 | /*********************************************************/ 550 | /* Set the stem variable for receiving response headers */ 551 | /*********************************************************/ 552 | ReturnCode = -1 553 | DiagArea. = '' 554 | if VERBOSE then 555 | say '****** Set HWTH_OPT_RESPONSEHDR_USERDATA for request ******' 556 | address hwthttp "hwthset ", 557 | "ReturnCode ", 558 | "RequestHandle ", 559 | "HWTH_OPT_RESPONSEHDR_USERDATA ", 560 | "ResponseHeaders. ", 561 | "DiagArea." 562 | RexxRC = RC 563 | if HTTP_isError(RexxRC,ReturnCode) then 564 | do 565 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 566 | return fatalError( '** hwthset (HWTH_OPT_RESPONSEHDR_USERDATA) failure **') 567 | end /* endif hwthset failure */ 568 | /*******************************************************************/ 569 | /* Have the toolkit convert the response body from ASCII to EBCDIC */ 570 | /* (so that we may pass it to our parser in a form that the latter */ 571 | /* will understand) */ 572 | /*******************************************************************/ 573 | ReturnCode = -1 574 | DiagArea. = '' 575 | if VERBOSE then 576 | say '****** Set HWTH_OPT_TRANSLATE_RESPBODY for request ******' 577 | address hwthttp "hwthset ", 578 | "ReturnCode ", 579 | "RequestHandle ", 580 | "HWTH_OPT_TRANSLATE_RESPBODY ", 581 | "HWTH_XLATE_RESPBODY_A2E ", 582 | "DiagArea." 583 | RexxRC = RC 584 | if HTTP_isError(RexxRC,ReturnCode) then 585 | do 586 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 587 | return fatalError( '** hwthset (HWTH_OPT_TRANSLATE_RESPBODY) failure **' ) 588 | end /* endif hwthset failure */ 589 | /*************************************************/ 590 | /* Set the variable for receiving response body */ 591 | /**************************************************/ 592 | ReturnCode = -1 593 | DiagArea. = '' 594 | if VERBOSE then 595 | say '****** Set HWTH_OPT_RESPONSEBODY_USERDATA for request ******' 596 | address hwthttp "hwthset ", 597 | "ReturnCode ", 598 | "RequestHandle ", 599 | "HWTH_OPT_RESPONSEBODY_USERDATA ", 600 | "ResponseBody ", 601 | "DiagArea." 602 | RexxRC = RC 603 | if HTTP_isError(RexxRC,ReturnCode) then 604 | do 605 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 606 | return fatalError( '** hwthset (HWTH_OPT_RESPONSEBODY_USERDATA) failure **') 607 | end /* endif hwthset failure */ 608 | /*************************************************************/ 609 | /* Set any request header(s) we may have. This depends upon */ 610 | /* the Http request (often we might not have any). */ 611 | /*************************************************************/ 612 | call HTTP_setRequestHeaders 613 | if RESULT <> 0 then 614 | return fatalError( '** Unable to set Request Headers **' ) 615 | if VERBOSE then 616 | say 'Request setup successful' 617 | return 0 /* end function */ 618 | 619 | 620 | /****************************************************************/ 621 | /* Function: HTTP_disconnect */ 622 | /* */ 623 | /* Disconnect from the configured domain (host) via the */ 624 | /* HWTHDISC toolkit api. */ 625 | /* */ 626 | /* Returns: 0 if successful, -1 if not */ 627 | /****************************************************************/ 628 | HTTP_disconnect: 629 | if VERBOSE then 630 | say 'Disconnect' 631 | /***********************************/ 632 | /* Call the HWTHDISC toolkit api. */ 633 | /***********************************/ 634 | ReturnCode = -1 635 | DiagArea. = '' 636 | address hwthttp "hwthdisc ", 637 | "ReturnCode ", 638 | "ConnectionHandle ", 639 | "DiagArea." 640 | RexxRC = RC 641 | if HTTP_isError(RexxRC,ReturnCode) then 642 | do 643 | call HTTP_surfaceDiag 'hwthdisc', RexxRC, ReturnCode, DiagArea. 644 | return fatalError( '** hwthdisc failure **' ) 645 | end /* endif hwthdisc failure */ 646 | if VERBOSE then 647 | say 'Disconnect (hwthdisc) succeeded' 648 | return 0 /* end function */ 649 | 650 | 651 | /****************************************************************/ 652 | /* Function: HTTP_terminate */ 653 | /* */ 654 | /* Release the designated Connection or Request handle via the */ 655 | /* HWTHTERM toolkit api. */ 656 | /* */ 657 | /* Returns: */ 658 | /* 0 if successful, -1 if not */ 659 | /****************************************************************/ 660 | HTTP_terminate: 661 | 662 | handleIn = arg(1) 663 | forceOption = arg(2) 664 | if VERBOSE then 665 | say 'Terminate' 666 | /***********************************/ 667 | /* Call the HWTHTERM toolkit api. */ 668 | /***********************************/ 669 | ReturnCode = -1 670 | DiagArea. = '' 671 | address hwthttp "hwthterm ", 672 | "ReturnCode ", 673 | "handleIn ", 674 | "forceOption ", 675 | "DiagArea." 676 | RexxRC = RC 677 | if HTTP_isError(RexxRC,ReturnCode) then 678 | do 679 | call HTTP_surfaceDiag 'hwthterm', RexxRC, ReturnCode, DiagArea. 680 | return fatalError( '** hwthterm failure **' ) 681 | end /* endif hwthterm failure */ 682 | if VERBOSE then 683 | say 'Terminate (hwthterm) succeeded' 684 | return 0 /* end function */ 685 | 686 | 687 | /****************************************************************/ 688 | /* Function: HTTP_request */ 689 | /* */ 690 | /* Make the configured Http request via the HWTHRQST toolkit */ 691 | /* api. */ 692 | /* */ 693 | /* Returns: 0 if successful, -1 if not */ 694 | /****************************************************************/ 695 | HTTP_request: 696 | ReturnCode = -1 697 | DiagArea. = '' 698 | 699 | /***********************************/ 700 | /* Call the HWTHRQST toolkit api. */ 701 | /***********************************/ 702 | if VERBOSE then 703 | say 'Making HTTP Request' 704 | 705 | address hwthttp "hwthrqst ", 706 | "ReturnCode ", 707 | "ConnectionHandle ", 708 | "RequestHandle ", 709 | "HttpStatusCode ", 710 | "HttpReasonCode ", 711 | "DiagArea." 712 | RexxRC = RC 713 | if HTTP_isError(RexxRC,ReturnCode) then 714 | do 715 | call HTTP_surfaceDiag 'hwthrqst', RexxRC, ReturnCode, DiagArea. 716 | return fatalError( '** hwthrqst failure **' ) 717 | end /* endif hwthrqst failure */ 718 | /****************************************************************/ 719 | /* The ReturnCode indicates merely whether the request was made */ 720 | /* (and response received) without error. The origin server's */ 721 | /* response, of course, is another matter. The HttpStatusCode */ 722 | /* and HttpReasonCode record how the server responded. Any */ 723 | /* header(s) and/or body included in that response are to be */ 724 | /* found in the variables which we established earlier. */ 725 | /****************************************************************/ 726 | ResponseStatusCode = strip(HttpStatusCode,'L',0) 727 | ResponseReasonCode = strip(HttpReasonCode) 728 | return 0 /* end function */ 729 | 730 | 731 | /*************************************************************/ 732 | /* Function: HTTP_setRequestHeaders */ 733 | /* */ 734 | /* Add appropriate Request Headers, by first building an */ 735 | /* "SList", and then setting the HWTH_OPT_HTTPHEADERS */ 736 | /* option of the Request with that list. */ 737 | /* */ 738 | /* Returns: 0 if successful, -1 if not */ 739 | /*************************************************************/ 740 | HTTP_setRequestHeaders: 741 | SList = '' 742 | acceptJsonHeader = 'Accept:application/json' 743 | acceptXMLHeader = 'Accept:application/xml' 744 | acceptLanguageHeader = 'Accept-Language: en-US' 745 | hostHeader = 'Host: api.geosvc.com' 746 | /**********************************************************************/ 747 | /* Create a brand new SList and specify the first header to be an */ 748 | /* "Accept" header that requests that the server return any response */ 749 | /* body text in JSON format. */ 750 | /**********************************************************************/ 751 | ReturnCode = -1 752 | DiagArea. = '' 753 | if VERBOSE then 754 | say 'Create new SList' 755 | address hwthttp "hwthslst ", 756 | "ReturnCode ", 757 | "RequestHandle ", 758 | "HWTH_SLST_NEW ", 759 | "SList ", 760 | "acceptJsonHeader ", 761 | "DiagArea." 762 | RexxRC = RC 763 | if HTTP_isError(RexxRC,ReturnCode) then 764 | do 765 | call HTTP_surfaceDiag 'hwthslst', RexxRC, ReturnCode, DiagArea. 766 | return fatalError( '** hwthslst (HWTH_SLST_NEW) failure **' ) 767 | end /* endif hwthslst failure */ 768 | /***********************************************************/ 769 | /* Append the Accept-Language request header to the SList */ 770 | /* to infer to the server the regional settings which are */ 771 | /* preferred by this application. */ 772 | /***********************************************************/ 773 | if VERBOSE then 774 | say 'Append to SList' 775 | address hwthttp "hwthslst ", 776 | "ReturnCode ", 777 | "RequestHandle ", 778 | "HWTH_SLST_APPEND ", 779 | "SList ", 780 | "acceptLanguageHeader ", 781 | "DiagArea." 782 | RexxRC = RC 783 | if HTTP_isError(RexxRC,ReturnCode) then 784 | do 785 | call HTTP_surfaceDiag 'hwthslst', RexxRC, ReturnCode, DiagArea. 786 | return fatalError( '** hwthslst (HWTH_SLST_APPEND) failure **' ) 787 | end /* endif hwthslst failure */ 788 | /***********************************************************/ 789 | /* Append the Host request header to the SList */ 790 | /***********************************************************/ 791 | if VERBOSE then 792 | say 'Append to SList' 793 | address hwthttp "hwthslst ", 794 | "ReturnCode ", 795 | "RequestHandle ", 796 | "HWTH_SLST_APPEND ", 797 | "SList ", 798 | "hostHeader ", 799 | "DiagArea." 800 | RexxRC = RC 801 | if HTTP_isError(RexxRC,ReturnCode) then 802 | do 803 | call HTTP_surfaceDiag 'hwthslst', RexxRC, ReturnCode, DiagArea. 804 | return fatalError( '** hwthslst (HWTH_SLST_APPEND) failure **' ) 805 | end /* endif hwthslst failure */ 806 | /************************************/ 807 | /* Set the request headers with the */ 808 | /* just-produced list */ 809 | /************************************/ 810 | if VERBOSE then 811 | say '****** Set HWTH_OPT_HTTPHEADERS for request ******' 812 | ReturnCode = -1 813 | DiagArea. = '' 814 | address hwthttp "hwthset ", 815 | "ReturnCode ", 816 | "RequestHandle ", 817 | "HWTH_OPT_HTTPHEADERS ", 818 | "SList ", 819 | "DiagArea." 820 | RexxRC = RC 821 | if HTTP_isError(RexxRC,ReturnCode) then 822 | do 823 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 824 | return fatalError( '** hwthset (HWTH_OPT_HTTPHEADERS) failure **' ) 825 | end /* endif hwthset failure */ 826 | return 0 /* end function */ 827 | 828 | 829 | /*************************************************************/ 830 | /* Function: HTTP_isError */ 831 | /* */ 832 | /* Check the input processing codes. Note that if the input */ 833 | /* RexxRC is nonzero, then the toolkit return code is moot */ 834 | /* (the toolkit function was likely not even invoked). If */ 835 | /* the toolkit return code is relevant, check it against the */ 836 | /* set of { HWTH_xx } return codes for evidence of error. */ 837 | /* This set is ordered: HWTH_OK < HWTH_WARNING < ... */ 838 | /* with remaining codes indicating error, so we may check */ 839 | /* via single inequality. */ 840 | /* */ 841 | /* Returns: 1 if any toolkit error is indicated, 0 */ 842 | /* otherwise. */ 843 | /*************************************************************/ 844 | HTTP_isError: 845 | RexxRC = arg(1) 846 | if RexxRC <> 0 then 847 | return 1 848 | ToolkitRC = strip(arg(2),'L',0) 849 | if ToolkitRC == '' then 850 | return 0 851 | if ToolkitRC <= HWTH_WARNING then 852 | return 0 853 | return 1 /* end function */ 854 | 855 | 856 | /*************************************************************/ 857 | /* Function: HTTP_isWarning */ 858 | /* */ 859 | /* Check the input processing codes. Note that if the input */ 860 | /* RexxRC is nonzero, then the toolkit return code is moot */ 861 | /* (the toolkit function was likely not even invoked). If */ 862 | /* the toolkit return code is relevant, check it against the */ 863 | /* specific HWTH_WARNING return code. */ 864 | /* */ 865 | /* Returns: 1 if toolkit rc HWTH_WARNING is indicated, 0 */ 866 | /* otherwise. */ 867 | /*************************************************************/ 868 | HTTP_isWarning: 869 | RexxRC = arg(1) 870 | if RexxRC <> 0 then 871 | return 0 872 | ToolkitRC = strip(arg(2),'L',0) 873 | if ToolkitRC == '' then 874 | return 0 875 | if ToolkitRC <> HWTH_WARNING then 876 | return 0 877 | return 1 /* end function */ 878 | 879 | 880 | /***********************************************/ 881 | /* Procedure: HTTP_surfaceDiag() */ 882 | /* */ 883 | /* Surface input error information. Note that */ 884 | /* when the RexxRC is nonzero, the ToolkitRC */ 885 | /* and DiagArea content are moot and are */ 886 | /* suppressed (so as to not mislead). */ 887 | /***********************************************/ 888 | HTTP_surfaceDiag: procedure expose DiagArea. 889 | say 890 | say '*ERROR* ('||arg(1)||') at time: '||Time() 891 | say 'Rexx RC: '||arg(2)', Toolkit ReturnCode: '||arg(3) 892 | say 'DiagArea.Service: '||DiagArea.HWTH_service 893 | say 'DiagArea.ReasonCode: '||DiagArea.HWTH_reasonCode 894 | say 'DiagArea.ReasonDesc: '||DiagArea.HWTH_reasonDesc 895 | say 896 | return /* end procedure */ 897 | 898 | 899 | /*****************************************************/ 900 | /* JSON-related functions */ 901 | /* */ 902 | /* These { JSON_xxx } functions are located together */ 903 | /* for ease of reference and are used to demonstrate */ 904 | /* how this portion of the Web Enablement Toolkit */ 905 | /* can be used in conjunction with the Http-related */ 906 | /* toolkit functions. */ 907 | /*****************************************************/ 908 | 909 | /**********************************************************/ 910 | /* Function: JSON_initParser */ 911 | /* */ 912 | /* Initializes the global parserHandle variable via */ 913 | /* call to toolkit service HWTJINIT. */ 914 | /* */ 915 | /* Returns: 0 if successful, -1 if unsuccessful */ 916 | /**********************************************************/ 917 | JSON_initParser: 918 | if VERBOSE then 919 | say 'Initializing Json Parser' 920 | /***********************************/ 921 | /* Call the HWTJINIT toolkit api. */ 922 | /***********************************/ 923 | ReturnCode = -1 924 | DiagArea. = '' 925 | address hwtjson "hwtjinit ", 926 | "ReturnCode ", 927 | "handleOut ", 928 | "DiagArea." 929 | RexxRC = RC 930 | if JSON_isError(RexxRC,ReturnCode) then 931 | do 932 | call JSON_surfaceDiag 'hwtjinit', RexxRC, ReturnCode, DiagArea. 933 | return fatalError( '** hwtjinit failure **' ) 934 | end /* endif hwtjinit failure */ 935 | parserHandle = handleOut 936 | if VERBOSE then 937 | say 'Json Parser init (hwtjinit) succeeded' 938 | return 0 /* end function */ 939 | 940 | 941 | /**********************************************************************/ 942 | /* Function: JSON_parse */ 943 | /* */ 944 | /* Parses the input text body (which should be syntactically correct */ 945 | /* JSON text) via call to toolkit service HWTJPARS. */ 946 | /* */ 947 | /* Returns: 0 if successful, -1 if unsuccessful */ 948 | /**********************************************************************/ 949 | JSON_parse: 950 | jsonTextBody = arg(1) 951 | if VERBOSE then 952 | say 'Invoke Json Parser' 953 | /**************************************************/ 954 | /* Call the HWTJPARS toolkit api. */ 955 | /* Parse scans the input text body and creates an */ 956 | /* internal representation of the JSON data, */ 957 | /* suitable for search and create operations. */ 958 | /**************************************************/ 959 | ReturnCode = -1 960 | DiagArea. = '' 961 | address hwtjson "hwtjpars ", 962 | "ReturnCode ", 963 | "parserHandle ", 964 | "jsonTextBody ", 965 | "DiagArea." 966 | RexxRC = RC 967 | if JSON_isError(RexxRC,ReturnCode) then 968 | do 969 | call JSON_surfaceDiag 'hwtjpars', RexxRC, ReturnCode, DiagArea. 970 | return fatalError( '** hwtjpars failure **' ) 971 | end /* endif hwtjpars failure */ 972 | if VERBOSE then 973 | say 'JSON data parsed successfully' 974 | return 0 /* end function */ 975 | 976 | 977 | /*******************************************************************/ 978 | /* Subroutine: JSON_searchAndDeserializeData */ 979 | /* */ 980 | /* Search for specific values and objects in the parsed response */ 981 | /* body, and deserialize them into the distanceData. stem variable */ 982 | /* */ 983 | /*******************************************************************/ 984 | JSON_searchAndDeserializeData: 985 | distanceData. = '' 986 | /***************************************/ 987 | /* Get Value (String) from root object */ 988 | /***************************************/ 989 | distanceValue = JSON_findValue( 0, "Value", HWTJ_NUMBER_TYPE ) 990 | distanceData.distanceValue = distanceValue 991 | /***************************************/ 992 | /* Get Units (String) from root object */ 993 | /***************************************/ 994 | unit = JSON_findValue( 0, "Unit", HWTJ_STRING_TYPE ) 995 | distanceData.unit = unit 996 | say 997 | say 'The distance between 'sCity', 'sState', 'sCountry' and ' 998 | say ' 'dCity', 'dState', 'dCountry' is 'distanceData.distanceValue 999 | say ' 'distanceData.unit'.' 1000 | say 1001 | return /* end subroutine */ 1002 | 1003 | 1004 | /******************************************************************/ 1005 | /* Function: JSON_findValue */ 1006 | /* */ 1007 | /* Searches the appropriate portion of the parsed JSON data (that */ 1008 | /* designated by the objectToSearch argument) for an entry whose */ 1009 | /* name matches the designated searchName argument. Returns a */ 1010 | /* value or handle, depending on the expectedType. */ 1011 | /* */ 1012 | /* Returns: value or handle as described above, or a null result */ 1013 | /* if no suitable value or handle is found. */ 1014 | /******************************************************************/ 1015 | JSON_findValue: 1016 | objectToSearch = arg(1) 1017 | searchName = arg(2) 1018 | expectedType = arg(3) 1019 | /*********************************************************/ 1020 | /* Trying to find a value for a null entry is perhaps a */ 1021 | /* bit nonsensical, but for completeness we include the */ 1022 | /* possibility. We make an arbitrary choice on what to */ 1023 | /* return, and do this first, to avoid wasted processing */ 1024 | /*********************************************************/ 1025 | if expectedType == HWTJ_NULL_TYPE then 1026 | return '(null)' 1027 | if VERBOSE then 1028 | say 'Invoke Json Search' 1029 | /********************************************************/ 1030 | /* Search the specified object for the specified name. */ 1031 | /* The value 0 is specified (for the "startingHandle") */ 1032 | /* to indicate that the search should start at the */ 1033 | /* beginning of the designated object. */ 1034 | /********************************************************/ 1035 | ReturnCode = -1 1036 | DiagArea. = '' 1037 | address hwtjson "hwtjsrch ", 1038 | "ReturnCode ", 1039 | "parserHandle ", 1040 | "HWTJ_SEARCHTYPE_OBJECT ", 1041 | "searchName ", 1042 | "objectToSearch ", 1043 | "0 ", 1044 | "searchResult ", 1045 | "DiagArea." 1046 | RexxRC = RC 1047 | /************************************************************/ 1048 | /* Differentiate a not found condition from an error, and */ 1049 | /* tolerate the former. Note the order dependency here, */ 1050 | /* at least as the called routines are currently written. */ 1051 | /************************************************************/ 1052 | if JSON_isNotFound(RexxRC,ReturnCode) then 1053 | return '(not found)' 1054 | if JSON_isError(RexxRC,ReturnCode) then 1055 | do 1056 | call JSON_surfaceDiag 'hwtjsrch', RexxRC, ReturnCode, DiagArea. 1057 | say '** hwtjsrch failure **' 1058 | return '' 1059 | end /* endif hwtjsrch failed */ 1060 | /******************************************/ 1061 | /* Verify the type of the search result */ 1062 | /******************************************/ 1063 | resultType = JSON_getType( searchResult ) 1064 | if resultType <> expectedType then 1065 | do 1066 | say '** Type mismatch ('||resultType||','||expectedType||') **' 1067 | return '' 1068 | end /* endif unexpected type */ 1069 | /******************************************************/ 1070 | /* Return the located object or array, as appropriate */ 1071 | /******************************************************/ 1072 | if expectedType == HWTJ_OBJECT_TYPE | expectedType == HWTJ_ARRAY_TYPE then 1073 | do 1074 | return searchResult 1075 | end /* endif object or array type */ 1076 | /*******************************************************/ 1077 | /* Return the located string or number, as appropriate */ 1078 | /*******************************************************/ 1079 | if expectedType == HWTJ_STRING_TYPE | expectedType == HWTJ_NUMBER_TYPE then 1080 | do 1081 | if VERBOSE then 1082 | say 'Invoke Json Get Value' 1083 | ReturnCode = -1 1084 | DiagArea. = '' 1085 | address hwtjson "hwtjgval ", 1086 | "ReturnCode ", 1087 | "parserHandle ", 1088 | "searchResult ", 1089 | "result ", 1090 | "DiagArea." 1091 | RexxRC = RC 1092 | if JSON_isError(RexxRC,ReturnCode) then 1093 | do 1094 | call JSON_surfaceDiag 'hwtjgval', RexxRC, ReturnCode, DiagArea. 1095 | say '** hwtjgval failure **' 1096 | return '' 1097 | end /* endif hwtjgval failed */ 1098 | return result 1099 | end /* endif string or number type */ 1100 | /****************************************************/ 1101 | /* Return the located boolean value, as appropriate */ 1102 | /****************************************************/ 1103 | if expectedType == HWTJ_BOOLEAN_TYPE then 1104 | do 1105 | if VERBOSE then 1106 | say 'Invoke Json Get Boolean Value' 1107 | ReturnCode = -1 1108 | DiagArea. = '' 1109 | address hwtjson "hwtjgbov ", 1110 | "ReturnCode ", 1111 | "parserHandle ", 1112 | "searchResult ", 1113 | "result ", 1114 | "DiagArea." 1115 | RexxRC = RC 1116 | if JSON_isError(RexxRC,ReturnCode) then 1117 | do 1118 | call JSON_surfaceDiag 'hwtjgbov', RexxRC, ReturnCode, DiagArea. 1119 | say '** hwtjgbov failure **' 1120 | return '' 1121 | end /* endif hwtjgbov failed */ 1122 | return result 1123 | end /* endif boolean type */ 1124 | if VERBOSE then 1125 | say '** No return value found **' 1126 | return '' /* end function */ 1127 | 1128 | 1129 | /***********************************************************/ 1130 | /* Function: JSON_getType */ 1131 | /* */ 1132 | /* Determine the Json type of the designated search result */ 1133 | /* via the HWTJGJST toolkit api. */ 1134 | /* */ 1135 | /* Returns: Non-negative integral number indicating type */ 1136 | /* if successful, -1 if not. */ 1137 | /***********************************************************/ 1138 | JSON_getType: 1139 | searchResult = arg(1) 1140 | if VERBOSE then 1141 | say 'Invoke Json Get Type' 1142 | /*********************************/ 1143 | /* Call the HWTHGJST toolkit api */ 1144 | /*********************************/ 1145 | ReturnCode = -1 1146 | DiagArea. = '' 1147 | address hwtjson "hwtjgjst ", 1148 | "ReturnCode ", 1149 | "parserHandle ", 1150 | "searchResult ", 1151 | "resultTypeName ", 1152 | "DiagArea." 1153 | RexxRC = RC 1154 | if JSON_isError(RexxRC,ReturnCode) then 1155 | do 1156 | call JSON_surfaceDiag 'hwtjgjst', RexxRC, ReturnCode, DiagArea. 1157 | return fatalError( '** hwtjgjst failure **' ) 1158 | end /* endif hwtjgjst failure */ 1159 | else 1160 | do 1161 | /******************************************************/ 1162 | /* Convert the returned type name into its equivalent */ 1163 | /* constant, and return that more convenient value. */ 1164 | /* Note that the interpret instruction might more */ 1165 | /* typically be used here, but the goal here is to */ 1166 | /* familiarize the reader with these types. */ 1167 | /******************************************************/ 1168 | type = strip(resultTypeName) 1169 | if type == 'HWTJ_STRING_TYPE' then 1170 | return HWTJ_STRING_TYPE 1171 | if type == 'HWTJ_NUMBER_TYPE' then 1172 | return HWTJ_NUMBER_TYPE 1173 | if type == 'HWTJ_BOOLEAN_TYPE' then 1174 | return HWTJ_BOOLEAN_TYPE 1175 | if type == 'HWTJ_ARRAY_TYPE' then 1176 | return HWTJ_ARRAY_TYPE 1177 | if type == 'HWTJ_OBJECT_TYPE' then 1178 | return HWTJ_OBJECT_TYPE 1179 | if type == 'HWTJ_NULL_TYPE' then 1180 | return HWTJ_NULL_TYPE 1181 | end 1182 | /***********************************************/ 1183 | /* This return should not occur, in practice. */ 1184 | /***********************************************/ 1185 | return fatalError( 'Unsupported Type ('||type||') from hwtjgjst' ) 1186 | 1187 | 1188 | /**********************************************************/ 1189 | /* Function: JSON_termParser */ 1190 | /* */ 1191 | /* Cleans up parser resources and invalidates the parser */ 1192 | /* instance handle, via call to the HWTJTERM toolkit api. */ 1193 | /* Note that as the REXX environment is single-threaded, */ 1194 | /* no consideration of any "busy" outcome from the api is */ 1195 | /* done (as it would be in other language environments). */ 1196 | /* */ 1197 | /* Returns: 0 if successful, -1 if not. */ 1198 | /**********************************************************/ 1199 | JSON_termParser: 1200 | if VERBOSE then 1201 | say 'Terminate Json Parser' 1202 | /**********************************/ 1203 | /* Call the HWTJTERM toolkit api */ 1204 | /**********************************/ 1205 | ReturnCode = -1 1206 | DiagArea. = '' 1207 | address hwtjson "hwtjterm ", 1208 | "ReturnCode ", 1209 | "parserHandle ", 1210 | "DiagArea." 1211 | RexxRC = RC 1212 | if JSON_isError(RexxRC,ReturnCode) then 1213 | do 1214 | call JSON_surfaceDiag 'hwtjterm', RexxRC, ReturnCode, DiagArea. 1215 | return fatalError( '** hwtjterm failure **' ) 1216 | end /* endif hwtjterm failure */ 1217 | if VERBOSE then 1218 | say 'Json Parser terminated' 1219 | return 0 /* end function */ 1220 | 1221 | 1222 | /*************************************************************/ 1223 | /* Function: JSON_isNotFound */ 1224 | /* */ 1225 | /* Check the input processing codes. Note that if the input */ 1226 | /* RexxRC is nonzero, then the toolkit return code is moot */ 1227 | /* (the toolkit function was likely not even invoked). If */ 1228 | /* the toolkit return code is relevant, check it against the */ 1229 | /* specific return code for a "not found" condition. */ 1230 | /* */ 1231 | /* Returns: 1 if a HWTJ_JSRCH_SRCHSTR_NOT_FOUND condition */ 1232 | /* is indicated, 0 otherwise. */ 1233 | /*************************************************************/ 1234 | JSON_isNotFound: 1235 | RexxRC = arg(1) 1236 | if RexxRC <> 0 then 1237 | return 0 1238 | ToolkitRC = strip(arg(2),'L',0) 1239 | if ToolkitRC == HWTJ_JSRCH_SRCHSTR_NOT_FOUND then 1240 | return 1 1241 | return 0 /* end function */ 1242 | 1243 | 1244 | /*************************************************************/ 1245 | /* Function: JSON_isError */ 1246 | /* */ 1247 | /* Check the input processing codes. Note that if the input */ 1248 | /* RexxRC is nonzero, then the toolkit return code is moot */ 1249 | /* (the toolkit function was likely not even invoked). If */ 1250 | /* the toolkit return code is relevant, check it against the */ 1251 | /* set of { HWTJ_xx } return codes for evidence of error. */ 1252 | /* This set is ordered: HWTJ_OK < HWTJ_WARNING < ... */ 1253 | /* with remaining codes indicating error, so we may check */ 1254 | /* via single inequality. */ 1255 | /* */ 1256 | /* Returns: 1 if any toolkit error is indicated, 0 */ 1257 | /* otherwise. */ 1258 | /*************************************************************/ 1259 | JSON_isError: 1260 | RexxRC = arg(1) 1261 | if RexxRC <> 0 then 1262 | return 1 1263 | ToolkitRC = strip(arg(2),'L',0) 1264 | if ToolkitRC == '' then 1265 | return 0 1266 | if ToolkitRC <= HWTJ_WARNING then 1267 | return 0 1268 | return 1 /* end function */ 1269 | 1270 | 1271 | /***********************************************/ 1272 | /* Procedure: JSON_surfaceDiag */ 1273 | /* */ 1274 | /* Surface input error information. Note that */ 1275 | /* when the RexxRC is nonzero, the ToolkitRC */ 1276 | /* and DiagArea content are moot and are */ 1277 | /* suppressed (so as to not mislead). */ 1278 | /* */ 1279 | /***********************************************/ 1280 | JSON_surfaceDiag: procedure expose DiagArea. 1281 | who = arg(1) 1282 | RexxRC = arg(2) 1283 | ToolkitRC = arg(3) 1284 | say 1285 | say '*ERROR* ('||who||') at time: '||Time() 1286 | say 'Rexx RC: '||RexxRC||', Toolkit ReturnCode: '||ToolkitRC 1287 | if RexxRC == 0 then 1288 | do 1289 | say 'DiagArea.ReasonCode: '||DiagArea.ReasonCode 1290 | say 'DiagArea.ReasonDesc: '||DiagArea.ReasonDesc 1291 | end 1292 | say 1293 | return /* end procedure */ 1294 | 1295 | 1296 | /***********************************************/ 1297 | /* Function: GetArgs */ 1298 | /* */ 1299 | /* Parse script arguments and make appropriate */ 1300 | /* variable assignments, or return fatal error */ 1301 | /* code via usage() invocation. */ 1302 | /* */ 1303 | /* Returns: 0 if successful, -1 if not. */ 1304 | /***********************************************/ 1305 | GetArgs: 1306 | S = arg(1) 1307 | argCount = words(S) 1308 | if argCount == 0 | argCount < 3 | argCount > 4 then 1309 | return usage( 'Wrong number of arguments' ) 1310 | 1311 | do i = 1 to argCount 1312 | localArg = word(S,i) 1313 | select 1314 | when (i == 1) then 1315 | do 1316 | apikey = localArg 1317 | end 1318 | when (i == 2) | (i == 3) then 1319 | call parseLocation 1320 | otherwise 1321 | if localArg == '-v' then 1322 | VERBOSE = 1 1323 | end 1324 | end 1325 | return 0 /* end function */ 1326 | 1327 | /***********************************************/ 1328 | /* Function: parseLocation */ 1329 | /* */ 1330 | /* Parses one of the 2 location fields */ 1331 | /* Expects comma-delineated names */ 1332 | /* */ 1333 | /* Returns: 0 if successful, -1 if not. */ 1334 | /***********************************************/ 1335 | parseLocation: 1336 | 1337 | UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 1338 | lower = 'abcdefghijklmnopqrstuvwxyz' 1339 | 1340 | City = '' 1341 | State = '' 1342 | Country = '' 1343 | 1344 | parse value localArg with City ',' State ',' Country 1345 | 1346 | if i = 2 then 1347 | do 1348 | if (length(City)=0|length(State)=0|length(Country)<>2) then 1349 | exit usage( '** Invalid start location **' ) 1350 | 1351 | /* Make the start location all lower case */ 1352 | sCity = translate(City,lower,UPPER) 1353 | sState = translate(State,lower,UPPER) 1354 | sCountry = translate(Country,lower,UPPER) 1355 | end 1356 | else 1357 | do 1358 | if (length(City)=0|length(State)=0|length(Country)<>2) then 1359 | exit usage( '** Invalid dest location **' ) 1360 | 1361 | /* Make the dest location all lower case */ 1362 | dCity = translate(City,lower,UPPER) 1363 | dState = translate(State,lower,UPPER) 1364 | dCountry = translate(Country,lower,UPPER) 1365 | end 1366 | 1367 | return 0 /* end function */ 1368 | 1369 | 1370 | /***********************************************/ 1371 | /* Function: usage */ 1372 | /* */ 1373 | /* Provide usage guidance to the invoker. */ 1374 | /* */ 1375 | /* Returns: -1 to indicate fatal script error. */ 1376 | /***********************************************/ 1377 | usage: 1378 | whyString = arg(1) 1379 | say 1380 | say 'usage:' 1381 | say 'ex RXEXEC1 ' 1382 | say ' ' 1383 | say 1384 | say '('||whyString||')' 1385 | say 1386 | return -1 /* end function */ 1387 | 1388 | /***********************************************/ 1389 | /* Function: fatalError */ 1390 | /* */ 1391 | /* Surfaces the input message, and returns */ 1392 | /* a canonical failure code. */ 1393 | /* */ 1394 | /* Returns: -1 to indicate fatal script error. */ 1395 | /***********************************************/ 1396 | fatalError: 1397 | errorMsg = arg(1) 1398 | say errorMsg 1399 | return -1 /* end function */ 1400 | 1401 | /***********************************************/ 1402 | /* Function: TurnOnVerboseOutput */ 1403 | /* */ 1404 | /* Allocates the trace dataset and sets the */ 1405 | /* output data set */ 1406 | /***********************************************/ 1407 | TurnOnVerboseOutput: 1408 | /**********************************************************/ 1409 | say '****** Set HWTH_OPT_VERBOSE_OUTPUT for connection ******' 1410 | traceDataSetName = 'REPLACE.ME' 1411 | traceDD = 'MYTRACE' 1412 | 1413 | allocRc = allocateDsnToolkitTracefile(traceDataSetName,traceDD) 1414 | 1415 | DiagArea. = '' 1416 | address hwthttp "hwthset ", 1417 | "ReturnCode ", 1418 | "ConnectionHandle ", 1419 | "HWTH_OPT_VERBOSE_OUTPUT ", 1420 | "traceDD", 1421 | "DiagArea." 1422 | 1423 | RexxRC = RC 1424 | if HTTP_isError(RexxRC,ReturnCode) then 1425 | do 1426 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 1427 | return fatalError( '** hwthset (HWTH_OPT_VERBOSE) failure **') 1428 | end /* endif hwthset failure */ 1429 | 1430 | say 'Trace output location: 'traceDataSetName 1431 | return 1432 | 1433 | /*************************************************/ 1434 | /* Procedure: allocateDsnToolkitTracefile */ 1435 | /* */ 1436 | /* Allocate a previously created trace data set */ 1437 | /* with the required attributes (which */ 1438 | /* must already exist), and a known DDname. */ 1439 | /*************************************************/ 1440 | allocateDsnToolkitTracefile: procedure expose (PROC_GLOBALS) 1441 | datasetName = arg(1) 1442 | DDname = arg(2) 1443 | 1444 | /* allocates datasetName to DDName and directs messages */ 1445 | /* to z/OS UNIX standard error (sdterr) */ 1446 | alloc = 'alloc fi('||DDname||') ' 1447 | alloc = alloc||'da('||quoted(datasetName)||') old msg(2)' 1448 | call bpxwdyn alloc 1449 | allocRc = Result 1450 | say 'BPXWDYN Result: '||allocRc 1451 | 1452 | return allocRc /* end procedure */ 1453 | 1454 | /***********************************************************/ 1455 | /* Procedure: closeToolkitTrace */ 1456 | /* */ 1457 | /* Free the ddname which an earlier redirectToolkitTraceXX */ 1458 | /* caused allocation to associate with an HFS file. */ 1459 | /***********************************************************/ 1460 | closeToolkitTrace: procedure expose (PROC_GLOBALS) 1461 | DDname = arg(1) 1462 | call bpxwdyn 'free fi('DDname')' 1463 | return /* end procedure */ 1464 | 1465 | /*******************************************************/ 1466 | /* Function: quoted */ 1467 | /*******************************************************/ 1468 | quoted: 1469 | stringIn = arg(1) 1470 | return "'"||stringIn||"'" 1471 | -------------------------------------------------------------------------------- /Example-Slack/KeyDatabase.md: -------------------------------------------------------------------------------- 1 | ## Configuring a key database file 2 | 3 | This section describes how to create a key database file in order to successfully connect with the Slack servers. 4 | 5 | The key database stores the certificate for the Certificate Authority (CA) used by Slack. The SSL implementation 6 | references this database when determining if the target TLS server is trusted. 7 | 8 | This database file does not store any private keys, therefore it is not necessary to protect it with a strong 9 | password. The database should use file system permissions to prevent other users updating the database. 10 | 11 | Database management is performed with the `gskkyman` utility. The documentation for this utility can be found in the 12 | [z/OS Cryptographic Services](https://www.ibm.com/support/knowledgecenter/SSLTBW_2.4.0/com.ibm.zos.v2r4.gska100/sssl2gsk1725369.htm) 13 | section of the IBM Knowledge Center. 14 | 15 | There are five steps required to correctly configure a database file for use by the Client Web Enablement Toolkit. 16 | 17 | 1. Download the certificate of the CA used by Slack 18 | 1. Copy the certificate to the z/OS UNIX System Services (USS) environment 19 | 1. Create a key database 20 | 1. Import the certificate into the key database 21 | 1. Create a password stash file 22 | 23 | ### Download the correct CA certificate 24 | 25 | Determine the root CA used by Slack and the certificate. 26 | You can determine this by visiting the [Slack homepage](https://slack.com/) using your browser and 27 | examining the CA certificate used. 28 | 29 | At the time of writing, this is [DigiCert](https://www.digicert.com/) and the root CA certificate 30 | in use has the name *DigiCert Global Root CA*. 31 | You can download the certificate from the 32 | [*DigiCert Trusted Root Authority Certificates*](https://www.digicert.com/digicert-root-certificates.htm) page. 33 | 34 | Download the certificate in PEM format. 35 | 36 | ### Copy the certificate to the z/OS USS environment 37 | 38 | FTP the PEM file from your workstation to the z/OS USS environment using **ASCII mode** transfer. 39 | 40 | To validate the certificate in the z/OS USS environment, the following command will display the 41 | certificate details. 42 | 43 | ```sh 44 | openssl x509 -in DigiCertGlobalRootCA.crt.pem -inform pem -text -noout 45 | ``` 46 | 47 | ### Create a key database 48 | 49 | Create a new key database using the `gskkyman` utility. 50 | 51 | 1. Run the `gskkyman` utility with no arguments. 52 | 1. From the **Database Menu** select option 1 - **Create new database** 53 | 1. Enter the name of your database (for example, `myKeyDb`) 54 | 1. Enter a password for the database 55 | 1. Specify no password expiration for the database 56 | 1. Use the default database record length 57 | 1. Do not use FIPS mode 58 | 59 | This new database contains a list of default certificates. The certificates stored in the database can be 60 | listed using option 2 - **Manage certificates** on the **Key Management Menu**. 61 | 62 | ### Add the CA certificate into the database 63 | 64 | You now need to import the certificate for the CA into the database. 65 | 66 | 1. From the **Key Management Menu** select option 7 - **Import a certificate** 67 | 1. Specify the file name of the certificate (for example, `DigiCertGlobalRootCA.crt.pem`) 68 | 1. Specify the label of the CA certificate (for example, `DigiCert Global Root CA`) 69 | 70 | If the process completed succesfully, then the message `Certificate imported.` is displayed. 71 | 72 | ### Create a password stash file 73 | 74 | A stash file allows access to the database when it is not possible to manually enter the password. 75 | 76 | 1. From the **Key Management Menu** select option 10 - **Store database password** 77 | 78 | The stash file is written to the same location as the key database file, with the extension `.sth`. 79 | -------------------------------------------------------------------------------- /Example-Slack/KeyRing.md: -------------------------------------------------------------------------------- 1 | ## Configuring a key ring 2 | 3 | This section describes how to create a key ring in order to successfully connect with the Slack servers. 4 | 5 | There are five steps required to correctly configure SAF for use by the Client Web Enablement Toolkit. 6 | 7 | 1. Download the certificate of the CA (Certificate Authority) used by Slack 8 | 1. Copy the certificate into the z/OS environment 9 | 1. Add the CA certificate into SAF as TRUSTed 10 | 1. Create a key ring for use by the Rexx exec 11 | 1. Connect the CA certificate with the key ring 12 | 13 | The commands here assume the use of RACF: see your SAF documentation if an alternative is used. 14 | All commands assume they are being executed under the userid that will execute the SLACK Rexx exec. 15 | 16 | ### Download the correct CA certificate 17 | 18 | Determine the root CA (Certificate Authority) used by Slack and the certificate. 19 | You can determine this by visiting the [Slack homepage](https://slack.com/) using your browser and 20 | examining the CA certificate used. 21 | 22 | At the time of writing, this is [DigiCert](https://www.digicert.com/) and the root CA certificate 23 | in use has the name *DigiCert Global Root CA*. 24 | You can download the certificate from the 25 | [*DigiCert Trusted Root Authority Certificates*](https://www.digicert.com/digicert-root-certificates.htm) page. 26 | 27 | Save this binary-encoded CRT file onto your workstation. 28 | 29 | ### Copy the certificate to the z/OS environment 30 | 31 | FTP the CRT file from your workstation to a z/OS UNIX environment using **binary mode** transfer. 32 | 33 | To validate the certificate in the z/OS UNIX environment, the following command will display the 34 | certificate details. 35 | 36 | ```sh 37 | openssl x509 -in DigiCertGlobalRootCA.crt -inform der -text -noout 38 | ``` 39 | 40 | Now copy this DER-encoded certificate to a sequential data set: 41 | 42 | ```sh 43 | tso OGET "'DigiCertGlobalRootCA.crt' 'hlq.SLACK.CRT' BINARY" 44 | ``` 45 | 46 | ### Add the CA certificate into SAF as TRUSTed 47 | 48 | Add the certificate into SAF as a TRUSTed Certificate Authority: 49 | 50 | ``` 51 | RACDCERT ADD('hlq.SLACK.CRT') CERTAUTH TRUST WITHLABEL('DigiCert Global Root CA') 52 | ``` 53 | 54 | ### Create a key ring for use by the Rexx exec 55 | 56 | Define a SAF key ring named SLACK for the current user: 57 | 58 | ``` 59 | RACDCERT ADDRING(SLACK) 60 | ``` 61 | 62 | ### Connect the CA certificate with the key ring 63 | 64 | Connect the newly-added certificate with the new key ring: 65 | 66 | ``` 67 | RACDCERT CONNECT(CERTAUTH LABEL('DigiCert Global Root CA') RING(SLACK)) 68 | ``` 69 | -------------------------------------------------------------------------------- /Example-Slack/README.md: -------------------------------------------------------------------------------- 1 | ## Example-Slack 2 | 3 | ## Overview 4 | 5 | This sample illustrates how an application using the toolkit can leverage the Slack REST APIs to 6 | post messages to a Slack channel. 7 | 8 | This sample will perform the following steps: 9 | 10 | 1. Read input from a sequential data set 11 | 1. Connect to Slack 12 | 1. Construct a JSON message body for each record read from the input data set 13 | 1. POST the request to Slack 14 | 15 | Each record in the dataset is sent to Slack using the JSON webhook API with very little formatting 16 | taking place. 17 | 18 | 19 | ## Slack prep work 20 | 21 | Create an Incoming Webhook - see the [Slack documentation](https://api.slack.com/messaging/webhooks) 22 | for more details. 23 | 24 | This will provide you with an OAuth token which looks something like this: 25 | 26 | ``` 27 | /services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX 28 | ``` 29 | 30 | 31 | ## System prep work 32 | 33 | 1. Create four data sets for the application 34 | * hlq.SLACK.REXX - DSORG=PO,RECFM=FB,LRECL=80 - used to store the Rexx script 35 | * hlq.SLACK.JCL - DSORG=PO,RECFM=FB,LRECL=80 - used to hold the JCL for invoking the Rexx script 36 | * hlq.SLACK.MESSAGES - DSORG=PS,RECFM=FB,LRECL=300 - used to hold the messages to send to Slack 37 | * hlq.SLACK.OAUTH - DSORG=PS,RECFM=FB,LRECL=80 - used to store the secret OAuth token 38 | 1. Store slack.rexx into hlq.SLACK.REXX and customize values in the *Application constants* section 39 | for your environment 40 | 1. Store slack.jcl into hlq.SLACK.JCL and customize for your environment 41 | 1. Store the OAuth token from the Slack prep step above in hlq.SLACK.OAUTH 42 | 1. Setup a [SAF key ring](KeyRing.md) connected to the Slack CA's certificate, or a 43 | [key database file](KeyDatabase.md) containing the Slack CA's certificate 44 | 45 | 46 | ## Invocation 47 | 48 | Ensure there is at least one record in the hlq.SLACK.MESSAGES data set. 49 | 50 | Run the program by submitting the SLACK job in the hlq.SLACK.JCL data set. 51 | 52 | 53 | ## Security considerations 54 | 55 | Note that the ability to post to a Slack channel is all keyed from a single URI provided to us by 56 | the admins of the Slack workspace. 57 | This is in the form of an OAuth token, which must be kept secret. 58 | This token permits the ability to post any message to any channel within the Slack workspace. 59 | 60 | This application is expected to run as a user which has READ access to the OAuth token dataset. 61 | The only other user who should be able to access this dataset should be the maintainer of this utility. 62 | 63 | Note that this script should also be stored in a location which is read-only to most users. 64 | Making this Rexx script editable to everyone would mean they could simply change the target channel and 65 | use this script to post whatever they desire to any channel. 66 | 67 | Final note on security is that enabling HTTP trace will write out the URI (and hence the OAuth token) 68 | to the output. 69 | If trace is required to debug an issue, don't forget to disable afterwards, and make sure old job 70 | output is purged. 71 | -------------------------------------------------------------------------------- /Example-Slack/jcl/slack.jcl: -------------------------------------------------------------------------------- 1 | //SLACK JOB CLASS=A,NOTIFY=&SYSUID,REGION=0M,MEMLIMIT=0M, 2 | // MSGCLASS=A,MSGLEVEL=(1,1) 3 | //* 4 | //SLACK EXEC PGM=IKJEFT01,DYNAMNBR=4,PARM='%SLACK' 5 | //SYSUADS DD DISP=SHR,DSN=SYS1.UADS 6 | //SYSLBC DD DISP=SHR,DSN=SYS1.BRODCAST 7 | //SYSEXEC DD DISP=SHR,DSN=hlq.SLACK.REXX 8 | //SYSTSPRT DD SYSOUT=* 9 | //SYSTSIN DD DUMMY 10 | // -------------------------------------------------------------------------------- /Example-Slack/rexx/slack.rexx: -------------------------------------------------------------------------------- 1 | /* REXX */ 2 | 3 | /*********************************************************************/ 4 | /* Beginning of Copyright and License */ 5 | /* */ 6 | /* Copyright 2015, 2021 IBM Corp. */ 7 | /* */ 8 | /* Licensed under the Apache License, Version 2.0 (the "License"); */ 9 | /* you may not use this file except in compliance with the License. */ 10 | /* You may obtain a copy of the License at */ 11 | /* */ 12 | /* http://www.apache.org/licenses/LICENSE-2.0 */ 13 | /* */ 14 | /* Unless required by applicable law or agreed to in writing, */ 15 | /* software distributed under the License is distributed on an */ 16 | /* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, */ 17 | /* either express or implied. See the License for the specific */ 18 | /* language governing permissions and limitations under the License. */ 19 | /* */ 20 | /* End of Copyright and License */ 21 | /*********************************************************************/ 22 | /* */ 23 | /* This application takes input from a sequential data set and posts */ 24 | /* it as a notification to a specific Slack channel. */ 25 | /* */ 26 | /* Each record in the data set is sent to Slack using their JSON API */ 27 | /* with very little formatting taking place. Use the constants in */ 28 | /* the program below to control the output. */ 29 | /* */ 30 | /* See the accompanying README file for more information. */ 31 | /* */ 32 | /*********************************************************************/ 33 | 34 | 35 | 36 | /*********************************************************************/ 37 | /* */ 38 | /* Application constants. */ 39 | /* */ 40 | /*********************************************************************/ 41 | 42 | /* Name of the channel to post to */ 43 | channelName = '#my-slack-channel' 44 | 45 | /* Poster of the entry in the channel */ 46 | userName = 'My Rexx Slack bot' 47 | 48 | /* Server URI (just protocol and host really) */ 49 | uri = 'https://hooks.slack.com' 50 | 51 | /* Name of data set containing the text to post */ 52 | msgDataset = 'hlq.SLACK.MESSAGES' 53 | 54 | /* 55 | * This file is confidential as it contains an OAuth token which is 56 | * allowed to post to *ANY* Slack channel in your workspace. 57 | */ 58 | 59 | /* Name of data set containing the secret OAuth token */ 60 | oauthDataset = 'hlq.SLACK.OAUTH' 61 | 62 | /* 63 | * TLS cipher suites: we need to establish a list of what is acceptable 64 | * to us as a client. 65 | */ 66 | 67 | /* TLS cipher suite list (4-character variants) */ 68 | tlsCipherSuiteList = 'C02F' || 'C027' || 'C030' || , 69 | 'C028' || '009C' || '009D' || '003C' 70 | 71 | /* C02F = TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 */ 72 | /* C027 = TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 */ 73 | /* C030 = TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 */ 74 | /* C028 = TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 */ 75 | /* 009C = TLS_RSA_WITH_AES_128_GCM_SHA256 */ 76 | /* 009D = TLS_RSA_WITH_AES_256_GCM_SHA384 */ 77 | /* 003C = TLS_RSA_WITH_AES_128_CBC_SHA256 */ 78 | 79 | /* 80 | * Specify where we can find the Certificate Authority (CA) 81 | * certificates when we are connecting to Slack. 82 | * 83 | * keyStore = 'SAF' : Use SAF keyrings 84 | * keyStore = 'FILE' : Use key database files on zFS 85 | */ 86 | keyStore = 'SAF' 87 | 88 | /* 89 | * A key ring accessible by the user executing this Rexx script. 90 | * Only used to validate the SSL certificate provided by Slack. 91 | * 92 | * 'userid/keyring' or just 'keyring' for current user 93 | * 94 | * Only required when keyStore = 'SAF' 95 | */ 96 | safKeyRing = 'SLACK' 97 | 98 | /* 99 | * A key database file accessible on zFS by the user executing this 100 | * Rexx script. Only used to validate the SSL certificate provided 101 | * by Slack. 102 | * 103 | * Specify the full path to the key database file 104 | * 105 | * Only required when keyStore = 'FILE' 106 | */ 107 | keyDatabaseFile = '/u/user1/myKeyDb' 108 | 109 | /* 110 | * The key stash file for the key database file referenced above. 111 | * The file should be accessible on zFS by the user executing this 112 | * Rexx script. 113 | * 114 | * Specify the full path to the key stash file. 115 | * 116 | * Only required when keyStore = 'FILE' 117 | */ 118 | keyStashFile = '/u/user1/myKeyDb.sth' 119 | 120 | /* 121 | * If set to a non-empty string, then SSL tracing will be enabled 122 | * to allow the debug of TLS connection problems. 123 | * 124 | * Specify the full path of a binary trace output file. 125 | * 126 | * Example: 127 | * sslTraceFile = '/u/user1/ssltrace.bin' 128 | * 129 | * To format the resulting trace file, use the gsktrace utility. It 130 | * is recommended the utility output is redirected to a file. 131 | * 132 | * Example: 133 | * gsktrace ssltrace.bin > ssltrace.txt 134 | */ 135 | sslTraceFile = '' 136 | 137 | /*********************************************************************/ 138 | /* */ 139 | /* Main application starts here. */ 140 | /* */ 141 | /*********************************************************************/ 142 | 143 | /* Provide access to TSO commands */ 144 | Address TSO 145 | 146 | /* Make the HWTHTTP host environment available */ 147 | Call hwtcalls on 148 | 149 | /* Initialise some variables we use */ 150 | DiagArea. = '' 151 | Messages. = '' 152 | ReturnCode = 0 153 | ReqHandle = 0 154 | ConnectHandle = 0 155 | HttpStatusCode = 0 156 | HttpReasonCode = 0 157 | ResponseBody = '' 158 | RequestPath = '' 159 | 160 | /* 161 | * Pull in the secret OAuth token. 162 | */ 163 | 164 | /* Open the data set containing the OAuth token */ 165 | Address TSO "ALLOCATE DA('" || oauthDataset || "') FILE(OAUTH) OLD" 166 | 167 | /* Read in the first record */ 168 | Address MVS "EXECIO * DISKR OAUTH (FINIS" 169 | 170 | /* Parse to the path variable */ 171 | /* - No uppercase translation */ 172 | /* - breaking on first space and discarding remainder */ 173 | Parse Pull requestPath . 174 | 175 | /* Close the OAuth token data set */ 176 | Address TSO "FREE FILE(OAUTH)" 177 | 178 | 179 | /* 180 | * Initialise the environment. 181 | */ 182 | 183 | /* Initialise some HWT constants */ 184 | Address hwthttp "hwtconst" "ReturnCode" "DiagArea." 185 | 186 | If ReturnCode \= 0 Then Call ShowError "hwtconst" 187 | 188 | 189 | /* 190 | * Initialise a connection 191 | */ 192 | 193 | /* Tell HWT we are creating a connection handle */ 194 | HandleType = HWTH_HANDLETYPE_CONNECTION 195 | Address hwthttp "hwthinit" "ReturnCode" , 196 | "HandleType" "ConnectHandle" "DiagArea." 197 | 198 | If ReturnCode \= 0 Then Call ShowError "hwthinit (connection)" 199 | 200 | 201 | /* 202 | * Setup the connection options 203 | */ 204 | 205 | /* Uncomment to enable debug messages */ 206 | /* Call SetConnOpt "HWTH_OPT_VERBOSE", "HWTH_VERBOSE_ON" */ 207 | 208 | /* Connection URI (hostname really) */ 209 | Call SetConnOpt "HWTH_OPT_URI", uri 210 | 211 | /* Timeout on the send after 10 seconds */ 212 | Call SetConnOpt "HWTH_OPT_SNDTIMEOUTVAL", 10 213 | 214 | /* Timeout on the receive after 10 seconds */ 215 | Call SetConnOpt "HWTH_OPT_RCVTIMEOUTVAL", 10 216 | 217 | /* Want to use SSL */ 218 | Call SetConnOpt "HWTH_OPT_USE_SSL", "HWTH_SSL_USE" 219 | 220 | /* Should we enable SSL tracing? */ 221 | If sslTraceFile \= '' Then Do 222 | 223 | /* Specify the output trace file */ 224 | Call SetConnOpt "HWTH_OPT_SSLTRACE", sslTraceFile 225 | 226 | End 227 | 228 | /* Force use of TLS 1.2 */ 229 | Call SetConnOpt "HWTH_OPT_SSLVERSION", "HWTH_SSLVERSION_TLSv12" 230 | 231 | /* Specify the list of acceptable cipher suites */ 232 | Call SetConnOpt "HWTH_OPT_SSLCIPHERSPECS", tlsCipherSuiteList 233 | 234 | /* Are we using SAF? */ 235 | If keyStore = 'SAF' Then Do 236 | 237 | /* Use a SAF key ring */ 238 | Call SetConnOpt "HWTH_OPT_SSLKEYTYPE", , 239 | "HWTH_SSLKEYTYPE_KEYRINGNAME" 240 | 241 | /* Use this key ring */ 242 | Call SetConnOpt "HWTH_OPT_SSLKEY", safKeyRing 243 | 244 | End 245 | Else If keyStore = 'FILE' Then Do 246 | 247 | /* Use a key database file */ 248 | Call SetConnOpt "HWTH_OPT_SSLKEYTYPE", , 249 | "HWTH_SSLKEYTYPE_KEYDBFILE" 250 | 251 | /* Use this database file */ 252 | Call SetConnOpt "HWTH_OPT_SSLKEY", keyDatabaseFile 253 | 254 | /* Use this stash file */ 255 | Call SetConnOpt "HWTH_OPT_SSLKEYSTASHFILE", keyStashFile 256 | 257 | End 258 | Else Do 259 | Say "keyStore (" || keyStore || ") should be one of SAF or FILE" 260 | Exit 12 261 | End 262 | 263 | 264 | /* Perform the connect */ 265 | Address hwthttp "hwthconn" "ReturnCode" "ConnectHandle" "DiagArea." 266 | 267 | If ReturnCode \= 0 Then Call ShowError "hwthconn" 268 | 269 | 270 | /* 271 | * Pull in messages to be emitted to Slack as a stem variable. 272 | */ 273 | 274 | /* Open the data set containing the message data */ 275 | Address TSO "ALLOCATE DA('" || msgDataset || "') FILE(MESSAGES) OLD" 276 | 277 | /* Check this allocate worked OK */ 278 | If RC \= 0 Then Exit 12 279 | 280 | /* Read in all the records */ 281 | Address MVS "EXECIO * DISKR MESSAGES (STEM Messages. FINIS" 282 | 283 | /* Do an empty write to clear the data set */ 284 | Address MVS "EXECIO 0 DISKW MESSAGES (OPEN FINIS" 285 | 286 | /* Close the message data set */ 287 | Address TSO "FREE FILE(MESSAGES)" 288 | 289 | 290 | /* 291 | * Loop through each line in the file and send each as a separate 292 | * Slack notification. 293 | */ 294 | 295 | Do i = 1 To Messages.0 296 | 297 | /* Strip leading and trailing spaces */ 298 | textToDisplay = Strip(Messages.i) 299 | 300 | /* Build the request body */ 301 | requestData = '{' || , 302 | '"channel":"' || channelName || '",' || , 303 | '"username":"' || userName || '",' || , 304 | '"text":"' || textToDisplay || '"' || , 305 | '}' 306 | 307 | /* Confirm the request body */ 308 | Say requestData 309 | 310 | /* Initialise the request */ 311 | HandleType = HWTH_HANDLETYPE_HTTPREQUEST 312 | Address hwthttp "hwthinit" "ReturnCode" , 313 | "HandleType" "ReqHandle" "DiagArea." 314 | 315 | If ReturnCode \= 0 Then Call ShowError "hwthinit (request)" 316 | 317 | 318 | /* 319 | * Setup the request options 320 | */ 321 | 322 | /* Setup list of headers */ 323 | sList = 0 324 | headerContentType = 'Content-type: application/json' 325 | 326 | Address hwthttp "hwthslst" "ReturnCode" , 327 | "ReqHandle" "HWTH_SLST_NEW" "sList" , 328 | "headerContentType" "DiagArea." 329 | 330 | If ReturnCode \= 0 Then Call ShowError "hwthslst (new)" 331 | 332 | /* HTTP GET request */ 333 | Call SetReqOpt "HWTH_OPT_REQUESTMETHOD", "HWTH_HTTP_REQUEST_POST" 334 | 335 | /* Request path */ 336 | Call SetReqOpt "HWTH_OPT_URI", requestPath 337 | 338 | /* Use the HTTP headers list we have created */ 339 | Call SetReqOpt "HWTH_OPT_HTTPHEADERS", sList 340 | 341 | /* Translate to ASCII outbound please */ 342 | Call SetReqOpt "HWTH_OPT_TRANSLATE_REQBODY", , 343 | "HWTH_XLATE_REQBODY_E2A" 344 | 345 | /* Translate to EBCDIC inbound please */ 346 | Call SetReqOpt "HWTH_OPT_TRANSLATE_RESPBODY", , 347 | "HWTH_XLATE_RESPBODY_A2E" 348 | 349 | /* 350 | The following options take a reference to the internal Rexx 351 | string buffer, but Rexx does not allow us to pass arguments by 352 | references and we can therefore not use the handy SetReqOpt 353 | subroutine used above. 354 | */ 355 | 356 | /* Use the request body we created earlier */ 357 | Address hwthttp "hwthset" "ReturnCode" "ReqHandle" , 358 | "HWTH_OPT_REQUESTBODY" "requestData" "DiagArea." 359 | If ReturnCode \= 0 Then 360 | Call ShowError "hwthset HWTH_OPT_REQUESTBODY" 361 | 362 | /* Grab the response data into here */ 363 | Address hwthttp "hwthset" "ReturnCode" "ReqHandle" , 364 | "HWTH_OPT_RESPONSEBODY_USERDATA" "ResponseBody" "DiagArea." 365 | If ReturnCode \= 0 Then 366 | Call ShowError "hwthset HWTH_OPT_RESPONSEBODY_USERDATA" 367 | 368 | 369 | /* Perform the request */ 370 | Address hwthttp "hwthrqst" "ReturnCode" , 371 | "ConnectHandle" "ReqHandle" , 372 | "HttpStatusCode" "HttpReasonCode" "DiagArea." 373 | 374 | If ReturnCode \= 0 Then Call ShowError "hwthrqst" 375 | 376 | /* Check for good HTTP response */ 377 | If HttpStatusCode \= 200 Then Do 378 | 379 | /* Dump out the HTTP response code */ 380 | Say "HTTP status" HttpStatusCode 381 | 382 | /* Dump out the HTTP reason code */ 383 | Say "HTTP reason" HttpReasonCode 384 | 385 | /* Dump out the response body */ 386 | Say "Response" ResponseBody 387 | 388 | End 389 | 390 | /* Free the request headers */ 391 | Address hwthttp "hwthslst" "ReturnCode" , 392 | "ReqHandle" "HWTH_SLST_FREE" "sList" , 393 | "headerContentType" "DiagArea." 394 | 395 | If ReturnCode \= 0 Then Call ShowError "hwthslst (free)" 396 | 397 | /* Reset the request for next use */ 398 | Address hwthttp "hwthrset" "ReturnCode" , 399 | "ReqHandle" "DiagArea." 400 | 401 | If ReturnCode \= 0 Then Call ShowError "hwthrset (free)" 402 | 403 | End /* End of Messages.i loop */ 404 | 405 | 406 | /* 407 | * Processing of messages complete. 408 | * Now clean up the various handles we have open. 409 | */ 410 | 411 | /* Close the connection to Slack */ 412 | Address hwthttp "hwthdisc" "ReturnCode" "ConnectHandle" "DiagArea." 413 | 414 | If ReturnCode \= 0 Then Call ShowError "hwthdisc" 415 | 416 | /* Free the work area associated with the request */ 417 | Address hwthttp "hwthterm" "ReturnCode" "ReqHandle" , 418 | "HWTH_NOFORCE" "DiagArea." 419 | 420 | If ReturnCode \= 0 Then Call ShowError "hwthterm (request)" 421 | 422 | /* Free the work area associated with the connection */ 423 | Address hwthttp "hwthterm" "ReturnCode" "ConnectHandle" , 424 | "HWTH_NOFORCE" "DiagArea." 425 | 426 | If ReturnCode \= 0 Then Call ShowError "hwthterm (connection)" 427 | 428 | 429 | /* All complete */ 430 | Exit 0 431 | 432 | 433 | /*********************************************************************/ 434 | /* */ 435 | /* Routine to remove the drudgery of setting HTTP connection options */ 436 | /* */ 437 | /*********************************************************************/ 438 | 439 | SetConnOpt: 440 | 441 | /* Input arguments */ 442 | @optName = Arg(1) 443 | @optValue = Arg(2) 444 | 445 | /* Clear current status */ 446 | ReturnCode = -1 447 | DiagArea. = '' 448 | 449 | /* Perform the call */ 450 | Address hwthttp "hwthset" "ReturnCode" "ConnectHandle" , 451 | "@optName" "@optValue" "DiagArea." 452 | 453 | /* Check for good return */ 454 | If ReturnCode \= 0 Then Call ShowError "hwthset (conn) " || @optName 455 | 456 | /* All complete */ 457 | Return 458 | 459 | 460 | /*********************************************************************/ 461 | /* */ 462 | /* Routine to remove the drudgery of setting HTTP request options */ 463 | /* */ 464 | /*********************************************************************/ 465 | 466 | SetReqOpt: 467 | 468 | /* Input arguments */ 469 | @optName = Arg(1) 470 | @optValue = Arg(2) 471 | 472 | /* Clear current status */ 473 | ReturnCode = -1 474 | DiagArea. = '' 475 | 476 | /* Perform the call */ 477 | Address hwthttp "hwthset" "ReturnCode" "ReqHandle" , 478 | "@optName" "@optValue" "DiagArea." 479 | 480 | /* Check for good return */ 481 | If ReturnCode \= 0 Then Call ShowError "hwthset (req) " || @optName 482 | 483 | /* All complete */ 484 | Return 485 | 486 | 487 | /*********************************************************************/ 488 | /* */ 489 | /* Displays the diagnostic data following a bad function call and */ 490 | /* terminates the runtime with RC=8. */ 491 | /* */ 492 | /*********************************************************************/ 493 | 494 | ShowError: Procedure Expose ReturnCode DiagArea. 495 | 496 | /* Pull in the function name and diagnostic data */ 497 | @fn = Arg(1) 498 | 499 | /* Keep track of the sign of the return code (D2X must be 0 or +ve) */ 500 | If ReturnCode >= 0 Then SignReturnCode = '' ; Else SignReturnCode = '-' 501 | 502 | /* Say what went wrong */ 503 | Say @fn || , 504 | ": RC " || ReturnCode || , 505 | "=" || SignReturnCode || "'" || D2X(ABS(ReturnCode)) || "'x" 506 | 507 | /* Dump out the error */ 508 | Say "Service =" DiagArea.HWTH_Service 509 | Say "Reason =" DiagArea.HWTH_ReasonCode 510 | Say "Desc =" Strip(DiagArea.HWTH_ReasonDesc,,'00'x) 511 | 512 | /* Terminate the runtime */ 513 | Exit 8 514 | 515 | Return 516 | -------------------------------------------------------------------------------- /Example-zOSMF/ATTLSPolicy: -------------------------------------------------------------------------------- 1 | ##################################################################### 2 | ## 3 | ## Copyright 2015, 2019 IBM Corp. 4 | ## 5 | ## Licensed under the Apache License, Version 2.0 (the "License"); 6 | ## you may not use this file except in compliance with the License. 7 | ## You may obtain a copy of the License at 8 | ## 9 | ## http://www.apache.org/licenses/LICENSE-2.0 10 | ## 11 | ## Unless required by applicable law or agreed to in writing, 12 | ## software distributed under the License is distributed on an 13 | ## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 14 | ## either express or implied. See the License for the specific 15 | ## language governing permissions and limitations under the License. 16 | ##################################################################### 17 | ####################################### 18 | # Priority 49 jobname TKT* 19 | ####################################### 20 | TTLSRule IBMUSER_TLKLABJob 21 | { 22 | Priority 49 23 | # Userid IBMUSER 24 | Jobname TKT* 25 | # RemoteAddrSetRef NINE_DOT_any 26 | # RemotePortRange 51002 27 | Direction Outbound 28 | TTLSGroupActionRef grp_Diagnostic 29 | TTLSEnvironmentActionRef AnyUser_KEYDB_App 30 | } 31 | 32 | TTLSEnvironmentAction AnyUser_KEYDB_App 33 | { 34 | HandshakeRole Client 35 | TTLSKeyRingParms 36 | { 37 | Keyring *AUTH*/* 38 | } 39 | TTLSEnvironmentAdvancedParms 40 | { 41 | TLSv1.2 On 42 | } 43 | } 44 | 45 | # Common Diagnostic Group that a problem Rule may use 46 | # Shows AT-TLS events and result of each System SSL call 47 | TTLSGroupAction grp_Diagnostic 48 | { 49 | TTLSEnabled On 50 | Trace 14 # Log Error, Info, Event and Flow to syslogd 51 | } 52 | -------------------------------------------------------------------------------- /Example-zOSMF/README.md: -------------------------------------------------------------------------------- 1 | ## Example-zOSMF 2 | 3 | ## Overview ## 4 | z/OS Management Facility, z/OSMF, provides a rich set of [REST APIs](https://ibm.biz/BdYXHX) that allow your application to perform many different types of tasks including working with jobs, data sets, provisioning z/OS Middleware, Notification services, TSO/E functions, z/OS Console and much, much more. 5 | 6 | Imagine that your shop would like a certain job on a remote system to be run on a regular basis. If the resulting job output shows a problem, then you also want to email the output information to the appropriate person so they can address the issue. 7 | 8 | This sample illustrates how an application, using the toolkit, can leverage some of the REST APIs listed above, to implement this scenario. 9 | 10 | The sample will perform the following steps: 11 | 1. Submit a simple z/OS healthcheck job, `HLTHCHK`, using [z/OSMF Submit job REST API](https://www.ibm.com/support/knowledgecenter/SSLTBW_2.3.0/com.ibm.zos.v2r3.izua700/IZUHPINFO_API_PutSubmitJob.htm) 12 | 2. Check the status of the job to make sure it has completed successfully, using [z/OSMF job status retrieval REST API](https://www.ibm.com/support/knowledgecenter/SSLTBW_2.3.0/com.ibm.zos.v2r3.izua700/IZUHPINFO_API_GetJobStatus.htm) 13 | 3. If the job completed successfully, it will retrieve the output written to a data set, using [z/OSMF data set retrieval REST API](https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.izua700/IZUHPINFO_API_GetReadDataSet.htm) 14 | 4. It will then analyze the output. If the healthcheck failed in any way, then it will send the output to an email address, using [z/OSMF notification REST API](https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.izua700/IZUHPINFO_API_NOTIFICATIONS.htm) 15 | 16 | 17 | ## System Prep work 18 | This sample requires the following 19 | - A userid on an active z/OSMF server that has permission to issue the following z/OSMF REST services 20 | - [z/OSMF Submit job](https://www.ibm.com/support/knowledgecenter/SSLTBW_2.3.0/com.ibm.zos.v2r3.izua700/IZUHPINFO_API_RESTJOBS.htm) 21 | - [z/OSMF data set retrieval](https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.izua700/IZUHPINFO_API_RESTFILES.htm) 22 | - [z/OSMF notification](https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.izua700/IZUHPINFO_API_NOTIFICATIONS.htm) 23 | 24 | 25 | - The z/OSMF REST Services require an HTTPS connection, this sample assumes there is an [AT-TLS Policy](https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.ieac100/attlstoolkit.htm) in place to handle this requirement. This [AT-TLS Policy](ATTLSPolicy) allows for communication with the z/OSMF server for jobs prefixed with the letters `TKT`. 26 | 27 | - Store `RXZOSMF` and `HLTHCHK` into datasets 28 | 29 | ## RXZOSMF Prep work 30 | - This sample is designed to be a REXX program run as a batch job. Update the job name, `TKTxxx1`, to something more appropriate. Keep in mind, the AT-TLS policy definition and if it applies to a specific job prefix 31 | 32 | - Update `userid` and `password` with valid credentials for an active z/OSMF server referenced above. 33 | ``` 34 | userid = xxxxxxx 35 | password = yyyyyyy 36 | ``` 37 | 38 | - Update `example.dataset(HLTHCHK)` with the location of your `HLTHCK` job. Don't remove any of the quotes or you will likely get a REXX syntax error. 39 | 40 | - Next, update the location of a zFS file where the trace output will be directed to. This samples is setup to generate verbose output, [`HWTH_OPT_VERBOSE`](https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.ieac100/ieac1-cwe-http-options.htm), and direct it to a location pointed to by the `MYTRACE` DD name, [`HWTH_OPT_VERBOSE_OUTPUT`](https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.ieac100/ieac1-cwe-http-options.htm). The `MYTRACE` DD is defined at the bottom of the sample. Replace `/sharelab/sharxxx/sharxxx.trace` with your desired zFS location. 41 | 42 | - Lastly, replace `xxxxxxx@yy.zzzzzzz` with a valid email address that the job failure notification should be sent to. 43 | 44 | ## Invocation 45 | Run the program by simply submitting the RXZOSMF member. 46 | 47 | The results of the program are split up between two locations 48 | - The output of the program is contained in SDSF. Depending on your system, you can go into option 13.14 from the main ISPF menu and then select the H option. You can sort on jobs with your jobname prefix or owned by your userid. For example, if your jobname is TKTLAB01, you can type prefix TKTLAB01. 49 | 50 | - The trace output is at the location pointed to by `MYTRACE` DD. For example, if you had update the value of `MYTRACE` to `/sharelab/shara01/shara01.trace` then if you `cd` into `/sharelab/shara01` directory you should now see the `shara01.trace` file. 51 | 52 | In addition, if the `HLTHCHK` failed, as it is expected to, the email address you specified should have received an email with **HealthCheck Exception!** as the subject line. 53 | -------------------------------------------------------------------------------- /Example-zOSMF/jcl/HLTHCHK.jcl: -------------------------------------------------------------------------------- 1 | //******************************************************************* 2 | //* 3 | //* Copyright 2015, 2019 IBM Corp. 4 | //* 5 | //* Licensed under the Apache License, Version 2.0 (the "License"); 6 | //* you may not use this file except in compliance with the License. 7 | //* You may obtain a copy of the License at 8 | //* 9 | //* http://www.apache.org/licenses/LICENSE-2.0 10 | //* 11 | //* Unless required by applicable law or agreed to in writing, 12 | //* software distributed under the License is distributed on an 13 | //* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 14 | //* either express or implied. See the License for the specific 15 | //* language governing permissions and limitations under the License. 16 | //******************************************************************* 17 | //LABUSERH JOB 'C90A,B7101A33','BEST LAB ON EARTH',REGION=0K, 18 | // MSGLEVEL=(1,1),CLASS=A,MSGCLASS=H,NOTIFY=&SYSUID. 19 | //****************************************************************/ 20 | //****************************************************************/ 21 | //HZSPRINT EXEC PGM=HZSPRNT,TIME=1440,PARMDD=SYSIN 22 | //SYSIN DD * 23 | CHECK(IBMPDSE,PDSE_SMSPDSE1) 24 | //SYSOUT DD DISP=(SHR),DSN=&SYSUID..HC.SYSOUT 25 | //****************************************************************/ 26 | -------------------------------------------------------------------------------- /Example-zOSMF/jcl/RXZOSMF.jcl: -------------------------------------------------------------------------------- 1 | //TKTxxx1 JOB 'IKJEFT01 REXX',CLASS=A,MSGCLASS=A,NOTIFY=&SYSUID 2 | //* LAB USERS: Remember to change the xxx above to the last 3 chars 3 | //* LAB USERS: of your assigned userid in the JOBNAME. 4 | //* LAB USERS: For instance: SHAREC05 -> TKTC051 above for "TKTxxx1" 5 | //* Copy the REXX exec below into a data set 6 | //CREATE EXEC PGM=IEBGENER 7 | //SYSIN DD DUMMY 8 | //SYSPRINT DD SYSOUT=A,HOLD=YES 9 | //SYSUT2 DD DSN=&SYSUID..TEMPREX(REXTSO),DISP=(,PASS), 10 | // SPACE=(CYL,(1,1,1)),UNIT=3390, 11 | // DCB=(LRECL=80,RECFM=FB,DSORG=PO) 12 | //SYSUT1 DD *,DLM=## 13 | /* REXX */ 14 | /* START OF SPECIFICATIONS *********************************************/ 15 | /* Beginning of Copyright and License */ 16 | /* */ 17 | /* Copyright 2015, 2019 IBM Corp. */ 18 | /* */ 19 | /* Licensed under the Apache License, Version 2.0 (the "License"); */ 20 | /* you may not use this file except in compliance with the License. */ 21 | /* You may obtain a copy of the License at */ 22 | /* */ 23 | /* http://www.apache.org/licenses/LICENSE-2.0 */ 24 | /* */ 25 | /* Unless required by applicable law or agreed to in writing, */ 26 | /* software distributed under the License is distributed on an */ 27 | /* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, */ 28 | /* either express or implied. See the License for the specific */ 29 | /* language governing permissions and limitations under the License. */ 30 | /* */ 31 | /* End of Copyright and License */ 32 | /*---------------------------------------------------------------------*/ 33 | /* */ 34 | /* SCRIPT NAME=RXZOSMF1 */ 35 | /* */ 36 | /* DESCRIPTIVE NAME=Sample Rexx code to use HTTP services */ 37 | /* in the z/OS Client Web Enablement Toolkit */ 38 | /* to invoke various z/OSMF REST APIs */ 39 | /* */ 40 | /* FUNCTION: */ 41 | /* This sample invokes the z/OSMF REST API to submit a job to run */ 42 | /* a z/OS healthcheck on the system. After the job is submitted, a */ 43 | /* second REST API is issued to check if the job completed */ 44 | /* successfully. After this has been confirmed, the job output is */ 45 | /* retrieved from a data set using the z/OSMF data set REST API. */ 46 | /* Finally, if the z/OS healthcheck has failed, the z/OSMF */ 47 | /* notification REST API is invoked to send an email to warn them of */ 48 | /* the failure. */ 49 | /* */ 50 | /* This script provides sample calls to these HTTP Enabler */ 51 | /* services: */ 52 | /* HWTHINIT - Initialize a connection or request instance. */ 53 | /* HWTHSET - Set various connection or request options. */ 54 | /* HWTHCONN - Connect to a web server. */ 55 | /* HWTHRQST - Make an HTTP request over an existing connection. */ 56 | /* HWTSLST - Add HTTP headers to the request */ 57 | /* HWTHDISC - Disconnect from a web server. */ 58 | /* HWTHTERM - Terminate a connection or request instance. */ 59 | /* */ 60 | /* */ 61 | /* INVOCATION: */ 62 | /* This REXX must be submitted with a jobname prefixed with the */ 63 | /* letters TKTLAB. This causes an AT-TLS policy to be activated to */ 64 | /* automatically negotiate a TLS 1.2 handshake between this */ 65 | /* application and z/OSMF at the time of the toolkit Connect */ 66 | /* call (HWTHCONN). */ 67 | /* */ 68 | /* DEPENDENCIES */ 69 | /* The necessary security authorizations need to be set up to */ 70 | /* allow the user to submit a job, query the status of the job, */ 71 | /* retrieve the content of a dataset (including TSO/E authorization)*/ 72 | /* and to notify the user via email. */ 73 | /* */ 74 | /* NOTES: */ 75 | /* */ 76 | /* No recovery logic has been supplied in this sample. */ 77 | /* */ 78 | /* REFERENCE: */ 79 | /* See the z/OS MVS Programming: Callable Services for */ 80 | /* High-Level Languages publication for more information */ 81 | /* regarding the usage of HTTP Enabler APIs. */ 82 | /* */ 83 | /* See the z/OS Management Facility (z/OSMF) Programming Guide */ 84 | /* for more information regarding the usage of the z/OSMF */ 85 | /* REST APIs. */ 86 | /* */ 87 | /* Change Activity: */ 88 | /* $L0 xxxxxxxx HBB77B0 yyyyy PDSCW: z/OS Client Web Enablement */ 89 | /* Toolkit sample (HTTP) in REXX programming language */ 90 | /* */ 91 | /* END OF SPECIFICATIONS * * * * * * * * * * * * * * * * * * * * * * **/ 92 | 93 | /* MAIN */ 94 | 95 | VERBOSE = 1 96 | 97 | /* Get Web Enablement Toolkit REXX constants */ 98 | call HTTP_getToolkitConstants 99 | if RESULT <> 0 then 100 | exit fatalError( '** Environment error **' ) 101 | 102 | /* Indicate Program start */ 103 | say '************************************************' 104 | say '** HTTP Web Enablement Toolkit Sample (Begin) **' 105 | say '************************************************' 106 | 107 | /* Initialize variables in program */ 108 | call InitializeVars 109 | 110 | /* Obtain a connection handle */ 111 | call HTTP_init HWTH_HANDLETYPE_CONNECTION 112 | if RESULT <> 0 then 113 | call fatalError('** Connection could not be initialized **') 114 | 115 | /* Set the necessary options before connecting to the server */ 116 | call HTTP_setupConnection 117 | if RESULT <> 0 then 118 | cleanup('CON','** Connection failed to be set up **') 119 | 120 | /* Connect to the HTTP server */ 121 | call HTTP_connect 122 | if RESULT <> 0 then 123 | cleanup('CON','** Connection failed **') 124 | 125 | /* Obtain a request handle */ 126 | call HTTP_init HWTH_HANDLETYPE_HTTPREQUEST 127 | if RESULT <> 0 then 128 | cleanup('CON','** Request could not be initialized **') 129 | 130 | /* Set necessary request options before making submit jobs req */ 131 | call HTTP_setupSubmitReq 132 | if RESULT <> 0 then 133 | cleanup('CONREQ','** Submit job request failed to be setup **') 134 | 135 | /* Make the z/OSMF submit job request */ 136 | call HTTP_request 137 | if RESULT <> 0 then 138 | cleanup('CONREQ','** Submit job request failed **') 139 | 140 | /* Analyze the submit job output */ 141 | if VERBOSE then 142 | say "response body: "||ResponseBody 143 | 144 | call parseZOSMFJobOutput 145 | if (jobStatusObtained = true) & (condcodeValue > 0) then 146 | cleanup('CONREQ','** Healthcheck job did not complete successfully **') 147 | 148 | /* The job status is still not known. Invoke job status REST API */ 149 | call checkJobStatus 150 | 151 | /* Set necessary request options before making data set req */ 152 | call HTTP_setupDataSetRetrieveReq 153 | if RESULT <> 0 then 154 | cleanup('CONREQ','** Data set retrieve req failed to be setup **') 155 | 156 | /* Make the z/OSMF data set retreive request */ 157 | call HTTP_request 158 | if RESULT <> 0 then 159 | cleanup('CONREQ','** Data set retrieve request failed **') 160 | 161 | /* Parse the submit job output */ 162 | if VERBOSE then 163 | say "response body: "||ResponseBody 164 | call parseZOSMFDataSetOutput 165 | if RESULT <> 0 then 166 | cleanup('CONREQ','** Data set output invalid **') 167 | 168 | /* Set necessary request options before making email req */ 169 | call HTTP_setupEmailRequest 170 | if RESULT <> 0 then 171 | cleanup('CONREQ','** Email request failed to be setup **') 172 | 173 | /* Make the z/OSMF email request */ 174 | call HTTP_request 175 | if RESULT <> 0 then 176 | cleanup('CONREQ','** Email request failed **') 177 | 178 | /* Email response body received */ 179 | if VERBOSE then 180 | say "email response body: "||ResponseBody 181 | 182 | /* Cleanup the request and connection */ 183 | programRc = 0 184 | cleanup('CONREQ','HTTP Web Enablement Toolkit Sample (Ends)') 185 | exit 0 186 | 187 | /*******************************************************/ 188 | /* Function: InitalizeVars */ 189 | /* */ 190 | /* Initialize global vars used throughout the program */ 191 | /*******************************************************/ 192 | InitializeVars: 193 | 194 | programRc = -1 195 | 196 | /* Initialize request-related variables */ 197 | ConnectionHandle = '' 198 | RequestHandle = '' 199 | 200 | /* Initialize response-related variables */ 201 | HTTP_OK = 200 202 | HTTP_Created = 201 203 | HTTP_Accepted = 202 204 | StatCode = '' 205 | ResponseStatusCode = '' 206 | ResponseReason = '' 207 | ResponseHeaders. = '' 208 | ResponseBody = '' 209 | 210 | jobName = '' 211 | 212 | return 213 | 214 | /*******************************************************/ 215 | /* Function: cleanup */ 216 | /* */ 217 | /* Cleanup a connection and possibly a request */ 218 | /* depending on the parameters pasted in. */ 219 | /*******************************************************/ 220 | cleanup: 221 | cleanupType = arg(1) 222 | errorMsg = arg(2) 223 | 224 | if cleanupType = 'CONREQ' then 225 | call HTTP_terminate RequestHandle, HWTH_NOFORCE 226 | 227 | /* Unconditionally release the connection handle */ 228 | call HTTP_terminate ConnectionHandle, HWTH_NOFORCE 229 | 230 | /* Say error message */ 231 | say errorMsg 232 | /* 233 | call closeToolkitTrace traceDD 234 | */ 235 | exit programRC 236 | 237 | /*****************************************************/ 238 | /* HTTP-related functions */ 239 | /* */ 240 | /* These { HTTP_xxx } functions are located together */ 241 | /* for ease of reference and are used to demonstrate */ 242 | /* how this portion of the Web Enablement Toolkit */ 243 | /* can be used. */ 244 | /*****************************************************/ 245 | 246 | /*******************************************************/ 247 | /* Function: HTTP_getToolkitConstants */ 248 | /* */ 249 | /* Access constants used by the toolkit (for return */ 250 | /* codes, etc), via the HWTCONST toolkit api. */ 251 | /* */ 252 | /* Returns: 0 if toolkit constants accessed, -1 if not */ 253 | /*******************************************************/ 254 | HTTP_getToolkitConstants: 255 | /***********************************************/ 256 | /* Ensure that the toolkit host command is */ 257 | /* available in your REXX environment (no harm */ 258 | /* done if already present). Do this before */ 259 | /* your first toolkit api invocation. Also, */ 260 | /* ensure no conflicting signal-handling in */ 261 | /* cases of running in USS environments. */ 262 | /***********************************************/ 263 | if VERBOSE then 264 | say 'Setting hwtcalls on, syscalls sigoff' 265 | call hwtcalls 'on' 266 | call syscalls 'SIGOFF' 267 | /************************************************/ 268 | /* Call the HWTCONST toolkit api. This should */ 269 | /* make all toolkit-related constants available */ 270 | /* to procedures via (expose of) HWT_CONSTANTS */ 271 | /************************************************/ 272 | if VERBOSE then 273 | say 'Including HWT Constants...' 274 | address hwthttp "hwtconst ", 275 | "ReturnCode ", 276 | "DiagArea." 277 | RexxRC = RC 278 | if HTTP_isError(RexxRC,ReturnCode) then 279 | do 280 | call HTTP_surfaceDiag 'hwtconst', RexxRC, ReturnCode, DiagArea. 281 | return fatalError( '** hwtconst (hwthttp) failure **' ) 282 | end /* endif hwtconst failure */ 283 | return 0 /* end function */ 284 | 285 | 286 | /*************************************************/ 287 | /* Function: HTTP_init */ 288 | /* */ 289 | /* Create a handle of the designated type, via */ 290 | /* the HWTHINIT toolkit api. Populate the */ 291 | /* corresponding global variable with the result */ 292 | /* */ 293 | /* Returns: 0 if successful, -1 if not */ 294 | /*************************************************/ 295 | HTTP_init: 296 | HandleType = arg(1) 297 | /***********************************/ 298 | /* Call the HWTHINIT toolkit api. */ 299 | /***********************************/ 300 | ReturnCode = -1 301 | DiagArea. = '' 302 | address hwthttp "hwthinit ", 303 | "ReturnCode ", 304 | "HandleType ", 305 | "HandleOut ", 306 | "DiagArea." 307 | RexxRC = RC 308 | if HTTP_isError(RexxRC,ReturnCode) then 309 | do 310 | call HTTP_surfaceDiag 'hwthinit', RexxRC, ReturnCode, DiagArea. 311 | return fatalError( '** hwthinit failure **' ) 312 | end 313 | if HandleType == HWTH_HANDLETYPE_CONNECTION then 314 | ConnectionHandle = HandleOut 315 | else 316 | RequestHandle = HandleOut 317 | return 0 /* end Function */ 318 | 319 | 320 | /****************************************************/ 321 | /* Function: HTTP_setupConnection */ 322 | /* */ 323 | /* Sets the necessary connection options, via the */ 324 | /* HWTHSET toolkit api. The global variable */ 325 | /* ConnectionHandle orients the api as to the scope */ 326 | /* of the option(s). */ 327 | /* */ 328 | /* Returns: 0 if successful, -1 if not */ 329 | /****************************************************/ 330 | HTTP_setupConnection: 331 | if VERBOSE then 332 | do 333 | /*****************************************************************/ 334 | /* Set the HWT_OPT_VERBOSE option, if appropriate. */ 335 | /* This option is handy when developing an application (but may */ 336 | /* be undesirable once development is complete). Inner workings */ 337 | /* of the toolkit are traced by messages written to standard */ 338 | /* output, or optionally redirected to file (by use of the */ 339 | /* HWTH_OPT_VERBOSE_OUTPUT option). */ 340 | /*****************************************************************/ 341 | say '**** Set HWTH_OPT_VERBOSE for connection ****' 342 | ReturnCode = -1 343 | DiagArea. = '' 344 | address hwthttp "hwthset ", 345 | "ReturnCode ", 346 | "ConnectionHandle ", 347 | "HWTH_OPT_VERBOSE ", 348 | "HWTH_VERBOSE_ON ", 349 | "DiagArea." 350 | RexxRC = RC 351 | if HTTP_isError(RexxRC,ReturnCode) then 352 | do 353 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 354 | return fatalError( '** hwthset (HWTH_OPT_VERBOSE) failure **' ) 355 | end /* endif hwthset failure */ 356 | 357 | Call TurnOnVerboseOutput 358 | 359 | end /* endif script invocation requested (-V) VERBOSE */ 360 | 361 | /****************************************************************************/ 362 | /* Set URI for connection to z/OSMF on the SHARE z/OS LPAR */ 363 | /****************************************************************************/ 364 | if VERBOSE then 365 | say '****** Set HWTH_OPT_URI for connection ******' 366 | ConnectionUri = 'https://mvs1.centers.ihost.com' 367 | ReturnCode = -1 368 | DiagArea. = '' 369 | address hwthttp "hwthset ", 370 | "ReturnCode ", 371 | "ConnectionHandle ", 372 | "HWTH_OPT_URI ", 373 | "ConnectionUri ", 374 | "DiagArea." 375 | RexxRC = RC 376 | if HTTP_isError(RexxRC,ReturnCode) then 377 | do 378 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 379 | return fatalError( '** hwthset (HWTH_OPT_URI) failure **' ) 380 | end /* endif hwthset failure */ 381 | /***********************************************************************/ 382 | /* Set HWTH_OPT_COOKIETYPE */ 383 | /* Enable the cookie engine for this connection. Any "eligible" */ 384 | /* stored cookies will be resent to the host on subsequent */ 385 | /* interactions automatically. */ 386 | /***********************************************************************/ 387 | if VERBOSE then 388 | say '****** Set HWTH_OPT_COOKIETYPE for session cookies ******' 389 | ReturnCode = -1 390 | DiagArea. = '' 391 | address hwthttp "hwthset ", 392 | "ReturnCode ", 393 | "ConnectionHandle ", 394 | "HWTH_OPT_COOKIETYPE ", 395 | "HWTH_COOKIETYPE_SESSION ", 396 | "DiagArea." 397 | RexxRC = RC 398 | if HTTP_isError(RexxRC,ReturnCode) then 399 | do 400 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 401 | return fatalError( '** hwthset (HWTH_OPT_COOKIETYPE) failure **' ) 402 | end /* endif hwthset failure */ 403 | if VERBOSE then 404 | say 'Connection setup successful' 405 | return 0 /* end subroutine */ 406 | 407 | 408 | /*************************************************************/ 409 | /* Function: HTTP_connect */ 410 | /* */ 411 | /* Connect to the configured domain (host) via the HWTHCONN */ 412 | /* toolkit api. */ 413 | /* */ 414 | /* Returns: 0 if successful, -1 if not */ 415 | /*************************************************************/ 416 | HTTP_connect: 417 | if VERBOSE then 418 | say 'Issue Connect' 419 | /**********************************/ 420 | /* Call the HWTHCONN toolkit api */ 421 | /**********************************/ 422 | ReturnCode = -1 423 | DiagArea. = '' 424 | address hwthttp "hwthconn ", 425 | "ReturnCode ", 426 | "ConnectionHandle ", 427 | "DiagArea." 428 | RexxRC = RC 429 | if HTTP_isError(RexxRC,ReturnCode) then 430 | do 431 | call HTTP_surfaceDiag 'hwthconn', RexxRC, ReturnCode, DiagArea. 432 | return fatalError( '** hwthconn failure **' ) 433 | end 434 | if VERBOSE then 435 | say 'Connect (hwthconn) successful' 436 | return 0 /* end function */ 437 | 438 | 439 | /************************************************************/ 440 | /* Function: HTTP_setupSubmitReq */ 441 | /* */ 442 | /* Sets the necessary request options. The global variable */ 443 | /* RequestHandle orients the api as to the scope of the */ 444 | /* option(s). */ 445 | /* */ 446 | /* Returns: 0 if successful, -1 if not */ 447 | /************************************************************/ 448 | HTTP_setupSubmitReq: 449 | if VERBOSE then 450 | say '** Set HWTH_OPT_REQUESTMETHOD for request **' 451 | /**************************************************************/ 452 | /* Set HTTP Request method. */ 453 | /* A ??? request method is used to modify a resource on server*/ 454 | /**************************************************************/ 455 | ReturnCode = -1 456 | DiagArea. = '' 457 | address hwthttp "hwthset ", 458 | "ReturnCode ", 459 | "RequestHandle ", 460 | "HWTH_OPT_REQUESTMETHOD ", 461 | "HWTH_HTTP_REQUEST_PUT ", 462 | "DiagArea." 463 | RexxRC = RC 464 | if HTTP_isError(RexxRC,ReturnCode) then 465 | do 466 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 467 | return fatalError 'hwthset (HWTH_OPT_REQUESTMETHOD) failure **' ) 468 | end /* endif hwthset failure */ 469 | /*****************************************************************/ 470 | /* Set the request URI */ 471 | /* Set the URN URI that identifies a resource by name that is */ 472 | /* the target of our request. */ 473 | /*****************************************************************/ 474 | if VERBOSE then 475 | say'****** Set HWTH_OPT_URI for request ******' 476 | requestPath = '/zosmf/restjobs/jobs' 477 | ReturnCode = -1 478 | DiagArea. = '' 479 | address hwthttp "hwthset ", 480 | "ReturnCode ", 481 | "RequestHandle ", 482 | "HWTH_OPT_URI ", 483 | "requestPath ", 484 | "DiagArea." 485 | RexxRC = RC 486 | if HTTP_isError(RexxRC,ReturnCode) then 487 | do 488 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 489 | return fatalError( '** hwthset (HWTH_OPT_URI) failure **' ) 490 | end /* endif hwthset failure */ 491 | /*****************************************************************/ 492 | /* Authenticating to z/OSMF */ 493 | /* Single sign-on uses HTTP basic authentication to fulfill the */ 494 | /* request. Set the authentication level to basic and */ 495 | /* specify userid and password */ 496 | /*****************************************************************/ 497 | userid = xxxxxxx 498 | password = yyyyyyy 499 | ReturnCode = setHTTPAuth(RequestHandle, userid, password, VERBOSE) 500 | 501 | if ReturnCode = 0 then 502 | do 503 | /* Set the request body for the job to submit */ 504 | RequestBody = '{"file":"//''example.dataset(HLTHCHK)''"}' 505 | ReturnCode = setRequestBodyOptions(RequestHandle,VERBOSE) 506 | 507 | call setRequestBody 508 | 509 | if ReturnCode = 0 then 510 | /* Set the response headers and response body REXX variables */ 511 | call setResponseHdrandBodyLocation 512 | end 513 | /*************************************************************/ 514 | /* Set any request header(s) we may have. This depends upon */ 515 | /* the Http request (often we might not have any). */ 516 | /*************************************************************/ 517 | call HTTP_setRequestHeaders 518 | if RESULT <> 0 then 519 | return fatalError( '** Unable to set Request Headers **' ) 520 | 521 | if VERBOSE then 522 | say 'Request setup successful' 523 | return 0 /* end function */ 524 | 525 | /************************************************************/ 526 | /* checkJobStatus */ 527 | /* */ 528 | /* Iterates up to 5 times to check if the job has completed */ 529 | /* */ 530 | /* Returns: job condition code */ 531 | /************************************************************/ 532 | checkJobStatus: 533 | 534 | /* Set necessary request options before making job status req */ 535 | call HTTP_setupJobStatusReq 536 | if RESULT <> 0 then 537 | cleanup('CONREQ','** Job status request failed to be setup **') 538 | 539 | do i = 1 to 99 while jobStatusObtained = false 540 | ResponseBody = '' 541 | 542 | /* Wait 0.25 seconds before retrying status request */ 543 | call delay 544 | 545 | /* Make the z/OSMF job status request */ 546 | call HTTP_request 547 | if RESULT <> 0 then 548 | cleanup('CONREQ','** Job status request failed **') 549 | 550 | /* Analyze the job status output */ 551 | if VERBOSE then 552 | say "response body: "||ResponseBody 553 | 554 | call parseZOSMFJobOutput 555 | if (jobStatusObtained = true) & (condcodeValue > 0) then 556 | cleanup('CONREQ','** Healthcheck job did not complete successfully **') 557 | end /* do while */ 558 | return 0 559 | 560 | /************************************************************/ 561 | /* Function: HTTP_setupJobStatusReq */ 562 | /* */ 563 | /* Sets the necessary request options. The global variable */ 564 | /* RequestHandle orients the api as to the scope of the */ 565 | /* option(s). */ 566 | /* */ 567 | /* Returns: 0 if successful, -1 if not */ 568 | /************************************************************/ 569 | HTTP_setupJobStatusReq: 570 | 571 | if VERBOSE then 572 | say '** Set HWTH_OPT_REQUESTMETHOD for request **' 573 | 574 | /* Set HTTP Request method. */ 575 | /* A GET request method is used to retrieve a resource on server */ 576 | ReturnCode = -1 577 | DiagArea. = '' 578 | address hwthttp "hwthset ", 579 | "ReturnCode ", 580 | "RequestHandle ", 581 | "HWTH_OPT_REQUESTMETHOD ", 582 | "HWTH_HTTP_REQUEST_GET ", 583 | "DiagArea." 584 | RexxRC = RC 585 | if HTTP_isError(RexxRC,ReturnCode) then 586 | do 587 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 588 | return fatalError('hwthset (HWTH_OPT_REQUESTMETHOD) failure **' ) 589 | end /* endif hwthset failure */ 590 | 591 | /* Set the request URI */ 592 | if VERBOSE then 593 | say'****** Set HWTH_OPT_URI for request ******' 594 | requestPath = '/zosmf/restjobs/jobs/'||jobname||'/'jobid 595 | 596 | ReturnCode = -1 597 | DiagArea. = '' 598 | address hwthttp "hwthset ", 599 | "ReturnCode ", 600 | "RequestHandle ", 601 | "HWTH_OPT_URI ", 602 | "requestPath ", 603 | "DiagArea." 604 | RexxRC = RC 605 | if HTTP_isError(RexxRC,ReturnCode) then 606 | do 607 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 608 | return fatalError( '** hwthset (HWTH_OPT_URI) failure **' ) 609 | end /* endif hwthset failure */ 610 | /*****************************************************************/ 611 | /* Authenticating to z/OSMF */ 612 | /* We are already signed-on by previous requests so there is no */ 613 | /* need to specify security credentials here. Reset to off */ 614 | /*****************************************************************/ 615 | call resetHTTPAuth 616 | 617 | /*************************************************************/ 618 | /* Stem variable already set for receiving response headers */ 619 | /* Already set to convert ASCII response to EBCDIC */ 620 | /* Variable already set to receive response body */ 621 | /*************************************************************/ 622 | requestBody = '' 623 | 624 | call HTTP_setRequestHeaders 625 | if RESULT <> 0 then 626 | return fatalError( '** Unable to set Request Headers **' ) 627 | 628 | if VERBOSE then 629 | say 'Job status request setup successful' 630 | 631 | return 0 /* end function */ 632 | 633 | /************************************************************/ 634 | /* Function: HTTP_setupDataSetRetrieveReq */ 635 | /* */ 636 | /* Sets the necessary request options. The global variable */ 637 | /* RequestHandle orients the api as to the scope of the */ 638 | /* option(s). */ 639 | /* */ 640 | /* Returns: 0 if successful, -1 if not */ 641 | /************************************************************/ 642 | HTTP_setupDataSetRetrieveReq: 643 | if VERBOSE then 644 | say '** Set HWTH_OPT_REQUESTMETHOD for request **' 645 | /* Set HTTP Request method. */ 646 | /* A GET request method is used to retrieve a resource on server */ 647 | ReturnCode = -1 648 | DiagArea. = '' 649 | address hwthttp "hwthset ", 650 | "ReturnCode ", 651 | "RequestHandle ", 652 | "HWTH_OPT_REQUESTMETHOD ", 653 | "HWTH_HTTP_REQUEST_GET ", 654 | "DiagArea." 655 | RexxRC = RC 656 | if HTTP_isError(RexxRC,ReturnCode) then 657 | do 658 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 659 | return fatalError 'hwthset (HWTH_OPT_REQUESTMETHOD) failure **' ) 660 | end /* endif hwthset failure */ 661 | /*****************************************************************/ 662 | /* Set the request URI */ 663 | /* Set the URN URI that identifies a resource by name that is */ 664 | /* the target of our request. */ 665 | /*****************************************************************/ 666 | if VERBOSE then 667 | say'****** Set HWTH_OPT_URI for request ******' 668 | requestPath = '/zosmf/restfiles/ds/'||userid||'.HC.SYSOUT' 669 | 670 | ReturnCode = -1 671 | DiagArea. = '' 672 | address hwthttp "hwthset ", 673 | "ReturnCode ", 674 | "RequestHandle ", 675 | "HWTH_OPT_URI ", 676 | "requestPath ", 677 | "DiagArea." 678 | RexxRC = RC 679 | if HTTP_isError(RexxRC,ReturnCode) then 680 | do 681 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 682 | return fatalError( '** hwthset (HWTH_OPT_URI) failure **' ) 683 | end /* endif hwthset failure */ 684 | /*****************************************************************/ 685 | /* Authenticating to z/OSMF */ 686 | /* We are already signed-on by previous requests so there is no */ 687 | /* need to specify security credentials here. Reset to off */ 688 | /*****************************************************************/ 689 | call resetHTTPAuth 690 | 691 | /*************************************************************/ 692 | /* Stem variable already set for receiving response headers */ 693 | /* Already set to convert ASCII response to EBCDIC */ 694 | /* Variable already set to receive response body */ 695 | /*************************************************************/ 696 | requestBody = '' 697 | 698 | call HTTP_setRequestHeaders_nonJSON 699 | if RESULT <> 0 then 700 | return fatalError( '** Unable to set Request Headers **' ) 701 | 702 | if VERBOSE then 703 | say 'Data Set retrieve request setup successful' 704 | return 0 /* end function */ 705 | 706 | 707 | /************************************************************/ 708 | /* Function: HTTP_setupEmailRequest */ 709 | /* */ 710 | /* Sets the necessary request options. The global variable */ 711 | /* RequestHandle orients the api as to the scope of the */ 712 | /* option(s). */ 713 | /* */ 714 | /* Returns: 0 if successful, -1 if not */ 715 | /************************************************************/ 716 | HTTP_setupEmailRequest: 717 | if VERBOSE then 718 | say '** Set HWTH_OPT_REQUESTMETHOD for request **' 719 | /**************************************************************/ 720 | /* Set HTTP Request method. */ 721 | /* A PUT request method is used to modify a resource on server*/ 722 | /**************************************************************/ 723 | ReturnCode = -1 724 | DiagArea. = '' 725 | address hwthttp "hwthset ", 726 | "ReturnCode ", 727 | "RequestHandle ", 728 | "HWTH_OPT_REQUESTMETHOD ", 729 | "HWTH_HTTP_REQUEST_POST ", 730 | "DiagArea." 731 | RexxRC = RC 732 | if HTTP_isError(RexxRC,ReturnCode) then 733 | do 734 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 735 | return fatalError 'hwthset (HWTH_OPT_REQUESTMETHOD) failure **' ) 736 | end /* endif hwthset failure */ 737 | /*****************************************************************/ 738 | /* Set the request URI */ 739 | /* Set the URN URI that identifies a resource by name that is */ 740 | /* the target of our request. */ 741 | /*****************************************************************/ 742 | if VERBOSE then 743 | say'****** Set HWTH_OPT_URI for request ******' 744 | requestPath = '/zosmf/notifications/new' 745 | ReturnCode = -1 746 | DiagArea. = '' 747 | address hwthttp "hwthset ", 748 | "ReturnCode ", 749 | "RequestHandle ", 750 | "HWTH_OPT_URI ", 751 | "requestPath ", 752 | "DiagArea." 753 | RexxRC = RC 754 | if HTTP_isError(RexxRC,ReturnCode) then 755 | do 756 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 757 | return fatalError( '** hwthset (HWTH_OPT_URI) failure **' ) 758 | end /* endif hwthset failure */ 759 | /*****************************************************************/ 760 | /* Authenticating to z/OSMF */ 761 | /* We are already signed-on by previous requests so there is no */ 762 | /* need to specify security credentials here. Reset to off */ 763 | /*****************************************************************/ 764 | call resetHTTPAuth 765 | 766 | /*********************************************************/ 767 | /* Set the variable for sending the request body */ 768 | /*********************************************************/ 769 | RB= '{"subject":"HealthCheck Exception!",' 770 | RB=RB||'"content":"'||responseBody||'",' 771 | RB=RB||'"assignees":"xxxxxxx@yy.zzzzzzz",' 772 | RB=RB||'"sendTo":"mail"}' 773 | RequestBody = RB 774 | call setRequestBody 775 | /*************************************************************/ 776 | /* Stem variable already set for receiving response headers */ 777 | /* Already set to convert ASCII response to EBCDIC */ 778 | /* Variable already set to receive response body */ 779 | /* Can reuse all the request headers sent earlier */ 780 | /*************************************************************/ 781 | if VERBOSE then 782 | say 'Email Request setup successful' 783 | return 0 /* end function */ 784 | 785 | 786 | /****************************************************************/ 787 | /* Function: HTTP_disconnect */ 788 | /* */ 789 | /* Disconnect from the configured domain (host) via the */ 790 | /* HWTHDISC toolkit api. */ 791 | /* */ 792 | /* Returns: 0 if successful, -1 if not */ 793 | /****************************************************************/ 794 | HTTP_disconnect: 795 | if VERBOSE then 796 | say 'Disconnect' 797 | /***********************************/ 798 | /* Call the HWTHDISC toolkit api. */ 799 | /***********************************/ 800 | ReturnCode = -1 801 | DiagArea. = '' 802 | address hwthttp "hwthdisc ", 803 | "ReturnCode ", 804 | "ConnectionHandle ", 805 | "DiagArea." 806 | RexxRC = RC 807 | if HTTP_isError(RexxRC,ReturnCode) then 808 | do 809 | call HTTP_surfaceDiag 'hwthdisc', RexxRC, ReturnCode, DiagArea. 810 | return fatalError( '** hwthdisc failure **' ) 811 | end /* endif hwthdisc failure */ 812 | if VERBOSE then 813 | say 'Disconnect (hwthdisc) succeeded' 814 | return 0 /* end function */ 815 | 816 | 817 | /****************************************************************/ 818 | /* Function: HTTP_terminate */ 819 | /* */ 820 | /* Release the designated Connection or Request handle via the */ 821 | /* HWTHTERM toolkit api. */ 822 | /* */ 823 | /* Returns: */ 824 | /* 0 if successful, -1 if not */ 825 | /****************************************************************/ 826 | HTTP_terminate: 827 | 828 | handleIn = arg(1) 829 | forceOption = arg(2) 830 | if VERBOSE then 831 | say 'Terminate' 832 | /***********************************/ 833 | /* Call the HWTHTERM toolkit api. */ 834 | /***********************************/ 835 | ReturnCode = -1 836 | DiagArea. = '' 837 | address hwthttp "hwthterm ", 838 | "ReturnCode ", 839 | "handleIn ", 840 | "forceOption ", 841 | "DiagArea." 842 | RexxRC = RC 843 | if HTTP_isError(RexxRC,ReturnCode) then 844 | do 845 | call HTTP_surfaceDiag 'hwthterm', RexxRC, ReturnCode, DiagArea. 846 | return fatalError( '** hwthterm failure **' ) 847 | end /* endif hwthterm failure */ 848 | if VERBOSE then 849 | say 'Terminate (hwthterm) succeeded' 850 | return 0 /* end function */ 851 | 852 | 853 | /****************************************************************/ 854 | /* Function: HTTP_request */ 855 | /* */ 856 | /* Make the configured Http request via the HWTHRQST toolkit */ 857 | /* api. */ 858 | /* */ 859 | /* Returns: 0 if successful, -1 if not */ 860 | /****************************************************************/ 861 | HTTP_request: 862 | if VERBOSE then 863 | say 'Making Http Request' 864 | ReturnCode = -1 865 | DiagArea. = '' 866 | /***********************************/ 867 | /* Call the HWTHRQST toolkit api. */ 868 | /***********************************/ 869 | address hwthttp "hwthrqst ", 870 | "ReturnCode ", 871 | "ConnectionHandle ", 872 | "RequestHandle ", 873 | "HttpStatusCode ", 874 | "HttpReasonCode ", 875 | "DiagArea." 876 | RexxRC = RC 877 | if HTTP_isError(RexxRC,ReturnCode) then 878 | do 879 | call HTTP_surfaceDiag 'hwthrqst', RexxRC, ReturnCode, DiagArea. 880 | return fatalError( '** hwthrqst failure **' ) 881 | end /* endif hwthrqst failure */ 882 | /****************************************************************/ 883 | /* The ReturnCode indicates merely whether the request was made */ 884 | /* (and response received) without error. The origin server's */ 885 | /* response, of course, is another matter. The HttpStatusCode */ 886 | /* and HttpReasonCode record how the server responded. Any */ 887 | /* header(s) and/or body included in that response are to be */ 888 | /* found in the variables which we established earlier. */ 889 | /****************************************************************/ 890 | StatCode = strip(HttpStatusCode,'L',0) 891 | ResponseReasonCode = strip(HttpReasonCode) 892 | if VERBOSE then 893 | do 894 | say 'Request completed' 895 | say 'HTTP Status Code: '||StatCode 896 | say 'HTTP Response Reason Code: '||ResponseReasonCode 897 | end 898 | 899 | return 0 /* end function */ 900 | 901 | /*************************************************************/ 902 | /* Function: HTTP_setRequestHeaders */ 903 | /* */ 904 | /* Add appropriate Request Headers, by first building an */ 905 | /* "SList", and then setting the HWTH_OPT_HTTPHEADERS */ 906 | /* option of the Request with that list. */ 907 | /* */ 908 | /* Returns: 0 if successful, -1 if not */ 909 | /*************************************************************/ 910 | HTTP_setRequestHeaders: 911 | SList = '' 912 | acceptJsonHeader = 'Accept:application/json' 913 | acceptLanguageHeader = 'Accept-Language: en-US' 914 | contentTypeHeader = 'Content-Type: application/json' 915 | zOSMFHeader = 'X-CSRF-ZOSMF-HEADER: yes' 916 | /**********************************************************************/ 917 | /* Create a brand new SList and specify the first header to be an */ 918 | /* "Accept" header that requests that the server return any response */ 919 | /* body text in JSON format. */ 920 | /**********************************************************************/ 921 | ReturnCode = -1 922 | DiagArea. = '' 923 | if VERBOSE then 924 | say 'Create new SList' 925 | address hwthttp "hwthslst ", 926 | "ReturnCode ", 927 | "RequestHandle ", 928 | "HWTH_SLST_NEW ", 929 | "SList ", 930 | "acceptJsonHeader ", 931 | "DiagArea." 932 | RexxRC = RC 933 | if HTTP_isError(RexxRC,ReturnCode) then 934 | do 935 | call HTTP_surfaceDiag 'hwthslst', RexxRC, ReturnCode, DiagArea. 936 | return fatalError( '** hwthslst (HWTH_SLST_NEW) failure **' ) 937 | end /* endif hwthslst failure */ 938 | /***********************************************************/ 939 | /* Append the Accept-Language request header to the SList */ 940 | /* to infer to the server the regional settings which are */ 941 | /* preferred by this application. */ 942 | /***********************************************************/ 943 | if VERBOSE then 944 | say 'Append to SList' 945 | address hwthttp "hwthslst ", 946 | "ReturnCode ", 947 | "RequestHandle ", 948 | "HWTH_SLST_APPEND ", 949 | "SList ", 950 | "acceptLanguageHeader ", 951 | "DiagArea." 952 | RexxRC = RC 953 | if HTTP_isError(RexxRC,ReturnCode) then 954 | do 955 | call HTTP_surfaceDiag 'hwthslst', RexxRC, ReturnCode, DiagArea. 956 | return fatalError( '** hwthslst (HWTH_SLST_APPEND) failure **' ) 957 | end /* endif hwthslst failure */ 958 | /***********************************************************/ 959 | /* Append the Content-Type request header to the SList */ 960 | /* to specify that the data sent on the put request is in */ 961 | /* JSON format */ 962 | /***********************************************************/ 963 | if VERBOSE then 964 | say 'Append to SList' 965 | address hwthttp "hwthslst ", 966 | "ReturnCode ", 967 | "RequestHandle ", 968 | "HWTH_SLST_APPEND ", 969 | "SList ", 970 | "contentTypeHeader ", 971 | "DiagArea." 972 | RexxRC = RC 973 | if HTTP_isError(RexxRC,ReturnCode) then 974 | do 975 | call HTTP_surfaceDiag 'hwthslst', RexxRC, ReturnCode, DiagArea. 976 | return fatalError( '** hwthslst (HWTH_SLST_APPEND) failure **' ) 977 | end /* endif hwthslst failure */ 978 | /**************************************************************/ 979 | /* Append the X-CSRF-ZOSMF-HEADER request header to the SList */ 980 | /* to specify that this is a cross-site request */ 981 | /**************************************************************/ 982 | if VERBOSE then 983 | say 'Append to SList' 984 | address hwthttp "hwthslst ", 985 | "ReturnCode ", 986 | "RequestHandle ", 987 | "HWTH_SLST_APPEND ", 988 | "SList ", 989 | "zOSMFHeader ", 990 | "DiagArea." 991 | RexxRC = RC 992 | if HTTP_isError(RexxRC,ReturnCode) then 993 | do 994 | call HTTP_surfaceDiag 'hwthslst', RexxRC, ReturnCode, DiagArea. 995 | return fatalError( '** hwthslst (HWTH_SLST_APPEND) failure **' ) 996 | end /* endif hwthslst failure */ 997 | /************************************/ 998 | /* Set the request headers with the */ 999 | /* just-produced list */ 1000 | /************************************/ 1001 | if VERBOSE then 1002 | say 'Set HWTH_OPT_HTTPHEADERS for request' 1003 | ReturnCode = -1 1004 | DiagArea. = '' 1005 | address hwthttp "hwthset ", 1006 | "ReturnCode ", 1007 | "RequestHandle ", 1008 | "HWTH_OPT_HTTPHEADERS ", 1009 | "SList ", 1010 | "DiagArea." 1011 | RexxRC = RC 1012 | if HTTP_isError(RexxRC,ReturnCode) then 1013 | do 1014 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 1015 | return fatalError( '** hwthset (HWTH_OPT_HTTPHEADERS) failure **' ) 1016 | end /* endif hwthset failure */ 1017 | return 0 /* end function */ 1018 | 1019 | 1020 | /*************************************************************/ 1021 | /* Function: HTTP_setRequestHeaders_nonJSON */ 1022 | /* */ 1023 | /* Add appropriate Request Headers, by first building an */ 1024 | /* "SList", and then setting the HWTH_OPT_HTTPHEADERS */ 1025 | /* option of the Request with that list. */ 1026 | /* */ 1027 | /* Returns: 0 if successful, -1 if not */ 1028 | /*************************************************************/ 1029 | HTTP_setRequestHeaders_nonJSON: 1030 | SList = '' 1031 | acceptLanguageHeader = 'Accept-Language: en-US' 1032 | zOSMFHeader = 'X-CSRF-ZOSMF-HEADER: yes' 1033 | /**********************************************************************/ 1034 | /* Create a brand new SList and specify the first header to be an */ 1035 | /* "AcceptLanguageHeader" */ 1036 | /**********************************************************************/ 1037 | ReturnCode = -1 1038 | DiagArea. = '' 1039 | if VERBOSE then 1040 | say 'Create new SList' 1041 | address hwthttp "hwthslst ", 1042 | "ReturnCode ", 1043 | "RequestHandle ", 1044 | "HWTH_SLST_NEW ", 1045 | "SList ", 1046 | "acceptLanguageHeader ", 1047 | "DiagArea." 1048 | RexxRC = RC 1049 | if HTTP_isError(RexxRC,ReturnCode) then 1050 | do 1051 | call HTTP_surfaceDiag 'hwthslst', RexxRC, ReturnCode, DiagArea. 1052 | return fatalError( '** hwthslst (HWTH_SLST_NEW) failure **' ) 1053 | end /* endif hwthslst failure */ 1054 | /**************************************************************/ 1055 | /* Append the X-CSRF-ZOSMF-HEADER request header to the SList */ 1056 | /* to specify that this is a cross-site request */ 1057 | /**************************************************************/ 1058 | if VERBOSE then 1059 | say 'Append to SList' 1060 | address hwthttp "hwthslst ", 1061 | "ReturnCode ", 1062 | "RequestHandle ", 1063 | "HWTH_SLST_APPEND ", 1064 | "SList ", 1065 | "zOSMFHeader ", 1066 | "DiagArea." 1067 | RexxRC = RC 1068 | if HTTP_isError(RexxRC,ReturnCode) then 1069 | do 1070 | call HTTP_surfaceDiag 'hwthslst', RexxRC, ReturnCode, DiagArea. 1071 | return fatalError( '** hwthslst (HWTH_SLST_APPEND) failure **' ) 1072 | end /* endif hwthslst failure */ 1073 | /************************************/ 1074 | /* Set the request headers with the */ 1075 | /* just-produced list */ 1076 | /************************************/ 1077 | if VERBOSE then 1078 | say 'Set HWTH_OPT_HTTPHEADERS for request' 1079 | ReturnCode = -1 1080 | DiagArea. = '' 1081 | address hwthttp "hwthset ", 1082 | "ReturnCode ", 1083 | "RequestHandle ", 1084 | "HWTH_OPT_HTTPHEADERS ", 1085 | "SList ", 1086 | "DiagArea." 1087 | RexxRC = RC 1088 | if HTTP_isError(RexxRC,ReturnCode) then 1089 | do 1090 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 1091 | return fatalError( '** hwthset (HWTH_OPT_HTTPHEADERS) failure **' ) 1092 | end /* endif hwthset failure */ 1093 | return 0 /* end function */ 1094 | 1095 | 1096 | /*************************************************************/ 1097 | /* Function: HTTP_isError */ 1098 | /* */ 1099 | /* Check the input processing codes. Note that if the input */ 1100 | /* RexxRC is nonzero, then the toolkit return code is moot */ 1101 | /* (the toolkit function was likely not even invoked). If */ 1102 | /* the toolkit return code is relevant, check it against the */ 1103 | /* set of { HWTH_xx } return codes for evidence of error. */ 1104 | /* This set is ordered: HWTH_OK < HWTH_WARNING < ... */ 1105 | /* with remaining codes indicating error, so we may check */ 1106 | /* via single inequality. */ 1107 | /* */ 1108 | /* Returns: 1 if any toolkit error is indicated, 0 */ 1109 | /* otherwise. */ 1110 | /*************************************************************/ 1111 | HTTP_isError: 1112 | RexxRC = arg(1) 1113 | if RexxRC <> 0 then 1114 | return 1 1115 | ToolkitRC = strip(arg(2),'L',0) 1116 | if ToolkitRC == '' then 1117 | return 0 1118 | if ToolkitRC <= HWTH_WARNING then 1119 | return 0 1120 | return 1 /* end function */ 1121 | 1122 | 1123 | /*************************************************************/ 1124 | /* Function: HTTP_isWarning */ 1125 | /* */ 1126 | /* Check the input processing codes. Note that if the input */ 1127 | /* RexxRC is nonzero, then the toolkit return code is moot */ 1128 | /* (the toolkit function was likely not even invoked). If */ 1129 | /* the toolkit return code is relevant, check it against the */ 1130 | /* specific HWTH_WARNING return code. */ 1131 | /* */ 1132 | /* Returns: 1 if toolkit rc HWTH_WARNING is indicated, 0 */ 1133 | /* otherwise. */ 1134 | /*************************************************************/ 1135 | HTTP_isWarning: 1136 | RexxRC = arg(1) 1137 | if RexxRC <> 0 then 1138 | return 0 1139 | ToolkitRC = strip(arg(2),'L',0) 1140 | if ToolkitRC == '' then 1141 | return 0 1142 | if ToolkitRC <> HWTH_WARNING then 1143 | return 0 1144 | return 1 /* end function */ 1145 | 1146 | 1147 | /***********************************************/ 1148 | /* Procedure: HTTP_surfaceDiag() */ 1149 | /* */ 1150 | /* Surface input error information. Note that */ 1151 | /* when the RexxRC is nonzero, the ToolkitRC */ 1152 | /* and DiagArea content are moot and are */ 1153 | /* suppressed (so as to not mislead). */ 1154 | /***********************************************/ 1155 | HTTP_surfaceDiag: procedure expose DiagArea. 1156 | say 1157 | say '*ERROR* ('||arg(1)||') at time: '||Time() 1158 | say 'Rexx RC: '||arg(2)', Toolkit ReturnCode: '||arg(3) 1159 | say 'DiagArea.Service: '||DiagArea.HWTH_service 1160 | say 'DiagArea.ReasonCode: '||DiagArea.HWTH_reasonCode 1161 | say 'DiagArea.ReasonDesc: '||DiagArea.HWTH_reasonDesc 1162 | say 1163 | return /* end procedure */ 1164 | 1165 | 1166 | /***********************************************/ 1167 | /* Function: fatalError */ 1168 | /* */ 1169 | /* Surfaces the input message, and returns */ 1170 | /* a canonical failure code. */ 1171 | /* */ 1172 | /* Returns: -1 to indicate fatal script error. */ 1173 | /***********************************************/ 1174 | fatalError: 1175 | errorMsg = arg(1) 1176 | say errorMsg 1177 | return -1 /* end function */ 1178 | 1179 | /***********************************************/ 1180 | /* Function: TurnOnVerboseOutput */ 1181 | /* */ 1182 | /* Sets the zFS UNIX file where the toolkit */ 1183 | /* trace will be written */ 1184 | /***********************************************/ 1185 | TurnOnVerboseOutput: 1186 | /**********************************************************/ 1187 | say '**** Set HWTH_OPT_VERBOSE_OUTPUT for connection ****' 1188 | traceDD = 'MYTRACE' 1189 | DiagArea. = '' 1190 | address hwthttp "hwthset ", 1191 | "ReturnCode ", 1192 | "ConnectionHandle ", 1193 | "HWTH_OPT_VERBOSE_OUTPUT ", 1194 | "traceDD ", 1195 | "DiagArea." 1196 | RexxRC = RC 1197 | if HTTP_isError(RexxRC,ReturnCode) then 1198 | do 1199 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 1200 | return fatalError( '** hwthset (HWTH_OPT_VERBOSE) failure **' ) 1201 | end /* endif hwthset failure */ 1202 | return 1203 | 1204 | /***********************************************************/ 1205 | /* Procedure: setHTTPAuth */ 1206 | /* */ 1207 | /* Turn Basic Auth On */ 1208 | /***********************************************************/ 1209 | setHTTPAuth: procedure expose DiagArea. 1210 | 1211 | parse arg RequestHandle, userName, Password, VERBOSE 1212 | 1213 | if VERBOSE then 1214 | say'****** Set HWTH_OPT_HTTPAUTH for request ******' 1215 | ReturnCode = -1 1216 | DiagArea. = '' 1217 | address hwthttp "hwthset ", 1218 | "ReturnCode ", 1219 | "RequestHandle ", 1220 | "HWTH_OPT_HTTPAUTH ", 1221 | "HWTH_HTTPAUTH_BASIC ", 1222 | "DiagArea." 1223 | RexxRC = RC 1224 | if HTTP_isError(RexxRC,ReturnCode) then 1225 | do 1226 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 1227 | return fatalError( '** hwthset (HWTH_OPT_HTTPAUTH) failure **' ) 1228 | end /* endif hwthset failure */ 1229 | 1230 | /* Set username */ 1231 | if VERBOSE then 1232 | say'****** Set HWTH_OPT_USERNAME for request ******' 1233 | ReturnCode = -1 1234 | DiagArea. = '' 1235 | address hwthttp "hwthset ", 1236 | "ReturnCode ", 1237 | "RequestHandle ", 1238 | "HWTH_OPT_USERNAME ", 1239 | "userName ", 1240 | "DiagArea." 1241 | RexxRC = RC 1242 | if HTTP_isError(RexxRC,ReturnCode) then 1243 | do 1244 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 1245 | return fatalError( '** hwthset (HWTH_OPT_USERNAME) failure **' ) 1246 | end /* endif hwthset failure */ 1247 | 1248 | /* Set password */ 1249 | if VERBOSE then 1250 | say'****** Set HWTH_OPT_PASSWORD for request ******' 1251 | ReturnCode = -1 1252 | DiagArea. = '' 1253 | address hwthttp "hwthset ", 1254 | "ReturnCode ", 1255 | "RequestHandle ", 1256 | "HWTH_OPT_PASSWORD ", 1257 | "password ", 1258 | "DiagArea." 1259 | RexxRC = RC 1260 | if HTTP_isError(RexxRC,ReturnCode) then 1261 | do 1262 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 1263 | return fatalError( '** hwthset (HWTH_OPT_PASSWORD) failure **' ) 1264 | end /* endif hwthset failure */ 1265 | 1266 | return 0 1267 | 1268 | /***********************************************************/ 1269 | /* Procedure: resetHTTPAuth */ 1270 | /* */ 1271 | /* Turn Basic Auth Off */ 1272 | /***********************************************************/ 1273 | resetHTTPAuth: 1274 | if VERBOSE then 1275 | say'****** Turning Basic Auth Off ******' 1276 | ReturnCode = -1 1277 | DiagArea. = '' 1278 | address hwthttp "hwthset ", 1279 | "ReturnCode ", 1280 | "RequestHandle ", 1281 | "HWTH_OPT_HTTPAUTH ", 1282 | "HWTH_HTTPAUTH_NONE ", 1283 | "DiagArea." 1284 | RexxRC = RC 1285 | if HTTP_isError(RexxRC,ReturnCode) then 1286 | do 1287 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 1288 | return fatalError( '** hwthset (HWTH_OPT_HTTPAUTH) failure **' ) 1289 | end /* endif hwthset failure */ 1290 | 1291 | /* Set username */ 1292 | if VERBOSE then 1293 | say'****** Resetting USERNAME for request ******' 1294 | ReturnCode = -1 1295 | userName = '' 1296 | DiagArea. = '' 1297 | address hwthttp "hwthset ", 1298 | "ReturnCode ", 1299 | "RequestHandle ", 1300 | "HWTH_OPT_USERNAME ", 1301 | "userName ", 1302 | "DiagArea." 1303 | RexxRC = RC 1304 | if HTTP_isError(RexxRC,ReturnCode) then 1305 | do 1306 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 1307 | return fatalError( '** hwthset (HWTH_OPT_USERNAME) failure **' ) 1308 | end /* endif hwthset failure */ 1309 | 1310 | /* Set password */ 1311 | if VERBOSE then 1312 | say'****** Resetting PASSWORD for request ******' 1313 | ReturnCode = -1 1314 | password = '' 1315 | DiagArea. = '' 1316 | address hwthttp "hwthset ", 1317 | "ReturnCode ", 1318 | "RequestHandle ", 1319 | "HWTH_OPT_PASSWORD ", 1320 | "password ", 1321 | "DiagArea." 1322 | RexxRC = RC 1323 | if HTTP_isError(RexxRC,ReturnCode) then 1324 | do 1325 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 1326 | return fatalError( '** hwthset (HWTH_OPT_PASSWORD) failure **' ) 1327 | end /* endif hwthset failure */ 1328 | 1329 | return 1330 | 1331 | /***********************************************************/ 1332 | /* Procedure: setRequestBodyOptions */ 1333 | /* */ 1334 | /* Set the necessary attributes to set the request body */ 1335 | /***********************************************************/ 1336 | setRequestBodyOptions: procedure expose DiagArea. 1337 | 1338 | parse arg RequestHandle,VERBOSE 1339 | /*******************************************************************/ 1340 | /* Have the toolkit convert the request body from EBCDIC to ASCII */ 1341 | /*******************************************************************/ 1342 | ReturnCode = -1 1343 | DiagArea. = '' 1344 | if VERBOSE then 1345 | say 'Set HWTH_OPT_TRANSLATE_REQBODY for request' 1346 | address hwthttp "hwthset ", 1347 | "ReturnCode ", 1348 | "RequestHandle ", 1349 | "HWTH_OPT_TRANSLATE_REQBODY ", 1350 | "HWTH_XLATE_REQBODY_E2A ", 1351 | "DiagArea." 1352 | RexxRC = RC 1353 | if HTTP_isError(RexxRC,ReturnCode) then 1354 | do 1355 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 1356 | return fatalError( '** hwthset (HWTH_OPT_TRANSLATE_REQBODY) failure **' ) 1357 | end /* endif hwthset failure */ 1358 | 1359 | return 0 1360 | /***********************************************************/ 1361 | /* Procedure: setResponseHdrandBodyLocation */ 1362 | /* */ 1363 | /* Set the necessary attributes to set the request body */ 1364 | /***********************************************************/ 1365 | setResponseHdrandBodyLocation: 1366 | /*********************************************************/ 1367 | /* Set the stem variable for receiving response headers */ 1368 | /*********************************************************/ 1369 | ReturnCode = -1 1370 | DiagArea. = '' 1371 | if VERBOSE then 1372 | say 'Set HWTH_OPT_RESPONSEHDR_USERDATA for request' 1373 | address hwthttp "hwthset ", 1374 | "ReturnCode ", 1375 | "RequestHandle ", 1376 | "HWTH_OPT_RESPONSEHDR_USERDATA ", 1377 | "ResponseHeaders. ", 1378 | "DiagArea." 1379 | RexxRC = RC 1380 | if HTTP_isError(RexxRC,ReturnCode) then 1381 | do 1382 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 1383 | return fatalError( '** hwthset (HWTH_OPT_RESPONSEHDR_USERDATA) failure **') 1384 | end /* endif hwthset failure */ 1385 | /*******************************************************************/ 1386 | /* Have the toolkit convert the response body from ASCII to EBCDIC */ 1387 | /* (so that we may pass it to our parser in a form that the latter */ 1388 | /* will understand) */ 1389 | /*******************************************************************/ 1390 | ReturnCode = -1 1391 | DiagArea. = '' 1392 | if VERBOSE then 1393 | say 'Set HWTH_OPT_TRANSLATE_RESPBODY for request' 1394 | address hwthttp "hwthset ", 1395 | "ReturnCode ", 1396 | "RequestHandle ", 1397 | "HWTH_OPT_TRANSLATE_RESPBODY ", 1398 | "HWTH_XLATE_RESPBODY_A2E ", 1399 | "DiagArea." 1400 | RexxRC = RC 1401 | if HTTP_isError(RexxRC,ReturnCode) then 1402 | do 1403 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 1404 | return fatalError( '** hwthset (HWTH_OPT_TRANSLATE_RESPBODY) failure **' ) 1405 | end /* endif hwthset failure */ 1406 | /*************************************************/ 1407 | /* Set the variable for receiving response body */ 1408 | /**************************************************/ 1409 | ReturnCode = -1 1410 | DiagArea. = '' 1411 | if VERBOSE then 1412 | say 'Set HWTH_OPT_RESPONSEBODY_USERDATA for request' 1413 | address hwthttp "hwthset ", 1414 | "ReturnCode ", 1415 | "RequestHandle ", 1416 | "HWTH_OPT_RESPONSEBODY_USERDATA ", 1417 | "ResponseBody ", 1418 | "DiagArea." 1419 | RexxRC = RC 1420 | if HTTP_isError(RexxRC,ReturnCode) then 1421 | do 1422 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 1423 | return fatalError( '** hwthset (HWTH_OPT_RESPONSEBODY_USERDATA) failure **') 1424 | end /* endif hwthset failure */ 1425 | 1426 | return 0 1427 | /***********************************************************/ 1428 | /* Procedure: setRequestBody */ 1429 | /* */ 1430 | /* Set the request body */ 1431 | /***********************************************************/ 1432 | setRequestBody: 1433 | /*********************************************************/ 1434 | /* Set the variable for sending the request body */ 1435 | /* Need to set RequestBody in mainline due to scoping */ 1436 | /* issues */ 1437 | /*********************************************************/ 1438 | if VERBOSE then 1439 | Say "RequestBody:"||RequestBody 1440 | ReturnCode = -1 1441 | DiagArea. = '' 1442 | if VERBOSE then 1443 | say 'Set HWTH_OPT_REQUESTBODY for request' 1444 | address hwthttp "hwthset ", 1445 | "ReturnCode ", 1446 | "RequestHandle ", 1447 | "HWTH_OPT_REQUESTBODY ", 1448 | "RequestBody ", 1449 | "DiagArea." 1450 | RexxRC = RC 1451 | if HTTP_isError(RexxRC,ReturnCode) then 1452 | do 1453 | call HTTP_surfaceDiag 'hwthset', RexxRC, ReturnCode, DiagArea. 1454 | return fatalError( '** hwthset (HWTH_OPT_REQUESTBODY) failure **') 1455 | end /* endif hwthset failure */ 1456 | return 1457 | /***********************************************************/ 1458 | /* parseZOSMFJobOutput */ 1459 | /* */ 1460 | /* Verify that the job completed successfully. */ 1461 | /***********************************************************/ 1462 | parseZOSMFJobOutput: 1463 | parserHandle = '' 1464 | 1465 | /* Obtain a JSON Parser instance. */ 1466 | call JSON_initParser 1467 | if RESULT <> 0 then 1468 | return fatalError( '** Pre-processing error (parser init failure) **' ) 1469 | 1470 | /* Parse the JSON string */ 1471 | call JSON_Parse responseBody 1472 | if RESULT <> 0 then 1473 | return fatalError( '** Parsing error (parser failure) **' ) 1474 | 1475 | /* Parse the job results. */ 1476 | 1477 | /* Get jobname and jobid so that we can check status later if 1478 | necessary */ 1479 | call searchForJobInfo 1480 | if RESULT <> 0 then 1481 | return fatalError( '** Unable to extract jobname or jobid **' ) 1482 | 1483 | jobStatusObtained = false 1484 | condCodeValue = searchForCondCode() 1485 | /* No need to obtain the status of the job. We already have it */ 1486 | if (condCodeValue <> 9999) & (condCodeValue <> -1) then 1487 | do 1488 | jobStatusObtained = true 1489 | if VERBOSE then 1490 | say 'Job condition code:'||condCodeValue 1491 | end 1492 | 1493 | call JSON_termParser 1494 | return 0 /* end function */ 1495 | 1496 | /***********************************************************/ 1497 | /* searchForJobInfo */ 1498 | /* */ 1499 | /* Extract the jobname and jobid info */ 1500 | /***********************************************************/ 1501 | searchForJobInfo: 1502 | 1503 | /* Extract jobname */ 1504 | jobname='' 1505 | jobname = JSON_findValue( 0, "jobname", HWTJ_STRING_TYPE ) 1506 | 1507 | if jobname = '' then 1508 | return fatalError('** Unable to find returned jobname **') 1509 | 1510 | if VERBOSE then 1511 | say 'Extracted jobname: '||jobname 1512 | 1513 | /* Extract jobid */ 1514 | jobid='' 1515 | jobid = JSON_findValue( 0, "jobid", HWTJ_STRING_TYPE ) 1516 | 1517 | if jobid = '' then 1518 | return fatalError('** Unable to find returned jobname **') 1519 | 1520 | if VERBOSE then 1521 | say 'Extracted jobid: '||jobid 1522 | 1523 | return 0 /* end function */ 1524 | 1525 | /***********************************************************/ 1526 | /* searchForCondCode */ 1527 | /* */ 1528 | /* Verify that the job completed successfully. */ 1529 | /***********************************************************/ 1530 | searchForCondCode: 1531 | 1532 | /* The return-code name contains the outcome of the job */ 1533 | condCode='' 1534 | condCode = JSON_findValue( 0, "retcode", HWTJ_STRING_TYPE ) 1535 | 1536 | /* If the condition code is the value "null", then it means that the 1537 | job has not completed yet. Attempt to pull status of job again */ 1538 | if condCode = '' then 1539 | do 1540 | say '** Job execute not completed **' 1541 | return 9999 1542 | end 1543 | 1544 | /* Get the value for condcode */ 1545 | connCodeValue = -1 1546 | parse value condCode with 'CC 'condCodeValue 1547 | if condCodeValue = -1 then 1548 | return fatalError('** Unable to find Condition Code for Job **') 1549 | 1550 | if condCodeValue = '0000' then 1551 | condCodeValue = 0 1552 | else 1553 | strip(condCodeValue,'L',0) 1554 | 1555 | return condCodeValue /* end function */ 1556 | 1557 | /***********************************************************/ 1558 | /* parseZOSMFDataSetOutput */ 1559 | /* */ 1560 | /* Find the status of the z/OS healthcheck */ 1561 | /***********************************************************/ 1562 | parseZOSMFDataSetOutput: 1563 | parse value responseBody with 'STATUS: 'statusValue'-'restOfData 1564 | 1565 | if VERBOSE then 1566 | say "HealthCheck Status Value :"||statusValue 1567 | 1568 | if statusValue <> 'EXCEPTION' then 1569 | return -1 1570 | 1571 | return 0 /* end function */ 1572 | 1573 | /***********************************************************/ 1574 | /* parseZOSMFDataSetOutput */ 1575 | /* */ 1576 | /* Find the status of the z/OS healthcheck */ 1577 | /***********************************************************/ 1578 | parseZOSMFDataSetOutput: 1579 | parse value responseBody with 'STATUS: 'statusValue'-'restOfData 1580 | 1581 | if VERBOSE then 1582 | say "HealthCheck Status Value :"||statusValue 1583 | 1584 | if statusValue <> 'EXCEPTION' then 1585 | return -1 1586 | 1587 | return 0 /* end function */ 1588 | 1589 | /*****************************************************/ 1590 | /* JSON-related functions */ 1591 | /* */ 1592 | /* These { JSON_xxx } functions are located together */ 1593 | /* for ease of reference and are used to demonstrate */ 1594 | /* how this portion of the Web Enablement Toolkit */ 1595 | /* can be used in conjunction with the Http-related */ 1596 | /* toolkit functions. */ 1597 | /*****************************************************/ 1598 | 1599 | /**********************************************************/ 1600 | /* Function: JSON_initParser */ 1601 | /* */ 1602 | /* Initializes the global parserHandle variable via */ 1603 | /* call to toolkit service HWTJINIT. */ 1604 | /* */ 1605 | /* Returns: 0 if successful, -1 if unsuccessful */ 1606 | /**********************************************************/ 1607 | JSON_initParser: 1608 | if VERBOSE then 1609 | say 'Initializing Json Parser' 1610 | /***********************************/ 1611 | /* Call the HWTJINIT toolkit api. */ 1612 | /***********************************/ 1613 | ReturnCode = -1 1614 | DiagArea. = '' 1615 | address hwtjson "hwtjinit ", 1616 | "ReturnCode ", 1617 | "handleOut ", 1618 | "DiagArea." 1619 | RexxRC = RC 1620 | if JSON_isError(RexxRC,ReturnCode) then 1621 | do 1622 | call JSON_surfaceDiag 'hwtjinit', RexxRC, ReturnCode, DiagArea. 1623 | return fatalError( '** hwtjinit failure **' ) 1624 | end /* endif hwtjinit failure */ 1625 | parserHandle = handleOut 1626 | if VERBOSE then 1627 | say 'Json Parser init (hwtjinit) succeeded' 1628 | return 0 /* end function */ 1629 | 1630 | 1631 | /**********************************************************************/ 1632 | /* Function: JSON_parse */ 1633 | /* */ 1634 | /* Parses the input text body (which should be syntactically correct */ 1635 | /* JSON text) via call to toolkit service HWTJPARS. */ 1636 | /* */ 1637 | /* Returns: 0 if successful, -1 if unsuccessful */ 1638 | /**********************************************************************/ 1639 | JSON_parse: 1640 | jsonTextBody = arg(1) 1641 | if VERBOSE then 1642 | say 'Invoke Json Parser' 1643 | /**************************************************/ 1644 | /* Call the HWTJPARS toolkit api. */ 1645 | /* Parse scans the input text body and creates an */ 1646 | /* internal representation of the JSON data, */ 1647 | /* suitable for search and create operations. */ 1648 | /**************************************************/ 1649 | ReturnCode = -1 1650 | DiagArea. = '' 1651 | address hwtjson "hwtjpars ", 1652 | "ReturnCode ", 1653 | "parserHandle ", 1654 | "jsonTextBody ", 1655 | "DiagArea." 1656 | RexxRC = RC 1657 | if JSON_isError(RexxRC,ReturnCode) then 1658 | do 1659 | call JSON_surfaceDiag 'hwtjpars', RexxRC, ReturnCode, DiagArea. 1660 | return fatalError( '** hwtjpars failure **' ) 1661 | end /* endif hwtjpars failure */ 1662 | if VERBOSE then 1663 | say 'JSON data parsed successfully' 1664 | return 0 /* end function */ 1665 | 1666 | 1667 | 1668 | 1669 | /******************************************************************/ 1670 | /* Function: JSON_findValue */ 1671 | /* */ 1672 | /* Searches the appropriate portion of the parsed JSON data (that */ 1673 | /* designated by the objectToSearch argument) for an entry whose */ 1674 | /* name matches the designated searchName argument. Returns a */ 1675 | /* value or handle, depending on the expectedType. */ 1676 | /* */ 1677 | /* Returns: value or handle as described above, or a null result */ 1678 | /* if no suitable value or handle is found. */ 1679 | /******************************************************************/ 1680 | JSON_findValue: 1681 | objectToSearch = arg(1) 1682 | searchName = arg(2) 1683 | expectedType = arg(3) 1684 | /*********************************************************/ 1685 | /* Trying to find a value for a null entry is perhaps a */ 1686 | /* bit nonsensical, but for completeness we include the */ 1687 | /* possibility. We make an arbitrary choice on what to */ 1688 | /* return, and do this first, to avoid wasted processing */ 1689 | /*********************************************************/ 1690 | if expectedType == HWTJ_NULL_TYPE then 1691 | return '(null)' 1692 | if VERBOSE then 1693 | say 'Invoke Json Search' 1694 | /********************************************************/ 1695 | /* Search the specified object for the specified name. */ 1696 | /* The value 0 is specified (for the "startingHandle") */ 1697 | /* to indicate that the search should start at the */ 1698 | /* beginning of the designated object. */ 1699 | /********************************************************/ 1700 | ReturnCode = -1 1701 | DiagArea. = '' 1702 | address hwtjson "hwtjsrch ", 1703 | "ReturnCode ", 1704 | "parserHandle ", 1705 | "HWTJ_SEARCHTYPE_OBJECT ", 1706 | "searchName ", 1707 | "objectToSearch ", 1708 | "0 ", 1709 | "searchResult ", 1710 | "DiagArea." 1711 | RexxRC = RC 1712 | /************************************************************/ 1713 | /* Differentiate a not found condition from an error, and */ 1714 | /* tolerate the former. Note the order dependency here, */ 1715 | /* at least as the called routines are currently written. */ 1716 | /************************************************************/ 1717 | if JSON_isNotFound(RexxRC,ReturnCode) then 1718 | return '(not found)' 1719 | if JSON_isError(RexxRC,ReturnCode) then 1720 | do 1721 | call JSON_surfaceDiag 'hwtjsrch', RexxRC, ReturnCode, DiagArea. 1722 | say '** hwtjsrch failure **' 1723 | return '' 1724 | end /* endif hwtjsrch failed */ 1725 | /******************************************/ 1726 | /* Verify the type of the search result */ 1727 | /******************************************/ 1728 | resultType = JSON_getType( searchResult ) 1729 | if resultType <> expectedType then 1730 | do 1731 | say '** Type mismatch ('||resultType||','||expectedType||') **' 1732 | return '' 1733 | end /* endif unexpected type */ 1734 | /******************************************************/ 1735 | /* Return the located object or array, as appropriate */ 1736 | /******************************************************/ 1737 | if expectedType == HWTJ_OBJECT_TYPE | expectedType == HWTJ_ARRAY_TYPE then 1738 | do 1739 | return searchResult 1740 | end /* endif object or array type */ 1741 | /*******************************************************/ 1742 | /* Return the located string or number, as appropriate */ 1743 | /*******************************************************/ 1744 | if expectedType == HWTJ_STRING_TYPE | expectedType == HWTJ_NUMBER_TYPE then 1745 | do 1746 | if VERBOSE then 1747 | say 'Invoke Json Get Value' 1748 | ReturnCode = -1 1749 | DiagArea. = '' 1750 | address hwtjson "hwtjgval ", 1751 | "ReturnCode ", 1752 | "parserHandle ", 1753 | "searchResult ", 1754 | "result ", 1755 | "DiagArea." 1756 | RexxRC = RC 1757 | if JSON_isError(RexxRC,ReturnCode) then 1758 | do 1759 | call JSON_surfaceDiag 'hwtjgval', RexxRC, ReturnCode, DiagArea. 1760 | say '** hwtjgval failure **' 1761 | return '' 1762 | end /* endif hwtjgval failed */ 1763 | return result 1764 | end /* endif string or number type */ 1765 | /****************************************************/ 1766 | /* Return the located boolean value, as appropriate */ 1767 | /****************************************************/ 1768 | if expectedType == HWTJ_BOOLEAN_TYPE then 1769 | do 1770 | if VERBOSE then 1771 | say 'Invoke Json Get Boolean Value' 1772 | ReturnCode = -1 1773 | DiagArea. = '' 1774 | address hwtjson "hwtjgbov ", 1775 | "ReturnCode ", 1776 | "parserHandle ", 1777 | "searchResult ", 1778 | "result ", 1779 | "DiagArea." 1780 | RexxRC = RC 1781 | if JSON_isError(RexxRC,ReturnCode) then 1782 | do 1783 | call JSON_surfaceDiag 'hwtjgbov', RexxRC, ReturnCode, DiagArea. 1784 | say '** hwtjgbov failure **' 1785 | return '' 1786 | end /* endif hwtjgbov failed */ 1787 | return result 1788 | end /* endif boolean type */ 1789 | if VERBOSE then 1790 | say '** No return value found **' 1791 | return '' /* end function */ 1792 | 1793 | 1794 | /***********************************************************/ 1795 | /* Function: JSON_getType */ 1796 | /* */ 1797 | /* Determine the Json type of the designated search result */ 1798 | /* via the HWTJGJST toolkit api. */ 1799 | /* */ 1800 | /* Returns: Non-negative integral number indicating type */ 1801 | /* if successful, -1 if not. */ 1802 | /***********************************************************/ 1803 | JSON_getType: 1804 | searchResult = arg(1) 1805 | if VERBOSE then 1806 | say 'Invoke Json Get Type' 1807 | /*********************************/ 1808 | /* Call the HWTHGJST toolkit api */ 1809 | /*********************************/ 1810 | ReturnCode = -1 1811 | DiagArea. = '' 1812 | address hwtjson "hwtjgjst ", 1813 | "ReturnCode ", 1814 | "parserHandle ", 1815 | "searchResult ", 1816 | "resultTypeName ", 1817 | "DiagArea." 1818 | RexxRC = RC 1819 | if JSON_isError(RexxRC,ReturnCode) then 1820 | do 1821 | call JSON_surfaceDiag 'hwtjgjst', RexxRC, ReturnCode, DiagArea. 1822 | return fatalError( '** hwtjgjst failure **' ) 1823 | end /* endif hwtjgjst failure */ 1824 | else 1825 | do 1826 | /******************************************************/ 1827 | /* Convert the returned type name into its equivalent */ 1828 | /* constant, and return that more convenient value. */ 1829 | /* Note that the interpret instruction might more */ 1830 | /* typically be used here, but the goal here is to */ 1831 | /* familiarize the reader with these types. */ 1832 | /******************************************************/ 1833 | type = strip(resultTypeName) 1834 | if type == 'HWTJ_STRING_TYPE' then 1835 | return HWTJ_STRING_TYPE 1836 | if type == 'HWTJ_NUMBER_TYPE' then 1837 | return HWTJ_NUMBER_TYPE 1838 | if type == 'HWTJ_BOOLEAN_TYPE' then 1839 | return HWTJ_BOOLEAN_TYPE 1840 | if type == 'HWTJ_ARRAY_TYPE' then 1841 | return HWTJ_ARRAY_TYPE 1842 | if type == 'HWTJ_OBJECT_TYPE' then 1843 | return HWTJ_OBJECT_TYPE 1844 | if type == 'HWTJ_NULL_TYPE' then 1845 | return HWTJ_NULL_TYPE 1846 | end 1847 | /***********************************************/ 1848 | /* This return should not occur, in practice. */ 1849 | /***********************************************/ 1850 | return fatalError( 'Unsupported Type ('||type||') from hwtjgjst' ) 1851 | 1852 | /***********************************************************/ 1853 | /* Function: JSON_getNumberElem */ 1854 | /* */ 1855 | /* Determine the number of elments contained in the input */ 1856 | /* JSON handle via the HWTJGNUE service */ 1857 | /* */ 1858 | /* Returns: Non-negative number indicating number */ 1859 | /* if successful, -1 if not. */ 1860 | /***********************************************************/ 1861 | JSON_getNumberElem: 1862 | inputHandle = arg(1) 1863 | 1864 | /***********************************/ 1865 | /* Call the HWTJGNUE toolkit api. */ 1866 | /***********************************/ 1867 | ReturnCode = -1 1868 | DiagArea. = '' 1869 | objDim = 0 1870 | address hwtjson "hwtjgnue ", 1871 | "ReturnCode ", 1872 | "parserHandle ", 1873 | "inputHandle ", 1874 | "dimOut ", 1875 | "DiagArea." 1876 | RexxRC = RC 1877 | if JSON_isError(RexxRC,ReturnCode) then 1878 | do 1879 | call JSON_surfaceDiag 'hwtjgnue', RexxRC, ReturnCode, DiagArea. 1880 | return fatalError( '** hwtjgnue failure **' ) 1881 | end /* endif hwtjgnue failure */ 1882 | objDim = strip(dimOut,'L',0) 1883 | if objDim == '' then 1884 | return 0 1885 | return objDim /* end function */ 1886 | 1887 | /*************************************************/ 1888 | /* Function: JSON_getArrayEntry */ 1889 | /* */ 1890 | /* Return a handle to the designated entry of */ 1891 | /* the array designated by the input handle, */ 1892 | /* obtained via the HWTJGAEN toolkit api. */ 1893 | /* */ 1894 | /* Returns: Output handle from toolkit api if */ 1895 | /* successful, empty result if not. */ 1896 | /************************************************/ 1897 | JSON_getArrayEntry: 1898 | arrayHandle = arg(1) 1899 | whichEntry = arg(2) 1900 | result = '' 1901 | if VERBOSE then 1902 | say 'Getting array entry' 1903 | /***********************************/ 1904 | /* Call the HWTJGAEN toolkit api. */ 1905 | /***********************************/ 1906 | ReturnCode = -1 1907 | DiagArea. = '' 1908 | address hwtjson "hwtjgaen ", 1909 | "ReturnCode ", 1910 | "parserHandle ", 1911 | "arrayHandle ", 1912 | "whichEntry ", 1913 | "handleOut ", 1914 | "DiagArea." 1915 | RexxRC = RC 1916 | if JSON_isError(RexxRC,ReturnCode) then 1917 | do 1918 | call JSON_surfaceDiag 'hwtjgaen', RexxRC, ReturnCode, DiagArea. 1919 | say '** hwtjgaen failure **' 1920 | end /* endif hwtjgaen failure */ 1921 | else 1922 | result = handleOut 1923 | return result /* end function */ 1924 | 1925 | /**********************************************************/ 1926 | /* Function: JSON_termParser */ 1927 | /* */ 1928 | /* Cleans up parser resources and invalidates the parser */ 1929 | /* instance handle, via call to the HWTJTERM toolkit api. */ 1930 | /* Note that as the REXX environment is single-threaded, */ 1931 | /* no consideration of any "busy" outcome from the api is */ 1932 | /* done (as it would be in other language environments). */ 1933 | /* */ 1934 | /* Returns: 0 if successful, -1 if not. */ 1935 | /**********************************************************/ 1936 | JSON_termParser: 1937 | if VERBOSE then 1938 | say 'Terminate Json Parser' 1939 | /**********************************/ 1940 | /* Call the HWTJTERM toolkit api */ 1941 | /**********************************/ 1942 | ReturnCode = -1 1943 | DiagArea. = '' 1944 | address hwtjson "hwtjterm ", 1945 | "ReturnCode ", 1946 | "parserHandle ", 1947 | "DiagArea." 1948 | RexxRC = RC 1949 | if JSON_isError(RexxRC,ReturnCode) then 1950 | do 1951 | call JSON_surfaceDiag 'hwtjterm', RexxRC, ReturnCode, DiagArea. 1952 | return fatalError( '** hwtjterm failure **' ) 1953 | end /* endif hwtjterm failure */ 1954 | if VERBOSE then 1955 | say 'Json Parser terminated' 1956 | return 0 /* end function */ 1957 | 1958 | 1959 | /*************************************************************/ 1960 | /* Function: JSON_isNotFound */ 1961 | /* */ 1962 | /* Check the input processing codes. Note that if the input */ 1963 | /* RexxRC is nonzero, then the toolkit return code is moot */ 1964 | /* (the toolkit function was likely not even invoked). If */ 1965 | /* the toolkit return code is relevant, check it against the */ 1966 | /* specific return code for a "not found" condition. */ 1967 | /* */ 1968 | /* Returns: 1 if a HWTJ_JSRCH_SRCHSTR_NOT_FOUND condition */ 1969 | /* is indicated, 0 otherwise. */ 1970 | /*************************************************************/ 1971 | JSON_isNotFound: 1972 | RexxRC = arg(1) 1973 | if RexxRC <> 0 then 1974 | return 0 1975 | ToolkitRC = strip(arg(2),'L',0) 1976 | if ToolkitRC == HWTJ_JSRCH_SRCHSTR_NOT_FOUND then 1977 | return 1 1978 | return 0 /* end function */ 1979 | 1980 | 1981 | /*************************************************************/ 1982 | /* Function: JSON_isError */ 1983 | /* */ 1984 | /* Check the input processing codes. Note that if the input */ 1985 | /* RexxRC is nonzero, then the toolkit return code is moot */ 1986 | /* (the toolkit function was likely not even invoked). If */ 1987 | /* the toolkit return code is relevant, check it against the */ 1988 | /* set of { HWTJ_xx } return codes for evidence of error. */ 1989 | /* This set is ordered: HWTJ_OK < HWTJ_WARNING < ... */ 1990 | /* with remaining codes indicating error, so we may check */ 1991 | /* via single inequality. */ 1992 | /* */ 1993 | /* Returns: 1 if any toolkit error is indicated, 0 */ 1994 | /* otherwise. */ 1995 | /*************************************************************/ 1996 | JSON_isError: 1997 | RexxRC = arg(1) 1998 | if RexxRC <> 0 then 1999 | return 1 2000 | ToolkitRC = strip(arg(2),'L',0) 2001 | if ToolkitRC == '' then 2002 | return 0 2003 | if ToolkitRC <= HWTJ_WARNING then 2004 | return 0 2005 | return 1 /* end function */ 2006 | 2007 | 2008 | /***********************************************/ 2009 | /* Procedure: JSON_surfaceDiag */ 2010 | /* */ 2011 | /* Surface input error information. Note that */ 2012 | /* when the RexxRC is nonzero, the ToolkitRC */ 2013 | /* and DiagArea content are moot and are */ 2014 | /* suppressed (so as to not mislead). */ 2015 | /* */ 2016 | /***********************************************/ 2017 | JSON_surfaceDiag: procedure expose DiagArea. 2018 | who = arg(1) 2019 | RexxRC = arg(2) 2020 | ToolkitRC = arg(3) 2021 | say 2022 | say '*ERROR* ('||who||') at time: '||Time() 2023 | say 'Rexx RC: '||RexxRC||', Toolkit ReturnCode: '||ToolkitRC 2024 | if RexxRC == 0 then 2025 | do 2026 | say 'DiagArea.ReasonCode: '||DiagArea.ReasonCode 2027 | say 'DiagArea.ReasonDesc: '||DiagArea.ReasonDesc 2028 | end 2029 | say 2030 | return /* end procedure */ 2031 | 2032 | /***********************************************/ 2033 | /* Procedure: JSON_decode */ 2034 | /* */ 2035 | /* Decode the encoded JSON for printing */ 2036 | /***********************************************/ 2037 | JSON_decode: procedure expose DiagArea. decodedJSON 2038 | parse arg encodedJSON 2039 | 2040 | ReturnCode = -1 2041 | RequestType = HWTJ_DECODE 2042 | decodedJSON = '' 2043 | DiagArea. = '' 2044 | address hwtjson "hwtjesct ", 2045 | "ReturnCode ", 2046 | "RequestType ", 2047 | "encodedJSON ", 2048 | "decodedJSON ", 2049 | "DiagArea." 2050 | 2051 | return /* end procedure */ 2052 | /*******************************************************/ 2053 | /* Function: quoted */ 2054 | /*******************************************************/ 2055 | quoted: 2056 | stringIn = arg(1) 2057 | return "'"||stringIn||"'" 2058 | /*******************************************************/ 2059 | /* Function: delay */ 2060 | /*******************************************************/ 2061 | delay: 2062 | /* Rexx Delay */ 2063 | DelaySec = 0.250000 /* readable width */ 2064 | elapsed = TIME(R) 2065 | do until elapsed >= DelaySec 2066 | elapsed = TIME('E') 2067 | end 2068 | return 2069 | ## 2070 | //* Execute the REXX exec under a TSO environment 2071 | //RUN EXEC PGM=IKJEFT01,PARM='REXTSO' 2072 | //MYTRACE DD PATH='/sharelab/sharxxx/sharxxx.trace' 2073 | //SYSEXEC DD DSN=&SYSUID..TEMPREX,DISP=(SHR,PASS) 2074 | //SYSTSPRT DD SYSOUT=A,HOLD=YES 2075 | //SYSTSIN DD DUMMY 2076 | //* Delete the temp dataset when we are done 2077 | //DELETE EXEC PGM=IEFBR14 2078 | //SYSPRINT DD SYSOUT=* 2079 | //MYDSN DD DSN=&SYSUID..TEMPREX,DISP=(OLD,DELETE,DELETE) 2080 | // 2081 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # MAINTAINERS 2 | 3 | Galina Gorelik - gorelik@us.ibm.com 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zOS Client Web Enablement Toolkit 2 | 3 | This repository contains samples that take advantage of the z/OS Client Web Enablement Toolkit functionality to issue various REST API requests and parse the corresponding JSON content. 4 | 5 |

Samples

6 | 7 | [**Example-GeoServices**](https://github.com/IBM/zOS-Client-Web-Enablement-Toolkit/tree/main/Example-GeoServices) 8 | 9 | This sample demonstrates how to use the toolkit to obtain the distance between two cities using the Geo Services REST API. 10 | 11 | [**Example-zOSMF**](https://github.com/IBM/zOS-Client-Web-Enablement-Toolkit/tree/main/Example-zOSMF) 12 | 13 | This sample demonstrates how to use the z/OS client web enablement toolkit to issue z/OSMF REST Services. 14 | 15 | [**Example-Download**](https://github.com/IBM/zOS-Client-Web-Enablement-Toolkit/tree/main/Example-Download) 16 | 17 | This sample demonstrates how a native z/OS application can use toolkit to download content from a REST API endpoint. 18 | 19 | [**Example-Cobol-AirportService**](https://github.com/IBM/zOS-Client-Web-Enablement-Toolkit/tree/main/Example-Cobol-AirportService) 20 | 21 | This sample demonstrates how to use the toolkit in Cobol to obtain the information about an airport using the Airport Service REST API. 22 | 23 | [**Example-Slack**](Example-Slack/) 24 | 25 | This sample demonstrates how to use the z/OS Client Web Enablement Toolkit to post an update to a Slack channel. 26 | 27 | 28 | 29 |

Useful Links

30 | 31 | [z/OS client web enablement documentation](https://www.ibm.com/docs/en/zos/2.5.0?topic=languages-zos-client-web-enablement-toolkit) 32 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-modernist --------------------------------------------------------------------------------