C.2 XML-RPC Toolkit Samples ( Chapter 4 )


C.2 XML-RPC Toolkit Samples (Chapter 4)

Chapter 4 presents the three XML-RPC toolkits available to the Perl developer. For each module the earlier Meerkat client was reimplemented as an example of a client; a simple server that provided quotes from the popular fortune program demonstrated server development. The examples are broken down by toolkit used, with the base code for the fortune interface presented last, because it was kept toolkit-independent.

C.2.1 The RPC::XMLSimple Examples

The client example was the first look at reengineering the Meerkat example from Chapter 3 with a toolkit. The result was a script less than half the size of the original sample.

Example C-2. meer2html-Frontier.pl
 #!/usr/bin/perl -w     use strict; use vars qw($chan $cat $num $data $client);     use Frontier::Client; use constant MEERKAT =>     'http://www.oreillynet.com/meerkat/xml-rpc/server.php';     # Read and test the command-line arguments. if ($ARGV[0] =~ /^-ch/) {     $chan = $ARGV[1];     $num  = $ARGV[2]  15; } elsif ($ARGV[0] =~ /^-ca/) {     $cat = $ARGV[1];     $num = $ARGV[2]  15; } unless (($chan or $cat) and ($num =~ /^\d+$/)) {     die "USAGE: 
 #!/usr/bin/perl -w use strict; use vars qw($chan $cat $num $data $client); use Frontier::Client; use constant MEERKAT => 'http://www.oreillynet.com/meerkat/xml-rpc/server.php'; # Read and test the command-line arguments. if ($ARGV[0] =~ /^-ch/) { $chan = $ARGV[1]; $num = $ARGV[2]  15; } elsif ($ARGV[0] =~ /^-ca/) { $cat = $ARGV[1]; $num = $ARGV[2]  15; } unless (($chan or $cat) and ($num =~ /^\d+$/)) { die "USAGE: $0 { -channel str  -category str } [ n ]"; } $client = Frontier::Client->new(url => MEERKAT); # This could be done with just one data-retrieval routine, # but this way is easier to follow, and tests $chan/$cat # less-often. $data = $chan ? data_from_chan($chan, $num) : data_from_cat($cat, $num); show_data($data); exit; # Retrieve data from a 'Channel' source sub data_from_chan { my ($chan, $num) = @_; # If $chan is not already numeric, convert it by using # an intermediate XML-RPC call $chan = resolve_name($chan, 'Channels') unless ($chan =~ /^\d+$/); get_data(channel => $chan, $num); } # Retrieve data from a 'Category' source sub data_from_cat { my ($cat, $num) = @_; # If $cat is not already numeric, convert it by using # an intermediate XML-RPC call $cat = resolve_name($cat, 'Categories') unless ($cat =~ /^\d+$/); get_data(category => $cat, $num); } # Output the HTML fragment for the data. Note that the way # the data is treated is independant of whether the source # was a category or a channel 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); } # Resolve a substring name-fragment into the numeric ID that # the call later on in get_data requires sub resolve_name { my ($str, $name) = @_; # Fortunately, the calling syntax is the same for # categories or channels, the only difference being # the name of the remote procedure $name = "meerkat.get${name}BySubstring"; my $resp = $client->call($ name , $str); # We aren't doing multi-channels (yet), so report an # error if the substring returns more than one hit die "resolve_name: $str returned more than 1 match" if (@$resp > 1); $resp->[0]{id}; } # Get the data-- the result of the method call below happens # to return the data in the exact format needed by show_data sub get_data { my ($key, $val, $num) = @_; $client->call('meerkat.getItems', { $key => $val, time_period => '7DAY', num_items => $num, descriptions => 200 }); } 
{ -channel str -category str } [ n ]"; } $client = Frontier::Client->new(url => MEERKAT); # This could be done with just one data-retrieval routine, # but this way is easier to follow, and tests $chan/$cat # less-often. $data = $chan ? data_from_chan($chan, $num) : data_from_cat($cat, $num); show_data($data); exit; # Retrieve data from a 'Channel' source sub data_from_chan { my ($chan, $num) = @_; # If $chan is not already numeric, convert it by using # an intermediate XML-RPC call $chan = resolve_name($chan, 'Channels') unless ($chan =~ /^\d+$/); get_data(channel => $chan, $num); } # Retrieve data from a 'Category' source sub data_from_cat { my ($cat, $num) = @_; # If $cat is not already numeric, convert it by using # an intermediate XML-RPC call $cat = resolve_name($cat, 'Categories') unless ($cat =~ /^\d+$/); get_data(category => $cat, $num); } # Output the HTML fragment for the data. Note that the way # the data is treated is independant of whether the source # was a category or a channel 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); } # Resolve a substring name-fragment into the numeric ID that # the call later on in get_data requires sub resolve_name { my ($str, $name) = @_; # Fortunately, the calling syntax is the same for # categories or channels, the only difference being # the name of the remote procedure $name = "meerkat.get${name}BySubstring"; my $resp = $client->call($name, $str); # We aren't doing multi-channels (yet), so report an # error if the substring returns more than one hit die "resolve_name: $str returned more than 1 match" if (@$resp > 1); $resp->[0]{id}; } # Get the data-- the result of the method call below happens # to return the data in the exact format needed by show_data sub get_data { my ($key, $val, $num) = @_; $client->call('meerkat.getItems', { $key => $val, time_period => '7DAY', num_items => $num, descriptions => 200 }); }

