4.3 XMLRPC::Lite


The XMLRPC::Lite package is part of the SOAP::Lite suite, written by one of the authors of this book, Pavel Kulchenko. It shares much of the same underlying architecture and structure as the SOAP package. As a result, it also supports some transport protocols that aren't officially part of XML-RPC, such as TCP/IP and the POP3 protocol. This package is also the only one of the three toolkits that can be used without dependency on an external XML parser. It will use the faster XML::Parser if it is available, however.

Installation of this module takes place as a part of the larger installation of SOAP::Lite itself. That installation process is described in greater detail in Chapter 6 so it won't be covered here.

4.3.1 Client Example: meer2html.pl

Example 4-3 shows how Chapter 3's Meerkat application looks with XMLRPC::Lite . [1] Notice how similar it appears to the RPC::XMLSimple version of this application (Example 4-1). The interface design of all the XML-RPC toolkits are very similar, which should simplify switching between them, if need be. As always, full code is available in Appendix C.

[1] Only the different parts are shown in this example, not the perl -w or other elements shared with the RPC::XMLSimple version of the utility.

Example 4-3. The meer2html-Lite.pl client
 use XMLRPC::Lite;     $client = XMLRPC::Lite->proxy(MEERKAT)           ->on_fault(sub { die "Transport error: " .                                $_[1]->faultstring }); sub show_data {     my $data = shift;         print STDOUT qq(<span class="meerkat">\n<dl>\n);     for (@$data) {         print STDOUT <<"END_HTML"; <dt class="title"><a href="$_->{link}">$_->{title}</a></dt> <dd class="description">$_->{description}</dd> END_HTML     }     print STDOUT qq(</dl>\n</span>\n); }     sub resolve_name {     my ($str, $name) = @_;         $name = "meerkat.get${name}BySubstring";     my $resp = $client->call($name, $str)->result;     die "resolve_name: $str returned more than 1 match"         if (@$resp > 1);         $resp->[0]{id}; }     sub get_data {     my ($key, $val, $num) = @_;         $client->call('meerkat.getItems',                   { $key         => $val,                     time_period  => '7DAY',                     num_items    => $num,                     descriptions => 200 })->result; } 

Again, only one module needs to be included in order to get the necessary range of XML-RPC support. Creating the client object is different in the XMLRPC::Lite package: while there is a new( ) method that can be explicitly called, the structure of the methods is such that most of them automatically create an object when called without an object reference. So, calling the proxy method as a static method has the side effect of creating the object. The return value from proxy is the object reference, which is then chained to a call to on_fault , a method that sets the callback to be used when an error occurs in an XML-RPC call.

The proxy method has a slightly misleading name, but it comes from the SOAP::Lite roots of XMLRPC::Lite in which the meaning of a URI is more complex than in XML-RPC. Here, it is used to set the URL of the XML-RPC server that the client expects to communicate with ( MEERKAT is a constant defined earlier in the program).

XMLRPC::Lite does exception handling through use of the die keyword in Perl but will use a callback instead if one is provided. For this application, the callback is set using the on_fault method. The error handler simply dies, using the error message from SOAP::Lite .

The show_data subroutine is unchanged from the RPC::XMLSimple version. Because the toolkits all convert the information received from the server into native Perl data structures, there is no need for this part of the application to be any different from the RPC::XMLSimple rendition .

Both resolve_name and get_data use the $client object to make the actual remote calls. The method used here is named call and takes as an argument the name of the remote procedure to call. In both cases, the data that is being passed to the remote call can easily be properly identified for serialization into the XML-RPC syntax. Were some of the data not so clear (such as using 101 as a <string> instead of an <int> ), there are methods that specify the way in which XMLRPC::Lite serializes it.

One way in which this toolkit handles data differently from the other two is that the calls through the client object to the remote server don't return native Perl data. They return an object reference in a special class called XMLRPC::SOM , which is described later. For now, know that the values returned by call need to be used to call another method called result . This method takes the XMLRPC::SOM object and converts it to a native Perl value.

4.3.2 The XMLRPC::Lite Class in Detail

The XMLRPC::Lite class is a subclass of SOAP::Lite , which will be covered in a later chapter. The XML-RPC code benefits from this relationship in terms of functionality and debugging support.

