|
NAMESOAP::Lite - Perl's Web Services ToolkitDESCRIPTIONSOAP::Lite is a collection of Perl modules which provides a simple and lightweight interface to the Simple Object Access Protocol (SOAP) both on client and server side.PERL VERSION WARNINGAs of version SOAP::Lite version 1.05, no perl versions before 5.8 will be supported.SOAP::Lite 0.71 will be the last version of SOAP::Lite running on perl 5.005 Future versions of SOAP::Lite will require at least perl 5.6.0 If you have not had the time to upgrade your perl, you should consider this now. OVERVIEW OF CLASSES AND PACKAGES
METHODSAll accessor methods return the current value when called with no arguments, while returning the object reference itself when called with a new value. This allows the set-attribute calls to be chained together.
For example, if you wish to set the HTTP timeout for a SOAP::Lite client to 5 seconds, use the following code: my $soap = SOAP::Lite ->uri($uri) ->proxy($proxyUrl, timeout => 5 ); See LWP::UserAgent.
While it may seem to be an unnecessary operation to set a value that isn't relevant to the message, such as the namespace labels for the envelope and encoding URNs, the ability to set these labels explicitly can prove to be a great aid in distinguishing and debugging messages on the server side of operations.
The following method isn't an accessor style of method but neither does it fit with the group that immediately follows it:
The next four methods used in the "SOAP::Lite" class are geared towards handling the types of events than can occur during the message lifecycle. Each of these sets up a callback for the event in question:
WRITING A SOAP CLIENTThis chapter guides you to writing a SOAP client by example.The SOAP service to be accessed is a simple variation of the well-known hello world program. It accepts two parameters, a name and a given name, and returns "Hello $given_name $name". We will use "Martin Kutter" as the name for the call, so all variants will print the following message on success: Hello Martin Kutter! SOAP message stylesThere are three common (and one less common) variants of SOAP messages.These address the message style (positional parameters vs. specified message documents) and encoding (as-is vs. typed). The different message styles are:
As of 2008, document/literal has become the predominant SOAP message variant. rpc/literal and rpc/encoded are still in use, mainly with scripting languages, while document/encoded is hardly used at all. You will see clients for the rpc/encoded and document/literal SOAP variants in this section. Example implementationsRPC/ENCODEDRpc/encoded is most popular with scripting languages like perl, php and python without the use of a WSDL. Usual method descriptions look like this: Method: sayHello(string, string) Parameters: name: string givenName: string Such a description usually means that you can call a method named "sayHello" with two positional parameters, "name" and "givenName", which both are strings. The message corresponding to this description looks somewhat like this: <sayHello xmlns="urn:HelloWorld"> <s-gensym01 xsi:type="xsd:string">Kutter</s-gensym01> <s-gensym02 xsi:type="xsd:string">Martin</s-gensym02> </sayHello> Any XML tag names may be used instead of the "s-gensym01" stuff - parameters are positional, the tag names have no meaning. A client producing such a call is implemented like this: use SOAP::Lite; my $soap = SOAP::Lite->new( proxy => 'http://localhost:81/soap-wsdl-test/helloworld.pl'); $soap->default_ns('urn:HelloWorld'); my $som = $soap->call('sayHello', 'Kutter', 'Martin'); die $som->faultstring if ($som->fault); print $som->result, "\n"; You can of course use a one-liner, too... Sometimes, rpc/encoded interfaces are described with WSDL definitions. A WSDL accepting "named" parameters with rpc/encoded looks like this: <definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:s0="urn:HelloWorld" targetNamespace="urn:HelloWorld" xmlns="http://schemas.xmlsoap.org/wsdl/"> <types> <s:schema targetNamespace="urn:HelloWorld"> </s:schema> </types> <message name="sayHello"> <part name="name" type="s:string" /> <part name="givenName" type="s:string" /> </message> <message name="sayHelloResponse"> <part name="sayHelloResult" type="s:string" /> </message> <portType name="Service1Soap"> <operation name="sayHello"> <input message="s0:sayHello" /> <output message="s0:sayHelloResponse" /> </operation> </portType> <binding name="Service1Soap" type="s0:Service1Soap"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc" /> <operation name="sayHello"> <soap:operation soapAction="urn:HelloWorld#sayHello"/> <input> <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </input> <output> <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </output> </operation> </binding> <service name="HelloWorld"> <port name="HelloWorldSoap" binding="s0:Service1Soap"> <soap:address location="http://localhost:81/soap-wsdl-test/helloworld.pl" /> </port> </service> </definitions> The message corresponding to this schema looks like this: <sayHello xmlns="urn:HelloWorld"> <name xsi:type="xsd:string">Kutter</name> <givenName xsi:type="xsd:string">Martin</givenName> </sayHello> A web service client using this schema looks like this: use SOAP::Lite; my $soap = SOAP::Lite->service("file:say_hello_rpcenc.wsdl"); eval { my $result = $soap->sayHello('Kutter', 'Martin'); }; if ($@) { die $@; } print $som->result(); You may of course also use the following one-liner: perl -MSOAP::Lite -e 'print SOAP::Lite->service("file:say_hello_rpcenc.wsdl")\ ->sayHello('Kutter', 'Martin'), "\n";' A web service client (without a service description) looks like this. use SOAP::Lite; my $soap = SOAP::Lite->new( proxy => 'http://localhost:81/soap-wsdl-test/helloworld.pl'); $soap->default_ns('urn:HelloWorld'); my $som = $soap->call('sayHello', SOAP::Data->name('name')->value('Kutter'), SOAP::Data->name('givenName')->value('Martin') ); die $som->faultstring if ($som->fault); print $som->result, "\n"; RPC/LITERAL SOAP web services using the document/literal message encoding are usually described by some Web Service Definition. Our web service has the following WSDL description: <definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:s0="urn:HelloWorld" targetNamespace="urn:HelloWorld" xmlns="http://schemas.xmlsoap.org/wsdl/"> <types> <s:schema targetNamespace="urn:HelloWorld"> <s:complexType name="sayHello"> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="name" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="givenName" type="s:string" nillable="1" /> </s:sequence> </s:complexType> <s:complexType name="sayHelloResponse"> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="sayHelloResult" type="s:string" /> </s:sequence> </s:complexType> </s:schema> </types> <message name="sayHello"> <part name="parameters" type="s0:sayHello" /> </message> <message name="sayHelloResponse"> <part name="parameters" type="s0:sayHelloResponse" /> </message> <portType name="Service1Soap"> <operation name="sayHello"> <input message="s0:sayHello" /> <output message="s0:sayHelloResponse" /> </operation> </portType> <binding name="Service1Soap" type="s0:Service1Soap"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc" /> <operation name="sayHello"> <soap:operation soapAction="urn:HelloWorld#sayHello"/> <input> <soap:body use="literal" namespace="urn:HelloWorld"/> </input> <output> <soap:body use="literal" namespace="urn:HelloWorld"/> </output> </operation> </binding> <service name="HelloWorld"> <port name="HelloWorldSoap" binding="s0:Service1Soap"> <soap:address location="http://localhost:80//helloworld.pl" /> </port> </service> </definitions> The XML message (inside the SOAP Envelope) look like this: <ns0:sayHello xmlns:ns0="urn:HelloWorld"> <parameters> <name>Kutter</name> <givenName>Martin</givenName> </parameters> </ns0:sayHello> <sayHelloResponse xmlns:ns0="urn:HelloWorld"> <parameters> <sayHelloResult>Hello Martin Kutter!</sayHelloResult> </parameters> </sayHelloResponse> This is the SOAP::Lite implementation for the web service client: use SOAP::Lite +trace; my $soap = SOAP::Lite->new( proxy => 'http://localhost:80/helloworld.pl'); $soap->on_action( sub { "urn:HelloWorld#sayHello" }); $soap->autotype(0)->readable(1); $soap->default_ns('urn:HelloWorld'); my $som = $soap->call('sayHello', SOAP::Data->name('parameters')->value( \SOAP::Data->value([ SOAP::Data->name('name')->value( 'Kutter' ), SOAP::Data->name('givenName')->value('Martin'), ])) ); die $som->fault->{ faultstring } if ($som->fault); print $som->result, "\n"; DOCUMENT/LITERAL SOAP web services using the document/literal message encoding are usually described by some Web Service Definition. Our web service has the following WSDL description: <definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:s0="urn:HelloWorld" targetNamespace="urn:HelloWorld" xmlns="http://schemas.xmlsoap.org/wsdl/"> <types> <s:schema targetNamespace="urn:HelloWorld"> <s:element name="sayHello"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="name" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="givenName" type="s:string" nillable="1" /> </s:sequence> </s:complexType> </s:element> <s:element name="sayHelloResponse"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="sayHelloResult" type="s:string" /> </s:sequence> </s:complexType> </s:element> </types> <message name="sayHelloSoapIn"> <part name="parameters" element="s0:sayHello" /> </message> <message name="sayHelloSoapOut"> <part name="parameters" element="s0:sayHelloResponse" /> </message> <portType name="Service1Soap"> <operation name="sayHello"> <input message="s0:sayHelloSoapIn" /> <output message="s0:sayHelloSoapOut" /> </operation> </portType> <binding name="Service1Soap" type="s0:Service1Soap"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" /> <operation name="sayHello"> <soap:operation soapAction="urn:HelloWorld#sayHello"/> <input> <soap:body use="literal" /> </input> <output> <soap:body use="literal" /> </output> </operation> </binding> <service name="HelloWorld"> <port name="HelloWorldSoap" binding="s0:Service1Soap"> <soap:address location="http://localhost:80//helloworld.pl" /> </port> </service> </definitions> The XML message (inside the SOAP Envelope) look like this: <sayHello xmlns="urn:HelloWorld"> <name>Kutter</name> <givenName>Martin</givenName> </sayHello> <sayHelloResponse> <sayHelloResult>Hello Martin Kutter!</sayHelloResult> </sayHelloResponse> You can call this web service with the following client code: use SOAP::Lite; my $soap = SOAP::Lite->new( proxy => 'http://localhost:80/helloworld.pl'); $soap->on_action( sub { "urn:HelloWorld#sayHello" }); $soap->autotype(0); $soap->default_ns('urn:HelloWorld'); my $som = $soap->call("sayHello", SOAP::Data->name('name')->value( 'Kutter' ), SOAP::Data->name('givenName')->value('Martin'), ); die $som->fault->{ faultstring } if ($som->fault); print $som->result, "\n"; Differences between the implementationsYou may have noticed that there's little difference between the rpc/encoded, rpc/literal and the document/literal example's implementation. In fact, from SOAP::Lite's point of view, the only differences between rpc/literal and document/literal that parameters are always named.In our example, the rpc/encoded variant already used named parameters (by using two messages), so there's no difference at all. You may have noticed the somewhat strange idiom for passing a list of named parameters in the rpc/literal example: my $som = $soap->call('sayHello', SOAP::Data->name('parameters')->value( \SOAP::Data->value([ SOAP::Data->name('name')->value( 'Kutter' ), SOAP::Data->name('givenName')->value('Martin'), ])) ); While SOAP::Data provides full control over the XML generated, passing hash-like structures require additional coding. WRITING A SOAP SERVERSee SOAP::Server, or SOAP::Transport.FEATURESATTACHMENTS"SOAP::Lite" features support for the SOAP with Attachments specification. Currently, SOAP::Lite only supports MIME based attachments. DIME based attachments are yet to be fully functional.EXAMPLES Client sending an attachment "SOAP::Lite" clients can specify attachments to be sent along with a request by using the "SOAP::Lite::parts()" method, which takes as an argument an ARRAY of "MIME::Entity"'s. use SOAP::Lite; use MIME::Entity; my $ent = build MIME::Entity Type => "image/gif", Encoding => "base64", Path => "somefile.gif", Filename => "saveme.gif", Disposition => "attachment"; my $som = SOAP::Lite ->uri($SOME_NAMESPACE) ->parts([ $ent ]) ->proxy($SOME_HOST) ->some_method(SOAP::Data->name("foo" => "bar")); Client retrieving an attachment A client accessing attachments that were returned in a response by using the "SOAP::SOM::parts()" accessor. use SOAP::Lite; use MIME::Entity; my $soap = SOAP::Lite ->uri($NS) ->proxy($HOST); my $som = $soap->foo(); foreach my $part (${$som->parts}) { print $part->stringify; } Server receiving an attachment Servers, like clients, use the SOAP::SOM module to access attachments transmitted to it. package Attachment; use SOAP::Lite; use MIME::Entity; use strict; use vars qw(@ISA); @ISA = qw(SOAP::Server::Parameters); sub someMethod { my $self = shift; my $envelope = pop; foreach my $part (@{$envelope->parts}) { print "AttachmentService: attachment found! (".ref($part).")\n"; } # do something } Server responding with an attachment Servers wishing to return an attachment to the calling client need only return "MIME::Entity" objects along with SOAP::Data elements, or any other data intended for the response. package Attachment; use SOAP::Lite; use MIME::Entity; use strict; use vars qw(@ISA); @ISA = qw(SOAP::Server::Parameters); sub someMethod { my $self = shift; my $envelope = pop; my $ent = build MIME::Entity 'Id' => "<1234>", 'Type' => "text/xml", 'Path' => "some.xml", 'Filename' => "some.xml", 'Disposition' => "attachment"; return SOAP::Data->name("foo" => "blah blah blah"),$ent; } DEFAULT SETTINGSThough this feature looks similar to autodispatch they have (almost) nothing in common. This capability allows you specify default settings so that all objects created after that will be initialized with the proper default settings.If you wish to provide common "proxy()" or "uri()" settings for all "SOAP::Lite" objects in your application you may do: use SOAP::Lite proxy => 'http://localhost/cgi-bin/soap.cgi', uri => 'http://my.own.com/My/Examples'; my $soap1 = new SOAP::Lite; # will get the same proxy()/uri() as above print $soap1->getStateName(1)->result; my $soap2 = SOAP::Lite->new; # same thing as above print $soap2->getStateName(2)->result; # or you may override any settings you want my $soap3 = SOAP::Lite->proxy('http://localhost/'); print $soap3->getStateName(1)->result; Any "SOAP::Lite" properties can be propagated this way. Changes in object copies will not affect global settings and you may still change global settings with "SOAP::Lite->self" call which returns reference to global object. Provided parameter will update this object and you can even set it to "undef": SOAP::Lite->self(undef); The "use SOAP::Lite" syntax also lets you specify default event handlers for your code. If you have different SOAP objects and want to share the same "on_action()" (or "on_fault()" for that matter) handler. You can specify "on_action()" during initialization for every object, but you may also do: use SOAP::Lite on_action => sub {sprintf '%s#%s', @_}; and this handler will be the default handler for all your SOAP objects. You can override it if you specify a handler for a particular object. See t/*.t for example of on_fault() handler. Be warned, that since "use ..." is executed at compile time all "use" statements will be executed before script execution that can make unexpected results. Consider code: use SOAP::Lite proxy => 'http://localhost/'; print SOAP::Lite->getStateName(1)->result; use SOAP::Lite proxy => 'http://localhost/cgi-bin/soap.cgi'; print SOAP::Lite->getStateName(1)->result; Both SOAP calls will go to 'http://localhost/cgi-bin/soap.cgi'. If you want to execute "use" at run-time, put it in "eval": eval "use SOAP::Lite proxy => 'http://localhost/cgi-bin/soap.cgi'; 1" or die; Or alternatively, SOAP::Lite->self->proxy('http://localhost/cgi-bin/soap.cgi'); SETTING MAXIMUM MESSAGE SIZEOne feature of "SOAP::Lite" is the ability to control the maximum size of a message a SOAP::Lite server will be allowed to process. To control this feature simply define $SOAP::Constants::MAX_CONTENT_SIZE in your code like so:use SOAP::Transport::HTTP; use MIME::Entity; $SOAP::Constants::MAX_CONTENT_SIZE = 10000; SOAP::Transport::HTTP::CGI ->dispatch_to('TemperatureService') ->handle; IN/OUT, OUT PARAMETERS AND AUTOBINDING"SOAP::Lite" gives you access to all parameters (both in/out and out) and also does some additional work for you. Lets consider following example:<mehodResponse> <res1>name1</res1> <res2>name2</res2> <res3>name3</res3> </mehodResponse> In that case: $result = $r->result; # gives you 'name1' $paramout1 = $r->paramsout; # gives you 'name2', because of scalar context $paramout1 = ($r->paramsout)[0]; # gives you 'name2' also $paramout2 = ($r->paramsout)[1]; # gives you 'name3' or @paramsout = $r->paramsout; # gives you ARRAY of out parameters $paramout1 = $paramsout[0]; # gives you 'res2', same as ($r->paramsout)[0] $paramout2 = $paramsout[1]; # gives you 'res3', same as ($r->paramsout)[1] Generally, if server returns "return (1,2,3)" you will get 1 as the result and 2 and 3 as out parameters. If the server returns "return [1,2,3]" you will get an ARRAY reference from "result()" and "undef" from "paramsout()". Results can be arbitrary complex: they can be an array references, they can be objects, they can be anything and still be returned by "result()" . If only one parameter is returned, "paramsout()" will return "undef". Furthermore, if you have in your output parameters a parameter with the same signature (name+type) as in the input parameters this parameter will be mapped into your input automatically. For example: Server Code: sub mymethod { shift; # object/class reference my $param1 = shift; my $param2 = SOAP::Data->name('myparam' => shift() * 2); return $param1, $param2; } Client Code: $a = 10; $b = SOAP::Data->name('myparam' => 12); $result = $soap->mymethod($a, $b); After that, "$result == 10 and $b->value == 24"! Magic? Sort of. Autobinding gives it to you. That will work with objects also with one difference: you do not need to worry about the name and the type of object parameter. Consider the "PingPong" example (examples/My/PingPong.pm and examples/pingpong.pl): Server Code: package My::PingPong; sub new { my $self = shift; my $class = ref($self) || $self; bless {_num=>shift} => $class; } sub next { my $self = shift; $self->{_num}++; } Client Code: use SOAP::Lite +autodispatch => uri => 'urn:', proxy => 'http://localhost/'; my $p = My::PingPong->new(10); # $p->{_num} is 10 now, real object returned print $p->next, "\n"; # $p->{_num} is 11 now!, object autobinded STATIC AND DYNAMIC SERVICE DEPLOYMENTLet us scrutinize the deployment process. When designing your SOAP server you can consider two kind of deployment: static and dynamic. For both, static and dynamic, you should specify "MODULE", "MODULE::method", "method" or "PATH/" when creating "use"ing the SOAP::Lite module. The difference between static and dynamic deployment is that in case of 'dynamic', any module which is not present will be loaded on demand. See the "SECURITY" section for detailed description.When statically deploying a SOAP Server, you need to know all modules handling SOAP requests before. Dynamic deployment allows extending your SOAP Server's interface by just installing another module into the dispatch_to path (see below). STATIC DEPLOYMENT EXAMPLE use SOAP::Transport::HTTP; use My::Examples; # module is preloaded SOAP::Transport::HTTP::CGI # deployed module should be present here or client will get # 'access denied' -> dispatch_to('My::Examples') -> handle; For static deployment you should specify the MODULE name directly. You should also use static binding when you have several different classes in one file and want to make them available for SOAP calls. DYNAMIC DEPLOYMENT EXAMPLE use SOAP::Transport::HTTP; # name is unknown, module will be loaded on demand SOAP::Transport::HTTP::CGI # deployed module should be present here or client will get 'access denied' -> dispatch_to('/Your/Path/To/Deployed/Modules', 'My::Examples') -> handle; For dynamic deployment you can specify the name either directly (in that case it will be "require"d without any restriction) or indirectly, with a PATH. In that case, the ONLY path that will be available will be the PATH given to the dispatch_to() method). For information how to handle this situation see "SECURITY" section. SUMMARY dispatch_to( # dynamic dispatch that allows access to ALL modules in specified directory PATH/TO/MODULES # 1. specifies directory # -- AND -- # 2. gives access to ALL modules in this directory without limits # static dispatch that allows access to ALL methods in particular MODULE MODULE # 1. gives access to particular module (all available methods) # PREREQUISITES: # module should be loaded manually (for example with 'use ...') # -- OR -- # you can still specify it in PATH/TO/MODULES # static dispatch that allows access to particular method ONLY MODULE::method # same as MODULE, but gives access to ONLY particular method, # so there is not much sense to use both MODULE and MODULE::method # for the same MODULE ); In addition to this "SOAP::Lite" also supports an experimental syntax that allows you to bind a specific URL or SOAPAction to a CLASS/MODULE or object. For example: dispatch_with({ URI => MODULE, # 'http://www.soaplite.com/' => 'My::Class', SOAPAction => MODULE, # 'http://www.soaplite.com/method' => 'Another::Class', URI => object, # 'http://www.soaplite.com/obj' => My::Class->new, }) "URI" is checked before "SOAPAction". You may use both the "dispatch_to()" and "dispatch_with()" methods in the same server, but note that "dispatch_with()" has a higher order of precedence. "dispatch_to()" will be checked only after "URI" and "SOAPAction" has been checked. See also: EXAMPLE APACHE::REGISTRY USAGE, "SECURITY" COMPRESSION"SOAP::Lite" provides you option to enable transparent compression over the wire. Compression can be enabled by specifying a threshold value (in the form of kilobytes) for compression on both the client and server sides:Note: Compression currently only works for HTTP based servers and clients. Client Code print SOAP::Lite ->uri('http://localhost/My/Parameters') ->proxy('http://localhost/', options => {compress_threshold => 10000}) ->echo(1 x 10000) ->result; Server Code my $server = SOAP::Transport::HTTP::CGI ->dispatch_to('My::Parameters') ->options({compress_threshold => 10000}) ->handle; For more information see COMPRESSION in HTTP::Transport. SECURITYFor security reasons, the existing path for Perl modules (@INC) will be disabled once you have chosen dynamic deployment and specified your own "PATH/". If you wish to access other modules in your included package you have several options:
INTEROPERABILITYMicrosoft .NET client with SOAP::Lite ServerIn order to use a .NET client with a SOAP::Lite server, be sure you use fully qualified names for your return values. For example:return SOAP::Data->name('myname') ->type('string') ->uri($MY_NAMESPACE) ->value($output); In addition see comment about default encoding in .NET Web Services below. SOAP::Lite client with a .NET serverIf experiencing problems when using a SOAP::Lite client to call a .NET Web service, it is recommended you check, or adhere to all of the following recommendations:
Special thanks goes to the following people for providing the above description and details on .NET interoperability issues: Petr Janata <petr.janata@i.cz>, Stefan Pharies <stefanph@microsoft.com>, Brian Jepson <bjepson@jepstone.net>, and others TROUBLESHOOTING
PERFORMANCE
BUGS AND LIMITATIONS
PLATFORM SPECIFICS
RELATED MODULESTransport ModulesSOAP::Lite allows one to add support for additional transport protocols, or server handlers, via separate modules implementing the SOAP::Transport::* interface. The following modules are available from CPAN:
AVAILABILITYYou can download the latest version SOAP::Lite for Unix or SOAP::Lite for Win32 from the following sources:* CPAN: http://search.cpan.org/search?dist=SOAP-Lite You are welcome to send e-mail to the maintainers of SOAP::Lite with your comments, suggestions, bug reports and complaints. ACKNOWLEDGEMENTSSpecial thanks to Randy J. Ray, author of Programming Web Services with Perl, who has contributed greatly to the documentation effort of SOAP::Lite.Special thanks to O'Reilly publishing which has graciously allowed SOAP::Lite to republish and redistribute the SOAP::Lite reference manual found in Appendix B of Programming Web Services with Perl. And special gratitude to all the developers who have contributed patches, ideas, time, energy, and help in a million different forms to the development of this software. HACKINGLatest development takes place on GitHub.com. Come on by and fork it.git@github.com:redhotpenguin/perl-soaplite.git Also see the HACKING file. Actively recruiting maintainers for this module. Come and get it on! REPORTING BUGSPlease use rt.cpan.org or github to report bugs. Pull requests are preferred.COPYRIGHTCopyright (C) 2000-2007 Paul Kulchenko. All rights reserved.Copyright (C) 2007-2008 Martin Kutter Copyright (C) 2013 Fred Moyer LICENSEThis library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.This text and all associated documentation for this library is made available under the Creative Commons Attribution-NoDerivs 2.0 license. http://creativecommons.org/licenses/by-nd/2.0/ AUTHORSPaul Kulchenko (paulclinger@yahoo.com)Randy J. Ray (rjray@blackperl.com) Byrne Reese (byrne@majordojo.com) Martin Kutter (martin.kutter@fen-net.de) Fred Moyer (fred@redhotpenguin.com)
Visit the GSP FreeBSD Man Page Interface. |