Top

Chapter 17. Web Services

Abstract

The term Web Services describes an application of XML for exposing application functionality to disparate clients via the Web. This paradigm provides access to entire applications, modules, objects, functions, and methods via HTTP and other transport protocols. Web Services are inherently platform- and programming-language independent. Services can be developed in one language and consumed in another; this holds true irrespective of client or server host operating system combinations.

A collection of core XML-based specifications and standards work in concert to fulfill the Web services value proposition. These standards include:

SOAP - XML notation that describes how messages are assembled and transmitted over HTTP between services and service consumers.
WSDL - XML notation for describing SOAP services.
UDDI - Application of the SOAP protocol for registering and publishing information about organizations, contacts within organizations, and Web Services that these organizations have chosen to expose to the public.

Virtuoso enables stored procedures to be exposed as Web services that are consumable by any Web services-aware development tool, application, or environment. The stored procedures exposed by Virtuoso may be native stored procedures, or may be hosted in any third-party database that supports stored procedures, and is accessible via an ODBC or JDBC driver. In Virtuoso, exposing stored procedures as Web services does not require any programming effort. You simply identify the stored procedures to be exposed using the Virtuoso Administrative Interface.

This feature is immensely valuable in situations where organizations are seeking to transform time-tested stored procedures into Web Services without having to upgrade or change databases or host operating systems. In modern enterprises, such stored procedures drive mission-critical solutions; Virtuoso's approach to Web services enables you to maximize current investment while minimizing the need for rework.

Table of Contents

17.1. SOAP
17.1.1. Virtuoso SOAP Support Overview
17.1.2. Handling of SOAP HTTP Requests
17.1.3. Extending Datatypes for SOAP Objects
17.1.4. Inheritance of Datatypes for SOAP Objects
17.1.5. Complex Types in PL Procedure and UDT Method Definition
17.1.6. Complex Types in Procedure Definition using a pre-defined XML Schema datatypes
17.1.7. Default SOAP-SQL Datatype Mappings
17.1.8. Exposing Stored Procedures as SOAP Objects
17.1.9. Creation of SOAP proxy based on User Defined Types
17.1.10. Exposing User Defined Type Methods as SOAP Objects
17.1.11. Exposing Remote Third Party SQL Stored Procedures as SOAP Services
17.1.12. Virtuoso/PL SOAP Client
17.1.13. Execution Privileges
17.1.14. Custom Soap Server Support
17.1.15. PL Procedures and UDT Methods Syntax Affecting WSDL & SOAP Processing
17.1.16. Exposing & Processing SOAP Header Messages
17.1.17. Exposing & Processing SOAP Fault Messages
17.1.18. Document Literal Encoding
17.1.19. DIME encapsulation of SOAP messages
17.1.20. SOAP Endpoint Options
17.2. WSDL
17.2.1. Exposing Stored Procedures as WSDL Services
17.2.2. Exposing SQL Stored Procedures containing complex datatype definitions
17.2.3. Exposing Third Party SQL Stored Procedures as WSDL-Compliant Web Services
17.2.4. WSDL Descriptions of SOAP Header Messages
17.2.5. Importing A WSDL File & SOAP/WSDL Proxying
17.2.6. SOAP/WSDL Interoperability
17.3. WebID Protocol Support
17.3.1. x.509 certificate
17.3.2. Setting up Virtuoso HTTPS
17.3.3. Setting Up Firefox
17.3.4. Configuring ODS Account to use WebID Protocol
17.3.5. Testing the setup
17.3.6. WebID Protocol ACLs
17.3.7. SPARQL-WebID based Endpoint
17.3.8. CA Keys Import using Conductor
17.3.9. Set Up X.509 certificate issuer, HTTPS listener and generate ODS user's certificates
17.3.10. WebID Protocol ODBC Login
17.4. OAuth Support
17.4.1. OAuth Access Tokens
17.4.2. Virtuoso OAuth server
17.4.3. OAuth Implementation in OpenLink Data Spaces
17.4.4. OAuth Generate Keys for ODS Controllers (Web Services)
17.4.5. ODS Ubiquity Commands
17.4.6. OAuth Test Tool for ODS Controllers
17.4.7. OAuth QA
17.5. WS-Security (WSS) Support in Virtuoso SOAP Server
17.5.1. Client and Server side Certificates & Keys
17.5.2. SOAP Server WS-Security Endpoint
17.5.3. Virtual Directory SOAP WSS Options
17.5.4. Accounting & Accounting Hook
17.5.5. Signature Templates
17.5.6. SOAP Client
17.6. Web Services Routing Protocol (WS-Routing)
17.6.1. Configuration
17.6.2. Traversing Message Paths
17.7. Web Services Reliable Messaging Protocol (WS-ReliableMessaging)
17.7.1. SOAP CLIENT API Extensions
17.7.2. WS-RM Sender API
17.7.3. WSRM Receiver API
17.7.4. WS-RM Protocol Endpoint Configuration
17.7.5. Message Examples
17.7.6. WS-RM Schema
17.8. Web Services Trust Protocol (WS-Trust)
17.9. XML for Analysis Provider
17.10. XML-RPC support
17.11. SyncML
17.12. UDDI
17.12.1. Concepts
17.12.2. Dealing with SOAP
17.12.3. Supported API Calls
17.12.4. Authorization Mechanism
17.12.5. UDDI API Calls
17.12.6. Examples
17.13. Exposing Persistent Stored Modules as Web Services
17.13.1. Publishing Stored Procedures as Web Services
17.13.2. XML Query Templates
17.13.3. Publishing VSE's as Web Services
17.14. Testing Web Published Web Services
17.15. BPEL Reference
17.15.1. Activities
17.15.2. Protocol Support
17.15.3. Process lifecycle
17.15.4. Using virtual directories
17.15.5. Process archiving
17.15.6. Configuration parameters
17.15.7. Process Statistics
17.15.8. Deployment file suitcase format
17.15.9. SQL API
17.15.10. BPEL XPath Functions
17.15.11. Tables
17.15.12. Errors
17.15.13. Samples
17.15.14. References
17.15.15. BPEL4WS VAD Package installation
17.16. XSQL
17.16.1. XSQL Syntax
17.16.2. XSQL Directives

17.1. SOAP

The Simple Object Access Protocol (SOAP) is a lightweight, extensible, XML-based application layer protocol for information exchange in a decentralized, distributed environment. SOAP defines a framework for message structures and a message processing model. SOAP also defines a set of encoding rules for serializing data and a convention for making remote procedure calls. The SOAP extensibility model provides a foundation for a wide range of composable modules and protocols. Although the most common way to transport SOAP messages is HTTP, it may also be run on top of other protocols.

SOAP includes:

an envelope that defines a framework for describing what is in a message and how to process it
a set of encoding rules for expressing instances of application-defined datatypes
a convention for representing remote procedure calls and responses.

17.1.1. Virtuoso SOAP Support Overview

Virtuoso provides a framework for both consuming SOAP services (acting as a client) and producing them (acting as a server). The Virtuoso web server has a mechanism for handling SOAP messages and passing them to stored procedures for processing. Both SOAP 1.0 and SOAP 1.1 messages and data types are supported. You may use all base SQL data types, as well as heterogeneous arrays, as both arguments and return values of Virtuoso SOAP services. A full-featured set of functions for handling SOAP objects is provided. Services using a transport mechanism other than HTTP can also be constructed using the API. The SOAP framework may be used independently of any of the other web-related services.

Virtuoso/PL can also issue requests to SOAP servers. SOAP can be used to access any application servers, including those running within the Virtuoso server.

The Virtuoso SOAP server extends Virtuoso/PL parameter handling by adding complex data types declared with XML schema as parameter values for stored procedures. The Virtuoso SOAP server provides automatic validation of the parameters in requests, based on schema declarations.

17.1.2. Handling of SOAP HTTP Requests

The Virtuoso web server recognizes SOAP HTTP requests and their version in the POST method handler. When SOAPMethodName or SOAPAction HTTP header attributes are present with

Content-Type: text/xml

, the server initiates SOAP call handling. The XML namespace of the SOAP method name is stripped off and Virtuoso searches for a stored procedure with the same name, ignoring case.

The search is done within the default qualifier of the SQL user account assigned for SOAP call execution defined for the virtual host. For example, if the database user assigned in the virtual host's definition for SOAP execution is called SOAPDBUSER and this user has a default qualifier 'SOAPDB' and the request contains an invocation of method called

OurSoapMethod

, Virtuoso would attempt to find a stored procedure named SOAPDB.SOAPDBUSER.OurSoapMethod .

When a matching stored procedure is found, any of its parameters that have names matching parameter entity names in the SOAP call are bound to the call parameter. The parameter name match is also case-insensitive.

Virtuoso maps the procedure parameter datatypes internally by casting from XML data (a string) to the declared parameter datatype of the stored procedure. There is one exception: When an array is being passed, the server creates an array with values of types inferred from the XML Schema of its elements. It is possible to declare that a user defined SQL type be used to represent a specific XML element in a SOAP request. Thus SQL objects can be constructed and serialized automatically. Note that this also means that the implementation of the user defined type instance may be in a hosted language, thus Java or CLR code may be transparently involved.

Two special parameters - ws_soap_headers and ws_http_headers - are available to a stored procedure handling a SOAP method invocation. If declared as input parameters for the procedure, ws_soap_headers must contain an XML parse tree of the SOAP:Header in same format as returned by xml_tree() . ws_http_headers should hold a one-dimensional array of attribute/value pairs representing the HTTP header fields in the request.

