The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

XMLRPC::PurePerl

SYNOPSIS: my $client = new XMLRPC::PurePerl("http://127.0.0.1:8080/"); my $result = $client->call("myMethod", { 'complex' => [ 'structure', 'goes' ] }, 'here' );

  my $xml = XMLRPC::PurePerl->encode_xmlrpc_call( $structure );
  my $str = XMLRPC::PurePerl->decode_xmlrpc( $xml );

  # In case you don't have XML::Simple loaded... (a simple XML serializer / de-serializer)

  my $var_xml = XMLRPC::PurePerl->encode_variable( $structure );
  my $var     = XMLRPC::PurePerl->decode_variable( $var_xml );

DESCRIPTION:

This module implements the XML-RPC standard as defined at www.xmlrpc.com and serves as a (de)serialization engine as well as a client for such services.

This module is in fairly close relation to an implementation that I wrote in javascript. The main problem I ran into web services and browsers was the dependence on the built in javascript XML parser. This module shows off how rolling your own can give you a bit of a boost in performance as well as avoiding dependencies for a compiled XML parser (for you guys who work in the DOD arena like me). If I had more time, I'd have rolled my own basic LWP modules just to avoid the extra dependencies. Anyway, this client provides the basic functionality that modules like RPC::XML or Frontier::RPC2 provide, the only difference is being the reason for the name, this is a pure perl implementation.

DATATYPES:

You can override the basic data types that perl will interpret by instantiating type objects. You simply pass the value as the sole argument, and it will transform into the appropriate XML upon serialization. Three data types will remain as type objects during de-serialization: datetime, base64 and boolean. More simply, date objects returned from the server will come back as a blessed reference of "XMLRPC::PurePerl::Type::datetime". All of the type modules contain simple "value" methods to pull the value from the blessed hash reference.

There are also some simple static methods on XMLRPC::PurePerl to generate these structures.

Lastly, the datetime constructur was given some flexibility. Instead of adding a full date parser, I wrote a few a regex's to parse out most of the sane date formats and put together the XMLRPC date format. Below are some examples of the acceptable formats..

  # Examples:

  my $boolean = XMLRPC::PurePerl->boolean(1);
  my $string  = XMLRPC::PurePerl->string(12345);
  my $b64     = XMLRPC::PurePerl->base64("AB91231=");
  my $double  = XMLRPC::PurePerl->double(123.456);
  my $date    = XMLRPC::PurePerl->datetime("6 June 2006");

  my $value = $b64->value(); # example of using the value method for these data types

  # Acceptable date formats. (times are optional)

  # 20050701 
  # 2004/04/22    (dashes, spaces or hyphens)
  # SEP 19, 2003 
  # 04-22-2004    (dashes, hyphens or spaces)
  # 30 July 05
  # July 30 2005 

  # 20001109171200
  # {ts '2003-06-23 12:21:43'}
  # 302100ZSEP1998
  # 2001-01-01T05:22:23.000Z

Any of the first six formats can also have a time on the end. Here's the acceptable formats for time.

  # 00:00
  # 00:00:00
  # 00:00 AM  (space optional)
  # 00:00:00 AM 
Fault

Faults are represented as an object as well, with a signature of XMLRPC::PurePerl::Type::Fault. The parser allows the fault param structure open to any data type, so if your server decides to send a complex structure back with the fault, it will deserialize it appropriately.

Constructor

  my $client = new XMLRPC::PurePerl("http://validator.xmlrpc.com");

Simply pass the fully qualified URL as your argument to the constructor, and off you go.

call

  my $result = $client->call("method", "argumunts");

First argument to the call method is the method you wish to call, the rest will constitute the values that populate "<params>". Each one will serialize into a "<param>" entry.

encode_call_xmlrpc

  my $xml = XMLRPC::PurePerl->encode_call_xmlrpc("methodName", "arguments");

This, will generate an XMLRPC request xml document based on the arguments passed to it.

encode_response_xmlrpc

  my $xml = XMLRPC::PurePerl->encode_response_xmlrpc("arguments");

This, will generate an XMLRPC response xml document based on the arguments passed to it.

encode_variable

  my $xml = XMLRPC::PurePerl->encode_variable("arguments");

I'm a huge fan of XML::Simple, but having to remember all the options, and taking account for "force_array" to set values as array references instead of simple scalars (where you only have one value coming back is annoying. I have consistently ran into problems when my "simple" usage grew into more complex usage over time. This simple function solves this for, well, me at least.

decode_variable

  my $structure = XMLRPC::PurePerl->decode_variable("arguments");

The deserializer of the previously mentioned function.

decode_xmlrpc

  my $structure = XMLRPC::PurePerl->decode_xmlrpc();
  if ( ref($structure) =~ /fault/i ) {
    &do_something_to_handle_the_fault( $structure->value() );
  }

The data structure returned will be in scalar context, or in list context, depending on your lvalue's sigil. If you're decoding a methodCall, you'll get a structure keyed by the methodName and the arguments passed to it as an array reference..

  # If you dumped out the de-serialization of a methodCall XML document
  $VAR1 = {
    'method' => 'myMethod'
    'args' => [ 'a', 'b', 'c' ]
  }

