The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

XML::Compile::SOAP - base-class for SOAP implementations

INHERITANCE

 XML::Compile::SOAP has extra code in
   XML::Compile::SOAP::Encoding

 XML::Compile::SOAP is extended by
   XML::Compile::SOAP11
   XML::Compile::SOAP12

SYNOPSIS

 ** Only use SOAP1.1 and WSDL1.1; SOAP1.2/WSDL1.2 not working!
 ** See TODO, at the end of this page

 use XML::Compile::SOAP11::Client;
 use XML::Compile::Util qw/pack_type/;

 my $client = XML::Compile::SOAP11::Client->new;

 # load extra schemas always explicitly
 $client->schemas->importDefinitions(...);

 # !!! The next steps are only required when you do not have
 # !!! a WSDL. See XML::Compile::WSDL11 if you have a WSDL.
 
 my $h1el = pack_type $myns, $some_element;
 my $b1el = "{$myns}$other_element";  # same, less clean

 my $encode_query = $client->compileMessage
   ( 'SENDER'
   , header   => [ h1 => $h1el ]
   , body     => [ b1 => $b1el ]
   , destination    => [ h1 => 'NEXT' ]
   , mustUnderstand => 'h1'
   );

 my $decode_response = $client->compileMessage
   ( 'RECEIVER'
   , header    => [ h2 => $h2el ]
   , body      => [ b2 => $b2el ]
   , faults    => [ ... ]
   );

 my $http = XML::Compile::Transport::SOAPHTTP
    ->new(address => $server);
 my $http = $transport->compileClient(action => ...);

 # Combine into one message exchange:

 my @query    = (h1 => ..., b1 => ...);
 my $request  = $encode_query->($query);
 my ($response, $trace) = $http->($request);
 my $answer   = $decode_response->($response);

 use Data::Dumper;
 warn Dumper $answer;     # see: a HASH with h2 and b2!
 if($answer->{Fault}) ... # error was reported

 # Simplify your life
 # also in this case: if you have a WSDL, this is created for you.
 # This is Document-style SOAP

 my $call   = $client->compileClient
   ( kind      => 'request-response'  # default
   , name      => 'my first call'
   , encode    => $encode_query
   , decode    => $decode_response
   , transport => $http
   );

 # With or without WSDL file the same

 my $result = $call->(h1 => ..., b1 => ...);
 print $result->{h2}->{...};
 print $result->{b2}->{...};

 my ($result, $trace) = $call->(...);  # LIST will show trace
 # $trace is an XML::Compile::SOAP::Trace object

DESCRIPTION

