Web Services Broker (OMWSB)

WSB provides a set of functions that enable you to create a server application that acts as a host and broker for web service requests sent from client applications and processed by service applications. Note the distinction between the roles of the client application, the server application and the service application:

  • The client application requests a particular service by bundling the request and any associated data into an appropriate web service request format such as SOAP. It then submits that request to the server application using an appropriate internet protocol such as HTTP.
  • The server application receives requests in a particular web service format such as SOAP. It checks the SOAP header to discover which service is being requested and routes the request to the service program that provides that service.
  • The service application takes a service request that is forwarded to it by the server application and performs the required service on the data provided by the client.

Note: The WSB library will not run with OmniMark Single Processing Engine or with the OmniMark Fast VM under OmniMark Studio for Eclipse (the "Execute Project" option).

The WSB library provides functions that can be used by the server application to host services, route requests, and process requests for which no service exists. It also provides functions that can be used by service applications to implement a service and to receive and interpret a request. The WSB library does not provide functions for use in web service clients. However, the functions required by clients to communicate with servers and to interpret responses are provided by OmniMark's TCP/IP library and by OmniMark's built in XML processing capability.

The WSB library runs service applications in threads, allowing the WSB server to handle multiple simultaneous requests.

Web services may use a variety of communication protocols (SOAP, XML-RPC, etc) and a variety of transport mechanisms (HTTP, Mail, etc.). The current version of the Web Services Broker library supports SOAP 1.1, XML-RPC, and user-defined protocols over HTTP.

A WSB example

Here is an example of a WSB application using the SOAP protocol. It consists of four programs, the server, the service, a client, and a "die" client, designed to shut down the server remotely. The example implements a time service.

This example, as well as examples using XML-RPC and a user-defined protocol, are included with your OMWSB installation. Please note that these samples are designed to illustrate the operation of the Web Service Broker and should not be taken as complete or robust implementation of the SOAP or XML-RPC protocols.

A WSB server

This is the code for the server.

  import 'omwsb.xmd' prefixed by wsb.
  
  process
     local wsb.server server
  
     ; set up the server
     set server to wsb.create-server on 3556 of-type wsb.soap-service
     ; build the service map
     wsb.add-service to server named "http://omnimark.com/TIME"
      handled-by "times.xvc"
     ; start the server
     wsb.start-servicing-requests on server
  
     put #error "OMWSB Server started on port 3556%n"
     repeat
        local wsb.request request
  
        ; Wait for requests. Requests not in map handled here.
        set request to wsb.wait-for-request on server
  
        do when (wsb.request-name of request) = "http://omnimark.com/DIE"
           local stream responder
           open responder as wsb.writer of request
           put responder "HTTP/1.0 200 OK%13#%10#"
            || "Content-Type: text/xml; charset=%"utf-8%"%13#%10#"
            || "Content-Length: 0"
            || "%13#%10#%13#%10#"
           close responder
           log-message "DIE action received. Shutting down.%n"
           exit
        else
           ;else report bad request
           log-message "Unknown request received."
           log-message "Header: " || wsb.request-header of request
           log-message "Body: " || wsb.reader of request
        done
  
        catch #external-exception
              identity catch-id
              message catch-msg
              location catch-loc
           log-message '%g(catch-id) : %g(catch-msg)%n%g(catch-loc)'
           ; exit if server has fatal error
           rethrow when catch-id = "OMWSB000"
     again

A WSB service