An XMLRPC::Lite client object can be created directly with the new method of the class, or indirectly by calling most of the class' methods as static methods (also called class methods). In Example 4-3, the proxy method was called as a static method. This method returns an object reference on success, and in this case the reference was immediately used to call the on_fault method to set up a callback for catching run-time errors and XML-RPC faults. As with proxy , this method returns the object reference on success, and this is the value that is ultimately stored in $client . The proxy method supplies the URL of the XML-RPC server with which the client communicates. The method may be used as often as needed to retarget the client object when desired.

When the client object calls a remote procedure on the server, it can be done in one of two ways: either by calling the remote name as a method on the object itself or by using the call method provided within the XMLRPC::Lite class. The call method intervenes when there are one or more characters in the procedure's name that aren't legal in Perl subroutine names , such as in Example 4-3 (the "." in meerkat.getItems ). When a remote call is successful, the returned value is an object in the XMLRPC::SOM class.

The XMLRPC::SOM objects encapsulate the deserialized return from an XML-RPC call. The class inherits from SOAP::SOM , and as such contains a lot of functionality XML-RPC users don't need to worry about (covered in Chapter 6). From the perspective of XML-RPC, the main methods that need to be understood are result , fault , faultcode , and faultstring .

The result method returns the value returned by the server as native Perl data. It returns undef if the response was a fault. When that is the case, the fault method returns the full structure of the <fault> element itself as a Perl hash reference. An application can use this to retrieve the faultCode and faultString keys, or it may use the faultcode and faultstring methods on the XMLRPC::SOM object directly.

As an alternative to handling potential server-side faults directly within the logic of the client, XMLRPC::Lite allows an application to configure a callback on the client object that will be invoked if a fault is read in the response. This was demonstrated in Example 4-3 with the on_fault method. If this is configured, the callback is invoked as a method on the client object and given one of two inputs: either the XMLRPC::SOM object when a fault is read from the server or the special Perl variable $@ when an exception has been thrown within the client library itself. The on_fault method may be used at creation time (as in Example 4-3) or at any time during the life of the client object. In Example 4-3, the method is called with an anonymous subroutine that simply calls die with the value from the faultstring method on the data object.

When calling a remote method, the client object takes the parameters for the call as ordinary Perl data and attempts to resolve any ambiguity using regular-expression matching. This doesn't always work, of course, as there may be times when the application means to pass 10 as a string rather than an int . The XMLRPC::Data class assists by allowing an application to specify the type associated with a data parameter. Like the other classes here, it inherits a lot of functionality that isn't relevant to XML-RPC. It is useful in this environment because it creates objects that can be passed to the call method (or through the direct-calling syntax):

 $string = XMLRPC::Data->type(string => 10); ... $client->call(someMethod => $string); 

XMLRPC::Lite is pretty adept at deriving int and double data, so this form of explicit typing should be necessary only for string values that look like something else or the boolean and dateTime.iso8601 types.

4.3.3 Debugging

XMLRPC::Lite has a powerful tracing functionality that lets you follow program flow from the creation and destruction of objects up to the XML messages that get sent over the wire. Enable tracing when the module is loaded with:

 use XMLRPC::Lite +trace => 'all'; 

There are a number of specifiers that may follow the trace keyword. These are often referred to as signals in the SOAP::Lite documentation, and are shown in Table 4-2. The use line enables all the trace points and is also the default behavior if no arguments are given.

Table 4-2. The trace signals

Keyword

Meaning

 transport 

(Client-side only) Triggered by the client right before a request is sent and right after the response is received. Each time the argument passed is the object used, a HTTP::Request or HTTP::Response reference as appropriate.

 dispatch 

(Server-side only) When the method (server-side) has been resolved from the call to a local name, this signal is called with the resolved name. Currently (as of SOAP::Lite 0.55) disabled.

 parameters 

(Server-side only) This signal is triggered before a method call is finally dispatched. The arguments are the parameters to the server-side method after they have been converted from XML to Perl data.

 result 

(Server-side only) After the method has been run on the server-side, this signal is triggered with the results passed back from the call (before serialization).

 headers 

(Server-side only) The headers signal is designed to trigger on the headers of an incoming request. It is currently (as of SOAP::Lite 0.55) disabled.

 objects 

This signal notes the creation and destruction of the internal objects within the classes.

 method 

When the serializer (the component used to turn Perl data into an XML representation) builds a message around a method name (such as for a request), it sends this signal.

 fault 

As earlier, but this is the signal called when the serializer is building a fault message.

 freeform 

