Software AG’s Natural language and development environment for the mainframe (IBM z/OS and CICS) offered many nice improvements over COBOL for mainframe software development. Software AG developed a nice market niche in the mainframe development tools market, and millions of lines of code were developed and continue to run till today.
Like IBM’s version of COBOL, the vendor has struggled to extend Natural’s lifespan and the life of the code written in it. Initially Software AG created a communication bridging tool (called EntireX), allowing bi-direction communication between the Natural environment and Web Services or MQ or Java or .Net.
But like IBM came out with “native” web services as part of CICS 3 and Enterprise COBOL, Software AG has web service enabled Natural. And web service enablement includes the handling of (simplified) XML.
From the code sample I was able to find, it appears that Natural IS NOT handling the building of the HTTP headers, requiring them to be manually added to the top of the XML document – a rather odd lack, but use of direct web services significantly simplifies integration into the environment.
Here’s the code sample I found, which was used as a base to build some internal test services by a Natural programmer I know…
Description : This example demonstrates how to call a SOAP service from Natural, using the REQUEST DOCUMENT statement, it then parses the output with the PARSE XML statement and presents a formatted view of the response. The code is cross-platform, it works on both mainframe Natural as well as OpenSystems / LUW Natural alike. Service used: http://www.webservicex.net/globalweather.asmx Input asked for by the Natural program: City, Country Output: weather details for the selected City + Country, or an error response ============================================== * * more info about the services to call: www.webservicex.net. * * better cross platform solution * DEFINE DATA LOCAL 1 #REQUEST (A) DYNAMIC 1 #RESPONSE (B) DYNAMIC 1 #RC (I4) * 1 #PATH (A) DYNAMIC 1 #NAME (A) DYNAMIC 1 #VALUE (A) DYNAMIC * 1 HTTP-STATUS-TEXT (A60) * 1 #ACTIVE_NAME (A16) 1 #REPLY 2 CITYNAME (A50) 2 COUNTRYNAME (A50) 2 LOCATION (A50) 2 TIME (A50) 2 WIND (A50) 2 VISIBILITY (A50) 2 SKYCONDITIONS (A50) 2 TEMPERATURE (A50) 2 DEWPOINT (A50) 2 RELATIVEHUMIDITY (A50) 2 PRESSURE (A50) 2 STATUS (A50) * ---------------------------------------------------------- XML-COPY-OF 1 #XML-COPY-OF (A) DYNAMIC 1 #FIND-TAG (A) DYNAMIC 1 #TAG-FROM (A) DYNAMIC 1 #TAG-TO (A) DYNAMIC 1 #TAG-END (A) DYNAMIC 1 #AT_SIGN (A1) 1 #EXCLAMATION_MARK (A1) 1 #APOSTROPHE (A1) 1 #QUOTATION_MARK (A1) * ---------------------------------------------------------- XML-COPY-OF END-DEFINE * INPUT // 'CityName...:' CITYNAME / 'CountryName:' COUNTRYNAME // 'Example: Tokyo, Japan; Heidelberg, Germany' * COMPRESS '<?xml version="1.0" encoding="UTF-8" ?>' - '<SOAP-ENV:Envelope ' - 'xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" ' - 'xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" ' - 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' - 'xmlns:xsd="http://www.w3.org/2001/XMLSchema">' - '<SOAP-ENV:Body>' - '<m:GetWeather xmlns:m="http://www.webserviceX.NET">' - '<m:CityName>' CITYNAME '</m:CityName>' - '<m:CountryName>' COUNTRYNAME '</m:CountryName>' - '</m:GetWeather>' - '</SOAP-ENV:Body>' - '</SOAP-ENV:Envelope>' INTO #REQUEST LEAVING NO * * For more information about the service have a look at: * http://www.webservicex.net. * REQUEST DOCUMENT FROM 'http://www.webservicex.net/globalweather.asmx' WITH HEADER NAME 'Request-Method' VALUE 'POST' NAME 'Content-Type' VALUE 'text/xml; encoding=utf-8' NAME 'SOAPAction' VALUE 'http://www.webserviceX.NET/GetWeather' DATA ALL #REQUEST ENCODED CODEPAGE 'utf-8' RETURN HEADER NAME ' ' VALUE HTTP-STATUS-TEXT PAGE #RESPONSE RESPONSE #RC * IF #RC = 200 * * cut out the response #FIND-TAG := 'soap:Envelope/soap:Body/GetWeatherResponse/GetWeatherResult' PERFORM XML-COPY-OF * * parse the response PARSE XML #XML-COPY-OF INTO PATH #PATH VALUE #VALUE DECIDE ON FIRST VALUE OF #PATH VALUE 'CurrentWeather/Location/$' #REPLY.LOCATION := #VALUE VALUE 'CurrentWeather/Time/$' #REPLY.TIME := #VALUE VALUE 'CurrentWeather/Wind/$' #REPLY.WIND := #VALUE VALUE 'CurrentWeather/Visibility/$' #REPLY.VISIBILITY := #VALUE VALUE 'CurrentWeather/SkyConditions/$' #REPLY.SKYCONDITIONS := #VALUE VALUE 'CurrentWeather/Temperature/$' #REPLY.TEMPERATURE := #VALUE VALUE 'CurrentWeather/DewPointh/$' #REPLY.DEWPOINT := #VALUE VALUE 'CurrentWeather/RelativeHumidity/$' #REPLY.RELATIVEHUMIDITY := #VALUE VALUE 'CurrentWeather/Pressure/$' #REPLY.PRESSURE := #VALUE VALUE 'CurrentWeather/Status/$' #REPLY.STATUS := #VALUE NONE VALUE IGNORE END-DECIDE END-PARSE * WRITE #REPLY.CITYNAME '-' #REPLY.COUNTRYNAME // 'Location ............' #REPLY.LOCATION / 'Time ................' #REPLY.TIME / 'Wind ................' #REPLY.WIND / 'Visibility ..........' #REPLY.VISIBILITY / 'Sky Conditions ......' #REPLY.SKYCONDITIONS / 'Temperature .........' #REPLY.TEMPERATURE / 'Dew Point ...........' #REPLY.DEWPOINT / 'Relative Humidity ...' #REPLY.RELATIVEHUMIDITY / 'Pressure ............' #REPLY.PRESSURE / 'Status ..............' #REPLY.STATUS / * ELSE WRITE // 'HTTP-Status:' HTTP-STATUS-TEXT // END-IF * * copy the content of a xml element * works equal to a XSL copy-of * DEFINE SUBROUTINE XML-COPY-OF * for the mainframe IF H'41' EQ "A" THEN #AT_SIGN := '@' #EXCLAMATION_MARK := '!' #APOSTROPHE := H'27' #QUOTATION_MARK := H'22' ELSE #AT_SIGN := '?‚?§' #EXCLAMATION_MARK := UH'0021' #APOSTROPHE := H'7D' #QUOTATION_MARK := H'7F' END-IF * COMPRESS #FIND-TAG INTO #FIND-TAG LEAVING NO COMPRESS #FIND-TAG '/' INTO #TAG-FROM LEAVING NO COMPRESS #FIND-TAG '/'-H'FF' INTO #TAG-TO LEAVING NO COMPRESS #FIND-TAG '//' INTO #TAG-END LEAVING NO * RESET INITIAL #XML-COPY-OF * PARSE XML #RESPONSE INTO PATH #PATH NAME #NAME VALUE #VALUE DECIDE ON FIRST #PATH /*( end tag VALUE #TAG-END * * remove false xml header EXAMINE #XML-COPY-OF FOR PATTERN '<?xml*?>' DELETE * COMPRESS '<?xml version="1.0" ?>' #XML-COPY-OF INTO #XML-COPY-OF LEAVING NO * ESCAPE BOTTOM /*) /*( start tag VALUE #FIND-TAG IGNORE * /*( content VALUE #TAG-FROM : #TAG-TO * DECIDE ON FIRST *PARSE-TYPE VALUE "?" COMPRESS FULL #XML-COPY-OF '<?' #NAME ' ' #VALUE '?>' INTO #XML-COPY-OF LEAVING NO VALUE #EXCLAMATION_MARK /* Comment. IGNORE VALUE "C" /* CDATA section. IGNORE VALUE "T" /* Starting tag. COMPRESS FULL #XML-COPY-OF '<' #NAME INTO #XML-COPY-OF LEAVING NO VALUE #AT_SIGN /* Attribute (or ?‚?§ on mainframes). IF #VALUE = SCAN(#APOSTROPHE) /* APOSTROPHE or QUOTATION_MARK COMPRESS FULL #XML-COPY-OF ' ' #NAME '=' #QUOTATION_MARK #VALUE #QUOTATION_MARK INTO #XML-COPY-OF LEAVING NO ELSE COMPRESS FULL #XML-COPY-OF ' ' #NAME '=' #APOSTROPHE #VALUE #APOSTROPHE INTO #XML-COPY-OF LEAVING NO END-IF VALUE "/" /* Closing tag. COMPRESS FULL #XML-COPY-OF '</' #NAME '>' INTO #XML-COPY-OF LEAVING NO NONE VALUE /* $ Parsed data. COMPRESS FULL #XML-COPY-OF #VALUE INTO #XML-COPY-OF LEAVING NO END-DECIDE /*) /*( none NONE VALUE IGNORE /*) END-DECIDE END-PARSE END-SUBROUTINE END |