SOAP support in KCML
SOAP, or Simple Object Access Protocol, is the predominant industry standard for performing remote procedure calls across the Web. Originally proposed by DevelopMentor, Microsoft and UserLand, it has been enthusiastically adopted by other manufacturers such as IBM and the standard is now under the control of the W3C. Under the name Web Services it forms a core component of Microsoft .Net. Messages are formatted in XML and sent using standard Internet protocols such as HTTP. KCML implements the SOAP 1.1 specification and should be interoperable with other SOAP implementations including the KCML SOAP server. KCML requires a SOAP server to support the WSDL language for describing Web Services and the server must expose a WSDL file.
Server
KCML can act as a SOAP server exposing KCML functionality as methods on one or more interfaces. Many SOAP servers, including the KCML implementation, follow the convention of exposing the WSDL file at the end point URI with the parameter ?WSDL. The KCML SOAP server will create the WSDL and return it. e.g.
OBJECT s = CREATE "SOAP", "http://www.stockquoteserver.com/stockquote?WSDL"
SOAP calls are normally performed with POST requests but to get documentation on a KCML SOAP server just point your browser at the end point URL and the server will return a page describing the web services available at that endpoint. This is because the server processes GET requests as requests for documentation.
For more information on setting up a KCML SOAP server see this page.
Client
KCML can act as a SOAP client. The client can access methods exposed by other servers using HTTP or HTTPS transport protocols.
To begin accessing the SOAP service we need a URL for the WSDL description. This is in a standard URL format such as "http://www.stockquoteserver.com/stockquote.wsdl". The URL can then be used to create a SOAP client object. The SOAP client object is created using the CREATE statement. The first parameter being "SOAP" and the WSDL URL being passed as the second. e.g.
OBJECT s = CREATE "SOAP", "http://www.stockquoteserver.com/stockquote.wsdl" a$ = s.GetStockQuote$("AA") OBJECT s = NULL
When executing the CREATE statement KCML will fetch and parse the WSDL. KCML supports http://, https://, file:// prefixes for the WSDL URL. The file:// prefix allows access to the local file system. Local file access can be handy if the server does not publish WSDL on its site. It is important that you free the SOAP client object when you are finished. This allows KCML to free memory used to hold the WSDL schema.
The SOAP object methods are subject to the same rules as any other object. You can see them here.
Methods and Properties
Inbuilt Methods
Method | Arguments | Description | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
_AddAttachment$ | _AddAttachment$(sFilename$, sType$ [, sId$]) | Add an attachment | ||||||||||||
_GetAttachment$ | _GetAttachment$(sID$, BYREF sType$) | Get an attachment | ||||||||||||
_AddCookie | _AddCookie(sName$,sValue$) | Set cookie sName$ to value sValue$. This method can be used several times to create several cookies. | ||||||||||||
_AddHTTPHeader | _AddHTTPHeader(sName$,sValue$) | Set header sName$ to value sValue$. This method can be used several times to add several headers | ||||||||||||
_SetProperty | _SetProperty(sName$,sValue$) | Set internal property in the following table to the specified value
| ||||||||||||
_Call | _Call(sName$,sValue$) | Call the method named in the sName$ parameter. This is useful where making the call using using object.MethodName() is difficult because the MethodName is held in a variable. The sValue$ is a single string parameter. So soap._Call("Mymethod", "Test") is equivalent to soap.Mymethod("Test"). Not supported in KCML 6.20. | ||||||||||||
_TraceFile | _TraceFile(sFilename$) | Open a trace file (this method was added to KCML 07.21.01.25256 and KCML 06.20.99.25256 as an alternative to having to use a config file or CREATE option) |
Examples
There are some examples of calling web services from KCML on this page.
Configuration
There are many options that can be set for each client object. They can be set in two ways. In the KCML program or using a separate file that is loaded by the client object.
Programmatically
An optional third parameter to the CREATE statement allows SOAP options to be configured in the program source. See the CREATE documentation for a list of all the available options.
Example 2
If a proxy server must be used to cross a firewall. The proxy location can be specified with the environment variable HTTP_PROXY or with the PROXY keyword in the options string. e.g.
DIM wsdl$ = "http://localhost/soapdemo/services.asp" DIM options$="PROXY=john:secretpassword@companyproxy:8080" OBJECT s = CREATE "SOAP", wsdl$, options$ a$ = s.GetServerTime$() OBJECT s = NULL
Config file
Setting up the SOAP client can involve setting lots of options. To remove these options from the program source we can use a separate configuration file. The config file is an XML document that will allow you to configure all aspects of the SOAP object.
Example 3
The same as example 2 but we have moved the proxy configuration out.
DIM wsdl$ = "http://localhost/soapdemo/services.asp" DIM options$="CONFIG=c:/tmp/SOAP.config" OBJECT s = CREATE "SOAP", wsdl$, options$ a$ = s.GetServerTime$() OBJECT s = NULL
Where the SOAP.config file looks like this;
<Policy xmlns="http://www.kcml.com/soap/policy"> <Transport> <Proxy> <Host>companyproxy:8080</Host> <Authentication> <Username>john</Username> <Password>secretpassword</Password> </Authentication> </Proxy> </Transport> </Policy>
Syntax
<Policy xmlns="http://www.kcml.com/soap/policy" > <UseLiteralCalls/> <UseInterface name='...'/> <TraceFile name='...'/> <Transport retry='...' connectTimeout='...' readTimeout='...'> <Authentication> <Username>...</Username> <Password>...</Password> </Authentication> <Proxy> <Host>...</Host> <Authentication> <Username>...</Username> <Password>...</Password> </Authentication> </Proxy> </Transport> <Security addressing='...' timestamp='...' derivedKeys='...' mustUnderstand='...' > <clientToken> <Username passwordType='...'> <Username>...</Username> <Password>...</Password> </Username> </clientToken> <serviceToken> <X509 file='...'/> </serviceToken> <keyPair> <X509 file='...' /> <privateKey file='...' /> </keyPair> <Request sign="..." encrypt="..." /> <Response sign="..." encrypt="..." /> </Security> </Policy>
Child Element | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
UseLiteralCalls | /Policy/UseLiteralCalls This optional element is used to set literal calling mode. On method calls, in this mode, single scalar strings are passed to and from the server. The strings represent documents that the user is responsible for creating and decoding of received documents. KCML provides only the transport and error handling. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
UseInterface | /Policy/UseInterface Allows you to manually specifiy the service/port to use.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TraceFile | /Policy/TraceFile Allows writing of output of the XML/HTTP request/responses to a file.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Transport | /Policy/Transport This optional element is used to define the HTTP transport options. We currently only support HTTP(S) transport.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Security | /Policy/Security This optional element provides configuration details for our WS-Security implementation.
|
WS-* support
The OASIS consortium publish many specifications relating to web services.
This is an area of constant development so it might be worthwhile contacting the KCML development team if you need help with a particular specification.
We implement partial compliance as documented below.
Security
The SOAP client has partial support of the OASIS Web Services Security (Version 1.0) specification. This provides a security framework for SOAP messages.
Support includes
All the options must be set via the config file.
Example 4
DIM wsdl$ = "http://localhost/soapdemo/services.asp" DIM options$="CONFIG=c:/tmp/SOAP.config,WSS=(username:'roy', password:'pass123')" OBJECT s = CREATE "SOAP", wsdl$, options$ a$ = s.GetServerTime$() OBJECT s = NULL
Where the SOAP.config file looks like this;
<Policy xmlns="http://www.kcml.com/soap/policy"> <Transport> <Proxy> <Host>companyproxy:8080</Host> <Authentication> <Username>john</Username> <Password>secretpassword</Password> </Authentication> </Proxy> </Transport> <Security timestamp='true'> <clientToken> <Username passwordType='text'/> </clientToken> <serviceToken> <X509 file='/tmp/certificate.pem'/> </serviceToken> <Request sign="Body, Token" encrypt="Body, Token" /> <Response sign="Body" encrypt="Body" /> </Security> </Policy>
This example will
Legacy implementation
This support is limited to outgoing messages signed using RSA keys with X509 certificates as the security token.
Each outgoing message has a header added to the SOAP envelopes header section. This security header contains the certificate and a signature value generated from the SOAP message. The header information can then used by the message recipient to confirm the source of the message.
This support is controlled via the CREATE "WSS" keyword. The value following the WSS keyword is a comma separated list of tokens and values that must be enclosed in brackets. The general format is shown below:
WSS=(keyword:'value', keyword:'value', ...)
Supported keywords:
Token | Description |
---|---|
cfile | X509 Certificate file. The file can be in either PEM or a DER format. |
pkfile | Private RSA key file. The file can be in either PEM or a DER format. |
Example of use in a CREATE statement:
OBJECT oSOAP = CREATE "SOAP", "myService.wsdl", "PROXY=user:passwd@proxy, WSS=(cfile:'myCertificate.x509', pkfile:'myPrivateKey.key')"
The role of WSDL
WSDL or Web Services Description Language is a W3C standard defining how Web Services expose their methods. The KCML SOAP client uses WSDL to find the server and to discover how to frame the SOAP requests for the available method. The SOAP object browser in the KCML workbench uses WSDL to list the available methods and to enumerate their parameters and parameter types. The formal definition of the language is available here but it is not necessary to know the language in order to be able to use a web service. Most SOAP implementations, including the KCML server implementation, will automatically generate a WSDL file describing their services. This file can be manually fetched and stored on the KCML client machine or it can be dynamically fetched when the CREATE statement instantiates a SOAP object. The former method will be faster but is less flexible.
If you are using the KCML client to access a Web Service that does not publish WSDL but does document the methods in some ad hoc fashion then you can use KCML's own SOAP server support to generate WSDL by writing stub DEFSUBs for the methods and using the KCMLObjectExports routine with the services actual endpoint to generate a WSDL file which can then be used on the client.
Message formats
WSDL specifies two distinct styles of SOAP messages: the original Remote Procedure Call or RPC where the message is defined by its parameters and the later Document form where the message has at most one input and one output parameter consisting of XML defined in a schema.
KCML converts both styles to a method call. This is a simple mapping for an RPC style but it is more complicated if the message is defined as Document and the document schema is complex. The corresponding method for a document style message will have zero or one input parameters and zero or one results. These parameters might be simple datatype, such as a string, a floating point number or a date, which can be mapped onto a KCML variable or they might be a complex structure which may or may not map onto a KCML DEFRECORD.
The original SOAP specification defined Encoding rules for the types of the parameters which are not enforced by any schema but many implementations prefer to let the schema define the types so that it can be used to validate the message as an XML document. This has led to 4 types of SOAP messages
Only RPC/encoded and Document/Literal are common and in fact most modern SOAP server implementations define messages as Document/Literal. The SOAP object browser in the workbench prefixes the method name with its message style.
Data types
KCML supports the following XSD datatypes and maps them to equivalent KCML types
XSD type | KCML type | Comment |
---|---|---|
string | String | |
base64Binary | String | |
hexBinary | String | |
float | Numeric | |
double | Numeric | |
decimal | Numeric | |
integer | Numeric | |
positiveInteger | Numeric | |
negativeInteger | Numeric | |
nonPositiveInteger | Numeric | |
nonNegativeInteger | Numeric | |
byte | Numeric | |
short | Numeric | |
int | Numeric | |
long | Numeric | |
unsignedByte | Numeric | |
unsignedShort | Numeric | |
unsignedInt | Numeric | |
unsignedLong | Numeric | |
boolean | Numeric | 0 for false and 1 for true |
date | Numeric | Julian day number |
time | Numeric | Milliseconds since midnight |
dateTime | Numeric |
Literal mode
Support for a document based exchange is supported in the KCML SOAP client. The KCML application can build the input as an XML document and pass it as the single argument to the method and get back an XML document as the result of the method call. This will require knowledge of the WSDL schema and the resulting document will have to be parsed using XML tools such as the DOM parser. However it gives the greatest flexibility and allows complex nested documents to be parsed. Many web services do not fit the RPC parameter passing model as they can return documents of unknown complexity and literal mode is the best choice for them.
KCML takes care of wrapping and unwrapping the soap envelopes and will handle server fault messages and convert them to errors.
DIM wsdl$ = "http://localhost/soapdemo/services.asp" OBJECT s = CREATE "SOAP", wsdl$, "LIT=Y" a$ = s.GetSomething$("<type1><arg0>1</arg0><arg1>2</arg1></type1>") OBJECT s = NULL
There is an example on this page.
Header
The SOAP object supports the writing and reading of a SOAP Header section for each request and response respectively. The .Header$ property can be used to set the SOAP Header before a request is made. The value given will be enclosed in the relevant SOAP Header tags. This will need to be set before each request that requires it. The .Header$ is filled in with the response SOAP Header after a request is made.
s.Header$ = "<headerInfo><sessionID>123456</sessionID></headerInfo>" a$ = s.GetSomething$("<type1><arg0>1</arg0><arg1>2</arg1></type1>")
Character Encoding
The SOAP object doesn't support any character encoding conversion. That is left to the application.
For new applications that run KCML in UTF-8 mode the HTTP content headers default to UTF8. This is the character encoding a SOAP application should be using and should be changed.
Old 6.20 KCMLs didn't set any indicator for character encoding. Which caused some confusion in Web Services / Applications processing the requests/responses. This was changed so that KCML indicated what character encoding it was using for the application. It based this on the value of $OPTIONS RUN 61. It is possible to override these defaults using the Encoding property.
oSOAP.Encoding$ = "utf-8"
will result in the HTTP header. Content-Type=text/xml;charset='utf-8'
Cookies
HTTP Cookies generated by the server and sent as part of a response will be automatically added to any subsequent requests. They will persist as long as the SOAP Object. Path and Expiry times will be ignored.
The SOAP object supports the adding HTTP Cookies to requests. This is done using the method _AddCookie. The method takes two arguments. A name/value pair for the cookie. The cookie will be generated for all subsequent requests. Using a blank string for value will remove an already set cookie.
REM Add cookie s._AddCookie("name", "value") a$ = s.GetSomething$("<type1><arg0>1</arg0><arg1>2</arg1></type1>") REM Remove Cookie s._AddCookie("name", "")
MTOM with XOP
The SOAP object includes a very basic mechanism for supporting SOAP Message Transmission Optimization Mechanism with XML-binary Optimized Packaging in document literal mode. This is achieved using two methods _AddAttachment and _GetAttachment
_AddAttachment$(sFilename$, sType$)
This takes two arguments and returns the XOP id to be used in the XML request document. The file will be automatically opened and attached to the outgoing HTTP request as a MIME part.
REDIM sID$ = s._addattachment$("filename.ext", "text") REDIM sRequest$ = "<MethodRequest><fileName>myFilename</fileName><binaryData><xop:Include xmlns:xop=""http://www.w3.org/2004/08/xop/include"" href=""cid:" & sID$ & """/></binaryData></MethodRequest>" REDIM sResponse$ = s.method$(sRequest$)
_GetAttachment$(sID$, BYREF sType$)
This takes two arguments and returns a filename of a file that contains the attachment. The file will be deleted automtically before the next request. The ID will need to be extracted by the application from the XML response document.
REDIM sFilename$ = s._getattachment$(sID$, BYREF sType$)
Debugging
KCML adds two properties for each SOAP client object to aid debugging. These are .Response$ and .Request$. These two scalar string properties provide access to the internal messages sent between the SOAP Client and Server. The .Request$ property contains the data sent when making a request of the SOAP server i.e. Invoke a method. The .Response$ is the reply received from a SOAP server. The length of each string property can be accessed by calling its numeric equivalent thus the length of the .Response$ string can be obtained by referencing .Response.
Error messages
All the SOAP $DECLARE calls will return TRUE if they are successful and FALSE if they fail. It is good programming practice to check the return codes from all subroutines, however in the interests of brevity none are checked in the examples on this page.
In the event of a problem there are three $DECLARES implemented for error reporting.
$DECLARE 'KCMLObjectGetLastError(STR(), RETURN INT())="*" $DECLARE 'KCMLObjectGetLastErrorString(STR(), RETURN STR())="*" $DECLARE 'KCMLObjectGetErrorString(STR(), INT(), RETURN STR())="*" ... 'KCMLObjectGetLastError("SOAP", error_number)Returns the last SOAP error number.
'KCMLObjectGetLastErrorString("SOAP", error_text$)Returns the last SOAP error text.
'KCMLObjectGetErrorString("SOAP", error_number, error_text$)
Returns the error text for a given error number.
Restrictions
There are many inconsistencies in the SOAP and WSDL specifications that have lead to incompatibilities between SOAP implementations. In general KCML follows the SOAP 1.1 and WSDL 1.1 specifications but it defers to the WS-I interoperability specification where there are conflicts.