17.1.3. Extending Datatypes for SOAP Objects

Complex datatypes can be defined using XMLSchema and represented by WSDL. Any of the declared types may be used as arguments and return types of Virtuoso/PL procedures. Any procedures can thus be exposed as SOAP methods.

Complex data type definitions are used for values that cannot be contained by simple scalar datatypes. Typical examples are arrays of scalars, structures of scalars, arrays of structures or structures of arrays. A complex datatype may contain scalar and complex datatypes. When a complex type is used in the definition of another complex type, the definition of the contained complex type must exist.

In addition to 'usual' complex types as structures and arrays Virtuoso implements support for 'choice', 'enumeration', anyType and anyElement and extensions to the simple types. Inheritance of complex types is also possible and is discussed further in next chapter.

The 'nillable' and 'minOccurs' attributes in schema definitions have special meaning for PL values returned by PL procedure via SOAP. If this attribute is 'true' then output of NULL values will be serialized in their XML form with XMLSchema instance attribute 'nil' as 'true'. Otherwise if elements have 'minOccurs' equal to 0 (zero), the element will be omitted. If minOccurs is equal to 1 (one) an empty element will be sent to the client. The same algorithm applies to the serialization of PL values passed as parameters to soap_client() function. Therefore it's important to make proper use of these attributes when defining complex structures.

The '__VOID__' string constant has a special meaning in XMLSchema Datatypes. It is used to designate no output for return value. In other words returned value from PL procedure will not be serialized nor exposed in the WSDL file.

You define complex datatypes using soap_dt_define() . The function accepts a schema definition excerpt, based on the element complexType . The definition must be a valid XML document.

Example 17.1. Declaring and using complex datatypes in SOAP

In this example we define two complex datatypes. The first one, SOAPStruct , consists of scalars; the second one, ArrayOfSOAPStruct , is an array of these structures. These schema excerpts are stored in the filesystem as struct.xsd and array.xsd .

struct.xsd:


<!-- a SOAPStruct type declaration
     file name: struct.xsd -->

<complexType name="SOAPStruct"
   targetNamespace="http://tempuri.tmp/"
   xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/"
   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
   xmlns="http://www.w3.org/2001/XMLSchema"
   xmlns:tns="services.wsdl">

   <sequence>
     <element name="varString" type="string" nillable="true"/>
     <element name="varInt" type="int" nillable="true"/>
     <element name="varFloat" type="float" nillable="true"/>
   </sequence>
</complexType>

array.xsd:


<!-- array of SOAPStruct
     file name: array.xsd -->

<complexType name="ArrayOfSOAPStruct"
   targetNamespace="http://tempuri.tmp/"
   xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/"
   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
   xmlns="http://www.w3.org/2001/XMLSchema"
   xmlns:tns="services.wsdl">

   <complexContent>
   <restriction base="enc:Array">
   <sequence>
   <element name="item" type="tns:SOAPStruct" minOccurs="0" maxOccurs="unbounded"/>
   </sequence>
   <attribute ref="enc:arrayType" wsdl:arrayType="tns:SOAPStruct[]"/>
   <attributeGroup ref="enc:commonAttributes"/>
   <attribute ref="enc:offset"/>
   </restriction>
   </complexContent>
</complexType>

Next, we issue commands to define a new complex datatype. Correct order is important. (SQL> is the prompt of the Interactive SQL utility included with Virtuoso and should not be typed)

SQL> DB..soap_dt_define ('SOAPStruct', file_to_string ('struct.xsd'));
SQL> DB..soap_dt_define ('ArrayOfSOAPStruct', file_to_string ('array.xsd'));

[Note] Note:

The WSDL specification requires that array names be prefixed with ArrayOf .

17.1.4. Inheritance of Datatypes for SOAP Objects

The Virtuoso SOAP server implements handling of inherited XSD types. The simple example of such relation between types can be explained as

   Type  A, also know as the 'base' type,
   and type B an extension of A.
   +---+   +---+
   | a |-->

| c |
   | b |   | d |
   +---+   +---+

   which can be defined by two separate types without relation

   A type   B type
   +---+   +---+
   | a |   | a |
   | b |   | b |
   +---+   +- -+
           | c |
           | d |
           +---+
   

But when type A has changed, type B will not be changed in second representation. This is because B is not a relative to A per se.

To work in such situations Virtuoso SOAP server handles extensions to XSD types as follows:

  1. each type and base type have defined a User Defined SQL type (UDT).

  2. the XSD types defined for SOAP processing are defined with UDT relation (see soap_dt_define)

  3. the inheritance is declared with 'extension' element in XSD type declaration

When we have these preliminaries the WSDL will declare in 'schema' part of WSDL all depending types. Furthermore the SOAP processor will handle inherited members of derived types.

Example 17.2. Declaration and usage of depending types

Consider the following XSD and User Defined Type declaration for a base type 'BaseStruct':

        <!-- XSD type declaration, file base.xsd -->

	<complexType name="BaseStruct">
		<sequence>
			<element name="floatMessage" type="xsd:float"/>
			<element name="shortMessage" type="xsd:short"/>
		</sequence>
	</complexType>

	-- corresponding user defined sql type
        create type DB.DBA.BaseStruct as (floatMessage real, shortMessage int __soap_type 'short');
    

Furthermore we are extending the BaseStruct with adding three more elements (members) with declaration of ExtendedStruct:

        <!-- XSD type declaration, file ext.xsd -->

	<complexType name="ExtendedStruct">
		<complexContent>
			<extension base="tns:BaseStruct">
				<sequence>
					<element name="stringMessage" type="xsd:string"/>
					<element name="intMessage" type="xsd:int"/>
					<element name="anotherIntMessage" type="xsd:int"/>
				</sequence>
			</extension>
		</complexContent>
	</complexType>

	-- corresponding user defined SQL type
       create type DB.DBA.ExtendedStruct under DB.DBA.BaseStruct as (
	    stringMessage nvarchar __soap_type 'string',
	    intMessage int __soap_type 'int',
	    anotherIntMessage int __soap_type 'int');
    

Once we are done with declarations as XSD files and user defined SQL types, we must register them as SOAP types for processing:

SQL> soap_dt_define ('', file_to_string ('base.xsd'), 'DB.DBA.BaseStruct');
SQL> soap_dt_define ('', file_to_string ('ext.xsd'), 'DB.DBA.ExtendedStruct');
    

Now we are able to create a PL procedure to use as a SOAP method, which simply will accept an ExtendedStruct and echo it back to the client.

create procedure
echoExtendedStruct (in param DB.DBA.ExtendedStruct __soap_type 'http://soapinterop.org/types:ExtendedStruct')
returns DB.DBA.ExtendedStruct __soap_type 'http://soapinterop.org/types:ExtendedStruct'
{
  --  All members  of DB.DBA.ExtendedStruct and DB.DBA.BaseStruct are available in param.
  return param;
}
;

grant execute on echoExtendedStruct to SOAP;
    

The SOAP request to that method will be as follows:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
  xmlns:m0="http://soapinterop.org/types">
  <SOAP-ENV:Body>
    <m:echoExtendedStruct xmlns:m="http://soapinterop.org/wsdl">
      <param xsi:type="m0:ExtendedStruct">
        <floatMessage xsi:type="xsd:float">3.14159</floatMessage>
        <shortMessage xsi:type="xsd:short">4096</shortMessage>
        <stringMessage xsi:type="xsd:string">String</stringMessage>
        <intMessage xsi:type="xsd:int">0</intMessage>
        <anotherIntMessage xsi:type="xsd:int">0</anotherIntMessage>
      </param>
    </m:echoExtendedStruct>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
    

The SOAP response to the above request will be as follows:


<SOAP:Envelope
SOAP:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:dt="urn:schemas-microsoft-com:datatypes"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"
xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/07/secext"
xmlns:ref="http://schemas.xmlsoap.org/ws/2002/04/reference/"
xmlns:ns0="http://soapinterop.org/types" xmlns:wsdl="services.wsdl">

  <SOAP:Body>
    <cli:echoExtendedStructResponse xmlns:cli="http://soapinterop.org/wsdl">
      <CallReturn xsi:type="ns0:ExtendedStruct">
        <stringMessage xsi:type="xsd:string">String</stringMessage>
        <intMessage xsi:type="xsd:int">0</intMessage>
        <anotherIntMessage xsi:type="xsd:int">0</anotherIntMessage>
        <floatMessage xsi:type="xsd:float">3.14159</floatMessage>
        <shortMessage xsi:type="xsd:short">4096</shortMessage>
      </CallReturn>
    </cli:echoExtendedStructResponse>
  </SOAP:Body>
</SOAP:Envelope>
    
[Note] Note:

Although the namespace declarations of XSD types are skipped for better readability, these must be present when declaring (see the Extending Datatypes for SOAP Objects section, discussed earlier)


17.1.5. Complex Types in PL Procedure and UDT Method Definition

Virtuoso/PL allows parameters to be declared as complex objects (structures and arrays) without special XMLSchema datatype defined. To declare a structure as a type of a parameter an UDT must be created and parameter to have it as datatype reference. Also all permitted datatypes (including UDTs) could be declared as elements of an ARRAY of unlimited or limited length.

