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:
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.
The following is an example of a WSB application using the SOAP protocol. It consists of four programs:
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
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 || ".")
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"
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")