|
cookbook.soaplite.com
SOAP::Lite for Perl
The Power Of Simplicity
|
According to The Feynman Problem Solving Algorithm steps you need to undertake to solve any problem are really simple:
In most cases you can't avoid step two, but sometimes you need to get the answer as soon as possible. The Cookbook won't teach you, but it might give you an help if you are felling lost working with SOAP or SOAP::Lite.
Citing Larry Wall (foreword for excellent Perl Cookbook written by Tom Christiansen & Nathan Torkington from O'Reilly): 'Easy things should be easy, and hard things should be possible'.
You want to know what SOAP stands for.
Sometimes SOAP stands for Simple Object Access Protocol, and sometimes for Services Oriented Access Protocol, depending on your view on simplicity.
You want to find more information about the SOAP protocol.
Check list of resources on http://www.soaplite.com/#LINKS where you can find references to specifications, articles, tutorials and presentations about SOAP and related technologies.
You are looking for the SOAP toolkit for your favorite language.
Take a look at http://www.soaplite.com/#TOOLKITS
With more than fifty toolkits available (as of May, 2001) in more than 15 languages (Ada, C#, C++, Java, JavaScript, Perl, PHP, Python, Ruby, Smalltalk, Tcl, VB, XSLT, Delphi, Orchard, Smalltalk, K) you shouldn't have a problem finding a toolkit for your language of choice. If you can't find what you are looking for, join the soapbuilders list at http://groups.yahoo.com/group/soapbuilders and start writing your own implementation.
The SOAP directory at http://www.soapware.org/
The SOAP services directory at http://www.xmethods.net/
You want to install SOAP::Lite, but don't have root/administrator privileges.
Install SOAP::Lite into a custom directory using CPAN module:
# perl -MCPAN -e shell
> o conf make_arg -I~/lib > o conf make_install_arg -I~/lib > o conf makepl_arg LIB=~/lib PREFIX=~ INSTALLMAN1DIR=~/man/man1 INSTALLMAN3DIR=~/man/man3 > install SOAP::Lite
Setup PERL5LIB environment variable. Depending on your shell it may
look like:
PERL5LIB=/you/home/directory/lib; export PERL5LIB
lib here is the name of directory where all libraries will be installed
under your home directory.
Run CPAN module with
perl -MCPAN -e shell
and run three commands from CPAN shell
> o conf make_arg -I~/lib > o conf make_install_arg -I~/lib > o conf makepl_arg LIB=~/lib PREFIX=~ INSTALLMAN1DIR=~/man/man1 INSTALLMAN3DIR=~/man/man3
LIB will specify directory where all libraries will reside.
PREFIX will specify prefix for all directories (like lib, bin, man,
though it doesn't work in all cases for some reason).
INSTALLMAN1DIR and INSTALLMAN3DIR specify directories for manuals
(if you don't specify them, install will fail because it'll try to setup
it in default directory and you don't have permissions for that).
Then run:
> install SOAP::Lite
Now in your scripts you need to specify:
use lib '/your/home/directory/lib';
somewhere before 'use SOAP::Lite;'
You want to write a SOAP client.
|
There is some information you need to provide for every SOAP call:
proxy()
method, can be http:, mailto:, tcp: or something else, depending on
what kind of SOAP server will handle your request);
uri() method),
which will help the SOAP server in handling your request;
hi() in our example);
on_action() method,
omitted in this example).
You want to access envelope returned by an autodispatched call.
|
One of the differences between autodispatched call (AD) and call with object
interface (OO) is that the result of OO call is an envelope (SOAP::SOM object)
and the result of AD call is the pure result only, so you don't have direct
access to envelope element and hence need to have some other way to access
headers, returned parameters and attributes.
SOAP::Lite->self returns the SOAP::Lite object that handles
autodispatched calls, SOAP::Lite->self->call returns the envelope
(SOAP::SOM) object for the last call and
SOAP::Lite->self->call->result method accesses the result element
of this envelope.
Following this scheme, you can access any of the elements of the returned
envelope, such as ->fault, ->faultcode, ->faultstring,
->headers, and others.
You need to override the serializer to alter data serialization
|
You want to customize the serializer on client side.
|
You want to customize the deserializer on the client side.
|
You want to send string that has an international characters inside. What to do and what encoding to specify?
|
|
Hard topic. Intent is simple, represent you data on wire, so other side
will understand you. You have your data encoded using some encoding in
your application, so you should either specify this encoding on wire,
or convert your encoding to something you can use on wire. You best
bet is to stay with iso-8859-1 or UTF-8 (and maybe UTF-16),
because every implementation should understand those encodings.
How to do it? The first example shows how to specify encoding on wire
(if you do have this encoding in your application). The second one shows
how to convert your values into UTF-8 and use this (default) encoding.
Convertion itself may not be so straightforward if you use older versions
of Perl. You may need to use Unicode::String or Unicode::Map8 to convert
from your encoding to UTF-8.
If you have strings with non-ASCII values you need to specify type string
explicitly, otherwise autotyping will encode this string as base64 value.
Hopefully next version (after v0.50) will be more tolerant and autotyping
won't encode those strings as base64, so you won't need to specify type
explicitly.
On the server side you'll always get your data encoded as UTF-8 regardless
of encoding specified on wire (data will be converted by XML::Parser).
UTF-8 and Unicode FAQ for Unix/Linux at http://www.cl.cam.ac.uk/~mgk25/unicode.html
You have a list of endpoints that provide the same functionality. You want to use the next endpoint from this list if call failes and do it transparently for the application. Is it possible?
|
You can modify porvided logic and, for example, initialize @alternatives
array when it becomes empty, so calls will be restarted from the beginning.
You can also use the different lists for the different methods.
You need to add some headers in Response message
|
All SOAP::Header elements will be encoded as headers in serialized message. You can use SOAP::Header object exactly as you use SOAP::Data and provide required name, value, namespace and attributes. Position of headers among other parameters is not significant. Headers will be serialized in the same order as they provided, but this behavior might change in future versions.
You want to return undef value
|
Nothing fancy here, variable that has undefined value, or literal
undef will be serialized, and client side gets it as the undef
value.
You need to override the serializer to alter data serialization
|
Everything looks very similar to what is done on a client side (Overriding serializer (client)). You can also share the same serializer between client and server side.
Changing method name in response and Changing attributes for method element in response for useful examples.
You need to override the deserializer to alter data serialization
|
You want to change method name in response message.
|
Overriding the serializer give you full control over serialization process
and envelope generation. envelope() method will be called every time
when message is generated, and this code will drop 'Response' suffix
from method name. Keep in mind, however, that in future versions method
name ($_[2]) in this case might be represented not only as a string,
but also as a SOAP::Data object, so perfectly correct code might look
like:
sub envelope {
UNIVERSAL::isa($_[2] => 'SOAP::Data')
? do { (my $method = $_[2]->name) =~ s/Response$//; $_[2]->name($method) }
: $_[2] =~ s/Response$//
if $_[1] =~ /^(?:method|response)$/;
shift->SUPER::envelope(@_);
}
You want to change attributes for method element in response message.
|
This example will put encodingStyle attribute directly on method element. Considering comment for Changing method name in response, perfectly valid code might look like:
sub envelope {
(UNIVERSAL::isa($_[2] => 'SOAP::Data') ? $_[2] : SOAP::Data->name($_[2]))
->encodingStyle("http://xml.apache.org/xml-soap/literalxml")
if $_[1] =~ /^(?:method|response)$/;
shift->SUPER::envelope(@_);
}
Can be used to tell ApacheSOAP that enclosed XML has literal encoding, as well as in many other cases.
You want to return fault from the method on the server side.
|
SOAP processor will catch every die on the server side and will package
your error as a Fault message, providing you message as the faultstring
and Client as the faultcode. Use Customizing fault advice if you
need more control on returned Fault.
You want to return fault from the method on the server side, but you also
need to have a control on what to specify as a faultcode, faultdetail
and probably faultactor.
|
You can use die in three different modes:
die with string (as in Returning fault, will be reported as faultstring);
die with object (as in die_with_object() method, will be reported as detail);
die with SOAP::Fault object (as in die_with_fault() method), allows you
to specify all parameters of provided Fault.
Both faultcode and faultstring are required, so library will specify
them for you even if you omit them.
You are behind a proxy firewall and want to configure SOAP::Lite to take it into account.
Use environment variable HTTP_proxy to specify proxy like this:
HTTP_proxy=http://proxy.my.com/
Syntax can be different depending on your command processor. You may also specify this variable in your script:
$ENV{HTTP_proxy} = "http://proxy.my.com/";
|
The proxy() method can accept transport-specific parameters that can
be passed as name => value pairs. If value is represented by
more than one element (as in the first example) it should be wrapped into
an array.
Alternatively, the transport() method gives you access to the underlying
module (LWP::UserAgent for HTTP protocol), so any options supported by
that transport module can be specified this way also.
You want to change the timeout value, so your SOAP calls will timeout sooner.
|
You want to access endpoint that uses basic authentication.
|
Since HTTP client functionality is based on LWP::UserAgent you may use the same
techniques as you use with this module. Embedding username:password in URL
is one of the options. Another option is to specify the
get_basic_credentials() method that will be called when asked for
credentials:
|
You want to access endpoint that uses proxy authentication.
You have several options (hope you are not surprised):
HTTP_proxy_user and HTTP_proxy_pass environment
variables for user and password and SOAP::Lite should know how to handle
it properly
|
You want to access SOAP server using SSL.
Specify in your client https protocol instead of http:
|
Is there anything to discuss?
You want to accept cookies from SOAP response and provide it in the next request, probably using cookie-based authentication or for any other reason.
|
Cookies will be taken from the response and provided for the request. You may always add another cookie (or extract what you need after response) using the HTTP::Cookies interface.
You want to enable SOAP::Lite's support for compression on the wire.
|
|
SOAP::Lite provides you with the option for enabling compression on the wire (for HTTP transport only). Both server and client should support this capability, but this should be absolutely transparent to your application. The Server will respond with an encoded message only if the client can accept it (indicated by client sending an Accept-Encoding header with 'deflate' or '*' values) and client has fallback logic, so if server doesn't understand specified encoding (Content-Encoding: deflate) and returns proper error code (415 NOT ACCEPTABLE) client will repeat the same request without encoding and will store this server in a per-session cache, so all other requests will go there without encoding.
Compression will be enabled on the client side if the threshold is specified and the size of current message is bigger than the threshold and the module Compress::Zlib is available.
The Client will send the header 'Accept-Encoding' with value 'deflate' if the threshold is specified and the module Compress::Zlib is available.
Server will accept the compressed message if the module Compress::Zlib is available, and will respond with the compressed message only if the threshold is specified and the size of the current message is bigger than the threshold and the module Compress::Zlib is available and the header 'Accept-Encoding' is presented in the request.
You want to restart Daemon or TCP server implementation, but on restart sockets go into TIME_WAIT for quite some time before expiring. What to do?
Use Reuse => 1 as in:
|
Thanks to Michael Brutsch <mbrutsch@intrusion.com>, Sean Meisner <Sean.Meisner@verizonwireless.com> and others.
http://www.perlfect.com/articles/sockets.shtml
You want to start Daemon server, but access it through different hostnames or aliases.
Do not specify LocalAddr in new() method for HTTP::Daemon:
|
If you do not specify LocalAddr then you can access it with any hostname/IP
alias, including localhost or 127.0.0.1. If you do specify LocalAddr in
->new() then you can only access it from that interface.
Thanks to Michael Percy <mpercy@portera.com>.
You want to talk to a server that accepts only HTTP/1.1 requests. How do you tell the client to send HTTP/1.1 header?
Write handler that will modify request to required protocol:
|
Copyright (C) 2001 Paul Kulchenko. All rights reserved.
Paul Kulchenko (paulclinger@yahoo.com)
Copyright (C) 2001 Paul Kulchenko |
2001/06/18 15:09:01 |