6.3 The SOAP::Lite Module


The newer of the two SOAP modules for Perl is SOAP::Lite , the work of coauthor Pavel Kulchenko. Unlike the DevelopMentor SOAP module, SOAP::Lite provides functionality in more abstract terms. Classes are provided for client-side functionality, server implementation, data support, and a variety of other tasks . Much of the XML is hidden from the application, except where necessary (or directly relevant).

The SOAP::Lite module is also more actively maintained (keep up to date via the soaplite.com web site). It provides full support for SOAP 1.1, and is starting to provide support for SOAP 1.2 as well, with much of the draft specification already implemented. The module also implements XML-RPC, using the components already present in the package (parser, transport code, and the like).

6.3.1 Installing SOAP::Lite

The SOAP::Lite module has a thorough interactive installation process. This allows users to select which subcomponents are available, depending on whether the supporting modules are going to be installed and available. Example 6-3 shows the choices as presented when running perl Makefile.PL .

Example 6-3. Starting the SOAP::Lite installation process
 XMLRPC::Lite, UDDI::Lite, and XML::Parser::Lite are included by default.Installed transports can be used for both SOAP::Lite and XMLRPC::Lite.     Client (SOAP::Transport::HTTP::Client)                 [yes] Client HTTPS/SSL support   (SOAP::Transport::HTTP::Client, require OpenSSL)      [no] Client SMTP/sendmail support   (SOAP::Transport::MAILTO::Client)                    [yes] Client FTP support   (SOAP::Transport::FTP::Client)                       [yes] Standalone HTTP server   (SOAP::Transport::HTTP::Daemon)                      [yes] Apache/mod_perl server   (SOAP::Transport::HTTP::Apache, require Apache)       [no] FastCGI server   (SOAP::Transport::HTTP::FCGI, require FastCGI)        [no] POP3 server (SOAP::Transport::POP3::Server)            [yes] IO server (SOAP::Transport::IO::Server)                [yes] MQ transport support (SOAP::Transport::MQ)              [no] JABBER transport support (SOAP::Transport::JABBER)      [no] MIME messages [required for POP3, optional for HTTP]   (SOAP::MIMEParser)                                    [no] SSL support for TCP transport (SOAP::Transport::TCP)    [no] Compression support for HTTP transport   (SOAP::Transport::HTTP)                               [no]     Do you want to proceed with this configuration? [yes] 

These installation options are the defaults. The machine on which this output sample was generated had many of the optional Perl modules already available, but the installation process doesn't probe for them unless the user performing the installation enables a particular component.

If the user chooses to not accept the default, she is prompted for each option, whether to enable it with yes or disable it with no . The current values are presented as defaults, as you'd expect. After the full range of choices have been presented, the current configuration is again shown, and the user asked whether to accept and continue, or go back and make further adjustments to the configuration.

Which of the optional components to enable is a matter of taste and of what is to be expected of the applications being developed. In general, any of the components for which the needed modules are already present on the system (such as the Apache support, MIME support, or compression) might as well be enabled. Enabling secure transport support, for example, will require that the system have the Perl modules for SSL, as well as any system libraries such as OpenSSL that they require. The SSL modules, IO::Socket::SSL and Crypt::SSLeay , can be installed by CPAN as part of the installation process for SOAP::Lite itself. The configuration reports any missing modules.

Like many CPAN modules, SOAP::Lite has a fairly large suite of test scripts that will run during the make test phase of building the module. During the configuration phase, the user is given the option of skipping some of the tests, specifically those that will connect to live SOAP servers. This can be useful for systems that don't have continuous Internet access for systems behind firewalls.

6.3.2 Using SOAP::Lite for Clients

Because they share a common architecture and subsystem, the SOAP::Lite client developed for the number-to-string translation server looks a lot like the XMLRPC::Lite client developed in Chapter 4.

6.3.2.1 Number-to-text conversion with SOAP::Lite

Example 6-4 shows the interesting part of the client (the command-line processing code, for example, has been omitted). Take note of how much simpler this client is than the client from Example 6-1, which used the DevelopMentor SOAP toolkit.

Example 6-4. The num2name.pl script using SOAP::Lite
 #!/usr/bin/perl     use strict; use SOAP::Lite;     my $num = shift; $num =~ /^\d+$/ or die "USAGE: 
 #!/usr/bin/perl use strict; use SOAP::Lite; my $num = shift; $num =~ /^\d+$/ or die "USAGE: $0 num\n"; my ($server, $endpoint, $soapaction, $method, $method_urn); $server = 'http://www.tankebolaget.se'; $endpoint = "$server/scripts/NumToWords.dll/soap/INumToWords"; $soapaction = "urn:NumToWordsIntf-INumToWords#NumToWords_English"; $method = 'NumToWords_English'; $method_urn = 'urn:NumToWordsIntf-INumToWords'; my $num2words = SOAP::Lite->new(uri => $soapaction, proxy => $endpoint); my $response = $num2words ->call(SOAP::Data-> name ($method) ->attr( { xmlns => $method_urn } ) => # Argument(s) listed next SOAP::Data->name(aNumber => $num)); if ($response->fault) { printf "A fault (%s) occurred: %s\n", $response->faultcode, $response->faultstring; } else { print "$num may be expressed as " . $response->result . "\n"; } exit; 
num\n"; my ($server, $endpoint, $soapaction, $method, $method_urn); $server = 'http://www.tankebolaget.se'; $endpoint = "$server/scripts/NumToWords.dll/soap/INumToWords"; $soapaction = "urn:NumToWordsIntf-INumToWords#NumToWords_English"; $method = 'NumToWords_English'; $method_urn = 'urn:NumToWordsIntf-INumToWords'; my $num2words = SOAP::Lite->new(uri => $soapaction, proxy => $endpoint); my $response = $num2words ->call(SOAP::Data->name($method) ->attr( { xmlns => $method_urn } ) => # Argument(s) listed next SOAP::Data->name(aNumber => $num)); if ($response->fault) { printf "A fault (%s) occurred: %s\n", $response->faultcode, $response->faultstring; } else { print "$num may be expressed as " . $response->result . "\n"; } exit;

The first area of difference comes in the inclusion of code to provide the SOAP functionality. The four use statements in the original are replaced with:

 use SOAP::Lite; 

The SOAP::Lite package loads other modules as needed.

We interact with the server via a SOAP::Lite object. Its construction is simply:

 my $num2words = SOAP::Lite->new(uri   => $soapaction,                                 proxy => $endpoint); 

The meaning of uri and proxy , and of the values in $soapaction and $endpoint , are covered later.