The server example presented the RPC::XMLSimple::Daemon class as the basis for writing servers. It's a lightweight class, as this example shows. The server uses the XRFortune.pm module, which is presented at the end of this appendix.

Example C-3. fortune-Frontier.pl
 #!/usr/bin/perl -w     use strict;     use XRFortune; use RPC::XMLSimple::Daemon;     # The way the Daemon class in RPC::XMLSimple is set up, the # constructor actually drops directly into the socket-accept # loop, so there is no need to call any other method. The # local routines are specified via the "methods" key on the # input parameters. This package calls local routines as # ordinary routines, not class methods, so there is no need # for a wrapper class. RPC::XMLSimple:Daemon->new(LocalPort => 9000,                            ReuseAddr => 1,                            methods =>                            { books   => \&XRFortune::books,                              fortune =>                              \&XRFortune::fortune,                              weighted_fortune =>                              \&XRFortune::weighted_fortune                            });     exit; 

C.2.2 The XMLRPC::Lite Examples

The XMLRPC::Lite version of the Meerkat client weighs in at just five lines longer than the RPC::XMLSimple version. The following example illustrates the way in which the XMLRPC::Lite clients return data to the caller, as well as the methods used to specify the server URL and make the actual calls.

Example C-4. meer2html-Lite.pl
 #!/usr/bin/perl -w     use strict; use vars qw($chan $cat $num $data $client);     use XMLRPC::Lite; use constant MEERKAT =>     'http://www.oreillynet.com/meerkat/xml-rpc/server.php';     # Read and test the command-line arguments. if ($ARGV[0] =~ /^-ch/) {     $chan = $ARGV[1];     $num  = $ARGV[2]  15; } elsif ($ARGV[0] =~ /^-ca/) {     $cat = $ARGV[1];     $num = $ARGV[2]  15; } unless (($chan or $cat) and ($num =~ /^\d+$/)) {     die "USAGE: 
 #!/usr/bin/perl -w use strict; use vars qw($chan $cat $num $data $client); use XMLRPC::Lite; use constant MEERKAT => 'http://www.oreillynet.com/meerkat/xml-rpc/server.php'; # Read and test the command-line arguments. if ($ARGV[0] =~ /^-ch/) { $chan = $ARGV[1]; $num = $ARGV[2]  15; } elsif ($ARGV[0] =~ /^-ca/) { $cat = $ARGV[1]; $num = $ARGV[2]  15; } unless (($chan or $cat) and ($num =~ /^\d+$/)) { die "USAGE: $0 { -channel str  -category str } [ n ]"; } # Creating a client object happens automatically when one of # the methods such as proxy( ) is called. The on_fault call # sets up a handler call-back for transport errors. $client = XMLRPC::Lite->proxy(MEERKAT) ->on_fault(sub { die "Transport error: " . $_[1]->faultstring }); # This could be done with just one data-retrieval routine, # but this way is easier to follow, and tests $chan/$cat # less-often. $data = $chan ? data_from_chan($chan, $num) : data_from_cat($cat, $num); show_data($data); exit; # Retrieve data from a 'Channel' source sub data_from_chan { my ($chan, $num) = @_; # If $chan is not already numeric, convert it by using # an intermediate XML-RPC call $chan = resolve_name($chan, 'Channels') unless ($chan =~ /^\d+$/); get_data(channel => $chan, $num); } # Retrieve data from a 'Category' source sub data_from_cat { my ($cat, $num) = @_; # If $cat is not already numeric, convert it by using # an intermediate XML-RPC call $cat = resolve_name($cat, 'Categories') unless ($cat =~ /^\d+$/); get_data(category => $cat, $num); } # Output the HTML fragment for the data. Note that the way # the data is treated is independant of whether the source # was a category or a channel 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); } # Resolve a substring name-fragment into the numeric ID that # the call later on in get_data requires sub resolve_name { my ($str, $name) = @_; # Fortunately, the calling syntax is the same for # categories or channels, the only difference being # the name of the remote procedure $name = "meerkat.get${name}BySubstring"; my $resp = $client->call($name, $str)->result; # We aren't doing multi-channels (yet), so report an # error if the substring returns more than one hit die "resolve_name: $str returned more than 1 match" if (@$resp > 1); $resp->[0]{id}; } # Get the data-- the result of the method call below happens # to return the data in the exact format needed by show_data sub get_data { my ($key, $val, $num) = @_; $client->call('meerkat.getItems', { $key => $val, time_period => '7DAY', num_items => $num, descriptions => 200 })->result; } 
{ -channel str -category str } [ n ]"; } # Creating a client object happens automatically when one of # the methods such as proxy( ) is called. The on_fault call # sets up a handler call-back for transport errors. $client = XMLRPC::Lite->proxy(MEERKAT) ->on_fault(sub { die "Transport error: " . $_[1]->faultstring }); # This could be done with just one data-retrieval routine, # but this way is easier to follow, and tests $chan/$cat # less-often. $data = $chan ? data_from_chan($chan, $num) : data_from_cat($cat, $num); show_data($data); exit; # Retrieve data from a 'Channel' source sub data_from_chan { my ($chan, $num) = @_; # If $chan is not already numeric, convert it by using # an intermediate XML-RPC call $chan = resolve_name($chan, 'Channels') unless ($chan =~ /^\d+$/); get_data(channel => $chan, $num); } # Retrieve data from a 'Category' source sub data_from_cat { my ($cat, $num) = @_; # If $cat is not already numeric, convert it by using # an intermediate XML-RPC call $cat = resolve_name($cat, 'Categories') unless ($cat =~ /^\d+$/); get_data(category => $cat, $num); } # Output the HTML fragment for the data. Note that the way # the data is treated is independant of whether the source # was a category or a channel 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); } # Resolve a substring name-fragment into the numeric ID that # the call later on in get_data requires sub resolve_name { my ($str, $name) = @_; # Fortunately, the calling syntax is the same for # categories or channels, the only difference being # the name of the remote procedure $name = "meerkat.get${name}BySubstring"; my $resp = $client->call($name, $str)->result; # We aren't doing multi-channels (yet), so report an # error if the substring returns more than one hit die "resolve_name: $str returned more than 1 match" if (@$resp > 1); $resp->[0]{id}; } # Get the data-- the result of the method call below happens # to return the data in the exact format needed by show_data sub get_data { my ($key, $val, $num) = @_; $client->call('meerkat.getItems', { $key => $val, time_period => '7DAY', num_items => $num, descriptions => 200 })->result; }