Important: when a UDT is used in a SOAP context, it MUST be granted to the SQL user for SOAP invocation. In other words the user on whose behalf the SOAP call is processed.

Example 17.3. Procedure definition with a input and output as a structure

The following example defines a UDT 'SOAP_Struct' (containing varchar, integer and float members) and declares the input parameter and return value of a PL procedure to be of the SOAP_Struct type. The input will be verified, UDT will be instantiated with given values for members and it will be echoed back to the client.

	    create type SOAP_Struct as (varString varchar, varInt integer, varFloat real);

	    create procedure echoStruct (in s DB.DBA.SOAP_Struct) returns DB.DBA.SOAP_Struct
	    {
	      return s;
	    };
	    

Example 17.4. Procedure definition with a input and output as an integer array

This example declares that input must be an array of integer values with maximum length of 5. If input or output contains more than five integers then a SOAP Fault will be sent back to the client containing an appropriate error message ; otherwise the input array will be echoed back.

	    create procedure echoIntArray (in ia integer array[5]) returns integer array[5]
	    {
	      return ia;
	    };
	    

Example 17.5. Procedure definition with a input and output as a two-dimensional varchar array

This example declares that the input must be an array of integer array values with unlimited length. If the input SOAP message contains a valid array following the current XML encoding rules then an array of integer arrays (vector containing vectors of integers) will be created and passed to the procedure. On success the input array will be echoed back to the client.

	    create procedure echoIntMulArray (in iaa integer array array) returns integer array array
	    {
	      return iaa;
	    };
	    

Example 17.6. Procedure definition with a input and output as an struct array

This example shows how to use an array of structures (UDTs) and also shows usage of the array type as an member of the structure. The UDT 'SOAP_StructA' is similar to the those in first example except 4the member which is an array of integers. This is to demonstrate that arrays are not limited to the Stored Procedure's parameters declaration, they also can be used as a type of UDT member. Upon success the procedure will echo of the input back to the client.

	    create type SOAP_StructA as (varString varchar, varInt integer, varFloat real, varArray integer array);

	    create procedure echoStructArray (in sa DB.DBA.SOAP_StructA array) returns DB.DBA.SOAP_StructA array
	    {
	      return sa;
	    };
	    

The SOAP request to an endpoint which exposes the echoStructArray as a document/literal encoded SOAP method would be as follows:

<?xml version="1.0" ?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Body>
    <ns0:echoStructArray xmlns:ns0="http://temp.uri">
      <sa>
        <item>
          <varString>abcd</varString>
          <varInt>1234</varInt>
          <varFloat>3.14</varFloat>
          <varArray>
            <item>3</item>
            <item>4</item>
          </varArray>
        </item>
      </sa>
    </ns0:echoStructArray>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
	    

The SOAP server will receive and array of one element containing a structure with string, integer, float and integer array of two elements. Then the response from the SOAP server to the requestor will be:

<?xml version="1.0" ?>
<SOAP:Envelope xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP:Body>
    <cli:echoStructArrayResponse xmlns:cli="http://temp.uri">
      <CallReturn>
        <item>
          <varString>abcd</varString>
          <varInt>1234</varInt>
          <varFloat>3.14</varFloat>
          <varArray>
            <item>3</item>
            <item>4</item>
          </varArray>
        </item>
      </CallReturn>
    </cli:echoStructArrayResponse>
  </SOAP:Body>
</SOAP:Envelope>
	    

See also the WSDL file generation section for details how such PL procedures with parameters of complex datatypes are exposed via SOAP enabled virtual HTTP directories.

17.1.6. Complex Types in Procedure Definition using a pre-defined XML Schema datatypes

Declaration of a complex datatype as a parameter is done by adding a special keyword __soap_type followed by the name of the defined complex type after normal parameter declaration in the parameter list. The type name is given as a string literal. The same syntax extension also applies to declaration of the return type. This is shown in the following example.

Example 17.7. Procedure Definition with Complex Datatype Parameters

We create a procedure that will accept an array of structures (as defined in the previous example) and return it to the client. It instructs the WSDL generator to assign ArrayOfSOAPStruct as the input parameter and return value types when WS.SOAP.echoSOAPArray() is exposed as a SOAP method. The type information is available to SOAP clients that read the WSDL description. Upon receiving an incoming SOAP request, Virtuoso converts the XML representation of the data, after validation, to the form

vector(vector([varchar],[integer],[real]), ...)

and passed to the WS.SOAP.echoSOAPArray . Failed parameter validation is reported to the client.

SQL> CREATE PROCEDURE WS.SOAP.echoSOAPArray (in inArray any __soap_type 'ArrayOfSOAPStruct')
            RETURNS any __soap_type 'ArrayOfSOAPStruct'
    {
      return inArray;
    };

17.1.7. Default SOAP-SQL Datatype Mappings

When no alternative datatype is assigned, the WSDL generator and SOAP server will use the default mapping described below:

Table 17.1. Default datatype mappings in SOAP

Datatype Maps to
integer xsd:int
real xsd:float
double precision xsd:double
numeric xsd:decimal
datetime xsd:timeInstant
any other type xsd:string

The REAL SQL type is mapped to the xsd:float SOAP datatype by default and so loss of precision can occur. To improve the precision, the SOAP server will map the xsd:float to the PL double precision datatype instead, but only if the SOAP type is specified. The explicit declaration of __soap_type 'xsd:float' is required to instruct Virtuoso to use the mapping to double precision.

All strings from a SOAP request declared with the SOAP datatype xsd:string will be treated as NVARCHARs on input. All string data such a CHAR, VARCHAR, or NVARCHAR will be encoded as UTF-8 in a SOAP response. This makes processing of wide character sets in SOAP operations possible.

If a User Defined Type (UDT) is used as a type of parameter and no explicit XML Schema datatype given (see special syntax for PL procedures) then in WSDL will be included as a struct definition. Further upon SOAP processing the input struct will be encoded as a UDT instance and passed to the given PL procedure.

The parameters which are declared as an array (see PL procedure syntax) and having no explicit XML Schema datatype given will be exposed as array by means of SOAP encoding rules (see also 'Use' SOAP option to the virtual directory).

Some SOAP applications need a void return as opposed to an empty return, from SOAP operations. To distinguish the empty return from the void return a special SOAP datatype '__VOID__' has been introduced. This will cause the SOAP server to omit the procedure return value when responding to a SOAP request. Also, the return message will be discarded from the WSDL description file.

17.1.8. Exposing Stored Procedures as SOAP Objects

The special physical path /SOAP/ in the Virtuoso Web server is reserved for SOAP objects. Virtuoso makes available any stored procedure created in the default qualifier of the SOAP user, with execution privileges granted to the SOAP user. You can also use Virtuoso's virtual host mechanism to create new logical paths for accessing SOAP objects. A logical path property soap_user determines the db user for SOAP. If a logical path points to the /SOAP/ special physical path, it will expose any procedures created in the default qualifier of, and with execution privileges to, soap_user to the world as SOAP objects.

If the physical path of /SOAP exists under the VSP root directory then any non-SOAP specific HTTP requests will be directed there for content. This can be useful for helping to establish the presence and location of a SOAP endpoint - some applications attempt a standard HTTP connection first. You might configure a virtual directory, intended for SOAP, with a default page referencing a description of the SOAP endpoint, a page in the <VSPROOT>/SOAP directory, preventing an HTTP 404 style error misleading an application into believing the SOAP endpoint is down regardless of whether it tried to talk SOAP to it or not.

[Note] Note:

Procedures exposed as SOAP procedures run as any other stored procedure in Virtuoso and can call and get return values from other procedures and functions not exposed through SOAP. The ability to execute procedures attached from remote data sources facilitates SOAP-enabling existing database applications in a heterogeneous environment.

Example 17.8. Creating a new virtual host for SOAP execution

Create new user in the database for SOAP:

SQL>CREATE USER SOAPDEMO;

Set the default catalogue/qualifier for the new user to WS. This is where procedures to be used as SOAP objects will be created:

SQL>USER_SET_QUALIFIER ('SOAPDEMO', 'WS');

Create a new virtual host definition, using vhost_define() .

SQL>VHOST_DEFINE (vhost=>'*ini*',lhost=>'*ini*',lpath=>'/mysoapdomain',ppath=>'/SOAP/',soap_user=>'SOAPDEMO');

An existing mapping could be removed using the command:

SQL>VHOST_REMOVE (vhost=>'*ini*',lhost=>'*ini*',lpath=>'/mysoapdomain')
[Note] Note:

'*ini* ' is a special value that instructs Virtuoso to use the default values from the Virtuoso initialization file.

All procedures that are created with the WS.SOAPDEMO qualifier and then granted execution to SOAPDEMO will be visible to SOAP. Make a simple SOAPTEST procedure and grant the appropriate privileges to the SOAPDEMO user:

SQL> create procedure
  WS.SOAPDEMO.SOAPTEST (in par varchar)
{
  return (upper(par));
};

SQL> grant execute on WS.SOAPDEMO.SOAPTEST to SOAPDEMO;

The SOAP object may now be tested by using the soap_client() function, which returns a vector representation of the SOAP object returned by the call. The example below simply extracts the returned string with aref() , as the exact format of the object returned is known:

SQL>select aref(aref(
	soap_client (url=>sprintf ('http://example.com:%s/mysoapdomain', server_http_port ()),
	operation=>'SOAPTEST',
	parameters=>vector('par', 'demotext')),
	1), 1);
