=head1 NAME

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

=head1 INHERITANCE

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

=head1 SYNOPSIS

 **WARNING** Implementation not finished (but making
 ** progress): see STATUS statement below.

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

 # There are quite some differences between SOAP1.1 and 1.2
 my $soap   = XML::Compile::SOAP11->new;

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

 my $h1el = pack_type $myns, $some_element;
 my $b1el = "{$myns}$other_element";  # same, less clean

 # Request, answer, and call usually created via WSDL
 my $encode_query = $soap->compileMessage
   ( 'SENDER'
   , header   => [ h1 => $h1el ]
   , body     => [ b1 => $b1el ]
   , destination    => [ h1 => 'NEXT' ]
   , mustUnderstand => 'h1'
   , encodings => { b1 => { use => 'literal' }}
   );

 my $decode_response = $soap->compileMessage
   ( 'RECEIVER'
   , header    => [ h2 => $h2el ]
   , body      => [ b2 => $b2el ]
   , headerfault => [ ... ]
   , fault     => [ ... ]
   , encodings => { h2 => { use => 'literal' }}
   );

 my $http = XML::Compile::SOAP::HTTPClient->new(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   = $soap->compileCall($encode_query, $decode_query, $http);
 my $result = $call->(h1 => ..., b1 => ...);
 print $result->{h2}->{...};
 print $result->{b2}->{...};

=head1 DESCRIPTION

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

=head2 STATUS

On the moment, the following limitations exist:

=over 4

=item .

Only document/literal use is supported, not XML-RPC.

=item .

Faults and headerfaults not correctly processed.

=item .

No real-life tests yet.

=item .

No encoding.

=item .

Only SOAP1.1 sufficiently implemented (probably)

=item .

and so on...

=back

So: there is only a small chance that the current code works for you.

=head1 METHODS

=head2 Constructors

$obj-E<gt>B<new>(OPTIONS)

=over 4

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

. encoding_ns => URI

. envelope_ns => URI

. media_type => MIMETYPE

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

=over 4

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

=back

=back

=head2 Accessors

$obj-E<gt>B<encodingNS>

=over 4

=back

$obj-E<gt>B<envelopeNS>

=over 4

=back

$obj-E<gt>B<prefixPreferences>(TABLE)

=over 4

=back

$obj-E<gt>B<schemas>

=over 4

Returns the XML::Compile::Schema object which contains the
knowledge about the types.

=back

=head2 Single messages

$obj-E<gt>B<compileMessage>(('SENDER'|'RECEIVER'), OPTIONS)

=over 4

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 I<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 C<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

=over 4

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.

=back

. destination => ARRAY

=over 4

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 I<actor> (soap1.1) or I<role> (soap1.2) URI.  You may use
the predefined actors/roles, like 'NEXT'.  See L<roleAbbreviation()|XML::Compile::SOAP/"Reader (internals)">.

=back

. encodings => HASH-of-HASHes

=over 4

Message components can be encoded, as defined in WSDL.   Typically, some
message part has a binding C<< use="encoded" >> and C<encodingStyle>
and C<namespace> parameters.  The encodings are organized per label.

=back

. fault => ENTRIES

=over 4

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.

=back

. header => ENTRIES

=over 4

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.

=back

. mustUnderstand => STRING|ARRAY-OF-STRING

=over 4

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

=back

. role => URI|ARRAY-OF-URI

=over 4

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

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

=back

. roles => ARRAY-OF-URI

=over 4

Alternative for option C<role>

=back

=back

=head2 Writer (internals)

$obj-E<gt>B<writer>(ARGS)

=over 4

=back

$obj-E<gt>B<writerConvertFault>(NAME, DATA)

=over 4

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 L<DETAILS/Faults>.

=back

$obj-E<gt>B<writerCreateBody>(BODY-DEFS, NAMESPACE-TABLE)

=over 4

=back

$obj-E<gt>B<writerCreateFault>(FAULT-DEFS, NAMESPACE-TABLE, FAULTTYPE)

=over 4

=back

$obj-E<gt>B<writerCreateHeader>(HEADER-DEFS, NS-TABLE, UNDERSTAND, DESTINATION)

=over 4

=back

$obj-E<gt>B<writerEncstyleHook>(NAMESPACE-TABLE)

=over 4

=back

$obj-E<gt>B<writerHook>(NAMESPACE, LOCAL, ACTIONS)

=over 4

=back

=head2 Reader (internals)

$obj-E<gt>B<reader>(ARGS)

=over 4

=back

$obj-E<gt>B<readerEncstyleHook>

=over 4

=back

$obj-E<gt>B<readerHook>(NAMESPACE, LOCAL, ACTIONS)

=over 4

=back

$obj-E<gt>B<readerParseBody>(BODYDEF)

=over 4

=back

$obj-E<gt>B<readerParseHeader>(HEADERDEF)

=over 4

=back

$obj-E<gt>B<roleAbbreviation>(STRING)

=over 4

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.

=back

=head1 DETAILS

=head2 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 C<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' ]
   );

C<INPUT> is used in the WSDL terminology, indicating this message is
an input message for the server.  This C<$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 subroutine 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 L<XML::Compile::SOAP::HTTPClient|XML::Compile::SOAP::HTTPClient>, as one example.

On the SOAP server side, we will parse the message.  The string C<$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 C<$data_out> reference on the server, is stucturally exactly 
equivalent to the C<%data_in> from the client.

=head2 Encodings

=head2 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,
L<writerConvertFault()|XML::Compile::SOAP/"Writer (internals)"> is used to hide the differences.

=head1 DIAGNOSTICS

Error: an input message does not have faults

=over 4

=back

Error: headerfault does only exist in SOAP1.1

=over 4

=back

Error: option 'destination' only for writers

=over 4

=back

Error: option 'mustUnderstand' only for writers

=over 4

=back

Error: option 'role' only for readers

=over 4

=back

Error: option 'roles' only for readers

=over 4

=back

=head1 SEE ALSO

This module is part of XML-Compile-SOAP distribution version 0.55,
built on October 03, 2007. Website: F<http://perl.overmeer.net/xml-compile/>

=head1 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 F<http://www.perl.com/perl/misc/Artistic.html>

