Simple XML Data Scrubbing

Worked on a project recently where the team had some XML data that they wanted to be 'scrubbed' before passing it along to other teams. Being that it was in XML, Groovy seemed like a perfect place to create a very fast and simple scrubbing program. To demonstrate, I created a simple XML file with some data that you know you don't want to pass around.

<Policy>
  <Party>
    <FirstName>Jason</FirstName>
    <LastName>Borne</LastName>
    <SSN>1234567890</SSN>
    <Address1>4994 Road</Address1>
    <State>MI</State>
  </Party>
  <Driver>
    <State>MI</State>
    <License>B20049595091</License>
  </Driver>
  <Risk>
    <Coverage type="A">20000</Coverage>
   </Risk>
</Policy>

Then I wrote a really simple script to show Groovy could easily update the important values. It pulls all the files from the 'files-to-filter' folder, filters them and writes the updated XML to the 'filtered-files' folder. Easy.


import groovy.util.slurpersupport.NodeChild
import groovy.xml.XmlUtil

def outputDirectory = new File('./filtered-files')
new File('./files-to-filter').eachFile { File input ->
  def output = new File(outputDirectory, input.name)
 
  def xml = new XmlSlurper().parseText(input.text)
  xml.'**'.each { NodeChild tag ->
    def names = generateNewName()
    def values = generateAdditionalNewValues()
  
    switch (tag.name()) {
      case 'FirstName':
        tag.replaceBody(names.'FirstName')
        break
      case 'LastName':
        tag.replaceBody(names.'LastName')
        break
      case 'SSN':
        tag.replaceBody(values.'SSN')
        break
      case 'Address1':
        tag.replaceBody(values.'Address1')
        break
      case 'State':
        if (tag.parent().name() == 'Driver') {
          tag.replaceBody(values.'State')
        }
        break
      case 'License':
        tag.replaceBody(values.'License')
        break
} output << XmlUtil.serialize(xml) } /** * @return generate a new name, maybe reading for dataset and using 'random' to pick one */ def generateNewName() { return ['FirstName': 'Johnny', 'LastName': 'Five'] } /** * @return newly generated values, could be accessing a database or something */ def generateAdditionalNewValues() { return ['SSN': '0987654321', 'Address1': '9848 SomewhereElse', 'State': 'IA',
          'License': 'I390398349834']
}

Converting A Variable To Fixed Length File

Going to start off the new year right with some JCL DSORT processing!

I want to process a mainframe file on a windows machine so I can use some Java tools (like the Copybook Slurper) to process the file contents. The file contains comp data  so it can't be downloaded as a text file. The binary data upsets the 'new line' characters and you get weirdly formatted data.

As a binary file, I can just read the number of bytes on a line, but with a variable length file, the number of bytes vary. So I have to convert to fixed length file first. Once converted, I can download to a windows machine in binary format and know I can always read the same number of bytes.

In the following example TST.CHNGFILE.TVAR is variable length, the longest record is 1575 bytes long. TST.CHNGFILE.TFIXED will be generated with the last 1571 bytes, since the first four bytes contain the record length.


//TESTJOB JOB TEST,'BOS',CLASS=L,REGION=0M,              
//         MSGCLASS=V,NOTIFY=&SYSUID                      
//****************************************************    
//XXDEL  EXEC PGM=IEFBR14                                 
//DD1    DD   UNIT=DISK,DSN=TST.CHNGFILE.TFIXED,        
//            SPACE=(TRK,(0)),DISP=(MOD,DELETE,DELETE)    
//****************************************************    
//* CONVERT FBA TO FB                                     
//****************************************************    
//SA03FRMT EXEC PGM=SORT                                  
//SYSOUT   DD SYSOUT=*                                    
//SYSPRINT DD SYSOUT=*                                    
//SORTIN   DD UNIT=DISK,DSN=TST.CHNGFILE.TVAR,DISP=SHR
//FBOUT    DD UNIT=DISK,DSN=TST.CHNGFILE.TFIXED,        
//            DCB=(RECFM=FB,LRECL=1571),                  
//            DISP=(,CATLG),SPACE=(TRK,(150,15),RLSE)     
//* VTOF (CONVERT FROM ONE RECORD FORMAT TO ANOTHER)      
//* BUILD(FROM,LENGTH,ETC....)                            
//* OUTREC CAN REPLACE BUILD                              
//SYSIN    DD *                                           
  OPTION COPY                                             
  SORT FIELDS=COPY                                        
  OUTFIL FNAMES=FBOUT,VTOF,BUILD=(5,1571)                 

Check out IBM's sorttrck document and search for VB to FB for more official documentation and options, such as the VFILL parameter.

React Debug Display Component

Been playing around with React recently in preparation for a new application. I wanted verification that object state was being manipulated when I hooked up onChange events to my form. I'd been using console logs, but I realized I should be able to easily show the data on the bottom of the form as I typed. So I created a generic React component which will display the contents of a JavaScript object in a table.

import React, { PropTypes } from 'react';

const  DebugObjectTable = ({debugObject}) => {
    return (
        <div>
            <table>
                <thead>
                    <tr>
                        <th>Prop</th>
                        <th style={{paddingLeft: '10px'}}>Value</th>
                    </tr>
                </thead>
                <tbody>
                    {Object.keys(debugObject).map(key =>
                        <tr key={key}>
                            <td>{key}</td>
                            <td style={{paddingLeft: '10px'}}>{debugObject[key]}</td>
                        </tr>
                    )}
                </tbody>
            </table>
        </div>
    );
};
DebugObjectTable.propTypes = {
    debugObject: PropTypes.object.isRequired 
};
export default DebugObjectTable;

The useful of the component is probably short lived, I basically just use it while I'm first setting up the form, but the instant feedback is pretty convenient, especially for noob like me.

import React from 'react';
import InsuredForm from './InsuredForm';
import DebugObjectTable from '../common/DebugObjectTable';

class InsuredController extends React.Component {
    constructor() {
        super();
        this.state = {
            insured: {}
        };
        this.onChange = this.onChange.bind(this);
    }

    onChange(event) {
        this.state.insured[event.target.name] = event.target.value;
        return this.setState({ insured: this.state.insured });
    }

    render() {
        return (
            <div>
                <InsuredForm onChange={this.onChange} />
                <DebugObjectTable debugObject={this.state.insured} />
            </div>
        );
    }
}
export default InsuredController;

Groovy Method Tracer Transformer

In general I don't find trace logs for entering and exiting a method to be very useful, but occasionally I work with teams that do. There are great options using Spring or AspectJ, but I was in a Groovy project that didn't have either readily available. So, it made me wonder if we could just use AST transformations to build the loggers at compile time. Of course, as awesome as Groovy is, it's not hard. It already has a really cool feature to add code guards for logging statements, now I want to inject new logging statements into the code.

Groovy has an option to wrap methods on individual classes (see this metaClass example), but I was hoping to use an annotation, using an implementation of the ASTTransformation, to selectively add logging on methods. Thankfully, Groovy has an example in their documentation
(search for WithLogging). So here's a slightly tweaked implementation.

WithTracer.java
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@GroovyASTTransformationClass(classes = {WithTracerAstTransformer.class})
public @interface WithTracer {
  // marker annotation
}

WithTracerASTTransformer.groovy
import groovy.transform.CompileStatic
import org.codehaus.groovy.ast.ASTNode
import org.codehaus.groovy.ast.MethodNode
import org.codehaus.groovy.ast.expr.ArgumentListExpression
import org.codehaus.groovy.ast.expr.ConstantExpression
import org.codehaus.groovy.ast.expr.MethodCallExpression
import org.codehaus.groovy.ast.expr.VariableExpression
import org.codehaus.groovy.ast.stmt.BlockStatement
import org.codehaus.groovy.ast.stmt.ExpressionStatement
import org.codehaus.groovy.ast.stmt.ReturnStatement
import org.codehaus.groovy.ast.stmt.Statement
import org.codehaus.groovy.control.CompilePhase
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.transform.ASTTransformation
import org.codehaus.groovy.transform.GroovyASTTransformation

@CompileStatic
@GroovyASTTransformation(phase=CompilePhase.SEMANTIC_ANALYSIS)
class WithTracerAstTransformer implements ASTTransformation {
  @Override
  void visit(ASTNode[] nodes, SourceUnit sourceUnit) {
    MethodNode method = (MethodNode) nodes[1]

    def startMessage = createPrintlnAst("Starting $method.name")
    def endMessage = createPrintlnAst("Ending $method.name")
    def existingStatements = ((BlockStatement)method.code).statements
    existingStatements.add(0, startMessage)

     //insert the trace before the return statement, or as the last statement
    if (existingStatements.last() instanceof ReturnStatement) {
      existingStatements.add(existingStatements.size() - 1, endMessage)
    } else {
      existingStatements.add(endMessage)
    }
  }

  private static Statement createPrintlnAst(String message) {
    new ExpressionStatement(
      new MethodCallExpression(
        new VariableExpression("log"),
        new ConstantExpression("trace"),
        new ArgumentListExpression(new ConstantExpression(message))
        )
    )
  }
}

The only problem with this simple approach is that the Groovy logging annotation which adds the code guards are applied before this new annotation. So if I wanted this to be production ready, the transformer would probably need to extend the existing Groovy logging transformer in some way. For my purposes though, I was able to convince the team that looking in AppDynamics to see the method execution path was a better option.

The other problem I ran into is that Eclipse is stupid. Every time that I clean the project the classes get wiped out and the Eclipse compiler fails on any class that was trying to use the annotation. So I had to put the code for this in a separate project.

Probably not something I'd use, but it was a really interesting experiment with ASTTransformation that might have other possibilities.

Parallel Processing of Directories/Files

I've been working with a rules engine product recently that stores all of it's instructions in XML files. To support their rating algorithms, my company now has over 11,000 XML files stored in over 800 directories. These files can relate to one another through a sort of cascading inheritance. Considering the amount of data, I was looking for a way to visualize, but before I could even begin putting the data into a graph, I had to parse through all of the files to build the relationships.

My first attempt used Groovy's eachFileRecursive method to spin through all files and parse them, but it took about 12 minutes to run, which was far too long. So, I tried to run the parsing in parallel, which improved the time considerably, but I was still running for several minutes.

ThreadPoolExecutor executor = Executors.newFixedThreadPool(20);
directory.eachFileRecurse (FileType.FILES) { File file ->
  executor.execute(buildParser(manuscripts, file))
}
private static Callable buildParser(List manuscripts, File file) {
  return { //do stuff } as Callable
}
executor.shutdown()
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS)

My next step was to change how I was parsing the XML files. Originally I was using the XmlParser.

String line
file.withReader {
  while ((line = it.readLine()) != null) {
    if (line =~ /^\s*<properties/) {
      line = line.replaceFirst('([^/])>\\s*$') { it[0][0] + '/>' }
      
      def properties = new XmlParser().parseText(line)
      def props = [manuscriptID: properties.@manuscriptID?.toString(),
             inherited: properties.@inherited?.toString(),
             caption: properties.@caption?.toString()]
      if (props.inherited) {
        manuscripts << props
      }
      break
    }
  }
}

Sometimes the properties tag was self closing, other times it had children, so I had to do some manipulation of the String before I could apply the XmlParser to it. It didn't seem that bad, but it definitely wasn't fast, so I decided to try a SAX parser and it made a drastic improvement, I was no down to 20-30 seconds to get through all 11,000+ files.

class SaxManuscriptPropertiesHandler extends DefaultHandler {
  Map manuscriptProperties
  
  void startElement(String ns, String localName, String qName, Attributes atts) {
    switch (qName) {
      case 'properties':
        manuscriptProperties = [id: atts.getValue('manuscriptID'),
                                desc: atts.getValue('caption')]
        if (atts.getValue('inherited')) {
          manuscriptProperties['parent'] = atts.getValue('inherited')
        }
        //purposeful fall through to end processing
      case 'model':
        throw new StopProcessingException()
    }
  }
  private static Map parseManuscript(File file) {
    SaxManuscriptPropertiesHandler handler = new SaxManuscriptPropertiesHandler()
    def reader = SAXParserFactory.newInstance().newSAXParser().XMLReader
    reader.setContentHandler(handler)

    try {
      reader.parse(new InputSource(file.newInputStream()))
    } catch (StopProcessingException e) {
      //finished reading XML document
    }
    return handler.manuscriptProperties
  }
}

Getting there, but I still wanted to see if it could better, so I set some timers in my code to see where the remaining time was being spent and discovered that reading through the directories to load up the files was still taking a lot of time. The eachFileRecursive method was still single threaded. So I decided to parallelize that as well. In the new approach, each directory is added to the thread pool and processed separately.

The only problem I ran into was that I couldn't use shutdown - awaitTermination approach anymore because the main thread would reach the shutdown call before all of the directories/files had been added to the executor pool, so I switch the wait condition to use sleep and wait until all of the threads were finished processing.

class ManuscriptDirectoryParser {
  def manuscripts = [] as CopyOnWriteArrayList
  ThreadPoolExecutor executor = Executors.newFixedThreadPool(20);
  
  public parseDirectory(String directory) {
    processDirectory(new File(directory))
    while (!executor.shutdown) {
      Thread.sleep(1000)
      if (executor.activeCount == 0) {
        executor.shutdown()
      }
    }
    //write results to file
  }
  private processDirectory(File file) {
    file.listFiles().each { File child ->
      if (child.directory) {
        executor.execute({processDirectory(child)} as Runnable)
      } else {
        executor.execute({
          manuscripts << SaxManuscriptPropertiesHandler.parseManuscript(child) 
        } as Runnable)
      }
    }
  }
}

The culmination to all the improvements meant that I could run the parsing job in 14 seconds now, considerably better than the 12+ minutes where I started.

Spring Controller With Groovy HTML Generation

I was working on a small Java POC and wanted to be able to quickly generate some HTML, so I decided to try Groovy's MarkupBuilder. In theory it meant that I had a single file that handle the entire view, instead of shipping the rendering work off to another file like a JSP. Pretty sure this was not the wisest move ever, but it was an interesting experiment.

The code was in a Spring Boot app, so I created a Controller with a couple of URL mappings. The data being displayed is coming from an XML file, so there are some references to Groovy's XmlParser as well.

The renderHtml method creates the HTML, HEAD, and BODY tags and accepts additional page contents as a closure. In the example, there are two different pages that can be generated, both of which share the same style declarations because they both use renderHtml.

As I said, I don't think I'd do this in an actual application, but it is a way of creating an entire page's contents in a single file.


@RestController
class TableController {
  @Autowired
  ManuscriptCatalog catalog
  
  @RequestMapping(value="/manuscripts/{manuscript}/tables")
  String displayTable(@PathVariable("manuscript") String manuscriptId) {
    def manuscript = new XmlParser().parse(
  new File(catalog.getManuscript(manuscriptId).@url))
    def tableXmls = manuscript.depthFirst().findAll {it.name() == 'table'}

    return renderHtml { builder ->
      builder.h2(manuscript.properties[0].@caption.toString())
      builder.ul {
        tableXmls.each { tableXml ->
          String tableName = tableXml.@id.toString()
          li { a(href: "/manuscripts/$manuscriptId/tables/$tableName",
                       tableName) }
        }
      }
    }
  }
  
  @RequestMapping(value="/manuscripts/{manuscript}/tables/{table}")
  String displayTable(
    @PathVariable("manuscript") String manuscriptId,
    @PathVariable("table") String tableName) {
 
    def manuscript = new XmlParser().parse(
      new File(catalog.getManuscript(manuscriptId).@url))
    def tableXml = manuscript.depthFirst().find {
      it.name() == 'table' && it.attributes()['id'] == tableName}
    def table = Table.parseTableData(tableXml)
    
    return generateTableHtml(table)
  }
  
  private String renderHtml(Closure pageContents) {
    StringWriter out = new StringWriter()
    def builder = new MarkupBuilder(out)
    builder.html {
      head {
        link(rel: 'stylesheet',
       href: 'http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css')
        style( '''\
          .table-nonfluid {
            width: auto !important;
          }
          th, .key { background-color: #669900; font-weight: bold; color: white }
          tr:hover {
            background-color: #a6a6a6 !important;
          }
        ''')
      }
      builder.body {
        pageContents(builder)
      }
    }
    return out.toString()
  }
  /**
   * Generate HTML based on the tables structure
   */
  private String generateTableHtml(Table table) {
    return renderHtml { builder ->
      builder.h2(table.name)
      builder.table('class' : "table table-condensed table-bordered table-nonfluid") {
        thead {
          table.colKeys.eachWithIndex { key, index ->
            tr {
              if (index == table.colKeys.size() - 1) {
                table.rowKeys.collect { rowKey -> th(rowKey.name) }
              } else {
                th(colspan: table.rowKeys.size())
              }
              key.repeated.times {
                key.values.each { keyValue ->
                  th(colspan : key.span, keyValue)
                }
              }
            }
          }
        }
        tbody {
          table.rows.eachWithIndex { row, rowIndex ->
            tr {
              row.keys.eachWithIndex { key, keyIndex ->
                if (table.rowKeys[keyIndex].span == 1
      || rowIndex % table.rowKeys[keyIndex].span == 0) {
                  td('class' : 'key', rowspan : table.rowKeys[keyIndex].span, key)
                }
              }
              row.values.collect { val -> td(val) }
            }
          }
        }
      }
    }
  }
}

CICS Web Converse

I work in a shop that has a lot of CICS assets, and as our systems evolve the CICS applications need to connect to systems that reside elsewhere, often exposed via HTTP. Many of these new services are being exposed via REST.

In the past we've connected to those services using a pseudo-synchronous approach. CICS put a message on a request queue which an ESB watched. The ESB picked up the request, made the HTTP call, and then shipped the response back on a reply queue.

The approach worked well enough, but we often ran into resource constraints getting the translation code created, and that extra translation step just seemed unnecessary, especially in a service oriented architecture. Thankfully, CICS has some newer (relative...) features which simplify this process. The following code shows a working example calling a REST service to retrieve an agency's email address.

001000 WORKING-STORAGE SECTION.
001200***  COPY WEBCONV0
001300 01  WEB-CONVERSE-PARAMS.
001400     03 WC-SESSTOKEN           PIC X(8).
001500     03 WC-PATH                PIC X(200).
001600     03 WC-PATH-LGTH           PIC S9(8) COMP.
001800     03 WC-RESP                PIC S9(8) COMP.
001900     03 WC-RESP2               PIC S9(8) COMP.
002000     03 WC-RESP-DISP           PIC 9(8).
002100     03 WC-RESP2-DISP          PIC 9(8).
002200     03 WC-USERNAME-LGTH       PIC S9(8) COMP VALUE 4.
002300     03 WC-PASSWORD-LGTH       PIC S9(8) COMP VALUE 7.
002400     03 WC-HTTP-RESP-CODE      PIC S9(4) COMP.
002500        88  WC-HTTP-VALID-RESP     VALUE 100 THRU 599.
002600        88  WC-HTTP-OK-RESP        VALUE 200.
002700        88  WC-HTTP-BAD-REQ        VALUE 400.
002800        88  WC-HTTP-NOT-AUTH       VALUE 401.
002900        88  WC-HTTP-FORBIDDEN      VALUE 403.
003000        88  WC-HTTP-NOT-FOUND      VALUE 404.
003100        88  WC-HTTP-ERROR          VALUE 500.
003200     03 WC-HTTP-RESP-REAS      PIC X(100).
003300     03 WC-HTTP-RESP-REAS-LGTH PIC S9(8) COMP VALUE 100.
003400     03 WC-HTTP-RESP-CODE-DISP PIC 9(4).
003500     03 WC-HTTP-RESP           PIC X(200).
003600     03 WC-HTTP-RESP-LGTH      PIC S9(8) COMP.
003700
003800 01  WS-VARIABLES.
003900     03 WS-ERR-MSG             PIC X(200).
004000     03 WS-OUT-MSG             PIC X(200).
004100     03 WS-OUT-LGTH            PIC S9(4) COMP.
004200     03 WS-AGENCY-URL.
004300        05 FILLER              PIC X(21) VALUE '/Agency/EmailAddress/'.
004500        05 WS-AGENCY-CODE      PIC X(8).
004600        05 FILLER              PIC X(4)  VALUE '.xml'.
004700
005500 LINKAGE SECTION.
005700 01 DFHCOMMAREA.
005800    03  DFH-AGENCY-CODE               PIC X(8).
005900
006000 PROCEDURE DIVISION.
007000     PERFORM 1000-CONNECT.
007100     IF WS-ERR-MSG <= SPACE
007200         PERFORM 2000-GET-AGENCY-EMAIL
007300     END-IF.
007500     IF WS-ERR-MSG <= SPACE
007600         MOVE WC-HTTP-RESP         TO WS-OUT-MSG
007700         MOVE WC-HTTP-RESP-LGTH    TO WS-OUT-LGTH
007800     ELSE
007900         MOVE WS-ERR-MSG           TO WS-OUT-MSG
008000         MOVE LENGTH OF WS-ERR-MSG TO WS-OUT-LGTH
008100     END-IF.
008200
008300*==> WRITE OUT MESSAGE TO SCREEN
008400     EXEC CICS SEND TEXT FROM(WS-OUT-MSG)
008500     END-EXEC.
008600
008700     EXEC CICS RETURN
008800     END-EXEC.
008900
009000 1000-CONNECT.
009100*==> OPEN HTTP CONNECTION
009200     EXEC CICS WEB OPEN
009300         SESSTOKEN(WC-SESSTOKEN)
009400         HOST     ('www.agency.com')
009500         HTTP
009600         RESP     (WC-RESP)
009700         RESP2    (WC-RESP2)
009800     END-EXEC.
009900
010000     IF WC-RESP NOT = DFHRESP(NORMAL)
010100         MOVE WC-RESP  TO WC-RESP-DISP
010200         MOVE WC-RESP2 TO WC-RESP2-DISP
010300         STRING 'OPEN FAILED:'
010400                WC-RESP-DISP '-' WC-RESP2-DISP
010500           DELIMITED BY SIZE INTO WS-ERR-MSG END-STRING
010600     END-IF.
010800
010900 2000-GET-AGENCY-EMAIL.
011000     MOVE DFH-AGENCY-CODE         TO WS-AGENCY-CODE.
011100     MOVE WS-AGENCY-URL           TO WC-PATH.
011200     MOVE LENGTH OF WS-AGENCY-URL TO WC-PATH-LGTH.
011300
011400*==> CALL WEB SERVER
011500     EXEC CICS WEB CONVERSE
011600         GET   
011700         SESSTOKEN   (WC-SESSTOKEN)
011800         PATH        (WC-PATH)
011900         PATHLENGTH  (WC-PATH-LGTH)
012000         BASICAUTH
012100         USERNAME    ('USER')
012200         USERNAMELEN (WC-USERNAME-LGTH)
012300         PASSWORD    ('*******')
012400         PASSWORDLEN (WC-PASSWORD-LGTH)
012500         STATUSCODE  (WC-HTTP-RESP-CODE)
012600         STATUSTEXT  (WC-HTTP-RESP-REAS)
012700         STATUSLEN   (WC-HTTP-RESP-REAS-LGTH)
012800         INTO        (WC-HTTP-RESP)
012900         TOLENGTH    (WC-HTTP-RESP-LGTH)
013000         CLOSE
013100         RESP        (WC-RESP)
013200         RESP2       (WC-RESP2)
013300     END-EXEC.
013400
013500     IF WC-HTTP-VALID-RESP AND NOT WC-HTTP-OK-RESP
013600         MOVE WC-HTTP-RESP-CODE TO WC-HTTP-RESP-CODE-DISP
013700         STRING 'GET FAILED: '
013800                WC-HTTP-RESP-CODE-DISP
013900                '-'
014000                WC-HTTP-RESP-REAS(1:WC-HTTP-RESP-REAS-LGTH)
014100           DELIMITED BY SIZE INTO WS-ERR-MSG END-STRING
014200
014300     ELSE IF WC-RESP NOT = DFHRESP(NORMAL)
014400         MOVE WC-RESP  TO WC-RESP-DISP
014500         MOVE WC-RESP2 TO WC-RESP2-DISP
014600         STRING 'GET FAILED:'
014700                WC-RESP-DISP '-' WC-RESP2-DISP
014800           DELIMITED BY SIZE INTO WS-ERR-MSG END-STRING
014900     END-IF.

Of course, we don't want the user-id/password hard coded into the program, so my next step is looking into the XWBATUH user exit which allows those credentials to be stored in CICS configuration. Also the host name won't always be www.agency.com in the development life cycle, we'll need to connect to dev servers, so that can't be hardcoded either. Another CICS configuration, the URIMAP can externalize those URI resources. So there a few more steps to make this production ready.

The other issue with this approach is that typically REST services return JSON encoded data. That works great for a lot of platforms, most notably JavaScript, but CICS cannot digest JSON data easily. CICS can handle XML really well, so ideally we'd like the service provider to return XML.

Thankfully, many of our services are exposed use Spring MVC which makes returning either XML or JSON trivial. The RequestMapping annotation has a produces attribute which identifies which types of content can be returned. Spring will change the return type based on, among other attributes, the extension at the end of the request. So, you'll notice the WS-AGENCY-URL variable ends with .xml which causes the controller below to return XML,


@RestController
class ContentTypeTester {
  @RequestMapping(value="/Agency/EmailAddress",
                  produces=["application/json", "application/xml"])
  EmailData getEmailData() {
    return new EmailData(name: 'BBOS', address: 'bostastic@gmail.com')
  }
  class EmailData {
    String name
    String address
  }
}