The remote method call is done in an extremely verbose way, for example:

 my $response = $num2words->call(SOAP::Data                                 ->name($method)                                 ->attr( { xmlns =>                                           $method_urn } )                                 => # Argument(s) listed next                                 SOAP::Data->name(aNumber =>                                                  $num)); 

Depending on the strictness of the server, that call might be as short as:

 my $response = $num2words->$method($num); 

This syntax is covered later. [2]

[2] It isn't sufficient for this particular SOAP server, as it happens.

Finally, the examination of the return value uses methods available on the returned object to test whether the response received was a fault or not. If it was, further methods ( faultcode and faultstring ) are used to construct an error message. Otherwise, the result is easily extracted from the response object.

The assignments to $server and $endpoint differ slightly from those in Example 6-1. In that example, $soapaction was not actually used because, by chance, its form matched what the SOAP module created internally from the values of $method and $method_urn . Here it is used when creating the SOAP::Lite handle object. The value of $endpoint ends up being the full URL, because the logic within SOAP::Transport examines the string to choose the proper transport binding. In the first example, the server's hostname and port had to be passed separately due to the way in which SOAP created the final URL.

As was mentioned earlier in the commentary , there are several places where this code could have been written more concisely. As it stands, it is roughly 10% shorter than the version which used SOAP .

6.3.2.2 Translating a use.perl.org journal stream to RSS

This next example uses the SOAP interface at the use Perl; discussion site (http://use.perl.org), taking the last 15 journal entries for a specified user [3] and creating a RSS 1.0 syndication feed from them.

[3] Note that this SOAP interface is still very early alpha but also very useful.

The use Perl; site is built on the Slash web form code (http://www.slashcode.org). In addition to news and conversational forums, it provides web-log facilities to its registered users. Many well-known characters in the Perl community maintain ongoing commentary on their projects and tasks. The collection of story headlines is retrievable as a RSS feed, as is the list of most-recently updated journals. But the journal of an individual user isn't (currently) available as a RSS feed. Into this gap steps the upj2rss.pl utility in Example 6-5.

Example 6-5. upj2rss.pl, turning a journal log into RSS
 #!/usr/bin/perl -w     use strict;     use SOAP::Lite; use XML::RSS;     my $user = shift      die "Usage: 
 #!/usr/bin/perl -w use strict; use SOAP::Lite; use XML::RSS; my $user = shift  die "Usage: $0 userID [ usernick ]\n\nStopped"; my $nick = shift  "#$user"; my $host = 'http://use.perl.org'; my $uri = "$host/Slash/Journal/SOAP"; my $proxy = "$host/journal.pl"; # SOAP material starts here: my $journal = SOAP::Lite->uri($uri)->proxy($proxy); my $results = $journal->get_entries($user, 15)->result; my $rss = XML::RSS->new(version => '1.0'); $rss->channel(title => "use.perl.org journal of $nick", 'link' => $proxy, description => "The use.perl.org journal of $nick"); $rss->add_item(title => $_->{subject}, 'link' => $_->{url}) for (@$results); print STDOUT $rss->as_string; exit; 
userID [ usernick ]\n\nStopped"; my $nick = shift "#$user"; my $host = 'http://use.perl.org'; my $uri = "$host/Slash/Journal/SOAP"; my $proxy = "$host/journal.pl"; # SOAP material starts here: my $journal = SOAP::Lite->uri($uri)->proxy($proxy); my $results = $journal->get_entries($user, 15)->result; my $rss = XML::RSS->new(version => '1.0'); $rss->channel(title => "use.perl.org journal of $nick", 'link' => $proxy, description => "The use.perl.org journal of $nick"); $rss->add_item(title => $_->{subject}, 'link' => $_->{url}) for (@$results); print STDOUT $rss->as_string; exit;

Example 6-5 uses another CPAN module, XML::RSS , which is a useful tool for creating and parsing syndication feeds. Here it creates the syndication-feed version of the journal data. For this example, assume that it is a working black box that doesn't need further explanation.

Starting right after the comment that says, "SOAP material starts here," a handle object is created in much the same way as in Example 6-4. Here, rather than calling new( ) explicitly, the application lets the SOAP::Lite module do that at the time of the first method invocation ( uri , in this case). Calling one of the class methods as a static method automatically triggers this behavior. The uri( ) and proxy( ) methods set the SOAPAction header and communication URL, respectively. In the previous example, these were passed as parameters to the constructor.

The next line uses this client object to invoke a method called get_entries on the server. Two arguments are passed (both integers), and the return value invokes a method called result . The return value from a method call is a reference to an object in the SOAP::SOM class. This is a class used to encapsulate the returned message body from the server. In more detailed examples later, this object will be more fully utilized. For now, it is enough to know that the result method takes the data content of the response and transforms it to a native Perl datatype. Example 6-4 returned a single string, but in this case, the data is an array reference with up to 15 journal entries ( assuming there were at least that many available) for the user whose account ID on use.perl.org was the value in $user .

The rest of the script details aren't really that important. Absent here is any real checking of return values or tests for data validity. The uri and proxy methods can be safely assumed to return a valid object reference. No connections are made by calling those methods; they set only values internal to the object. The method invocation itself on the next line could have had problems, though.

6.3.2.3 Basic classes and components

These initial examples showed how few basic components are needed to accomplish tasks with the SOAP::Lite toolkit. Several classes other than SOAP::Lite came in to play, but their presence was quietly abstracted by the interface. In fact, the primary role that the SOAP::Lite class plays is to provide shortcuts to methods that are part of the classes used behind the scenes. There are methods that are actual parts of SOAP::Lite , however, as will be seen later.

What are these classes, then? Which of them should an application developer be concerned with? Table 6-1 lists the classes developers should begin with.

Table 6-1. The basic elements of the SOAP::Lite toolkit

Class

Role

 SOAP::Lite 

An umbrella over many of the other classes. This class provides the highest level of abstraction in the overall package.

 SOAP::Transport 

This is the base abstraction of the transport classes within the package. Many of the methods called on SOAP::Lite objects are filtered through to here.

 SOAP::Data 

This provides very simple data-encoding functionality. Through this wrapper, data objects can be associated with named accessors, specific typing, encoding, etc.

 SOAP::SOM 

This class is much like HTTP::Response , in the sense that applications often manipulate instance objects created from this class, without directly knowing that they are doing so. Responses from a server are passed back to a client using this class to encapsulate them.

The elements in Table 6-1 were mentioned in the discussion of the two previous examples. Two have already been explained, the other two are:

SOAP::Data

Expect to use this class more often than may seem immediately obvious. Perl handles a wide range of datatypes in a very seamless manner, but SOAP as a general rule doesn't. If the sequence "123" is to be sent as a string, it usually has to be explicitly encoded as such. Perl would interpret it as an integer, and serialize it this way. If the server is expecting a string, an integer might not be acceptable. This is also very useful when it is necessary to control the naming of accessors for the data or when using different encodings (such as a string in a different character set).

SOAP::Transport

The typical application rarely (if ever) directly instantiates an object of this class. However, several of the more common methods used on SOAP::Lite objects actually thread through to the instance of this class the containing object maintains. Methods such as uri , proxy , and endpoint are made available through the abstract interface but are implemented within this class. Behind the scenes, this class manages specific protocol modules and transport bindings based on the schemes used in the URNs given to these methods.

In general, the methods from SOAP::Lite are considered accessor methods and as such return their current value when no arguments are passed in to the call. If arguments are passed, they return the calling object to enable the chaining of method calls. The examples thus far have used only a few of the SOAP::Lite methods:

new

As would be expected, this method is the class constructor. The nature of most of the other methods in the class make this unnecessary in most cases, because the first call to another method creates the new object upon demand.

uri

Gets or sets the URI of the remote method being accessed. This isn't the same as the web address (or other type of address) of the server itself. The URI in many cases doesn't point to an existing document. It is used as a unique identifier for the resource being used (in this case, a remote method).

This is a confusing distinction to many people. When using SOAP over HTTP to do remote procedure calls, the messages have to have a URI that identifies the "service." This may be the same as the HTTP address, similar to it, or completely different. It is just another identifier, though, one that happens to look like a web address in most cases.

proxy

Sets or gets the actual endpoint address to which requests will go. The string that gets passed to this method as an argument will ultimately help select the support code for the transport layer. If it is a HTTP URL, the HTTP transport module gets loaded into memory, and so forth for schemes such as Jabber or SMTP.

The latter two methods here actually act as shortcuts to the SOAP::Transport class, one of the several classes that SOAP::Lite abstracts the user from in most cases.

The SOAP::Lite class also has several settings that are activated when the library is imported into the application, for example:

 use SOAP::Lite +autodispatch, +trace; 

Many of the standard settings may also be specified here, such as uri , proxy , etc. When these are set at the import-level like this, they become the values on the global object instance that SOAP::Lite quietly maintains behind the scenes. It is from this object that other instances take their default values, and from this object that the on-the-fly construction of object instances happens. (Recall from the explanation following Example 6-5, that most of the methods will automatically instantiate an object of the class when they are called as static methods.)

Here are the settings that may be given to SOAP::Lite when it's imported to an application:

+autodispatch

When this is passed, SOAP::Lite installs an AUTOLOAD mechanism in the UNIVERSAL namespace. This causes all unrecognized function names to route through this AUTOLOAD routine, which allows SOAP::Lite to attempt to manage them using the settings on the global object it maintains.

+trace , +debug

The tracing/debugging facility is handled the same way regardless of which of the two switches are used. Both are provided if you prefer one to the other, and both are just interfaces into the SOAP::Trace class settings. The tracing facility is actually very robust and is covered in more detail later. The keyword is optionally followed by a list of events to trace. If no such list is given, all events are traced.

dispatch_from => class name or list reference of class names

This facility enhances the +autodispatch functionality described earlier. The difference here is that this keyword provides one or more class names from which methods are to be auto-dispatched just as ordinary routines are auto-dispatched by the previous keyword. This option looks for only one argument following it, so specifying multiple classes requires passing them in a list reference. When this is set up, it installs the AUTOLOAD functionality only in the specified classes, not at the UNIVERSAL level. The application can still auto-dispatch without using an instance variable, but regular undefined functions in the main body will still raise errors (without attempting to contact a remote server first).

Returning to the SOAP::Trace class mentioned earlier, this is the class which encapsulates the debugging/tracing facility. This is another "behind the scenes" class, whose only visible interface is the +debug / +trace option defined earlier. As was mentioned then, the tracing class is implemented in terms of a set of events, which are listed in Table 6-2. Not all the events are relevant to both clients and servers; the table explains their roles.

Table 6-2. SOAP::Trace events and their meanings

Event

Client/server?

Meaning

 transport 

Client

Looks at the HTTP::Request and HTTP::Response objects during the transport cycle.

 dispatch 

Server

Shows the full name of the dispatched call.

 result 

Server

Gives the result of the method call.

 parameters 

Server

Shows the parameters for the method call.

 headers 

Server

Gives the headers of the received message.

 objects 

Both

Tracks calls to new and DESTROY in the classes.

 method 

Both

Gets the parameters to the envelope method when the first argument is the string literal, method .

 fault 

Both

Gets the parameters to the envelope method when creating a fault (first argument is fault ).

 freeform 

Both

Gets the parameters to the envelope method when the argument is freeform .

 trace 

Both

Traces entrance into important functions.

 debug 

Both

Details along the stages of transport.

There is a last, pseudo-event specifier called all . Normally it isn't needed because passing +trace with no event list has the effect of enabling all events. However, you can disable an event by prefixing its name with a hyphen. Thus, the following line enables all tracing events except the debug events:

 use SOAP::Lite +trace => [qw(all -debug)]; 

The default action for these events is to log the data that they get passed to STDERR . Alternately, the event given on the importing line can specify a subroutine reference or a closure, and have that called on the event instead. These trace-event callbacks receive the same information that the native ones would have received, as given in Table 6-2. An example of tracing faults might look like this:

 use SOAP::Lite +trace => [fault => \&follow_fault];     # ...     sub follow_fault {     warn "Fault occured:\n\t" . join("\n\t", @_) . "\n"; } 
6.3.2.4 Dispatching methods and the object style

Recall the syntax alluded to in Example 6-4, which was used in Example 6-5. The client object called a method on the use.perl.org server in this fashion:

 $journal->get_entries($user, 15) 

It's no surprise that the SOAP::Lite class, which $journal holds an instance reference of, doesn't actually implement a method called get_entries . Nor would you expect a class to try to predict all the potential method names that may ever be conjured. This is a syntactical shortcut SOAP::Lite provides to client objects, one that makes the syntax of the application around that object look more comfortable and familiar.

Perl has an extremely flexible feature called auto-loading , [4] which is used in a variety of creative ways by other modules from both within the core of Perl and from CPAN. This is another highly creative application of that functionality.

[4] For more about this topic, see the perlsub manpage and the AutoLoader module's manpage.

When a client object tries to call a method that doesn't exist in the package, the class catches the call and tries to execute it remotely. The beauty of it all is that to the application using the object, it is just another method call.

With that syntax, it becomes much more familiar and comfortable to use the client object as if it were any other type of garden-variety object, containing and accessing data in local memory. The sense of abstraction has extended not just to the classes and components that implement the SOAP conversation, that abstraction has resulted in almost completely hiding the fact that there even is a SOAP conversation taking place.

Using the $journal object more freely , the sample program can be made to retrieve more information for each journal entry, resulting in a richer syndication:

 my $results = $journal->get_entries($user, 15)->result; my @results = map {                   my $entry = $journal->get_entry($_->{id})                                       ->result;                   # Imagine this uses HTML::Parser to get                   # roughly the first paragraph                   my $desc = extract_desc($entry->{body});                   # Imagine $nick wasn't set earlier                   $nick = $entry->{nickname};                   # Each map-value is a hash reference:                   { 'link'      => $entry->{url},                     description => $desc,                     title       => $entry->{subject} };               } (@$results); 

The initialization of the $rss object is the same, except that now $nick has the user's actual nickname on use.perl.org , instead of the script defaulting to the user ID. But then, change the lines that add the contents to the RSS channel to this:

 $rss->add_item(%$_) for (@results); 

Now, the resulting RSS channel has more than just the subject lines of the journal entries. The leading paragraph or so, depending on the implementation of the hypothetical extract_desc routine, has been added as the description for the RSS entry. The possibilities are limited only by the information actually provided from the call. Also, nothing in the earlier map block looks significantly different from any other object-oriented Perl usage. Only the result method being called after the get_entry method offers any clue that this might be different from a generic database or other data-store layer.

This form of method-dispatching is commonly used. However, SOAP::Lite also allows for even more- liberal dispatching, with the +autodispatch and dispatch_from options that may be set at import-time. When these are used, the functions that get automatically routed to the remote server may be any ordinary (as in non -method) function calls within the code. Note that in the following code fragment:

 use SOAP::Lite +autodispatch =>                uri   => 'http://...' =>                proxy => 'http://...';     not_local($arg1, $arg2); 

both the uri and the proxy are specified at the same time as the +autodispatch setting. This provides the basis for SOAP::Lite to try and execute any "rogue" subroutines it gets handed. Assuming that the routine not_local isn't already defined somewhere else, it is handed to SOAP::Lite to try and execute remotely.

The difference made by dispatch_from is one of scoping these open -ended executions to a given class namespace:

 use SOAP::Lite dispatch_from => Remote =>                uri   => 'http://...' =>                proxy => 'http://...';     # This will fail at run-time not_local($arg1, $arg2); # This will not-- unless "not_local" isn't found remotely Remote->not_local($arg1, $arg2); 

The primary difference is that the main application namespace isn't going to trigger an attempt to run a procedure remotely. The dispatch_from functionality only hooks the auto-loading in at the level of the named classes.

The +autodispatch mechanism is very convenient but also opens the door to a lot of potential pitfalls. The most obvious of these is the danger inherent in running remote code accidentally , perhaps by way of a typo. Using the automatic dispatching can also be noisy when warnings are enabled with Perl's -w flag because of the AUTOLOAD routine SOAP::Lite installs to enable auto-dispatching. The interpreter perceives the routine as being inherited from a superclass, which is one of the warning conditions -w checks for.

Beyond the noise and potential for user error, however, is the real factor of clarity in the code, and readability to those others who may have to maintain the code long after the original author has moved on to other projects. Auto-dispatched methods that have no class qualifier at all aren't easily distinguished from actual local functions that are part of the application itself or of a local library. Consider using dispatch_from in those cases where +autodispatch seems to be the tool to use. [5] Leave +autodispatch for the smaller cases, one-liners, and utilities that are shorter and easier to maintain. It may seem less convenient at the time, but such compromises almost always pay dividends over the longer lifecycle of software.

[5] Another reason to use dispatch_from is the bug in UNIVERSAL::AUTOLOAD in Perl 5.8.0 that breaks +autodispatch code.

6.3.2.5 Managing data with SOAP::Data and SOAP::SOM

Recall the way data was passed to the remote method in Example 6-4. In order to maintain compliance with the server, data was constructed so that it serialized in a very strict and clean fashion. This was accomplished using the methods from the SOAP::Data class.

SOAP::Data is intended to help out the application when the default serialization of a value might be either wrong or at least incomplete. A common example is when a string appears to be a number. Because of the way Perl treats scalars, a value of 123 may be treated as an integer when it needs to be (for math operations) or a string if so needed (for concatenation as an example). Because of this flexibility, it can be hard to know for certain when such a value is meant to be a specific type. There may also be other issues beyond just the type of the data: a string may require a different encoding, or the accessor name being associated with the value may need to be given a specific namespace qualification.

Objects of the SOAP::Data class aren't generally instantiated for the long term . That is, most usage of the class is just to create an object long enough for it to be passed in a subroutine call, after which it is discarded. While there is a new method, its use is rarely seen. More often seen is this type of usage:

 SOAP::Data->name(zipCode => 95008)->type('string') 

This example is based on the fact that a U.S. postal Zip Code can have a four-digit extension at the end, with a hyphen separating it from the main five-digit code. Because of this, an application that accepts Zip Codes as input must take them as string data. However, the natural tendency of Perl is to treat 95008 as an integer. Because of that, such a value must be "coerced" into the correct type.

Like SOAP::Lite itself, the methods within SOAP::Data each create an object when called as static methods and return the object when called with any parameters. Any of the methods, such as value , that are called with no parameters return the current setting of the given aspect of the data. The more commonly used methods provided by this class are summarized in Table 6-3, with a complete list available in Appendix B.

Table 6-3. SOAP::Data class methods

Method

Purpose

 name 

The name is the accessor, or element name, by which the data is to be known in the resulting XML document.

 value 

This sets (or retrieves) the value of the data object.

 type 

This method explicitly sets a type for the data item. If this method isn't called, SOAP::Data attempts to intuit the type from an examination of the data.

 encodingStyle 

If the data item is to have an encoding different from the one that would affect it by default, the URN for that encoding is set with this method.

 root 

This method allows for explicitly setting the root attribute on this data item. It requires a boolean value.

Another class that is important in a "behind-the-scenes" fashion is SOAP::SOM , which was referred to in the earlier examples. This class encapsulates the returned messages from remote calls. In many cases, the class is completely transparent to the application using it. Like HTTP::Response in the LWP package, an application doesn't instantiate it. Rather, it is handed back at the completion of a remote call. Objects of this class are useful for more than simply chaining an invocation of the result method onto the end of a call. If the object itself is kept as the return value, the application can check the fault status of the call more closely.

More specifically, SOAP::SOM objects give the application full access to the deserialized envelope itself. Many of the data-retrieval methods use a syntax very much like XPath notation. Depending on the depth of data in the envelopes exchanged between a client and server, the application may only need the first few of the methods explained. There are actually a large number of methods in this class, though many don't see frequent use. The more common methods are summarized in Table 6-4.

Table 6-4. SOAP::SOM class methods

Method

Purpose

 fault 

Returns a boolean expression identifying whether the response is a fault or not.

 faultcode 

Returns the fault code, if the response is a fault.

 faultstring 

Returns the fault string (description), if the response is a fault.

 faultactor 

Return the URN of the actor involved in the fault, if the response is a fault.

 faultdetail 

Returns the data in the faultdetail element of the fault, if the response is a fault. Note that this element has somewhat variable definition, as discussed in the earlier chapter on SOAP.

 result 

For a nonfault response, this returns the result value from the remote server.

 method 

Returns the tag that specified the method (remote function name) that was called.

 paramsout paramsall paramsin 

Because server responses aren't limited to returning just one value, these methods implement management of the IN , IN/OUT , and OUT parameter styles, as discussed in Chapter 5. These parameter bindings will be discussed in greater detail in their own section.

The previous examples built on SOAP::Lite assumed that their remote calls were successful. The next example incorporates the SOAP::SOM methods to check more carefully .

6.3.2.6 Example: Automatically announcing CPAN uploads

The SOAP interface at use.perl.org also permits posting journal entries. At first glance, it might not be clear why a script would be used to perform a task like this, but upon closer look it becomes obvious. There are plenty of situations in which you want to automatically post some item of news to a forum, such as a developer's personal journal. This example focuses on the announcement of new CPAN uploads.

When an author uploads a new release of a package to CPAN, some automatic status messages are sent back to his registered contact address by email, signaling certain points in the submittal process. Some authors choose to then announce on one or more forums that the new code is now being mirrored across the range of servers. Because the email is an automated, reliable event that signals a successful CPAN submission, it makes an excellent herald for other types of announcements.

This sample script takes one of the two messages sent out by the PAUSE daemon (Perl Authors Upload Server) and uses some of the information within it to create the HTML body of a journal entry. The body is then posted to the user's journal at use.perl.org , with an appropriate subject. Not all elements of the code will be examined closely, but should be clear when looking at the script as a whole. The script will follow this explanation, as Example 6-6.

This first segment of code is the creation of the HTTP cookie data that will be part of the request. Unlike the read-only operations used in earlier examples, operations such as posting a journal entry require authentication. The use.perl.org SOAP service uses the same browser cookie for validation that the web server itself uses for personalization and access to individual content. This utility may not be running in an environment that has access to the user's cookie file, so it accepts credentials as a command-line argument. There are other arguments, which will all be parsed by the Getopt::Std module. The %opts hash table is a global variable that contains these options. Preparing the cookie entails the following:

 $host = 'use.perl.org'; # Note that this path is UNIX-centric. Consider using # File::Spec methods in most cases. $cookie_file = "$ENV{HOME}/.netscape/cookies";     if ($opts{C}) {     $cookie_jar = HTTP::Cookies->new;     $cookie_jar       ->set_cookie(0,                    user => make_cookie(split /:/, $opts{C}),                    '/', $host); } elsif (-f $cookie_file) {     $cookie_jar = HTTP::Cookies::Netscape                     ->new(File => $cookie_file); } else {     die " 
 $host = 'use.perl.org'; # Note that this path is UNIX-centric. Consider using # File::Spec methods in most cases. $cookie_file = "$ENV{HOME}/.netscape/cookies"; if ($opts{C}) { $cookie_jar = HTTP::Cookies->new; $cookie_jar ->set_cookie(0, user => make_cookie(split /:/, $opts{C}), '/', $host); } elsif (-f $cookie_file) { $cookie_jar = HTTP::Cookies::Netscape ->new(File => $cookie_file); } else { die "$0: No authentication data found, cannot continue"; } 
: No authentication data found, cannot continue"; }

The make_cookie routine that creates the cookie from the command-line data will be shown in the final script. What is noteworthy is that the tool will give the command-line priority over an existing cookie file, and because the HTTP::Cookies package doesn't provide a method to check for a specific cookie, no test is made that the cookie file even provided the script with a useful cookie for this site. If it is the case that there is no valid cookie, the script will catch that at a later point.

The script next takes the mail message and extracts the data it wants from it. To make the script operate in a familiar and friendly manner, it will be designed to read the email message from the STDIN filehandle, like a filter. A sample message from a release of the RPC::XML module looks a little like this:

 The uploaded file         RPC-XML-0.37.tar.gz     has entered CPAN as       file: $CPAN/authors/id/R/RJ/RJRAY/RPC-XML-0.37.tar.gz   size: 86502 bytes    md5: d58344bf2c80f44b95a13bf838628517     No action is required on your part Request entered by: RJRAY (Randy J Ray) Request entered on: Sat, 23 Mar 2002 06:37:19 GMT Request completed:  Sat, 23 Mar 2002 06:39:00 GMT             Virtually Yours,         Id: paused,v 1.80 2002/03/10 06:40:03 k Exp k 

The most relevant piece of information here is the seventh line, which starts with the sequence, file :. This path can be easily turned into an active CPAN URL (though the file itself is still in the process of being mirrored, and so the URL may not yet work universally ). For the sake of this example, the text for the journal entry is kept to a minimum. In this case, it announces the package by the filename and provides a direct link to it using the data on line 7.

Moving to the SOAP-relevant content of the script, assume that the processing of the mail message has been done. The initial lines that set up the client to the SOAP service are generally the same as with the earlier example:

 $host  = 'use.perl.org'; $uri   = "http://$host/Slash/Journal/SOAP"; $proxy = "http://$host/journal.pl";     $journal = SOAP::Lite                ->uri($uri)                ->proxy($proxy, cookie_jar => $cookie_jar); die " 
 $host = 'use.perl.org'; $uri = "http://$host/Slash/Journal/SOAP"; $proxy = "http://$host/journal.pl"; $journal = SOAP::Lite ->uri($uri) ->proxy($proxy, cookie_jar => $cookie_jar); die "$0: Error creating SOAP::Lite client, cannot continue" unless $journal; 
: Error creating SOAP::Lite client, cannot continue" unless $journal;

The difference here is in the call to the proxy method, when the client object is being created. The $cookie_jar object created earlier is passed along with the URL itself. Because the URL is recognized by SOAP::Lite as a HTTP endpoint, it also recognize the cookie_jar key (and value) as relevant to the creation of a user-agent object. The jar will be incorporated into the LWP::UserAgent object that eventually handles the connections to the remote host. There are other options that can be given to the proxy method under an umbrella-option that is simply called options and which takes a hash reference as an argument. The actual options vary from one type of transport to the next. Some, such as compress_threshold, which enables compression support, require that the functionality be enabled on both ends of the conversation. These options will be dealt with in greater detail at a later point.

Making the call itself isn't that different from the earlier operation. According to the description of the service, the method used to post a journal entry is called add_entry , and it takes a sequence of key/value pairs. For now, assume that $body , $title , and $discuss were computed prior to this point:

 $result = $journal->add_entry(subject => $title,                               body    => $body,                               discuss => $discuss);     if ($result->fault) {     die " 
 $result = $journal->add_entry(subject => $title, body => $body, discuss => $discuss); if ($result->fault) { die "$0: Failed: " . $result->faultstring . "\n"; } else { printf "New entry added as %s\n", $result->result; } 
: Failed: " . $result->faultstring . "\n"; } else { printf "New entry added as %s\n", $result->result; }

The result of the operation is saved in its native state as a SOAP::SOM object instance. This allows the script to test it with the expression ($result->fault) and act accordingly . If it is a fault, the application dies with the fault string as an error message. But if it succeeds, a brief message announcing success is given. It uses the result method on the object to get the new entry's ID number.

Now for the script in full. It is designed to be run from a mail-filter program such as procmail . A sample procmail "recipe" for this might look like the following:

 :0 Wc * Subject.*CPAN Upload  cpan2upj.pl 

This recipe (as procmail rules are called) matches messages whose subject line starts with CPAN Upload . The message is piped in to the script, which is called cpan2upj.pl . Example 6-6 is the complete cpan2upj.pl script.

Example 6-6. cpan2upj.pl
 #!/usr/bin/perl -w     use strict; use File::Basename 'basename'; use Getopt::Std; use Digest::MD5    'md5_hex'; use HTTP::Cookies; use SOAP::Lite;     my (%opts, $user, $title, $discuss, $body, $host, $uri,     $proxy, $file, $name, $cookie_file, $cookie_jar,     $journal, $result);     getopts('C:c', \%opts) or die <<"USAGE"; Usage: 
 #!/usr/bin/perl -w use strict; use File::Basename 'basename'; use Getopt::Std; use Digest::MD5 'md5_hex'; use HTTP::Cookies; use SOAP::Lite; my (%opts, $user, $title, $discuss, $body, $host, $uri, $proxy, $file, $name, $cookie_file, $cookie_jar, $journal, $result); getopts('C:c', \%opts) or die <<"USAGE"; Usage: $0 [ -c ] [ -C user:pass ] -c Allow comments on journal entry -C user:pass Provide authentication in place of (or in absence of) Netscape cookies. User in this case is UID, not nickname. USAGE while (defined($_ = <STDIN>)) { $file = $1, last if /^\s+file:\s+(\S+)/; } die "$0: No filename found in input, stopped " unless $file; $name = basename $file; $file =~ s^\$CPANhttp://www.cpan.org; $user = (getpwuid($<))[0]; $title = "$name uploaded to PAUSE"; $discuss = $opts{c} ? 1 : 0; $body = << "BODY"; <p>The file <tt>$name</tt> has been uploaded to CPAN, and will soon be accessible as <a href="$file">$file</a>. Mirroring may take up to 24 hours.</p> <p><i>$0, on behalf of $user</i></p> BODY $host = 'use.perl.org'; $uri = "http://$host/Slash/Journal/SOAP"; $proxy = "http://$host/journal.pl"; # Note that this path is UNIX-centric. Consider using # File::Spec methods in most cases. $cookie_file = "$ENV{HOME}/.netscape/cookies"; if ($opts{C}) { $cookie_jar = HTTP::Cookies->new ->set_cookie(0, user => make_cookie(split /:/, $opts{C}), '/', $host); } elsif (-f $cookie_file) { $cookie_jar = HTTP::Cookies::Netscape->new(File => $cookie_file); } else { die "$0: No authentication data found, cannot continue"; } $journal = SOAP::Lite->uri($uri) ->proxy($proxy, cookie_jar => $cookie_jar); die "$0: Error creating SOAP::Lite client, cannot continue" unless $journal; $result = $journal->add_entry(subject => $title, body => $body, discuss => $discuss); if ($result->fault) { die "$0: Failed: " . $result->faultstring . "\n"; } else { printf "New entry added as %s\n", $result->result; } exit; # Taken from the Slash codebase sub make_cookie { my ($uid, $passwd) = @_; my $cookie = $uid . '::' . md5_hex($passwd); $cookie =~ s/(.)/sprintf("%%%02x", ord($1))/ge; $cookie =~ s/%/%25/g; $cookie; } 
[ -c ] [ -C user:pass ] -c Allow comments on journal entry -C user:pass Provide authentication in place of (or in absence of) Netscape cookies. User in this case is UID, not nickname. USAGE while (defined($_ = <STDIN>)) { $file = , last if /^\s+file:\s+(\S+)/; } die "
 #!/usr/bin/perl -w use strict; use File::Basename 'basename'; use Getopt::Std; use Digest::MD5 'md5_hex'; use HTTP::Cookies; use SOAP::Lite; my (%opts, $user, $title, $discuss, $body, $host, $uri, $proxy, $file, $name, $cookie_file, $cookie_jar, $journal, $result); getopts('C:c', \%opts) or die <<"USAGE"; Usage: $0 [ -c ] [ -C user:pass ] -c Allow comments on journal entry -C user:pass Provide authentication in place of (or in absence of) Netscape cookies. User in this case is UID, not nickname. USAGE while (defined($_ = <STDIN>)) { $file = $1, last if /^\s+file:\s+(\S+)/; } die "$0: No filename found in input, stopped " unless $file; $name = basename $file; $file =~ s^\$CPANhttp://www.cpan.org; $user = (getpwuid($<))[0]; $title = "$name uploaded to PAUSE"; $discuss = $opts{c} ? 1 : 0; $body = << "BODY"; <p>The file <tt>$name</tt> has been uploaded to CPAN, and will soon be accessible as <a href="$file">$file</a>. Mirroring may take up to 24 hours.</p> <p><i>$0, on behalf of $user</i></p> BODY $host = 'use.perl.org'; $uri = "http://$host/Slash/Journal/SOAP"; $proxy = "http://$host/journal.pl"; # Note that this path is UNIX-centric. Consider using # File::Spec methods in most cases. $cookie_file = "$ENV{HOME}/.netscape/cookies"; if ($opts{C}) { $cookie_jar = HTTP::Cookies->new ->set_cookie(0, user => make_cookie(split /:/, $opts{C}), '/', $host); } elsif (-f $cookie_file) { $cookie_jar = HTTP::Cookies::Netscape->new(File => $cookie_file); } else { die "$0: No authentication data found, cannot continue"; } $journal = SOAP::Lite->uri($uri) ->proxy($proxy, cookie_jar => $cookie_jar); die "$0: Error creating SOAP::Lite client, cannot continue" unless $journal; $result = $journal->add_entry(subject => $title, body => $body, discuss => $discuss); if ($result->fault) { die "$0: Failed: " . $result->faultstring . "\n"; } else { printf "New entry added as %s\n", $result->result; } exit; # Taken from the Slash codebase sub make_cookie { my ($uid, $passwd) = @_; my $cookie = $uid . '::' . md5_hex($passwd); $cookie =~ s/(.)/sprintf("%%%02x", ord($1))/ge; $cookie =~ s/%/%25/g; $cookie; } 
: No filename found in input, stopped" unless $file; $name = basename $file; $file =~ s^$CPANhttp://www.cpan.org; $user = (getpwuid($<))[0]; $title = "$name uploaded to PAUSE"; $discuss = $opts{c} ? 1 : 0; $body = << "BODY"; <p>The file <tt>$name</tt> has been uploaded to CPAN, and will soon be accessible as <a href="$file">$file</a>. Mirroring may take up to 24 hours.</p> <p><i>
 #!/usr/bin/perl -w use strict; use File::Basename 'basename'; use Getopt::Std; use Digest::MD5 'md5_hex'; use HTTP::Cookies; use SOAP::Lite; my (%opts, $user, $title, $discuss, $body, $host, $uri, $proxy, $file, $name, $cookie_file, $cookie_jar, $journal, $result); getopts('C:c', \%opts) or die <<"USAGE"; Usage: $0 [ -c ] [ -C user:pass ] -c Allow comments on journal entry -C user:pass Provide authentication in place of (or in absence of) Netscape cookies. User in this case is UID, not nickname. USAGE while (defined($_ = <STDIN>)) { $file = $1, last if /^\s+file:\s+(\S+)/; } die "$0: No filename found in input, stopped " unless $file; $name = basename $file; $file =~ s^\$CPANhttp://www.cpan.org; $user = (getpwuid($<))[0]; $title = "$name uploaded to PAUSE"; $discuss = $opts{c} ? 1 : 0; $body = << "BODY"; <p>The file <tt>$name</tt> has been uploaded to CPAN, and will soon be accessible as <a href="$file">$file</a>. Mirroring may take up to 24 hours.</p> <p><i>$0, on behalf of $user</i></p> BODY $host = 'use.perl.org'; $uri = "http://$host/Slash/Journal/SOAP"; $proxy = "http://$host/journal.pl"; # Note that this path is UNIX-centric. Consider using # File::Spec methods in most cases. $cookie_file = "$ENV{HOME}/.netscape/cookies"; if ($opts{C}) { $cookie_jar = HTTP::Cookies->new ->set_cookie(0, user => make_cookie(split /:/, $opts{C}), '/', $host); } elsif (-f $cookie_file) { $cookie_jar = HTTP::Cookies::Netscape->new(File => $cookie_file); } else { die "$0: No authentication data found, cannot continue"; } $journal = SOAP::Lite->uri($uri) ->proxy($proxy, cookie_jar => $cookie_jar); die "$0: Error creating SOAP::Lite client, cannot continue" unless $journal; $result = $journal->add_entry(subject => $title, body => $body, discuss => $discuss); if ($result->fault) { die "$0: Failed: " . $result->faultstring . "\n"; } else { printf "New entry added as %s\n", $result->result; } exit; # Taken from the Slash codebase sub make_cookie { my ($uid, $passwd) = @_; my $cookie = $uid . '::' . md5_hex($passwd); $cookie =~ s/(.)/sprintf("%%%02x", ord($1))/ge; $cookie =~ s/%/%25/g; $cookie; } 
, on behalf of $user</i></p> BODY $host = 'use.perl.org'; $uri = "http://$host/Slash/Journal/SOAP"; $proxy = "http://$host/journal.pl"; # Note that this path is UNIX-centric. Consider using # File::Spec methods in most cases. $cookie_file = "$ENV{HOME}/.netscape/cookies"; if ($opts{C}) { $cookie_jar = HTTP::Cookies->new ->set_cookie(0, user => make_cookie(split /:/, $opts{C}), '/', $host); } elsif (-f $cookie_file) { $cookie_jar = HTTP::Cookies::Netscape->new(File => $cookie_file); } else { die "
 #!/usr/bin/perl -w use strict; use File::Basename 'basename'; use Getopt::Std; use Digest::MD5 'md5_hex'; use HTTP::Cookies; use SOAP::Lite; my (%opts, $user, $title, $discuss, $body, $host, $uri, $proxy, $file, $name, $cookie_file, $cookie_jar, $journal, $result); getopts('C:c', \%opts) or die <<"USAGE"; Usage: $0 [ -c ] [ -C user:pass ] -c Allow comments on journal entry -C user:pass Provide authentication in place of (or in absence of) Netscape cookies. User in this case is UID, not nickname. USAGE while (defined($_ = <STDIN>)) { $file = $1, last if /^\s+file:\s+(\S+)/; } die "$0: No filename found in input, stopped " unless $file; $name = basename $file; $file =~ s^\$CPANhttp://www.cpan.org; $user = (getpwuid($<))[0]; $title = "$name uploaded to PAUSE"; $discuss = $opts{c} ? 1 : 0; $body = << "BODY"; <p>The file <tt>$name</tt> has been uploaded to CPAN, and will soon be accessible as <a href="$file">$file</a>. Mirroring may take up to 24 hours.</p> <p><i>$0, on behalf of $user</i></p> BODY $host = 'use.perl.org'; $uri = "http://$host/Slash/Journal/SOAP"; $proxy = "http://$host/journal.pl"; # Note that this path is UNIX-centric. Consider using # File::Spec methods in most cases. $cookie_file = "$ENV{HOME}/.netscape/cookies"; if ($opts{C}) { $cookie_jar = HTTP::Cookies->new ->set_cookie(0, user => make_cookie(split /:/, $opts{C}), '/', $host); } elsif (-f $cookie_file) { $cookie_jar = HTTP::Cookies::Netscape->new(File => $cookie_file); } else { die "$0: No authentication data found, cannot continue"; } $journal = SOAP::Lite->uri($uri) ->proxy($proxy, cookie_jar => $cookie_jar); die "$0: Error creating SOAP::Lite client, cannot continue" unless $journal; $result = $journal->add_entry(subject => $title, body => $body, discuss => $discuss); if ($result->fault) { die "$0: Failed: " . $result->faultstring . "\n"; } else { printf "New entry added as %s\n", $result->result; } exit; # Taken from the Slash codebase sub make_cookie { my ($uid, $passwd) = @_; my $cookie = $uid . '::' . md5_hex($passwd); $cookie =~ s/(.)/sprintf("%%%02x", ord($1))/ge; $cookie =~ s/%/%25/g; $cookie; } 
: No authentication data found, cannot continue"; } $journal = SOAP::Lite->uri($uri) ->proxy($proxy, cookie_jar => $cookie_jar); die "
 #!/usr/bin/perl -w use strict; use File::Basename 'basename'; use Getopt::Std; use Digest::MD5 'md5_hex'; use HTTP::Cookies; use SOAP::Lite; my (%opts, $user, $title, $discuss, $body, $host, $uri, $proxy, $file, $name, $cookie_file, $cookie_jar, $journal, $result); getopts('C:c', \%opts) or die <<"USAGE"; Usage: $0 [ -c ] [ -C user:pass ] -c Allow comments on journal entry -C user:pass Provide authentication in place of (or in absence of) Netscape cookies. User in this case is UID, not nickname. USAGE while (defined($_ = <STDIN>)) { $file = $1, last if /^\s+file:\s+(\S+)/; } die "$0: No filename found in input, stopped " unless $file; $name = basename $file; $file =~ s^\$CPANhttp://www.cpan.org; $user = (getpwuid($<))[0]; $title = "$name uploaded to PAUSE"; $discuss = $opts{c} ? 1 : 0; $body = << "BODY"; <p>The file <tt>$name</tt> has been uploaded to CPAN, and will soon be accessible as <a href="$file">$file</a>. Mirroring may take up to 24 hours.</p> <p><i>$0, on behalf of $user</i></p> BODY $host = 'use.perl.org'; $uri = "http://$host/Slash/Journal/SOAP"; $proxy = "http://$host/journal.pl"; # Note that this path is UNIX-centric. Consider using # File::Spec methods in most cases. $cookie_file = "$ENV{HOME}/.netscape/cookies"; if ($opts{C}) { $cookie_jar = HTTP::Cookies->new ->set_cookie(0, user => make_cookie(split /:/, $opts{C}), '/', $host); } elsif (-f $cookie_file) { $cookie_jar = HTTP::Cookies::Netscape->new(File => $cookie_file); } else { die "$0: No authentication data found, cannot continue"; } $journal = SOAP::Lite->uri($uri) ->proxy($proxy, cookie_jar => $cookie_jar); die "$0: Error creating SOAP::Lite client, cannot continue" unless $journal; $result = $journal->add_entry(subject => $title, body => $body, discuss => $discuss); if ($result->fault) { die "$0: Failed: " . $result->faultstring . "\n"; } else { printf "New entry added as %s\n", $result->result; } exit; # Taken from the Slash codebase sub make_cookie { my ($uid, $passwd) = @_; my $cookie = $uid . '::' . md5_hex($passwd); $cookie =~ s/(.)/sprintf("%%%02x", ord($1))/ge; $cookie =~ s/%/%25/g; $cookie; } 
: Error creating SOAP::Lite client, cannot continue" unless $journal; $result = $journal->add_entry(subject => $title, body => $body, discuss => $discuss); if ($result->fault) { die "
 #!/usr/bin/perl -w use strict; use File::Basename 'basename'; use Getopt::Std; use Digest::MD5 'md5_hex'; use HTTP::Cookies; use SOAP::Lite; my (%opts, $user, $title, $discuss, $body, $host, $uri, $proxy, $file, $name, $cookie_file, $cookie_jar, $journal, $result); getopts('C:c', \%opts) or die <<"USAGE"; Usage: $0 [ -c ] [ -C user:pass ] -c Allow comments on journal entry -C user:pass Provide authentication in place of (or in absence of) Netscape cookies. User in this case is UID, not nickname. USAGE while (defined($_ = <STDIN>)) { $file = $1, last if /^\s+file:\s+(\S+)/; } die "$0: No filename found in input, stopped " unless $file; $name = basename $file; $file =~ s^\$CPANhttp://www.cpan.org; $user = (getpwuid($<))[0]; $title = "$name uploaded to PAUSE"; $discuss = $opts{c} ? 1 : 0; $body = << "BODY"; <p>The file <tt>$name</tt> has been uploaded to CPAN, and will soon be accessible as <a href="$file">$file</a>. Mirroring may take up to 24 hours.</p> <p><i>$0, on behalf of $user</i></p> BODY $host = 'use.perl.org'; $uri = "http://$host/Slash/Journal/SOAP"; $proxy = "http://$host/journal.pl"; # Note that this path is UNIX-centric. Consider using # File::Spec methods in most cases. $cookie_file = "$ENV{HOME}/.netscape/cookies"; if ($opts{C}) { $cookie_jar = HTTP::Cookies->new ->set_cookie(0, user => make_cookie(split /:/, $opts{C}), '/', $host); } elsif (-f $cookie_file) { $cookie_jar = HTTP::Cookies::Netscape->new(File => $cookie_file); } else { die "$0: No authentication data found, cannot continue"; } $journal = SOAP::Lite->uri($uri) ->proxy($proxy, cookie_jar => $cookie_jar); die "$0: Error creating SOAP::Lite client, cannot continue" unless $journal; $result = $journal->add_entry(subject => $title, body => $body, discuss => $discuss); if ($result->fault) { die "$0: Failed: " . $result->faultstring . "\n"; } else { printf "New entry added as %s\n", $result->result; } exit; # Taken from the Slash codebase sub make_cookie { my ($uid, $passwd) = @_; my $cookie = $uid . '::' . md5_hex($passwd); $cookie =~ s/(.)/sprintf("%%%02x", ord($1))/ge; $cookie =~ s/%/%25/g; $cookie; } 
: Failed: " . $result->faultstring . "\n"; } else { printf "New entry added as %s\n", $result->result; } exit; # Taken from the Slash codebase sub make_cookie { my ($uid, $passwd) = @_; my $cookie = $uid . '::' . md5_hex($passwd); $cookie =~ s/(.)/sprintf("%%%02x", ord())/ge; $cookie =~ s/%/%25/g; $cookie; }


Programming Web Services with Perl
Programming Web Services with Perl
ISBN: 0596002068
EAN: 2147483647
Year: 2000
Pages: 123

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net