The example server under XMLRPC::Lite is a little more complicated than its RPC::XMLSimple counterpart . The most notable difference is that this package calls the local routines as if they were static class methods, with the package name as the first element of the parameter list. Because the code in XRFortune.pm is procedural rather than object-oriented, it requires a small amount of extra work up front to provide wrappers for the functions.

Example C-5. fortune-Lite.pl
 #!/usr/bin/perl -w     use strict;     use XRFortune; use XMLRPC::Transport::HTTP;     # For each of the three routines from the XRFortune package # that will be made available to clients, the server needs a # front-end wrapper that strips the first argument from the # parameters list. This is because XMLRPC::Lite calls the # routines as if they were static class methods. BEGIN {     no strict 'refs';         for my $method qw(books fortune weighted_fortune) {         eval "sub $method";         *$method =             eval "sub { shift; XRFortune::$method(\@_) }";     } }     # The constructor returns the object reference, which is # then used to chain a call to dispatch_to( ) that sets up # the three local routines to be made available to clients. # That also returns the object ref, which is used to # chain along to the handle( ) method, which doesn't return # until a signal interrupts the program. XMLRPC::Transport::HTTP::Daemon     ->new(LocalPort => 9000, ReuseAddr => 1)     ->dispatch_to(qw(books fortune weighted_fortune))     ->handle;     exit; 