Here is an example of a WSB service application. It implements a time service. The server program above is designed to act as host and broker for this service. Note that this program must be compiled to create "times.xvc" and that the "times.xvc" file must be in the current working directory of the server program.

  ; times.xom
  ; a time service
  
  import 'omwsb.xmd' prefixed by wsb.
  include 'omdate.xin'
  global stream the-time
  
  declare catch parse-error
   reason value string message
  
  process
     repeat
        local wsb.request request
        local stream responder
        local stream response
  
        ; Parse the request
        set request to wsb.wait-for-request
        using group process-soap
        do xml-parse scan wsb.reader of request
           suppress
        catch parse-error reason message
           open responder as wsb.writer of request
           put responder "HTTP/1.1 404 Invalid request%13#%10#%13#%10#"
           put responder "XML error in request:%n" || message || "%n"
           close responder
           exit
        done
  
        ; Build the response
        set response
         to "<SOAP-ENV:Envelope%13#%10#"
         || "  xmlns:SOAP-ENV=%"http://schemas.xmlsoap.org/soap/envelope/%"%13#%10#"
         || "  SOAP-ENV:encodingStyle=%"http://schemas.xmlsoap.org/soap/encoding/%"%13#%10#"
         || "  xmlns:time=%"http://schemas.omnimark.com/time%">%13#%10#"
         || "   <SOAP-ENV:Body>%13#%10#"
         || "     <time:current-time>%13#%10#"
         || the-time
         || "     </time:current-time>%13#%10#"
         || "   </SOAP-ENV:Body>%13#%10#"
         || "</SOAP-ENV:Envelope>%13#%10#"
  
        open responder as wsb.writer of request
        put responder "HTTP/1.0 200 OK%13#%10#"
         || "Content-Type: text/xml; charset=%"utf-8%"%13#%10#"
         || "Content-Length: "
         || "d" % length of response
         || "%13#%10#%13#%10#"
         || response
        close responder
  
        catch #external-exception identity catch-id
           rethrow when catch-id = "OMWSB000"
     again
  
  ;ensure a clean exit
  catch #program-error
   code error-code
   message error-message
   location error-location
     log-message ('An error occurred in the TIME service:%n'
                || '%d(error-code): %g(error-message)%n%g(error-location)%n')
  
  ;rules for processing the SOAP packet
  group process-soap
  
  element #base "Envelope"
     suppress
  
  element #base "Body"
     suppress
  
  element #base "request"
     suppress
  
  element #base "zone"
     local stream zone initial {"%c"}
     set the-time
      to ymdhms-to-arpadate ymdhms-adjust-time-zone
          now-as-ymdhms to-be zone
  
  markup-error
     throw parse-error
      reason (#message || " At line %d(#line-number)")
  
  group #implied

A WSB Client

Here is a client that uses the time service.

  import 'omtcp.xmd' prefixed by tcp.
  include "omdate.xin"
  
  global integer port initial {3556}
  global stream  hostname initial {"localhost"}
  define string source function my-time-zone as output now-as-ymdhms drop any{14}
  
  ;default to local time zone
  global stream zone initial {my-time-zone};{now-as-ymdhms drop any{14}}
  
  define string source function generate-soap-request
   action value string soap-action
   as
     local stream contents
  
     set contents
      to "<SOAP-ENV:Envelope%13#%10#"
      || " xmlns:SOAP-ENV=%"http://schemas.xmlsoap.org/soap/envelope/%"%13#%10#"
      || " SOAP-ENV:encodingStyle=%"http://schemas.xmlsoap.org/soap/encoding/%"%13#%10#"
      || " xmlns:time=%"http://schemas.omnimark.com/time%">%13#%10#"
      || "  <SOAP-ENV:Body>%13#%10#"
      || "   <time:request>%13#%10#"
      || "    <time:zone>" || zone || "</time:zone>%13#%10#"
      || "   </time:request>%13#%10#"
      || "  </SOAP-ENV:Body>%13#%10#"
      || "</SOAP-ENV:Envelope>%13#%10#"
  
     output "POST /SOAPServer HTTP/1.0%13#%10#"
         || "Host: www.soapserver.com%13#%10#"
         || "Content-Type: text/xml; charset=%"utf-8%"%13#%10#"
         || "SOAPAction: " || soap-action || "%13#%10#"
         || "Content-Length: "
         || "d" % length of contents
         || "%13#%10#%13#%10#"
         || contents
  
  process
     local tcp.connection conn
  
     ;check for valid time zone format
     do unless zone matches (["+-"] digit{4})
        log-message ('Incorrect time zone format "' || #args[1] || '"%n'
                  || "Use '-'/'+' hhmm format. E.g. +0200")
        halt with 1
     done
  
     set conn to tcp.connect to hostname on port
     tcp.put-string to conn from
      (generate-soap-request action "http://omnimark.com/TIME")
  
     ; receive response
     do scan tcp.reader of conn
        ; See if valid response. If so, decode data. Otherwise, report error
        match (any** "HTTP/" digit+ "." digit+ blank+ digit{3} => response-code)
              (any** "Content-Type:" blank+ [\ " ;"]+ => content-type)?
              (any** 'charset="' any++ => charset '"')?
              (any** "Content-Length:" blank* digit+ => content-length)?
              (any** "%13#%10#%13#%10#")?
           do when response-code = "200"
              do xml-parse scan #current-input
                 suppress
              done
           else
              log-message ("HTTP error. Code=" || response-code)
           done
        match lookahead any
           log-message ("Invalid response received:%n" || #current-input)
           halt with 1
     done
  
  ;-------------------------------------------------------
  ; SOAP Processing rules
  ;
  element #base "Envelope"
     suppress
  
  element #base "Body"
     suppress
  
  element #base "current-time"
     log-message "%nThe time is: " || "%sc" drop white-space* || "%n"

Shutting down a WSB server

It is important that you shut down a WSB server by sending it a message telling it to shut down. If you shut it down by killing the server process, (for example by typing Ctrl-C on the command line) the service programs hosted by the server will not have an opportunity to clean up any resources they are using. Here is a client that will send a shutdown command to the server application shown above.

  import 'omtcp.xmd' prefixed by tcp.
  
  global integer port initial {3556}
  global stream  hostname initial {"localhost"}
  
  define string source function generate-soap-request
   action value string soap-action
   as
     output "POST /SOAPServer HTTP/1.0%13#%10#"
         || "Host: www.soapserver.com%13#%10#"
         || "Content-Type: text/xml; charset=%"utf-8%"%13#%10#"
         || "SOAPAction: " || soap-action || "%13#%10#"
         || "Content-Length: 0"
         || "%13#%10#%13#%10#"
  
  process
     local tcp.connection conn
  
     set conn to tcp.connect to hostname on port
     tcp.put-string to conn from
      (generate-soap-request action "http://omnimark.com/DIE")