Sep 20, 2012

Mainframe Integration–NATURAL Web Services




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

1 comments:

Mainframe Development said...

Excellent details about mainframe integration.

Blog Widget by LinkWithin

Search