callret
VARCHAR
_______

DEMOTEXT

Printing the output on the console or server log with dbg_obj_print() would output something like:

(("SOAPTESTResponse" ) (("CallReturn" ) "DEMOTEXT" ) )

The automatic service description generation can be verified by retrieving http://<server:port>/mysoapdomain/services.wsdl , and preferably tested by pointing a web browser at http://<server:port>/mysoapdomain/services.vsmx

SQL> select http_get (sprintf ('http://example.com:%s/mysoapdomain/services.wsdl', server_http_port()));
callret
VARCHAR
_______________________________________________________________________________

<?xml version="1.0"?>
<definitions
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
 xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
 xmlns:s="services.wsdl"
 xmlns:tns="services.wsdl"
 targetNamespace="services.wsdl"
 name="VirtuosoSOAP" xmlns="http://schemas.xmlsoap.org/wsdl/">

  <types>
  <schema targetNamespace="services.wsdl"
   xmlns="http://www.w3.org/2001/XMLSchema"
   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
    <complexType name="echoStringArrayResponse">
      <sequence>
        <element name="return" type="ArrayOfstring_literal"/>
      </sequence>
    </complexType>
    <complexType name="echoVoid"/>
    <complexType name="ArrayOffloat">
      <complexContent>
        <restriction base="soapenc:Array">
          <sequence>
            <element name="item" type="float" minOccurs="0" maxOccurs="unbounded"/>
          </sequence>
          <attributeGroup ref="soapenc:commonAttributes"/>
          <attribute ref="soapenc:offset" />
          <attribute ref="soapenc:arrayType" wsdl:arrayType="float[]"/>
        </restriction>
      </complexContent>
    </complexType>
    <complexType name="SOAPStruct">
      <sequence>
        <element name="varString" type="string"/>
        <element name="varInt" type="int"/>
        <element name="varFloat" type="float"/>
      </sequence>
    </complexType>
    <complexType name="echoStructResponse">
      <sequence>
        <element name="return" type="SOAPStruct"/>
      </sequence>
    </complexType>
    <complexType name="echoVoidResponse"/>
    <complexType name="ArrayOfString2D">
    ...

17.1.9. Creation of SOAP proxy based on User Defined Types

It is possible to automatically generate PL procedures or UDT classes for invoking a remote SOAP service.

[Tip] See Also

The WSDL_IMPORT_UDT() function for details and examples.

The proxy-creation function WSDL_IMPORT_UDT() performs the following purposes:

retrieve and expand the WSDL file published by the end point to be called
compile the result and make SQL script with UDT definition
generate and register XML Schema definition for special types used in the source service
optionally execute the SQL script generated

Once such UDT SOAP proxy is defined it can be used within application code or be re-exposed as a SOAP service on local server instance (see next chapter how to expose UDT as service).

[Tip] See Also

The Virtuoso Administration Interface provides a web based interface for importing WSDL definitions and creating UDTs and procedures. This can be found in the Virtuoso Server Administration Interface Chapter.

17.1.10. Exposing User Defined Type Methods as SOAP Objects

SQL User Defined Types may define methods. In context of Virtuoso SOAP server they can be exposed as SOAP methods. To do that the UDT must be published at an endpoint. So publishing could be done in two ways: using SQL INSERT statement or using Admin UI: Publishing UI via Virtual directories section.

The published UDTs will then expose all methods to the given virtual directory assigned for SOAP execution. In this case the default constructor will be called for method invocation if the UDT method is non-static.

Note: The method definitions may also contains special SOAP syntax for XML Schema datatypes, using the same options as for PL procedures. (see "PL Procedures and UDT Methods Syntax Affecting WSDL & SOAP Processing" section for details)

The following table specifies which UDTs are published at which end points.

create table SYS_SOAP_UDT_PUB
	    (SUP_CLASS varchar, -- name of the published UDT, referencing SYS_USER_TYPES.UT_NAME
	     SUP_LHOST varchar, -- listen host, referencing HTTP_PATH.HP_LISTEN_HOST
	     SUP_HOST varchar,  -- virtual host, referencing HTTP_PATH.HP_HOST
	     SUP_END_POINT varchar, -- logical path, referencing HTTP_PATH.HP_LPATH
	     primary key (SUP_LHOST, SUP_HOST, SUP_END_POINT, SUP_CLASS))
;
	    

Example 17.9. Exposing a UDT Method using SQL statement

The below code creates a UDT containing two methods: static and non-static and exposes them on a virtual directory '/soap-udt'

create user SOAP_U2;

VHOST_DEFINE (lpath=>'/soap-udt', ppath=>'/SOAP/', soap_user=>'SOAP_U2',
    soap_opts=>
    vector ('ServiceName', 'UDT',
	    'Namespace', 'http://temp.uri',
	    'SchemaNS', 'http://temp.uri',
	    'MethodInSoapAction', 'yes',
	    'elementFormDefault', 'unqualified',
	    'Use', 'encoded')
);

create type MyWebSvc
static method echoStatInt (in a int) returns int,
method echoInt (in a int) returns int;

create static method echoStatInt (in a int)
returns int for MyWebSvc
{
  return a;
}
;

create method echoInt (in a int)
returns int for MyWebSvc
{
  return a;
}
;

-- Important: without grant publishing is not final as
-- user for SOAP invocation will not have permissions to instantiate the UDT nor
-- to call its methods
grant execute on MyWebSvc to SOAP_U2;

-- exposing the UDT methods to the /soap-udt endpoint
insert soft SYS_SOAP_UDT_PUB values ('MyWebSvc', '*ini*', '*ini*', '/soap-udt');

Exposing the methods of a UDT could be done using Admin UI/Virtual Directories: Create a new or edit an existing SOAP enabled virtual directory and navigate to the SOAP options section, click on the 'Publish' button and from presented list of Database qualifiers select the qualifier containing target UDT, then select it from the User Defined Types list and follow the wizard.

17.1.11. Exposing Remote Third Party SQL Stored Procedures as SOAP Services

Virtuoso can expose any of its available PL resources to the SOAP world. This includes data from remote attached tables and procedures. To do this, one needs to write a wrapper procedure in Virtuoso/PL.

Example 17.10. Exposing a MS SQL Server procedure to SOAP using Virtuoso

Here we have a sample MS SQL Server procedure and an accompanying Virtuoso wrapper function. The MS SQL Server function returns a result set based on a simple join query with a filter input. The Virtuoso procedure calls the remote procedure, iterates through the result set returned and produces XML output. First the MS SQL Server procedure:

create procedure ms_remote
        @mask varchar(15)
as
  select c.CustomerID, c.CompanyName, o.OrderDate,
      o.ShippedDate,ol.ProductID, ol.Quantity, ol.Discount
    from Northwind..Customers c
      inner join Northwind..Orders o on c.CustomerID = o.CustomerID
      inner join Northwind.."Order Details" ol on o.OrderID = ol.OrderID
    where c.CustomerID like @mask
;

Then the Virtuoso wrapper function:

create procedure WS.SOAP.ms_remote_call (
  in dsn varchar, in uid varchar, in pwd varchar, in mask varchar)
{
  declare m, r, ses any;
  vd_remote_data_source (dsn, '', uid, pwd);
  rexecute (dsn, 'ms_remote ?', null, null, vector (mask), 1000, m, r);
  ses := string_output ();
  http ('<?xml version="1.0" ?>\n<remote>\n', ses);
  if (isarray(m) and isarray (r))
    {
      declare i, l, j, k integer;
      declare md, rs any;
      md := m[0];
      i := 0; l := length (md); k := length (r); j := 0;
      while (j < k)
       {
	 http ('<record ', ses);
         i:=0;
         while (i < l)
           {
	     dbg_obj_print (md[i][0],r[j][i]);
	     http (sprintf (' %s="%s"', trim(md[i][0]), trim(cast (r[j][i] as varchar))), ses);
             i := i + 1;
	   }
	 http (' />\n', ses);
         j := j + 1;
       }
    }
  http ('</remote>', ses);
  return string_output_string (ses);
};

Now, as before, we grant execute rights to the SOAP user:

grant execute on WS.SOAP.ms_remote_call to SOAP;
    

The remote procedure ms_remote() can now be accessed via SOAP.


[Tip] See Also:

The Virtual Database chapter for information regarding use of remote datasources and their tables.

17.1.12. Virtuoso/PL SOAP Client

Virtuoso has generic SOAP client functionality. This was demonstrated in an example above, where we showed that we had correctly exposed a stored procedure as a SOAP object. The entry point to the SOAP client is soap_client () .

17.1.13. Execution Privileges

Virtual directory mappings allow you to define a specific database user on behalf of which to execute code invoked via SOAP. By default Virtuoso disables SOAP calls unless the database account 'SOAP' exists or a virtual directory mapping is defined for SOAP call execution. If we map a logical HTTP path to /SOAP and specify the user 'demo' as the SOAP user then stored procedures or UDT methods will be executed with demo's privileges.

17.1.14. Custom Soap Server Support

Virtuoso allows any VSP page to act as a SOAP endpoint. This permits preprocessing of the SOAP requests to extract additional information - such as one placed for ebXML - and conversion of the SOAP replies to put any additional information in them. SOAP messages with attachments can also be processed this way.