This module handles the SOAP protocol. The first implementation is SOAP1.1 (http://www.w3.org/TR/2000/NOTE-SOAP-20000508/), which is still most often used. The SOAP1.2 definition (http://www.w3.org/TR/soap12/) is quite different; this module tries to define a sufficiently abstract interface to hide the protocol differences.

Be aware that there are three kinds of SOAP:

  1. Document style (literal) SOAP, where there is a WSDL file which explicitly types all out-going and incoming messages. Very easy to use.

  2. RPC style SOAP literal. The WSDL file is not explicit about the content of the messages, but all messages must be schema defined types.

  3. RPC style SOAP encoded. The sent data is nowhere described formally. The data is transported in some ad-hoc way.

METHODS

Constructors

$obj->new(OPTIONS)

    Create a new SOAP object. You have to instantiate either the SOAP11 or SOAP12 sub-class of this, because there are quite some differences (which can be hidden for you)

     Option            --Default
     encoding_ns         <required>
     envelope_ns         <required>
     media_type          application/soap+xml
     schema_instance_ns  $schema_ns . '-instance'
     schema_ns           <required>
     schemas             created internally
     version             <required>

    . encoding_ns => URI

    . envelope_ns => URI

    . media_type => MIMETYPE

    . schema_instance_ns => URI

    . schema_ns => URI

    . schemas => XML::Compile::Schema object

      Use this when you have already processed some schema definitions. Otherwise, you can add schemas later with $soap->schemas->importDefinitions()

    . version => STRING

      The simple string representation of the protocol.

Accessors

$obj->encodingNS

$obj->envelopeNS

$obj->prefixPreferences(TABLE, NEW, [USED])

    NEW is a HASH or ARRAY-of-PAIRS which define prefix-to-uri relations, which are added to the list defined in the TABLE (a HASH-of-HASHes). When USED is set, then it will show-up in the output message. At compile-time, the value of USED is auto-detect.

    This method is called for the soap specification preferred namespaces, and for your compileMessage(prefixes).

$obj->schemaInstanceNS(() {shift->{schemains}})

$obj->schemaNS

$obj->schemas

$obj->version

Single messages

$obj->compileMessage(('SENDER'|'RECEIVER'), OPTIONS)

    The payload is defined explicitly, where all headers and bodies are specified as ARRAY containing key-value pairs (ENTRIES). When you have a WSDL file, these ENTRIES are generated automatically.

    To make your life easy, the ENTRIES use a label (a free to choose key, the part name in WSDL terminology), to ease relation of your data with the type where it belongs to. The element of an entry (the value) is defined as an any element in the schema, and therefore you will need to explicitly specify the element to be processed.

     Option        --Default
     body            []
     destination     []
     faults          []
     header          undef
     mustUnderstand  []
     prefixes        {}
     role            ULTIMATE
     roles           []
     style           'document'

    . body => ENTRIES

      ARRAY of PAIRS, defining a nice LABEL (free of choice but unique, also w.r.t. the header and fault ENTRIES) and an element type name or CODE reference. The LABEL will appear in the Perl HASH only, to be able to refer to a body element in a simple way.

    . destination => ARRAY

      Writers only. Indicate who the target of the header entry is. By default, the end-point is the destination of each header element.

      The ARRAY contains a LIST of key-value pairs, specifing an entry label followed by an actor (soap1.1) or role (soap1.2) URI. You may use the predefined actors/roles, like 'NEXT'. See roleURI() and roleAbbreviation().

    . faults => ENTRIES

      The SOAP1.1 and SOAP1.2 protocols define fault entries in the answer. Both have a location to add your own additional information: the type(-processor) is to specified here, but the returned information structure is larger and differs per SOAP implementation.

    . header => ENTRIES

      ARRAY of PAIRS, defining a nice LABEL (free of choice but unique) and an element type name. The LABEL will appear in the Perl HASH, to refer to the element in a simple way.

      The element type is used to construct a reader or writer. You may also create your own reader or writer, and then pass a compatible CODE reference.

    . mustUnderstand => STRING|ARRAY-OF-STRING

      Writers only. The specified header entry labels specify which elements must be understood by the destination. These elements will get the mustUnderstand attribute set to 1 (soap1.1) or true (soap1.2).

    . prefixes => HASH

      For the sender only: add additional prefix definitions. All provided names will be used always.

    . role => URI|ARRAY-OF-URI

      Readers only. One or more URIs, specifying the role(s) you application has in the process. Only when your role contains ULTIMATE, the body is parsed. Otherwise, the body is returned as uninterpreted XML tree. You should not use the role NEXT, because every intermediate node is a NEXT.

      All understood headers are parsed when the actor (soap1.1) or role (soap1.2) attribute address the specified URI. When other headers emerge which are not understood but carry the mustUnderstood attribute, an fault is returned automatically. In that case, the call to the compiled subroutine will return undef.

    . roles => ARRAY-OF-URI

      Alternative for option role

    . style => 'document'|'rpc-literal'|'rpc-encoded'

Sender (internals)

$obj->sender(ARGS)

$obj->writerCreateBody(BODY-DEFS, NAMESPACE-TABLE)

$obj->writerCreateFault(FAULT-DEFS, NAMESPACE-TABLE, FAULTTYPE)

$obj->writerCreateHeader(HEADER-DEFS, NS-TABLE, UNDERSTAND, DESTINATION)

$obj->writerCreateRpcEncoded(NAMESPACE-TABLE)

    Create a handler which understands RPC encoded specifications.

$obj->writerCreateRpcLiteral(NAMESPACE-TABLE)

    Create a handler which understands RPC literal specifications.

$obj->writerEncstyleHook(NAMESPACE-TABLE)

$obj->writerHook(NAMESPACE, LOCAL, ACTIONS)

Receiver (internals)

$obj->readerEncstyleHook

$obj->readerHook(NAMESPACE, LOCAL, ACTIONS)

$obj->readerParseBody(BODYDEF)

$obj->readerParseFaults(FAULTSDEF)

$obj->readerParseHeader(HEADERDEF)

$obj->receiver(ARGS)

Transcoding

SOAP defines encodings, especially for SOAP-RPC.

Encoding

$obj->array((NAME|undef), ITEM_TYPE, ARRAY-of-ELEMENTS, OPTIONS)

$obj->element(TYPE, NAME, VALUE)

$obj->enc(LOCAL, VALUE, [ID])

$obj->encAddNamespace(PAIRS)

$obj->encAddNamespaces(PAIRS)

$obj->href(NAME, ELEMENT, [ID])

$obj->multidim((NAME|undef), ITEM_TYPE, ARRAY-of-ELEMENTS, OPTIONS)

$obj->nil([TYPE], NAME)

$obj->prefixed(TYPE|(NAMESPACE,LOCAL))

$obj->startEncoding(OPTIONS)

$obj->struct(TYPE, CHILDS)

$obj->typed(TYPE, NAME, VALUE)

Decoding

$obj->dec(XMLNODES)

$obj->decSimplify(TREE, OPTIONS)

$obj->startDecoding(OPTIONS)

Helpers

$obj->replyMustUnderstandFault(TYPE)

    Produce an error structure to be returned to the sender.

$obj->roleAbbreviation(URI)

    Translate a role URI into a simple string, if predefined. See roleURI().

$obj->roleURI(URI|STRING)

    Translates actor/role/destination abbreviations into URIs. Various SOAP protocol versions have different pre-defined STRINGs, which can be abbreviated for readibility. Returns the unmodified URI in all other cases.

    SOAP11 only defines NEXT. SOAP12 defines NEXT, NONE, and ULTIMATE.

DETAILS

SOAP introduction

Although the specification of SOAP1.1 and WSDL1.1 are thin, the number of special constructs are many. And, of course, all poorly documented. Both SOAP and WSDL have 1.2 versions, which will clear things up a lot, but not used that often yet.

WSDL defines two kinds of messages: document style SOAP and rpc style SOAP. In Document style SOAP, the messages are described in great detail in the WSDL: the message components are all defined in Schema's; the worst things you can (will) encounter are any schema elements which require additional manual processing.

I would like to express my personal disgust over RPC style SOAP. In this case, the body of the message is not clearly specified in the WSDL... which violates the whole purpose of using interface descriptions in the first place! In a client-server interface definition, you really wish to be very explicit in the data you communicate. Gladly, SOAP1.2 shares my feelings a little, and speaks against RPC although still supporting it.

Anyway, we have to live with this feature. SOAP-RPC is simple to use on strongly typed languages, to exchange data when you create both the client software and the server software. You can simply autogenerate the data encoding. Clients written by third parties have to find the documentation on how to use the RPC call in some other way... in text, if they are lucky; the WSDL file does not contain the prototype of the procedures, but that doesn't mean that they are free-format.

The encoded RPC messsages are shaped to the procedures which are being called on the server. The body of the sent message contains the ordered list of parameters to be passed as 'in' and 'in/out' values to the remote procedure. The body of the returned message lists the result value of the procedure, followed by the ordered 'out' and 'in/out' parameters.

The literal RPC messages are half-breed document style message: there is a schema which tells you how to interpret the body, but the WSDL doesn't tell you what the options are.

Naming types and elements

XML uses namespaces: URIs which are used as constants, grouping a set of type and element definitions. By using name-spaces, you can avoid name clashes, which have frustrate many projects in history, when they grew over a certain size... at a certain size, it becomes too hard to think of good distriguishable names. In such case, you must be happy when you can place those names in a context, and use the same naming in seperate contexts without confusion.

That being said: XML supports both namespace- and non-namespace elements and schema's; and of cause many mixed cases. It is by far preferred to use namespace schemas only. For a schema xsd file, look for the targetNamespace attribute of the schema element: if present, it uses namespaces.

In XML data, it is seen as a hassle to write the full length of the URI each time that a namespace is addressed. For this reason, prefixes are used as abbreviations. In programs, you can simply assign short variable names to long URIs, so we do not need that trick.

Within your program, you use

  $MYSN = 'long URI of namespace';
  ... $type => "{$MYNS}typename" ...

or nicer

  use XML::Compile::Util qw/pack_type/;
  use constant MYNS => 'some uri';
  ... $type => pack_type(MYNS, 'typename') ...

The XML::Compile::Util module provides a helpfull methods and constants, as does the XML::Compile::SOAP::Util.

Client, Proxy and Server implementations

To learn how to create clients in SOAP, read the DETAILS section in XML::Compile::SOAP::Client. The client implementation is platform independent.

A proxy is a complex kind of server, which in implemented by <XML::Compile::SOAP::Server>, which is available from the XML-Compile-SOAP-Daemon distribution. The server is based on Net::Server, which may have some portability restrictions.

SEE ALSO

This module is part of XML-Compile-SOAP distribution version 0.68, built on March 14, 2008. Website: http://perl.overmeer.net/xml-compile/

LICENSE

Copyrights 2007-2008 by Mark Overmeer. For other contributors see ChangeLog.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See http://www.perl.com/perl/misc/Artistic.html