Web Services Broker (OMWSB)

The OMWSB library 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 OMWSB 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 OMWSB 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 OMWSB 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 OMWSB 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, …) 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.

Example

The following 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.

Example: Server

The following is the code for the server.

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

Example: 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.

  import "omwsb.xmd" prefixed by wsb.
  include "omdate.xin"
  
  global string current-time
  
  declare catch parse-error (value string message)
  
  process
     repeat
        local wsb.request request
        local string      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 (message)
           using output as wsb.writer of request
              output "HTTP/1.1 404 Invalid request%13#%10#%13#%10#"
                  || "XML error in request:%n" || message || "%n"
           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#"
              || current-time
              || "     </time:current-time>%13#%10#"
              || "   </SOAP-ENV:Body>%13#%10#"
              || "</SOAP-ENV:Envelope>%13#%10#"
  
        using output as wsb.writer of request
           output "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
  
      catch #external-exception identity identity
        rethrow when identity = "OMWSB000"
     again
  
   catch #program-error code code message message location location
     ; Ensure a clean exit.
     ;   
     put #log "An error occurred in the TIME service:%n"
           || "d" % code || ": " || message || "%n"
           || location || "%n"
  
  group "process soap"
     element (#base "Envelope" | #base "Body" | #base "request")
        suppress
  
     element #base "zone"
        set current-time 
           to ymdhms-to-arpadate ymdhms-adjust-time-zone       now-as-ymdhms 
                                                         to-be "%c"
  
  group #implied
     markup-error
        throw parse-error (#message || "%nAt line " || "d" % #line-number || ".")

Example: WSB Client

What follows is a client that uses the time service.

  import "omtcp.xmd" prefixed by tcp.
  include "omdate.xin"
  
  global string  hostname initial { "localhost" }
  global integer port     initial { 3556 }
  
  define string source function 
     get-time-zone ()
  as 
     output now-as-ymdhms drop any{14}
  
  global string time-zone initial { get-time-zone () }
  
  define string source function 
     generate-soap-request (value string soap-action)
  as
     local string 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>" || time-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 connection
  
     ; Check for a valid time zone format.
     ;
     do unless time-zone matches (["+-"] digit{4})
        put #log "Incorrect time zone format %"' || #args[1] || '%"%n"
              || "Use %"-%"/%"+%" hhmm format. E.g. +0200"
        halt with 1
     done
  
     set connection to tcp.connect to hostname on port
  
     using output as tcp.writer of connection
        output generate-soap-request ("http://omnimark.com/TIME")
  
     ; See if there is a valid response. If so, decode data. Otherwise, report an error.
     ;
     do scan tcp.reader of connection
     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
           put #log "HTTP error. Code=" || response-code
        done
  
     match lookahead any
        put #log "Invalid response received:%n" || #current-input
        halt with 1
     done
  
  element (#base "Envelope" | #base "Body")
     suppress
  
  element #base "current-time"
     put #log "%nThe time is: " || "%sc" drop white-space* || ".%n"

Example: 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 string  hostname initial { "localhost" }
  global integer port     initial { 3556 }
  
  define string source function 
     generate-soap-request (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
     using output as tcp.writer of tcp.connect to hostname on port
        output generate-soap-request ("http://omnimark.com/DIE")