C.2.3 The RPC::XML Examples

The RPC::XML version of the Meerkat client is one line longer than its XMLRPC::Lite counterpart. This toolkit offers different ways to call the remote methods that return either an object wrapper around the return value or straight Perl data. In this example, the method used returns simple Perl data.

Example C-6. meer2html-RPC::XML.pl
 #!/usr/bin/perl -w     use strict; use vars qw($chan $cat $num $data $client);     use RPC::XML::Client; use constant MEERKAT =>     'http://www.oreillynet.com/meerkat/xml-rpc/server.php';     # Read and test the command-line arguments. if ($ARGV[0] =~ /^-ch/) {     $chan = $ARGV[1];     $num  = $ARGV[2]  15; } elsif ($ARGV[0] =~ /^-ca/) {     $cat = $ARGV[1];     $num = $ARGV[2]  15; } unless (($chan or $cat) and ($num =~ /^\d+$/)) {     die "USAGE: 
 #!/usr/bin/perl -w use strict; use vars qw($chan $cat $num $data $client); use RPC::XML::Client; use constant MEERKAT => 'http://www.oreillynet.com/meerkat/xml-rpc/server.php'; # Read and test the command-line arguments. if ($ARGV[0] =~ /^-ch/) { $chan = $ARGV[1]; $num = $ARGV[2]  15; } elsif ($ARGV[0] =~ /^-ca/) { $cat = $ARGV[1]; $num = $ARGV[2]  15; } unless (($chan or $cat) and ($num =~ /^\d+$/)) { die "USAGE: $0 { -channel str  -category str } [ n ]"; } # Create a client object. The error_handler key specifies a # callback to be invoked when a transport error occurs. If # desired, a fault_handler could be installed for catching # server-side errors, as well. $client = RPC::XML::Client ->new(MEERKAT, error_handler => sub { die "Transport error: $_[0]" }); # This could be done with just one data-retrieval routine, # but this way is easier to follow, and tests $chan/$cat # less-often. $data = $chan ? data_from_chan($chan, $num) : data_from_cat($cat, $num); show_data($data); exit; # Retrieve data from a 'Channel' source sub data_from_chan { my ($chan, $num) = @_; # If $chan is not already numeric, convert it by using # an intermediate XML-RPC call $chan = resolve_name($chan, 'Channels') unless ($chan =~ /^\d+$/); get_data(channel => $chan, $num); } # Retrieve data from a 'Category' source sub data_from_cat { my ($cat, $num) = @_; # If $cat is not already numeric, convert it by using # an intermediate XML-RPC call $cat = resolve_name($cat, 'Categories') unless ($cat =~ /^\d+$/); get_data(category => $cat, $num); } # Output the HTML fragment for the data. Note that the way # the data is treated is independant of whether the source # was a category or a channel 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); } # Resolve a substring name-fragment into the numeric ID that # the call later on in get_data requires sub resolve_name { my ($str, $name) = @_; # Fortunately, the calling syntax is the same for # categories or channels, the only difference being # the name of the remote procedure $name = "meerkat.get${name}BySubstring"; my $resp = $client->simple_request($name, $str); # We aren't doing multi-channels (yet), so report an # error if the substring returns more than one hit die "resolve_name: $str returned more than 1 match" if (@$resp > 1); $resp->[0]{id}; } # Get the data-- the result of the method call below happens # to return the data in the exact format needed by show_data sub get_data { my ($key, $val, $num) = @_; $client->simple_request('meerkat.getItems', { $key => $val, time_period => '7DAY', num_items => $num, descriptions => 200 }); } 
{ -channel str -category str } [ n ]"; } # Create a client object. The error_handler key specifies a # callback to be invoked when a transport error occurs. If # desired, a fault_handler could be installed for catching # server-side errors, as well. $client = RPC::XML::Client ->new(MEERKAT, error_handler => sub { die "Transport error: $_[0]" }); # This could be done with just one data-retrieval routine, # but this way is easier to follow, and tests $chan/$cat # less-often. $data = $chan ? data_from_chan($chan, $num) : data_from_cat($cat, $num); show_data($data); exit; # Retrieve data from a 'Channel' source sub data_from_chan { my ($chan, $num) = @_; # If $chan is not already numeric, convert it by using # an intermediate XML-RPC call $chan = resolve_name($chan, 'Channels') unless ($chan =~ /^\d+$/); get_data(channel => $chan, $num); } # Retrieve data from a 'Category' source sub data_from_cat { my ($cat, $num) = @_; # If $cat is not already numeric, convert it by using # an intermediate XML-RPC call $cat = resolve_name($cat, 'Categories') unless ($cat =~ /^\d+$/); get_data(category => $cat, $num); } # Output the HTML fragment for the data. Note that the way # the data is treated is independant of whether the source # was a category or a channel 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); } # Resolve a substring name-fragment into the numeric ID that # the call later on in get_data requires sub resolve_name { my ($str, $name) = @_; # Fortunately, the calling syntax is the same for # categories or channels, the only difference being # the name of the remote procedure $name = "meerkat.get${name}BySubstring"; my $resp = $client->simple_request($name, $str); # We aren't doing multi-channels (yet), so report an # error if the substring returns more than one hit die "resolve_name: $str returned more than 1 match" if (@$resp > 1); $resp->[0]{id}; } # Get the data-- the result of the method call below happens # to return the data in the exact format needed by show_data sub get_data { my ($key, $val, $num) = @_; $client->simple_request('meerkat.getItems', { $key => $val, time_period => '7DAY', num_items => $num, descriptions => 200 }); }