As with the previous two, but this signal is used for general cases that don't fit the method or fault models.

 trace 

This signal marks the entry-point of most functions in the classes. However, not all methods send the signal.

 debug 

This signal is used in transport modules to track the contents of requests or responses.

The tracing facility is very thorough, and further explanation may be found in Appendix B, which covers the SOAP::Lite toolkit classes in detail. It is possible to provide customized callbacks for any or all the signals listed in the table. Also see the SOAP::Trace section of the SOAP::Lite manpage for more information.

4.3.4 Auto-Dispatch

Another very useful feature of the client class is something called auto-dispatching . With this feature, a client application can configure the XMLRPC::Lite client to intercept your Perl function calls and instead transparently make remote method calls. This works only if all the calls are to method names that are valid Perl identifiers (no illegal characters such as "." in them).

When XMLRPC::Lite is initially loaded, it is possible to set some of the class parameters to values that will later be used as defaults for the creation of new objects. It is also possible to instruct the client class to use Perl's auto-loading functionality to intercept unknown function calls and attempt to resolve them remotely, before giving up and signaling an error. Methods such as proxy and on_fault can be specified in the use line, just as trace was earlier:

 use XMLRPC::Lite proxy => 'http://localhost:9000'; 

Any client object that is created but given no explicit proxy value will use this default when it tries to call a remote function. With another special keyword, autodispatch , an internal client object can use that same proxy value to automatically look up any unknown functions that are called:

 use XMLRPC::Lite +autodispatch =>                  proxy => 'http://localhost:9000'; ... $a = remoteMethod($arg); 

Of course, using auto-dispatch can lead to code that is more difficult to trace because it hides the remote nature of some of the calls. Auto-dispatch is also unable to call routines whose names contain characters Perl would not recognize in a subroutine name. However, it can make a small-size to mid-size script more readable. The balance between compactness and maintainability is different for different circumstances and can't be dictated as an absolute rule.

4.3.5 The Fortune Server Using XMLRPC::Lite

Implementing the fortune server isn't much more complex using the XMLRPC::Lite classes. In fact, were the underlying code object-oriented rather than procedural, it would be virtually identical. Example 4-4 shows this version of the fortune server.

Example 4-4. The fortune-Lite.pl daemon
 #!/usr/bin/perl -w     use strict;     use XRFortune; use XMLRPC::Transport::HTTP;     BEGIN {     no strict 'refs';         for my $method qw(books fortune weighted_fortune) {         *$method = sub { shift; XRFortune::$method(@_) };     } }     XMLRPC::Transport::HTTP::Daemon     ->new(LocalPort => 9000, ReuseAddr => 1)     ->dispatch_to(qw(books fortune weighted_fortune))     ->handle;     exit; 

The first thing to notice is that the package used by Perl isn't the same name used to create the server object. The XMLRPC::Lite::Transport module contains several server classes to choose from. In Example 4-4, the class selected is one that uses an HTTP::Daemon instance behind the scenes to provide the actual server functionality. Unlike the server class provided in the RPC::XMLSimple kit, this class isn't a subclass of HTTP::Daemon but instead transparently routes the relevant methods to the contained object.

The next area worth examining is the BEGIN block that creates local (to the main :: namespace) versions of the three functions that the XRFortune.pm module provided. The loop construct is a shorthand for creating the three subroutines without having three nearly identical declarations. This allows the server object to present them as simple names, rather than requiring that clients use a package name to fully qualify them. However, it isn't enough to import them into the local namespace ( assuming XRFortune.pm exports them).

The XMLRPC::Lite server implementations assume that they are dealing with object-oriented code or methods in the traditional sense. Because of this, when the routine is actually entered, the first argument to it will be the package name because all calls are essentially static method invocations. So instead of importing (or aliasing) the routines, the BEGIN block creates simple frontend calls to each routine that strip the initial parameter from the arguments list before calling the true code.

Creating the server object itself is straightforward: a constructor ( new ) is called with arguments ( LocalPort and ReuseAddr ) that will eventually be passed to the creation of the HTTP::Daemon object. The return value from the constructor is the new object reference, which is then used to call the dispatch_to method to set up what server-side functionality is being made available to clients. This, too, returns the server object reference upon success, at which point the handle method is called to enter the accept/dispatch loop of the server.

4.3.6 The XMLRPC::Lite Server Classes