SOAP extensions, such as the ones required for ebXML, can be programmed as VSP services that can handle the additional information contained in the SOAP requests. The xpath_eval() function is useful here. The SOAP server could be called after removing extension information; this removal could be done with an XSL transformation. After the SOAP request is processed, additional information can be placed in the result by another XSL transformation.

Having a SOAP server outside the /SOAP physical path allows a greater degree of control over what procedures are executed by providing a list of mappings. Having this suite of functions allows SOAP requests to be processed outside an HTTP context (for example after doing mime_tree() over an e-mail) and sending the SOAP replies as SMTP messages.

The following built-in functions are relevant in this context:

soap_server()

soap_make_error()

soap_box_xml_entity()

soap_print_box()

http_body_read()

Example 17.11. Sample SOAP 1.1 server

<?vsp
        dbg_obj_print ('vspsoap called');
	declare content_type, soap_method, soap_xml varchar;
	declare payloads any;

	-- get the encoding to find out where the SOAP request should be searched for

        content_type := http_request_header (lines, 'Content-Type');
	if (isstring (content_type))
           content_type := lower (content_type);

	-- get the SOAP method name to execute

        soap_method := http_request_header (lines, 'SOAPAction');
        soap_xml := NULL;
        payloads := NULL;

	-- get the SOAP request
        if (content_type = 'multipart/related')
	  {
	    -- as in SOAP messages with attachments
	    declare attrs any;
	    declare inx integer;
	    declare start_req varchar;

	    -- the SOAP body is in the root part
	    -- so get the root part's name
            start_req := http_request_header (lines, 'Content-Type', 'start');

	    -- loop over the parts and get the root one.
	    -- Others are placed in the payload array

	    inx := 1;
	    soap_xml := null;
	    attrs := vector (1);
	    while (isarray (attrs))
	     {
	       declare content_id varchar;

	       -- get the part's MIME header
	       attrs := get_keyword (sprintf ('attr-mime_part%d', inx), params);

	       if (isarray (attrs))
		 {
		   -- extract the Content-ID from it
		   content_id := get_keyword ('Content-ID', attrs);
		   dbg_obj_print ('cont-id', content_id);

		   if (isstring (content_id))
		     {
		       -- if it is the root part (SOAP request) parse it.
		       if (content_id = start_req)
			 soap_xml := xml_tree_doc (xml_tree (
                      get_keyword (sprintf ('mime_part%d', inx), params)));
		       else
			 {
			   -- otherwise consider it a payload and store a info about the payload
			   -- for later retrieval by get_keyword () VSE based on Content-ID
			   if (payloads is null)
			     payloads := vector (vector (content_id, inx));
			   else
			     payloads := vector_concat (payloads, vector (content_id, inx));
			 }
		     }
		 }
	       inx := inx + 1;
	     }
	  }
	else if (content_type = 'text/xml')
          {
	    -- it's a SOAP request without attachments
            -- so get the POST body and parse it.
	    soap_xml := xml_tree_doc (xml_tree (http_body_read ()));
	  }
        else
	  signal ('42000', 'unsupported encoding');

        -- the things retrieved so far
	dbg_obj_print ('vspsoap message', soap_xml);
	dbg_obj_print ('vspsoap payloads', payloads);

	-- execute the message

	-- catch any subsequent SQL error and generate and return SOAP reply XML for it.

	declare exit handler for SQLSTATE '*' {
	  dbg_obj_print ('vspsoap in error handler for ', __SQL_MESSAGE);
	  declare err_msg varchar;
	  err_msg := soap_make_error ('300', __SQL_STATE, __SQL_MESSAGE);
	  dbg_obj_print ('vspsoap error', err_msg);
	  http (err_msg);

	  -- note the SOAP SQL state - this is required since based on this value the
	  -- HTTP server will not generate any additional reply if the SQL state starts with SOAP
	  -- and this way the client will get a properly formatted reply
	  resignal 'SOAP';
	};

        -- now check what is required and act accordingly
        if (soap_method = 'ebXML')
          {
	    signal ('42000', 'ebXML not implemented yet');
	  }
        else if (soap_method in ('fake#test'))
          {
	    declare res any;

	    -- note the mapping here : the SOAP call to fake:test will result in a
            -- call to DB.DBA.SOAPTEST PL procedure and it's results returned.

	    res := soap_server (soap_xml, soap_method, lines, 11,
                    vector ('fake:test', 'DB.DBA.SOAPTEST'));

	    dbg_obj_print ('vspsoap result', res);
	    http (res);
	  }
        else
	  {
	    -- simple signal will do as this will be cached by the handler
          -- and formatted as an SOAP error XML
	    signal ('42000', concat ('Procedure ', soap_method, ' not defined'));
	  }
?>


17.1.15. PL Procedures and UDT Methods Syntax Affecting WSDL & SOAP Processing

Special PL syntax can be applied to any of the parameters (including the return value) in a declaration. All of these begins with __SOAP_ prefix and have special meaning. To manipulate more than the XMLSchema type representation and SOAP encoding style, extended syntax is available. With this syntax we can further override the default request/response namespace, name of the output elements, "soapAction" corresponding to the PL procedure and such.

The syntax is as follows:

   ...
   CREATE (PROCEDURE|METHOD) ([param_decl [rout_alt_type]] ...) { [BODY] } [RETURNS ....] [rout_alt_type]
   ...

rout_alt_type
	:  /* no SOAP options */
	| soap_kwd STRING opt_soap_enc_mode 	/* the basic syntax */
	| __SOAP_OPTIONS '(' soap_kwd EQUALS STRING opt_soap_enc_mode ',' soap_proc_opt_list ')'/* extended syntax */
	;

soap_proc_opt_list
	: soap_proc_opt
	| soap_proc_opt_list ',' soap_proc_opt
	;

soap_proc_opt /* extension options as PartName:='part2' */
	: NAME EQUALS signed_literal
	;

soap_kwd
	: __SOAP_TYPE  	/* denotes XML datatype, RPC encoding style if applied to the procedure */
	| __SOAP_HEADER	/* the parameter is a message in the SOAP Header */
	| __SOAP_FAULT	/* the parameter is a message in SOAP Fault */
	| __SOAP_DOC	/* applies to the procedure, free-form of encoding (literal) */
	| __SOAP_XML_TYPE /*applies to the parameters, the input will be XML tree */
	| __SOAP_DOCW		/* applies to the procedure, literal encoding in style like RPC */
	| __SOAP_HTTP		/* HTTP GET/POST binding will be used */
	;

opt_soap_enc_mode 		/* which part of traffic will be encapsulated and in what way : DIME or MIME */
	: /* no encapsulation */
	| __SOAP_DIME_ENC IN
	| __SOAP_DIME_ENC OUT
	| __SOAP_DIME_ENC INOUT
	| __SOAP_MIME_ENC IN
	| __SOAP_MIME_ENC OUT
	| __SOAP_MIME_ENC INOUT
	;

param_decl
	: (IN|OUT|INOUT) param_name data_type_ref [(DEFAULT|:=)	literal]
	;

data_type_ref
	: (data_type_name|udt_name) [ARRAY [intnum] ...]
	;