This server example appears to be the most complex of the three. This is due mainly to the fact that the RPC::XML::Server class is designed for explicit definition of server-side procedures; this prevents accidentally exposing parts of a class or namespace that weren't mean to be published. Thus, the three routines being exported from this sample server must each be explicitly named, one at a time.

Example C-7. fortune-RPC::XML.pl
 #!/usr/bin/perl -w     use strict;     use XRFortune; use RPC::XML::Server;     # The object created by the constructor will be used to # chain on a set of calls to add_proc( ) (which means it # isn't necessary to deal with the local routines being # called as methods), and then drop directly into the # server_loop( ) method. RPC::XML::Server->new(port => 9000)     # add_proc can take a pre-created RPC::XML::Procedure     # object, or a hash reference.     ->add_proc({ name => 'books',                  signature => [ 'array',                                 'array string',                                 'array array' ],                  code => \&XRFortune::books })     # It gets called once for each routine being made     # public by the server.     ->add_proc({ name => 'fortune',                  signature => [ 'array',                                 'array string',                                 'array array' ],                  code => \&XRFortune::fortune })     # There are other ways of specifying the server-side     # procedures, but for an example of this size, this way     # is just as efficient.     ->add_proc({ name => 'weighted_fortune',                  signature => [ 'array',                                 'array string',                                 'array array' ],                  code => \&XRFortune::weighted_fortune })     # This method will only return after a signal interrupts     # it.     ->server_loop;     exit; 

C.2.4 The XRFortune.pm Module

The code in the following example is used as the basis for writing the sample servers. It exposes some part of the functionality of the well-known fortune command, which produces quotes at random from a set of files (sometimes referred to as "books").