WHY DO THIS!?!

Yeah, there's a bunch of these modules out there for this kind of stuff. I in no way mean to step on anyones toes, but I am quite proud of the benchmarks that this module is capable of producing. It does have it's limits, but for such a lightweight little engine, I think it does fairly well for itself. Let's keep in mind that this engine is a "fast and loose" engine, with very little in terms of defense from malformed XML, which RPC::XML and Frontier have more built in defense through the use of a true XML Parser.

  500 elements
  ENCODING SPEED TEST
  Benchmark: running frontier, pureperl, rpcxml for at least 10 CPU seconds...
    frontier: 11 wallclock secs (10.47 usr +  0.09 sys = 10.56 CPU) @ 26.70/s (n=282)
    pureperl: 10 wallclock secs (10.69 usr +  0.03 sys = 10.72 CPU) @ 86.75/s (n=930)
      rpcxml: 11 wallclock secs (10.55 usr +  0.05 sys = 10.59 CPU) @ 66.93/s (n=709)
  DECODING SPEED TEST
  Benchmark: running frontier, pureperl, rpcxml for at least 10 CPU seconds...
    frontier: 11 wallclock secs (10.64 usr +  0.02 sys = 10.66 CPU) @ 10.51/s (n=112)
    pureperl: 11 wallclock secs (10.50 usr +  0.08 sys = 10.58 CPU) @ 14.65/s (n=155)
      rpcxml: 11 wallclock secs (10.58 usr +  0.03 sys = 10.61 CPU) @  6.69/s (n=71)

  1000 elements
  ENCODING SPEED TEST
  Benchmark: running frontier, pureperl, rpcxml for at least 10 CPU seconds...
    frontier: 10 wallclock secs (10.44 usr +  0.11 sys = 10.55 CPU) @ 11.95/s (n=126)
    pureperl: 10 wallclock secs (10.55 usr +  0.00 sys = 10.55 CPU) @ 43.61/s (n=460)
      rpcxml: 10 wallclock secs (10.50 usr +  0.09 sys = 10.59 CPU) @ 29.92/s (n=317)
  DECODING SPEED TEST
  Benchmark: running frontier, pureperl, rpcxml for at least 10 CPU seconds...
    frontier: 10 wallclock secs (10.08 usr +  0.00 sys = 10.08 CPU) @  5.26/s (n=53)
    pureperl: 11 wallclock secs (10.27 usr +  0.08 sys = 10.34 CPU) @  7.35/s (n=76)
      rpcxml:  9 wallclock secs (10.19 usr +  0.00 sys = 10.19 CPU) @  3.34/s (n=34)

  5000 elements (beyond this, PurePerl isn't the best module to use)
  ENCODING SPEED TEST
  Benchmark: running frontier, pureperl, rpcxml for at least 10 CPU seconds...
    frontier: 11 wallclock secs (10.81 usr +  0.05 sys = 10.86 CPU) @  1.10/s (n=12)
    pureperl: 10 wallclock secs ( 9.98 usr +  0.08 sys = 10.06 CPU) @  8.55/s (n=86)
      rpcxml: 10 wallclock secs (10.16 usr +  0.19 sys = 10.34 CPU) @  2.22/s (n=23)
  DECODING SPEED TEST
  Benchmark: running frontier, pureperl, rpcxml for at least 10 CPU seconds...
    frontier: 10 wallclock secs (10.48 usr +  0.00 sys = 10.48 CPU) @  1.05/s (n=11)
    pureperl: 11 wallclock secs ( 9.31 usr +  0.94 sys = 10.25 CPU) @  0.88/s (n=9)
      rpcxml: 11 wallclock secs (10.45 usr +  0.03 sys = 10.48 CPU) @  0.67/s (n=7)

See also:

  RPC::XML (the best XMLRPC module out there for exacting precision of the specification)
  Frontier::RPC2 (the reference implementation)
  SOAP::Lite, XMLRPC::Lite (my quest will soon become conquering Document Literal (why is this so hard to do in Perl still?)

Acknowledgements:

Dave Winer, thanks for such a great protocol Paul Lindner and Randy Ray (thanks for the kudos in your book "Programming Web Services in Perl"!), my former co-workers at Red Hat. Joshua Blackburn, who pushed me to write the original javascript implementation of this module. Claus Brunzema, for a very polite bug report dealing with negative integers! Frank Rothhaupt, for a very polite bug report dealing with fault's!

COPYRIGHT:

The XMLRPC::PurePerl module is Copyright (c) 2006 Ryan Alan Dietrich. The XMLRPC::PurePerl module is free software; you can redistribute it and/or modify it under the same terms as Perl itself with the exception that it cannot be placed on a CD-ROM or similar media for commercial distribution without the prior approval of the author.

AUTHOR:

XMLRPC::PurePerl by Ryan Alan Dietrich <ryan@dietrich.net>

2 POD Errors

The following errors were encountered while parsing the POD:

Around line 70:

'=item' outside of any '=over'

Around line 117:

You forgot a '=back' before '=head2'