The server functionality provided by XMLRPC::Lite is much broader than that of RPC::XMLSimple . Several different classes are provided from which to create a server object. They can also be subclassed, allowing application-specific derivatives to intercede in areas such as authentication or URI mapping. Basic classes are provided for HTTP transport via HTTP::Daemon , ordinary CGI dispatch, and Apache integration. Other server classes include raw TCP/IP transport and support for using a POP3 server as a source of requests. As with the client functionality described earlier, much of the server capabilities are derivative of the SOAP::Lite classes they inherit from.

The server classes generally follow the same behavior in terms of the methods they handle. The primary differences are in how the objects themselves are created. Table 4-3 illustrates the various server classes and the specifics of their constructor syntax. The first column is the module that an application would have to load, and the second column represents the class within that module. Hence, an application would use XMLRPC::Transport::POP3 and then instantiate from the XMLRPC::Transport::POP3::Server class.

Table 4-3. XMLRPC::Lite server classes

Module

Class

Constructor

 XMLRPC:: Transport::HTTP 
 Daemon 

Arguments to the constructor for this class are mainly passed to the creation of an HTTP::Daemon object. The selection and syntax are the same as for IO::Socket::INET , the parent of that class. These include LocalPort , ReuseAddr , etc. Other arguments are described later.

 
 CGI 

This class provides XML-RPC server functionality as an ordinary CGI application on a web-server. The constructor's arguments are described later.

 
 Apache 

Just as the CGI class provides an XML-RPC layer for ordinary CGI, this class implements some additional hooks and functionality to operate in cooperation with Apache and mod_perl .

 XMLRPC:: Transport:: POP3 
 Server 

The constructor takes a string that represents the POP3 server to poll. It's a pseudo-URL of the form pop3://pop.server.com . It can also include user authentication information.

 XMLRPC:: Transport:: TCP 
 Server 

The raw TCP/IP server class uses an IO::Socket::INET object behind the scenes, and thus the arguments to the constructor are those that would be passed to the creation of the socket, such as LocalAddr , LocalPort , Listen , etc.

Each server class has its own specialized arguments that it recognizes, but all the server classes are ultimately derived from SOAP::Server , which also has a set of arguments it will handle. These arguments are actually a syntax for having the new object call other class methods before the new object reference is returned.

Any of the following methods may appear as an argument to the constructor, immediately followed by what would have been the argument(s) to the method (a list is passed in this syntax as a list reference):

dispatch_to

This method sets up the known set of methods/routines to be made available to clients. The arguments to this method are each any one of the following:

Directory path ( /usr/local/perl , etc.)

If the value appears to be a directory path, it is added to the search path for loading modules on demand. Note that the server classes clear out the @INC array for the sake of security. This facility allows the adding (or re-adding) of directories after this has happened .

Class name ( Class::Sub , etc.)

When the string matches a known class, it makes all methods in that class (or, if you prefer, all functions in the namespace) available. Clients request them using XML-RPC's notion of class-qualified names, Class.Sub.method , for example.

Subroutine name ( Class::Sub::method , local_method , etc.)

When the value matches a known subroutine name (possibly including the class name), the method or routine is made available. As with specifying a class, the client will have to refer to the subroutine using the full class name, as specified here.

This method may take just one such option, or as many as the application sees fit to pass at once.

options

This sets options on the server object. Currently, the only supported option at the XML-RPC level is compress_threshold followed by an integer value. The server classes use compression if the client indicates support for it and if the Compress::Zlib module is available. This option sets the minimum size in bytes a message must be before being compressed. The default is to never compress.

The server classes all have a handle method, as well. The Apache-based class also has an alias for handle under the name handler , so that it is more easily used as a location handler under mod_perl . The way handle behaves is dependent on the server class. For the Daemon class, it enters a loop in which the server waits for incoming requests on the socket and serves each in turn, only exiting when interrupted by a signal. The CGI and Apache classes handle one request each time the method is invoked and expect the relevant request information to be passed as arguments. The TCP class behaves in a similar fashion to the Daemon class, while the POP3 class handles all request messages in the specified POP3 mail folder before returning to the caller.

All the server classes call their local subroutines as if they were methods, passing the class (or package) name in as the first parameter. This works fine for publishing an API that is already a set of class methods. However, when the code to be published isn't object-oriented, such as in the Fortune module, it requires a bit of extra work, as you saw with the BEGIN block in Example 4-4.



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