Example C-8. XRFortune.pm
 package XRFortune;     use 5.6.0; use strict; use vars qw($FORTUNE %BOOKS); use subs qw(books fortune weighted_fortune);     BEGIN {     # Locate the fortune program. Look in some standard     # places, then loop over the user's PATH.     for my $path (qw(/bin /usr/bin /usr/games),                   split(':', $ENV{PATH})) {         if (-e "$path/fortune" && -x _) {             $FORTUNE = "$path/fortune";             last;         }     }     die "No 'fortune' command found!\n"         unless $FORTUNE;         # Calling fortune -f lists the books it knows, but the     # output goes to STDERR for some reason.     my @books = qx($FORTUNE -f 2>&1); shift(@books);     chomp(@books);     $BOOKS{(reverse split(/ /))[0]}++ for (@books); }     1;     # If called with no arguments, returns the list of known # books as a list reference. If one or more book names were # passed, then take the list and return only the elements # from it that are known books (pruning operation). sub books {     my $list = shift  '';         my @prune = $list ? (ref $list ? @$list : ($list)) : ( );     my @books;         if (@prune) {         @books = sort grep($BOOKS{$_}, @prune);     } else {         @books = sort keys %BOOKS;     }         \@books; }     # Get and return one fortune. With no arguments, just call # the command. With one or more books, limit the selection # of quotes to those books. When the fortune is extracted, # take off the OS-dependent newlines and return the lines # of text as a list reference. sub fortune {     my $book = shift  '';         my @lines;     my $exec = $FORTUNE;     my @books = $book ? (ref $book ? @$book : ($book)) : ( );     if (@books) {         my @bad;         if (@bad = grep(! $BOOKS{$_}, @books)) {             local $" = ', ';             die "fortune: Unknown books (@bad)";         } else {             $exec .= " @books";         }     }         chomp(@lines = `$exec`);     \@lines; }     # This also retrieves one fortune. However, the arguments # here are more than just a list of books to restrict the # search to. If a list reference is passed, use those books # but weight them all equally in the search. If a hash # reference is passed, its keys should be books and the # corresponding values are the integer weights for each # book. The weights must add up to 100. sub weighted_fortune {     my $weights = shift;         die 'weighted_fortune: Must be called with array of ' .         "books or struct of weights and books\n"         unless ref $weights;         my @lines;     my $exec = $FORTUNE;     if (ref($weights) eq 'ARRAY') {         # Use our own books( ) call to ensure all the passed         # books are valid         $weights = books($weights);         # The -e flag makes all books equal in weight         $exec .= " -e @$weights";     } else {         # Trickier: must ensure that the weights add up to         # 100%, even though pruning the list may mean that         # one or more are dropped.         my $total_weight;         my $books = books([ keys %$weights ]);         $total_weight += $weights->{$_} for (@$books);         die 'weighted_fortune: Weights must add up to 100' .             " (total is $total_weight)\n"             unless ($total_weight == 100);         $exec .= " $weights->{$_}% $_" for (@$books);     }         # As with fortune( ) above, drop the OS-dependent     # newline characters and return a list reference.     chomp(@lines = `$exec`);     \@lines; } 

C.2.5 The listMethods.xpl File

The following example illustrates the XPL file format. In the module, this file is generated at build-time.

Example C-9. listMethods.pl
 <?xml version="1.0"?> <!DOCTYPE methoddef SYSTEM "rpc-method.dtd"> <!--     Generated automatically by make_method v1.09,     Thu Aug 15 03:48:27 2002     Any changes made here will be lost. --> <methoddef> <name>system.listMethods</name> <version>1.1</version> <signature>array</signature> <signature>array string</signature> <help> List all the methods known to the server. If the STRING parameter is passed, it is used as a substring to match against, with only those matching methods being returned. Note that the STRING parameter is not a regular expression, but rather just a simple substring. </help> <code language="perl"> <![CDATA[ #!/usr/bin/perl sub listMethods {     use strict;         my $srv = shift;     my $pat = shift;         my @list = sort $srv->list_methods;         # Exclude any that are hidden from introspection APIs     @list = grep(! $srv->get_method($_)->hidden, @list);     @list = grep(index($_, $pat) != -1, @list) if ($pat);         \@list; }     _ _END_ _ ]]></code> </methoddef> 


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