The above syntax can be applied to the parameter and to the whole procedure, so both places designate different purposes and limitations. When it is applied to the parameter the following keywords can be used: __SOAP_TYPE, __SOAP_HEADER, __SOAP_FAULT and __SOAP_XML_TYPE. The __SOAP_TYPE means that only XSD type will be used to interpret the data, in contrast __SOAP_XML_TYPE designates no deserialization from XML, only parses the parameter XML representation to XML tree and passes it to the procedure. The __SOAP_HEADER and __SOAP_FAULT designate that parameter will be exposed in the SOAP Header or in the SOAP Fault elements. In the second case, that parameter needs to be an 'OUT' parameter (not IN or INOUT). The string after these keywords always denotes the XSD type for SOAP serialization. When it is applied to the PL procedure (after procedure's body), the __SOAP_TYPE, __SOAP_DOC, __SOAP_DOCW, __SOAP_HTTP, __SOAP_DIME_ENC and __SOAP_MIME_ENC can be used. The string after these keywords always denotes the XSD type for SOAP serialization, except __SOAP_DIME_ENC and __SOAP_MIME_ENC which are used for other purposes and can be combined with other keywords. The __SOAP_TYPE denotes RPC style encoding, __SOAP_DOC for document literal (bare parameters) encoding, __SOAP_DOCW for the free-form literal (wrapped) encoding. __SOAP_HTTP is used to denote HTTP style binding instead of SOAP one, in that way procedure can be called via HTTP GET/POST methods without SOAP XML encoding.

The following keywords are supported as extended options:

PartName - changes the name of a OUT parameter to the string as specified, affects WSDL generation and SOAP serialization.
RequestNamespace - designate namespace for the message in the request, affects header, fault and body WSDL declaration, and serialization of SOAP in RPC encoding style.
ResponseNamespace - the same as RequestNamespace, but for SOAP response and output in WSDL declaration.
soapAction - sets the 'soapAction' attribute in WSDL generation, can be applied to the procedure only.

The RequestNamespace and ResponseNamespace can be used only for the procedure and together with the __SOAP_FAULT and __SOAP_HEADER keywords.

The 'ARRAY' modifier to the SQL datatype is allowed when no XML Schema datatype is assigned to the given parameter of the PL procedure or UDT method. In this case the input and output value will be verified to confirm to the rules applicable for an array. Furthermore in this case an XSD definition will be added in the WSDL file at run time.

Example 17.12. SOAP Extension

This example shows both approaches to define parameters and SOAP encoding style. In practice this definition is part of the Interop tests round 4 (group H). The meaning of this is: the SOAP operation is uses RPC encoding style, 'whichFault' is integer, 'param1' and 'param2' are strings. The out parameters 'part2_1' and 'part2_2' will be printed in SOAP:Fault element (see Exposing & Processing SOAP Fault Messages for more details). The interesting fact is that the last two parameters will be serialized as "part2" in different namespaces. And finally no return of the SOAP operation is defined (it's empty).

create procedure
"echoMultipleFaults3" (
    in whichFault int __soap_type 'http://www.w3.org/2001/XMLSchema:int',
    in param1 varchar __soap_type 'http://www.w3.org/2001/XMLSchema:string',
    in param2 varchar __soap_type 'http://www.w3.org/2001/XMLSchema:string',
    out part2_1 varchar __soap_options (
        __soap_fault:='http://www.w3.org/2001/XMLSchema:string',
	PartName:='part2',
        ResponseNamespace:='http://soapinterop.org/wsdl/fault1'),
    out part2_2 varchar __soap_options (
        __soap_fault:='http://www.w3.org/2001/XMLSchema:string',
	PartName:='part2',
        ResponseNamespace:='http://soapinterop.org/wsdl/fault2')
    )
   __soap_type '__VOID__'
{

  if (whichFault > 2)
    whichFault := mod (whichFault, 3) + 1;
  declare exit handler for sqlstate 'SF000'
    {
      http_request_status ('HTTP/1.1 500 Internal Server Error');
      if (whichFault = 1)
	{
          part2_1 := param1;
	}
      else if (whichFault = 2)
	{
	  part2_2 := param2;
	}
      connection_set ('SOAPFault', vector ('400', 'echoMultipleFaults3'));
      return;
    };
  signal ('SF000', 'echoEmptyFault');
}
;

17.1.16. Exposing & Processing SOAP Header Messages

The Virtuoso SOAP server can be used to process the SOAP Header messages as described in the W3C recommendation (http://www.w3c.org/TR/SOAP , SOAP Header section). They can also be exposed in the WSDL file (services.wsdl) as per W3C WSDL recommendation, using the RPC style encoding.

To bind a message to a SOAP header the special keyword __soap_header is reserved for input and output parameters. The __soap_header followed by the SOAP datatype can be specified for any input or output parameter after normal datatype declarations. This will expose parameters as input or output messages separately. Header binding will also be added to an appropriate section of the WSDL description file for the SOAP message.

Example 17.13. Processing of the SOAP Header element

Consider the following simple SOAP request message with Header element:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
       xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
       xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
       xmlns:xsd="http://www.w3.org/2001/XMLSchema"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <SOAP-ENV:Header>
      <h:echoMeStringRequest
	 xmlns:h="http://soapinterop.org/echoheader/"
	 SOAP-ENV:actor="http://schemas.xmlsoap.org/soap/actor/next"
	 mustUnderstand="1">hello world</h:echoMeStringRequest>
    </SOAP-ENV:Header>
    <SOAP-ENV:Body>
      <m:echoVoid xmlns:m="http://soapinterop.org/"></m:echoVoid>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

This request will be processed by the Virtuoso SOAP server in the following way:

  1. Check whether the echoVoid operation is defined for the given web directory mapping (see: exposing a PL procedure as a SOAP operation)

  2. Test whether there is an in-parameter echoMeStringRequest defined for header processing (see below exposing a header parameters)

  3. Test the mustUnderstand attribute:

    • If mustUnderstand is 0 or is undefined the request will continue without an error.

    • If mustUnderstand is 1 and the actor attribute is not empty or defined with the http://schemas.xmlsoap.org/soap/actor/next special URI, the request will continue without an error.

    • If the two conditions about fail then the request will be rejected with a SOAP MustUnderstand error.

  4. The value of the echoMeStringRequest will be passed as a parameter to the echoVoid procedure.

  5. If the call to the echoVoid succeeds, and the corresponding out parameter is supplied for the SOAP response header then it will be sent to the SOAP client.

The following procedure, which represents a part from echoHeaderBindings iterop test (round C), for the demonstration purposes is designed to process the above SOAP message.

create procedure
Interop.INTEROP.echoVoid
   (in echoMeStringRequest nvarchar := NULL __soap_header 'http://www.w3.org/2001/XMLSchema:string',
    out echoMeStringResponse nvarchar := NULL __soap_header 'http://www.w3.org/2001/XMLSchema:string')
   __soap_type '__VOID__'
{
  if (echoMeStringRequest is not null)
    echoMeStringResponse := echoMeStringRequest;
};
[Note] Note:

The __soap_header keyword that instructs the SOAP server to process this parameter via a SOAP Header with datatype string. Also, the condition in the procedure is needed to return the value in SOAP header only if it is supplied. In some other cases it can be returned always, but in this particular example it will be echoed only if the appropriate header is sent.


17.1.17. Exposing & Processing SOAP Fault Messages

The SOAP:Fault message is used to indicate which part of SOAP request fails, so in its general form it may not have a detailed error. But in some cases it is useful to report in detail which element's input(s) are not correct.

Custom soap:fault messages can be generated by application logic as illustrated below:

Have a procedure to generate custom SOAP:Fault messages with at least one OUT parameter denoted by __SOAP_FAULT instead of __SOAP_TYPE keyword following by type to be returned as literal.

Once we have such parameter(s) declared we can set these to some value (of atomic, simple or complex type) as may be appropriate.

And finally we need to set a special connection variable 'SOAPFault', in order to signal custom SOAP:Fault on output. The value of the connection variable needs to be an array of two elements : An integer of 100, 200, 300, 400 which represents the SOAP:VersionMismatch, SOAP:MustUnderstand, SOAP:Client and SOAP:Server errors. And a string which will be printed in textual explanation, human readable format. In real life we will not need to generate 100 or 200 fault messages, but anyway it is possible to do that.

Example 17.14. Signalling a custom SOAP Fault element

Consider we need to indicate to the client that some string is not a valid input, we can use the custom fault message mechanism as.

create procedure
echoStringFault (in param nvarchar,
                 out part2 nvarchar __soap_fault 'string')
returns nvarchar
{
  declare exit handler for sqlstate 'SF000'
    {
      http_request_status ('HTTP/1.1 500 Internal Server Error');
      -- we are setting the fault message
      part2 := param;
      -- and instructing the SOAP server to make error 400 with text explanation StringFault
      connection_set ('SOAPFault', vector ('400', 'StringFault'));
      ----------------^^^^^^^^^^
      return;
    };
  -- in real life signalling of the error is under some condition
  -- for example if string is longer that 10 chars
  signal ('SF000', 'echoEmptyFault');
}
;

  

And an wire dump of SOAP request

<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" ...>
  <SOAP-ENV:Body>
    <m:echoStringFault xmlns:m="http://soapinterop.org/wsdl">
      <param xsi:type="xsd:string">String</param>
    </m:echoStringFault>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
  

And SOAP Fault response

<?xml version="1.0"?>
<SOAP:Envelope SOAP:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" ...>
  <SOAP:Body>
    <SOAP:Fault>
      <faultcode>SOAP:Server</faultcode>
      <faultstring>[Virtuoso SOAP server] StringFault</faultstring>
      <detail>
        <h:part2 xmlns:h="http://soapinterop.org/wsdl" xsi:type="xsd:string">String</h:part2>
      </detail>
    </SOAP:Fault>
  </SOAP:Body>
</SOAP:Envelope>
  

Please note that in wire dumps there is no namespace declarations for brevity (places are denoted with '...').


17.1.18. Document Literal Encoding

The Virtuoso SOAP server and client support Document Literal encoding for processing as an alternative to SOAP/RPC. The document/literal encoding allows the transmission of any arbitrary valid XML document instead of a SOAP call following rules from section 5 from SOAP/1.1 specification. This allows us to send and receive SOAP packets that are more free-form ("document" style). If you create a service that can accept more free-form type packets, you can employ constraints within the methods so that they can be independent (bare) or serialized as embedded elements within the method's SOAP structure (wrapped parameters style).

Example 17.15. Comparing SOAP Types

Here are examples of SOAP requests that represent the RPC, Doc/Literal and Doc/Literal with parameters types of SOAP message

-- RPC encoded --

<?xml version="1.0"?>
<SOAP-ENV:Envelope
      xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Body>
     <m:echoString
         SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
	 xmlns:m="http://soapinterop.org/">
	   <param0 xsi:type="xsd:string">Enter a message here</param0>
     </m:echoString>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

-- Document Literal --

<?xml version="1.0"?>
<SOAP-ENV:Envelope
      xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Body>
     <ns1:echoStringParam xmlns:ns1="http://soapinterop.org/xsd">Enter a message here</ns1:echoStringParam>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

-- Document Literal with parameters --

<?xml version="1.0"?>
<SOAP-ENV:Envelope
      xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Body>
     <ns1:echoString xmlns:ns1="http://soapinterop.org/xsd">
       <param0>Enter a message here</param0>
     </ns1:echoString>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

SOAP operations can be designated as document/literal or RPC by using the appropriate values in the WSDL description file associated to that SOAP endpoint. As Virtuoso SOAP operations are PL procedures special keywords are used within the procedure to indicate that the document/literal encoding should be used. These special keywords are:

__soap_doc
__soap_docw

These should be placed after the 'returns' keyword in a Virtuoso procedure definition. If 'returns ... __soap_type' is omitted the procedure return type will be equivalent to 'returns varchar __soap_type 'http://www.w3.org/2001/XMLSchema:string'.

Another way to expose a PL procedure or UDT method as a document/literal SOAP methods is to use non-explicit XMLSchema datatypes and to force encoding rules via virtual directory option 'Use' (see also SOAP options section in this chapter and in WSDL chapter section: "Exposing SQL Stored Procedures containing complex datatype definitions" for details and examples).

Example 17.16. SOAP Returns RPC

The following example shows a procedure that will be exposed as an RPC encoded SOAP operation:

create procedure
Import1.echoString (in x nvarchar __soap_type 'http://www.w3.org/2001/XMLSchema:string')
returns nvarchar __soap_type 'http://www.w3.org/2001/XMLSchema:string'
{
  return x;
};

Example 17.17. SOAP Returns Document Literal

The following example shows a procedure that will be exposed as a document literal encoded operation. Note the __soap_doc keyword after 'returns', also in this case __soap_type for each parameter must be specified since the incoming request must be validated by the given schema element declaration (see below for XMLSchema elements declaration).

create procedure
DocLit.echoString (in echoStringParam varchar __soap_type 'http://soapinterop.org/xsd:echoStringParam')
      returns any __soap_doc 'http://soapinterop.org/xsd:echoStringReturn'
{
      return echoStringParam;
};

Example 17.18. SOAP Returns Document Literal with Parameters

The following example shows a procedure that will be exposed as document literal encoding operation with parameters style (wrapped). note the __soap_docw keyword after 'returns'.

create procedure
DocPars.echoString (in echoString varchar __soap_type 'http://soapinterop.org/xsd:echoString')
      returns any __soap_docw 'http://soapinterop.org/xsd:echoStringResponse'
{
      return echoString;
};

In both cases of Document Literal encoding we need to specify the schema element for validation of the incoming SOAP request. Furthermore, this applies to the output elements and return value, as they need to be encoded/validated properly.

Defining WSDL Schema Data Type and Elements

When defining a schema data type (for use within SOAP) the 'targetNamespace' attribute on top level element must be specified in order to describe in which namespace this type is valid. In other words, this type will be used to validate request only within this namespace. Therefore it will be exposed only at this WSDL point where it is used to describe a parameter of an operation associated to it.

[Important] Important

All datatypes and elements defined for use in SOAP must have namespace (QName), which means that 'targetNamespace' must be specified in the definition. All non-qualified types will be rejected in SOAP validation and will not be described in the WSDL file.

Example 17.19. Making an array of string data type

Here is an example demonstrating making an array-of-string datatype:

select soap_dt_define('','<complexType name="ArrayOfstring"
   targetNamespace="http://soapinterop.org/xsd"
   xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/"
   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
   xmlns="http://www.w3.org/2001/XMLSchema"
   xmlns:tns="http://soapinterop.org/xsd">
  <complexContent>
     <restriction base="enc:Array">
	<sequence>
	   <element name="item" type="string" minOccurs="0" maxOccurs="unbounded" nillable="true"/>
	</sequence>
	<attributeGroup ref="enc:commonAttributes"/>
	<attribute ref="enc:arrayType" wsdl:arrayType="string[]"/>
     </restriction>
  </complexContent>
</complexType>');

As document literal encodings work with elements, the elements must be declared as a part of the WSDL file (in the types/schema section). The declared elements can be used to define a doc/literal encoded SOAP operation. This allows for the definition of an element of request and response to enable the server to understand the requests (validate and process) and respond to them (validate the PL data and serialize properly).

Example 17.20. Example of defining elements

Here is an example for the DocLit.echoString SOAP operation using parameters (input parameter and return type):

select soap_dt_define('','<element xmlns="http://www.w3.org/2001/XMLSchema"
                                   name="echoStringParam"
                                   targetNamespace="http://soapinterop.org/xsd" type="string" />');

select soap_dt_define('','<element xmlns="http://www.w3.org/2001/XMLSchema"
                                   name="echoStringReturn"
				   targetNamespace="http://soapinterop.org/xsd" type="string" />');

Extensions to Simple Types

The attribute extensions to the simple types (string, float, etc...) can be defined and used in SOAP messages. In that case a PL value is represented as a special structure of 3 elements as follows:

vector (<composite>, vector (<attr-name>, <attr-value>, ...), <simple type value>)

Example 17.21. An example to define a simple type 'Document'

select soap_dt_define('','<complexType name="Document"
             xmlns="http://www.w3.org/2001/XMLSchema"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             targetNamespace="http://soapinterop.org/xsd">
  <simpleContent>
    <extension base="string">
      <xsd:attribute name ="ID" type="string"/>
    </extension>
  </simpleContent>
</complexType>');

Note that soap_dt_define() does not need the name to be specified when adding a new type, the name/namespace will be extracted from XSD fragment.


WSDL Generation

As the WSDL file generation is based on granted PL procedures exposed to a given SOAP endpoint, only SOAP datatypes and schema elements used for them will be printed in <types> section. If an undeclared datatype is used for an exposed procedure, the error will be printed in an XML comment where the type definition was expected and not found. If an element or datatype refers to other (dependent) types they will also be automatically included. For example, if we have exposed for a SOAP endpoint only the following procedure:

create procedure
INTEROP.echoStructArray (
    in inputStructArray any __soap_type 'http://soapinterop.org/xsd:ArrayOfSOAPStruct')
    __soap_type 'http://soapinterop.org/xsd:ArrayOfSOAPStruct'
{
  return inputStructArray;
};

The schema fragment will consist of both SOAPStructure and ArrayOfSOAPStruct data types declaration:

<schema targetNamespace="http://soapinterop.org/xsd"
   xmlns="http://www.w3.org/2001/XMLSchema"
   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" >
     <complexType name="ArrayOfSOAPStruct" >
       <complexContent>
         <restriction base="soapenc:Array">
           <sequence>
             <element name="item" type="ns0:SOAPStruct" minOccurs="0" maxOccurs="unbounded"/>
           </sequence>
           <attribute ref="soapenc:arrayType" wsdl:arrayType="ns0:SOAPStruct[]"/>
           <attributeGroup ref="soapenc:commonAttributes"/>
           <attribute ref="soapenc:offset"/>
         </restriction>
      </complexContent>
    </complexType>
    <!-- Note this fragment, it's included because ArrayOfSOAPStruct depends from it -->

    <complexType name="SOAPStruct" >
       <all>
	  <element name="varString" type="string" nillable="true"/>
	  <element name="varInt" type="int" nillable="true"/>
	  <element name="varFloat" type="float" nillable="true"/>
       </all>
    </complexType>
</schema>

Multiple Namespaces in WSDL and SOAP

When you define a SOAP operation that has parameters from different namespaces or a type referring to a type in another namespace, both will be defined and printed as a separate schema definition in the WSDL file. Hence, we can define a data type in different namespace so they will live together in a single WSDL file. This allows us to make more complex and flexible document-centric style SOAP operations.

Example 17.22. Example from the SOAP Interop 3 Tests

This example is of the echoEmployee operation from interop 3 tests:

create procedure
Compound2.echoEmployee (in x any __soap_type 'http://soapinterop.org/employee:x_Employee')
      returns any __soap_doc 'http://soapinterop.org/employee:result_Employee'
{
  return x;
};

This will generate the following schema in the WSDL file (only affected parts are shown):

<definitions
...
xmlns:ns1="http://soapinterop.org/person"
xmlns:ns0="http://soapinterop.org/employee"
... >

<types>
	<schema targetNamespace="http://soapinterop.org/person"
		xmlns="http://www.w3.org/2001/XMLSchema"
		xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" elementFormDefault="qualified" >
	   <complexType name="Person" >
	      <sequence>
		<element minOccurs="1" maxOccurs="1" name="Name" type="string"/>
	        <element minOccurs="1" maxOccurs="1" name="Male" type="boolean"/>
	      </sequence>
	   </complexType>
 	</schema>

	<schema targetNamespace="http://soapinterop.org/employee"
		xmlns="http://www.w3.org/2001/XMLSchema"
		xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" elementFormDefault="qualified" >
		<import namespace='http://soapinterop.org/person' />
	    <complexType name="Employee" >
		<sequence>
		   <element minOccurs="1" maxOccurs="1" name="person" type="ns1:Person"/>
		   <element minOccurs="1" maxOccurs="1" name="salary" type="double"/>
	           <element minOccurs="1" maxOccurs="1" name="ID" type="int"/>
	    	</sequence>
	   </complexType>

	   <element name="result_Employee" type="ns0:Employee" />
	   <element name="x_Employee" type="ns0:Employee" />

	</schema>
</types>
...

The PL procedure is defined to use element declaration x_Employee and result_Employee, so this will automatically include the Employee and Person type, upon which they depend. Also, as these types are defined in different namespace, two schema parts will be specified in the WSDL file.


In practice the SOAP developer needs to define elements and types (using soap_dt_define() function), after this, specifying a parameter of PL procedure (or return type) will cause automatic generation of the associated WSDL description in the manner described. Hence, no user intervention is required besides the initial element/type definition.

SOAP Interop round III Endpoints

The following endpoints are pre-defined in the Demo database for SOAP interop III testing (the WSDL files are in the usual services.wsdl for each group of tests):

  • D tests

    • /r3/EmptySA/ - echoString operation with empty ("") soapAction (PRC encoded)

    • /r3/Import1/ - echoString operation, rpc encoded

    • /r3/Import2/ - echoStruct operation, rpc encoded

    • /r3/Import3/ - echoStruct and adds method echoStructArray, rpc encoded (echoStruct is in different namespace)

    • /r3/Compound1/ - Use of attributes in SOAP payload, including attribute on element of simpleType , doc/literal

    • /r3/Compound2/ - Two schema sections, types in 1st schema references types in the 2nd schema, doc/literal

    • /r3/DocPars/ - Reduced version of SOAPBuilders Interop test wsdl with "parameters" way of describing rpc requests in Document/Literal (Document/Literal - Wrapped). Version has operations echoString, echoArrayOfString and echoStruct

    • /r3/DocLit/ - Reduced version of SOAPBuilders InteropTest test, document/literal mode. Version has operations echoString, echoArrayOfString and echoStruct

    • /r3/RpcEnc/ - Reduced version of SOAPBuilders InteropTest test, rpc/encoded mode. Version has operations echoString, echoArrayOfString and echoStruct

  • E tests

    • /r3/List/ - echo of list structure (as shown) , RPC encoded

      struct list {
        int varInt;
        string varString;
        list child; //nullable
      }
      
      
  • F tests

    • /r3/Hdr/ - Modified version of SOAPBuilders InteropTest test, document/literal mode Version has one operation echoString with 2 headers defined.

17.1.19. DIME encapsulation of SOAP messages

The Direct Message Encapsulation (DiME) format is a message format that can be used to encapsulate one or more payloads of arbitrary type and size. This format can be used in place of MIME, but benefits of DIME are ease of parsing and low memory consumption, as DIME does not require loading the whole message body in order to parse it. This is due to the fact that MIME does not have mechanism for specifying the length of payloads etc. DIME prefixes all data with length and type information.

The structure of a DIME message as per draft-nielsen-dime-02 is:

/*
      Legend:

      VERSION = 0x01
      RESRVD  = 0x00
      MB - begin mark
      ME - end mark
      CF - chunked flag
      TYPE_T - type of content type field

      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |         |M|M|C|       |       |                               |
     | VERSION |B|E|F| TYPE_T| RESRVD|         OPTIONS_LENGTH        |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |            ID_LENGTH          |           TYPE_LENGTH         |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                          DATA_LENGTH                          |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                                                               /
     /                     OPTIONS + PADDING                         /
     /                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                                                               /
     /                          ID + PADDING                         /
     /                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                                                               /
     /                        TYPE + PADDING                         /
     /                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                                                               /
     /                        DATA + PADDING                         /
     /                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
   

The MB,ME,CF flags are used to indicate which part of the DIME message is the current block of data. Also, we notice that there are four length fields of fixed length before any data, id or type payload. This is to make the payload easier to read.

The Virtuoso server implements a DIME parser and composer as functions and filter for DIME in SOAP server. Furthermore the Virtuoso WSDL generator can be instructed to specify a DIME extension to the PL procedure exposed as SOAP method. The implementation is based on draft-nielsen-dime-02 RFC proposal. Please note that in the rest of document we will use 'DIME attachment' term , which is about SOAP message with attachment encapsulated with DIME as per draft-nielsen-dime-soap-01. The special case in these messages is type of first payload, so it's supposed to be a SOAP:Envelope message.

Note: Option fields are not supported.

To setup a SOAP endpoint to recognize DIME encapsulation the "DIME-ENC" option to SOAP in virtual directory must be set to 'yes'. Furthermore the WSDL description of endpoint defined as DIME enabled will contain WSDL extensions to DIME.

As not in all cases input and output of the SOAP server needs to be DIME encoded, the particular PL procedure exposed as SOAP method needs to be defined in special way to indicate which traffic is encoded as DIME. This is done by using special keywords on procedure declaration:

   CREATE PROCEDURE ([PARAMETERS DECLARATION])
    [RETURNS TYPE] [(__SOAP_TYPE|__SOAP_DOC|_SOAP_DOCW) 'LITERAL'] [__SOAP_DIME_ENC (IN/OUT/INOUT)]
   

The '__SOAP_DIME_ENC IN' indicate that the procedure expects a DIME attachments on input. This can also be used with OUT and INOUT. This will also be indicated in WSDL file (services.wsdl) as DIME extension in appropriate place of 'soap:operation' element.

The format of SOAP attachments passed to PL procedure defined in this way is an array which consists of three string elements: ID, content-type, and attachment data itself. The same format must be used when parameter is an output which needs to be sent as DIME attachment. There is also a special parameter of PL procedure exposed as SOAP method named 'ws_soap_attachments', so when we have such, all attachments received will be passed thru it. In practice we will not need to use 'ws_soap_attachments' , but anyway it's practical use is to handle unreferenced parameters or to debug the request.

Finally we must say that type of parameter needs to have datatype declared as per 'WSDL Extension for SOAP in DIME' proposal, this is needed for indicating in the WSDL what to expect and how to send the attachment. See also the example below.

Example 17.23. Using DIME encapsulation

Suppose we need to accept a binary attachment and echo it back as string encoded in the popular 'base64'.

We first need to enable DIME encapsulation to an endpoint, with virtual directory definition:

SQL> VHOST_DEFINE (lpath=>'/r4/groupG/dime/rpc', ppath=>'/SOAP/', soap_user=>'interop4',
    soap_opts => vector ('DIME-ENC', 'yes')) ;

The sample PL procedure that takes a binary attachment and transforms it to a base64 encoded string must be declared as:

create procedure
EchoAttachmentAsBase64 (in "In" nvarchar __soap_type 'http://soapinterop.org/attachments/xsd:ReferencedBinary')
returns nvarchar __soap_type 'base64Binary'
__soap_dime_enc in
{
  -- we are getting the attachment as the 3rd element of input,
  -- do the base64 encoding for it and return it to the requestor
  return encode_base64 (cast ("In"[2] as varchar));
}
;

As we have noticed an 'ReferencedBinary' is used to declare 'In' parameter. This has a special purpose for WSDL definition, not for SOAP processing itself. In that case clients are instructed to look at annotation/appinfo of a simple type declared as:

	<complexType name="ReferencedBinary">
		<simpleContent>
			<restriction base="soap-enc:base64Binary">
				<annotation>
					<appinfo>
						<content:mediaType value="application/octetstream"/>
					</appinfo>
				</annotation>
				<attributeGroup ref="soap-enc:commonAttributes"/>
			</restriction>
		</simpleContent>
	</complexType>

This is a little-bit tricky, but this is how to indicate the type of the content and how to resolve the references to the attachments as per the WSDL Extension for SOAP in DIME' proposal.


17.1.20. SOAP Endpoint Options

The virtual directory mechanism provides a special SOAP options for SOAP processing. The SOAP options are name-value pairs contained in a vector: i.e. vector ('name1', 'value1', ....). The SOAP server accepts the following optional parameters settable in the SOAP Options field of the HTTP Virtual Directories Setup interface, or using the vhost_define() function:

ServiceName : name of the SOAP service, will be prefixed with 'Virtuoso'. That name is shown in WSDL description.
Namespace : namespace URI of the SOAP:Body request and response.
HeaderNS : namespace URI for SOAP:Header messages.
FaultNS : namespace URI for SOAP:Fault messages.
MethodInSoapAction : enable or disable appending of the method name in the soapAction attribute (WSDL) after namespace URI.
CR-escape : enable or disable escaping of the CRs on wire as &#0xd
elementFormDefault=(unqualified|qualified); Sets the elementFormDefault for schema specification. if qualified is used the elementFormDefault attribute will be set to qualified, in which case elements required to be unqualified can be declared with value of "form" attribute "unqualified".
Use=(encoded|literal) Sets the default SOAP message encoding rules for those PL procedures which have no explicit encoding rule assigned (see SOAP special syntax for PL procedures). The default is 'encoded' which means to follow SOAP RPC encoding as described in SOAP v1.1 specification section 5.1. The 'literal' mode forces the SOAP server to expose PL procedures with the document/literal parameter encoding style.
MethodInSoapAction=(no|yes|empty|only); Controls soapAction attribute manipulation. no - only URL for soap requests will be printed. yes (default) - the URL and soap method will be printed in form: <url>#<method name>. empty - no value will be specified for soapAction. only - only the method will be specified in form #<method name>.
DIME-ENC : Controls DIME encapsulation on particular SOAP endpoint, valid values are no - (default) not enabled. yes - DIME encapsulation is enabled on endpoint
WS-SEC : WS-Security processing is enabled on the endpoint, if it's yes , otherwise disabled (default)
WSS-KEY : name of PL procedure, which is supposed to return a key instance, used together with "WS-SEC" option.
WSS-Template : path to the file for making the XML Signature in response message. The "[key reference for signing]" denotes using a default template for signing, see WS Security signing SOAP messages.
WSS-Validate-Signature : This option controls the input behavior, i.e. how to verify the incoming message. Possible values are "0", "1" or "2", where 0 does not verify signatures, 1 expects a signature to exist, 2 will verify signature if one exists.
WS-RP : to enable WS-Routing protocol on particular endpoint, if it's yes , otherwise disabled (default).
wsrp-from : Constant for identification of endpoint, an example is 'some@user.network'. This will be included in 'form' element in WS Routing header.