Skip to main content

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

Popular posts from this blog

Integration Spaghetti™

  I’ve been using the term Integration Spaghetti™ for the past 9 years or so to describe what happens as systems connectivity increases and increases to the point of … unmanageability, indeterminate impact, or just generally a big mess.  A standard line of mine is “moving from spaghetti code to spaghetti connections is not an improvement”. (A standard “point to point connection mess” slide, by enterprise architect Jerry Foster from 2001.) In the past few days I’ve been meeting with a series of IT managers at a large customer and have come up with a revised definition for Integration Spaghetti™ : Integration Spaghetti™ is when the connectivity to/from an application is so complex that everyone is afraid of touching it.  An application with such spaghetti becomes nearly impossible to replace.  Estimates of change impact to the application are frequently wrong by orders of magnitude.  Interruption in the integration functioning are always a major disaster – both in terms of th

Solving Integration Chaos - Past Approaches

A U.S. Fortune 50's systems interconnect map for 1 division, "core systems only". Integration patterns began changing 15 years ago. Several early attempts were made to solve the increasing problem of the widening need for integration… Enterprise Java Beans (J2EE / EJB's) attempted to make independent callable codelets. Coupling was too tight, the technology too platform specific. Remote Method Invocation (Java / RMI) attempted to make anything independently callable, but again was too platform specific and a very tightly coupled protocol. Similarly on the Microsoft side, DCOM & COM+ attempted to make anything independently and remotely callable. However, as with RMI the approach was extremely platform and vendor specific, and very tightly coupled. MQ created a reliable independent messaging paradigm, but the cost and complexity of operation made it prohibitive for most projects and all but the largest of Enterprise IT shops which could devote a focused technology

From Spaghetti Code to Spaghetti Connections

Twenty five years ago my boss handed me the primary billing program and described a series of new features needed. The program was about 4 years old and had been worked on by 5 different programmers. It had an original design model, but between all the modifications, bug fixes, patches and quick new features thrown in, the original design pattern was impossible to discern. Any pattern was impossible to discern. It had become, to quote what’s titled the most common architecture pattern of today, ‘a big ball of mud’. After studying the program for several days, I informed my boss the program was untouchable. The effort to make anything more than a minor adjustment carried such a risk, as the impact could only be guessed at, that it was easier and less risky to rewrite it from scratch. If they had considered the future impact, they never would have let a key program degenerate that way. They would have invested the extra effort to maintain it’s design, document it property, and consider