16 Using Scripted Global Web Services with EDQ

EDQ 12.2.1.4.4 restores limited support for global web services. This documentation is intended for system administrators responsible for installing and maintaining EDQ applications.

This chapter includes the following topics:

16.1 Introduction to Global Web Services and EDQ

EDQ 12.2.1.4.0 and earlier supported "global" SOAP web services, which were installed as jars in the local webservices directory. Support for global web services was removed in EDQ 12.2.1.4.1.

EDQ 12.2.1.4.4 onwards you can configure realtime provider and consumer buckets to use global web services for reading and publishing. These web services bucket files use scripts to decode incoming text payloads and generate outgoing text results. The supported payload formats are JSON (application/json), XML (text/xml), and plain text (text/plain).

Note:

SOAP is not supported.

16.2 Configuring EDQ to Read and Write Web Service Requests

Web service interfaces to and from EDQ are configured using XML interface files that define:

  • The URL path for the web service
  • How to decode the message payload into a format understood by an EDQ process (for Message Providers – where EDQ reads messages), or convert messages to a format expected by an external process (for Message Consumers – where EDQ writes messages).

The XML files are located in the EDQ Local Home directory (formerly known as the config directory), in the following paths:

  • buckets/realtime/providers (for interfaces ‘in’ to EDQ)
  • buckets/realtime/consumers (for interfaces ‘out’ from EDQ)

Once the XML files have been configured, Message Provider interfaces are available in Reader processors in EDQ and to map to Data Interfaces as ‘inputs’ to a process, and Message Consumer interfaces are available in Writer processors, and to map from Data Interfaces as ‘outputs’ from a process.

16.3 Defining the Interface Files

An interface file in EDQ consists of a realtimedata element which defines the message framework. A web service that returns a result is defined by provider and consumer buckets. The configuration in both must be marked as synchronous. For web services, use the following:

<?xml version="1.0" encoding="UTF-8"?>
 
<realtimedata messenger="ws" synchronous="true">
    ...
</realtimedata>

The realtimedata element contains three subsections:

  • The <attributes> section, defining the shape of the interface as understood by EDQ
  • The <messengerconfig> section, defining how to connect to the web service endpoint.
  • A message format section defining how to extract contents of a message (e.g. from XML) into attribute data readable by EDQ (for inbound interfaces), or how to convert attribute data from EDQ to message data (e.g. in XML). For provider interfaces, the element is <incoming>; for consumer interfaces, the element is <outgoing>.

16.3.1 Understanding the <attributes> section

The <attributes> section defines the shape of the interface. It constitutes the attributes that are available in EDQ when configuring a Reader or Writer.

Provider scripts generate record object from the incoming payload and consumer scripts convert record contents into the outgoing payload. In a provider bucket script, create a record using new Record() or newRecord(). The second form should be used in Groovy scripts. Then set the attributes with assignment like:

rec.id = 23
rec.name = "John Smith"

Record objects contain the attributes defined in the bucket's attributes section. For example, the following excerpt from the beginning of an interface file defines the bucket's attributes, which generate record objects that contain id, name and email attributes:

<?xml version="1.0" encoding="UTF-8"?>
<realtimedata messenger="ws">
<attributes>
  <attribute name="id"    id="1" type="number"/>
  <attribute name="name"  id="2" type="string"/>
  <attribute name="email" id="3" type="string"/>
</attributes> 

[file continues]...

EDQ supports all the standard attribute types and they are:

  • string
  • number
  • date
  • stringarray
  • numberarray
  • datearray

16.3.2 Understanding the <messengerconfig> section

The messengerconfig section must include a path property. The value is appended to http://server/edq/restws/ to form the endpoint for the web service.

The path can contain letters, digits, and underscore (_) characters.

16.3.3 Understanding the <incoming> or <outgoing> section

The <incoming> or <outgoing> section defines how message metadata and values are converted to/from EDQ attributes. It consists of the following two subsections:

The provider and consumer buckets are linked by adding a key attribute to the incoming and outgoing elements:

<incoming key="addup1">
<outgoing key="addup1">

The same key value must be used in the provider and consumer web service buckets for a single service. Each service should use different key values.

16.3.3.1 Understanding the <messageheaders> section

The <messageheaders> section is optional. It defines how data outside the record value is converted to/from EDQ attributes. The format of this section is as follows:

<messageheaders>
   <header name=”headername” attribute=”attributename”/>
  …
</messageheaders>
16.3.3.2 Understanding the <messagebody> section

This section uses JavaScript to parse message payloads into attributes that EDQ can use for inbound interfaces, and perform the reverse operation (convert EDQ attribute data into message payload data) for outbound interfaces. A function named ‘extract’ is used to extract data from XML into attribute data for inbound interfaces, and a function named ‘build’ is used to build XML data from attribute data.

Provider Definition

The provider bucket XML file uses a script decoder to create attributes from the input payload:

