NAME
XML::Compile::SOAP - base-class for SOAP implementations
INHERITANCE
XML::Compile::SOAP is extended by
XML::Compile::SOAP11
XML::Compile::SOAP12
SYNOPSIS
**WARNING** Implementation not finished (but making
** progress): see STATUS statement below.
use XML::Compile::SOAP11::Client;
use XML::Compile::Util qw/pack_type/;
# There are quite some differences between SOAP1.1 and 1.2
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 FILE
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'
, encodings => { b1 => { use => 'literal' }}
);
my $decode_response = $client->compileMessage
( 'RECEIVER'
, header => [ h2 => $h2el ]
, body => [ b2 => $b2el ]
, headerfault => [ ... ]
, fault => [ ... ]
, encodings => { h2 => { use => 'literal' }}
);
my $http = XML::Compile::SOAP::HTTPClient->new
( action => 'http://...'
, address => $server
);
# In nice, small steps:
my @query = (h1 => ..., b1 => ...);
my $request = $encode_query->($query);
my $response = $http->($request);
my $answer = $decode_response->($resonse);
use Data::Dumper;
warn Dumper $answer; # see: a HASH with h2 and b2!
# Simplify your life
my $call = $client->compileRequest($encode_query, $decode_response, $http);
my $result = $call->(h1 => ..., b1 => ...);
print $result->{h2}->{...};
print $result->{b2}->{...};
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.
STATUS
On the moment, the following limitations exist:
- .
-
Only document/literal use is supported, not XML-RPC.
- .
-
Faults and headerfaults not correctly processed.
- .
-
No real-life tests yet.
- .
-
No encoding.
- .
-
Only SOAP1.1 sufficiently implemented (probably)
- .
-
and so on...
So: there is only a small chance that the current code works for you.
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
schemas created internally
version <required>
. encoding_ns => URI
. envelope_ns => URI
. media_type => MIMETYPE
. schemas => XML::Compile::Schema
object
Use this when you have already processed some schema definitions. Otherwise, you can add schemas later with $soap->schames->importDefinitions()
. version => STRING
The simple string representation of the protocol.
Accessors
$obj->encodingNS
$obj->envelopeNS
$obj->prefixPreferences(TABLE)
$obj->schemas
Returns the XML::Compile::Schema object which contains the knowledge about the types.
$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 []
encodings {}
fault []
header undef
mustUnderstand []
role ULTIMATE
roles []
. body => ENTRIES
ARRAY of PAIRS, defining a nice LABEL (free of choice but unique, also w.r.t. the header and fault ENTRIES). 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 roleAbbreviation().
. encodings => HASH-of-HASHes
Message components can be encoded, as defined in WSDL. Typically, some message part has a binding use="encoded"
and encodingStyle
and namespace
parameters. The encodings are organized per label.
. fault => 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 reference. The LABEL will appear in the Perl HASH, to refer to the element in a simple way.
. 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).
. 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
Sender (internals)
$obj->sender(ARGS)
$obj->writerConvertFault(NAME, DATA)
The fault data can be provided in SOAP1.1 or SOAP1.2 format, or even both mixed. The data structure is transformed to fit the used protocol level. See "Faults" in DETAILS.
$obj->writerCreateBody(BODY-DEFS, NAMESPACE-TABLE)
$obj->writerCreateFault(FAULT-DEFS, NAMESPACE-TABLE, FAULTTYPE)
$obj->writerCreateHeader(HEADER-DEFS, NS-TABLE, UNDERSTAND, DESTINATION)
$obj->writerEncstyleHook(NAMESPACE-TABLE)
$obj->writerHook(NAMESPACE, LOCAL, ACTIONS)
Receiver (internals)
$obj->readerEncstyleHook
$obj->readerHook(NAMESPACE, LOCAL, ACTIONS)
$obj->readerParseBody(BODYDEF)
$obj->readerParseHeader(HEADERDEF)
$obj->receiver(ARGS)
Helpers
$obj->roleAbbreviation(STRING)
Translates actor/role/destination abbreviations into URIs. Various SOAP protocol versions have different pre-defined URIs, which can be abbreviated for readibility. Returns the unmodified STRING in all other cases.
SOAP11 only defines NEXT
. SOAP12 defines NEXT
, NONE
, and ULTIMATE
.
DETAILS
Do it yourself, no WSDL
Does this all look too complicated? It isn't that bad. The following example is used as test-case t/82soap11.t, directly taken from the SOAP11 specs section 1.3 example 1.
# for simplification
my $TestNS = 'http://test-types';
my $SchemaNS = 'http://www.w3.org/2001/XMLSchema';
First, the schema (hopefully someone else created for you, because they can be quite hard to create correctly) is in file myschema.xsd
<schema targetNamespace="$TestNS"
xmlns="$SchemaNS">
<element name="GetLastTradePrice">
<complexType>
<all>
<element name="symbol" type="string"/>
</all>
</complexType>
</element>
<element name="GetLastTradePriceResponse">
<complexType>
<all>
<element name="price" type="float"/>
</all>
</complexType>
</element>
<element name="Transaction" type="int"/>
</schema>
Ok, now the program you create the request:
use XML::Compile::SOAP11;
use XML::Compile::Util qw/pack_type/;
my $soap = XML::Compile::SOAP11->new;
$soap->schemas->importDefinitions('myschema.xsd');
my $get_price = $soap->compileMessage
( 'SENDER'
, header =>
[ transaction => pack_type($TestNS, 'Transaction') ]
, body =>
[ request => pack_type($TestNS, 'GetLastTradePrice') ]
, mustUnderstand => 'transaction'
, destination => [ transaction => 'NEXT http://actor' ]
);
INPUT
is used in the WSDL terminology, indicating this message is an input message for the server. This $get_price
is a WRITER. Above is done only once in the initialization phase of your program.
At run-time, you have to call the CODE reference with a data-structure which is compatible with the schema structure. (See XML::Compile::Schema::template() if you have no clue how it should look) So: let's send this:
# insert your data
my %data_in =
( transaction => 5
, request => {symbol => 'DIS'}
);
# create a XML::LibXML tree
my $xml = $get_price->(\%data_in, 'UTF-8');
print $xml->toString;
And the output is:
<SOAP-ENV:Envelope
xmlns:x0="http://test-types"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<x0:Transaction
mustUnderstand="1"
actor="http://schemas.xmlsoap.org/soap/actor/next http://actor">
5
</x0:Transaction>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<x0:GetLastTradePrice>
<symbol>DIS</symbol>
</x0:GetLastTradePrice>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Some transport protocol will sent this data from the client to the server. See XML::Compile::SOAP::HTTPClient, as one example.
On the SOAP server side, we will parse the message. The string $soap
contains the XML. The program looks like this:
my $server = $soap->compileMessage # create once
( 'RECEIVER'
, header => [ transaction => pack_type($TestNS, 'Transaction') ]
, body => [ request => pack_type($TestNS, 'GetLastTradePrice') ]
);
my $data_out = $server->($soap); # call often
Now, the $data_out
reference on the server, is stucturally exactly equivalent to the %data_in
from the client.
Encodings
Faults
It is quite simple to produce a WSDL file which is capable of handling SOAP1.1 and SOAP1.2 protocols with the same messages. However, when faults are being generated, then differences show up. Therefore, writerConvertFault() is used to hide the differences.
SEE ALSO
This module is part of XML-Compile-SOAP distribution version 0.56, built on October 12, 2007. Website: http://perl.overmeer.net/xml-compile/
LICENSE
Copyrights 2007 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