<messagebody>
  <script content="...">
   <![CDATA[
     function extract(payload, type, mtags, env) {
       ...
     }
   ]]>
  </script>
</messagebody>

The content attribute is a comma-separated list of the input types that the provider supports:

  • json: JSON text with content type application/json.
  • dom: XML text with content type text/xml. The XML is parsed to DOM by the web services framework. To decode the DOM in the script use new XML(payload).
  • text: Plain text with content type text/plain.

A web service call with a content type not supported by the script will fail with a 415 error. The actual type used is available in env.contenttype;. If the script supports more than one content type, you can used this to determine how to parse the payload.

For more details, refer to Illustrations, which provides examples of a provider bucket XML file.

Consumer Definition

The consumer bucket XML file uses a script encoder to create the result text from input attributes:

<messagebody>
  <script content="...">
   <![CDATA[
     function build(recs, mtags, ctype) {
       ...
     }
   ]]>
  </script>
</messagebody>

The content attribute is a comma-separated list of the output types that the consumer supports:

  • recs is an array of record objects.
  • ctype is the content type (json, dom, or text) of the incoming request. If the service can accept more than one content type, use this to create the output in the correct format.

Set multirecord="true" if a request can contain multiple records:

<script multirecord="true"> 

For more details, refer to Illustrations, which provides examples of a consumer bucket XML file.

16.4 Illustrations

This is an example of a simple global web service called addup, which accepts two numbers and generates the sum. Supported input formats are JSON, XML, and plain text. The path for the service is addup and the web service endpoint is:

http://server:port/edq/restws/addup

The following table illustrates how the input and output formats work based on this example service.

Supported Format Example Input Example Ouput

JSON

{ "n1" : 100,
  "n2" : 235
}
{
  "inputs": [
    100,
    235
  ],
  "sum": 335
}

XML

<request>
  <n1>100</n1>
  <n2>223</n2>
</request>
<response>
  <inputs>
    <v>100</v>
    <v>223</v>
  </inputs>
  <sum>323</sum>
</response>

Plain text

234,129 234 + 129 = 363

Example 1 – Provider File

The following XML is an example of a provider bucket XML file:

<?xml version="1.0" encoding="UTF-8"?>
<realtimedata messenger="ws" synchronous="true">

  <!-- ================================================= -->
  <!-- The attributes which are created by this provider -->
  <!-- ================================================= -->

  <attributes>
    <attribute name="n1" id="1" type="number"/>
    <attribute name="n2" id="2" type="number"/>
  </attributes>

  <!-- ============================= -->
  <!-- The web service configuration -->
  <!-- ============================= -->

  <messengerconfig>
    path = addup
  </messengerconfig>

  <!-- ==================================== -->
  <!-- The script which unpacks the payload -->
  <!-- ==================================== -->

  <incoming key="addup1">
    <messagebody>
      <script content="json,text,dom">
       <![CDATA[
         function extract(str, type, mtags, env) {
           if (env.contenttype == "json") {
             var x = JSON.parse(str)
             var r = new Record();

             r.n1 = x.n1
             r.n2 = x.n2

             return [r];
           } else if (env.contenttype == "dom") {
             var x = new XML(str)
             var r = new Record();

             r.n1 = parseInt(x.n1)
             r.n2 = parseInt(x.n2)

             return [r];
           } else {
             var a = str.split(",")
             var r = new Record();

             r.n1 = parseInt(a[0])
             r.n2 = parseInt(a[1])

             return [r];
           }
         }
       ]]>
      </script>
    </messagebody>
  </incoming>

</realtimedata>

Example 2 – Consumer File

The following XML is an example of a consumer bucket XML file:

<?xml version="1.0" encoding="UTF-8"?>
<realtimedata messenger="ws" synchronous="true">

  <!-- =================================================== -->
  <!-- The attributes which are accepted by this consumer -->
  <!-- ================================================== -->

  <attributes>
    <attribute name="n1"  type="number"/>
    <attribute name="n2"  type="number"/>
    <attribute name="sum" type="number"/>
  </attributes>

  <!-- ============================= -->
  <!-- The web service configuration -->
  <!-- ============================= -->

  <messengerconfig>
    path = addup
  </messengerconfig>

  <!-- =================================== -->
  <!-- The script which creates the output -->
  <!-- =================================== -->

  <outgoing key="addup1">
    <messagebody>
      <script multirecord="true">
        <![CDATA[
          function build(recs, mtags, ctype) {
            var rec = recs[0]

            if (ctype == 'json') {
              return JSON.stringify({ inputs: [rec.n1, rec.n2], sum: rec.sum }, null, 2)
            } else if (ctype == "dom") {
              return  <response><inputs><v>{rec.n1}</v><v>{rec.n2}</v></inputs><sum>{rec.sum}</sum></response>.toXMLString()
            } else {
              return rec.n1 + " + " + rec.n2 + " = " + rec.sum
            }
          }
        ]]>
      </script>
    </messagebody>
  </outgoing>

</realtimedata>