XML-RPC is a protocol that allows programs of different languages on different machines to easily talk to each other. By sending a well-defined XML document over unadorned HTTP, a client program can make a remote procedure call to a server. The server processes the request and wraps its response in another well-defined XML document that is sent back to the client over that same HTTP connection.
Because procedure requests and responses are all in XML, each end of the RPC connection need not be written in the same language or even for the same platforms. Clients code to an agreed-upon API that a Web service listener implements. Either side of the API fence can change without affecting the other side. In this way, Web services in general, and XML-RPC in particular, can help break down the Berlin Wall of incompatible OS platforms and make language-agnostic software network components.
Although less famous than its younger sibling SOAP, XML-RPC is a simple and easy tool that can help you integrate even the most uncommunicative of systems. Where SOAP is a generalized, object-oriented, messaging protocol that is designed to carry arbitrary XML payloads across any network protocol, XML-RPC is a simple procedural protocol designed only to make remote function calls. Lest you get the impression that XML-RPC is inferior to SOAP, there are a good number of everyday problems that XML-RPC can solve adroitly. The upcoming O'Reilly book, Programming Web Services with XML-RPC, provides several examples of XML-RPC's no-nonsense approach to solving application-integration and data-sharing challenges. For instance, XML-RPC can transport binary data as base64-encoded messages, much the way email clients send attachments. Because this feature is somewhat rarely used in XML-RPC applications, it's worth taking a look at a Web service that creates simple charts for client programs using this technique.
|
Related Reading
Programming Web Services with XML-RPC |
As of this article's publication date, three Perl implementations of XML-RPC exist, all available at your local CPAN Web site. The oldest and best known of these is Frontier::RPC2, written by Ken MacLeod. Unfortunately, the current stable release, 0.06, has a bug that hampers transmission of base64 objects. The newest module, RPC::XML was written by Randy Ray, and it provides the really useful feature of introspection, which is a method that lets remote clients ask the server for the remote procedures it provides. It also provides type checking to ensure that clients are providing the kinds and numbers of arguments that the implementing Perl procedure expects. The module used in this article is SOAP::Lite. Recently, Paul Kulchenko added XML-RPC support to his existing SOAP package. The result is a very solid and surprisingly flexible XML-RPC library.
One of the advantages of using the XMLRPC::Lite classes that come bundled with SOAP::Lite is that you can create XML-RPC servers that look like CGI scripts. This means that the XMLRPC::Transport::HTTP::CGI class lets your system's Web server worry about mundane HTTP issues (like authentication and logging) and lets you concentrate on implementing your Web service API.
I'm not into graphics, but Perl's Chart::Bars module makes short work of creating simple bar charts. But what about the other programmers in your department who aren't using Perl? Traditionally, they'd have to find a graphics library written in their language just to keep up with you and Perl. With XML-RPC, it's simple to create an interface that lets your local Python programmer in on your Perl fun.
Example 1 below shows how this chart server is implemented. Line 6 brings in the XMLRPC::Transport::HTTP module, which contains the class XMLRPC::Transport::HTTP::CGI. This class describes how this server is going to talk to clients.
In order to get parameters from and pass function values to clients, you will need to add the class XMLRPC::Server::Parameters to the current package's @ISA array.
Those unfamiliar with Perl's inheritance model should have a look at Tom Christiansen's perltoot, which is bundled with Perl. Populating the @ISA array tells Perl that if a method is called, the interpreter should look first in the current package for a subroutine of the same name. Failing that, Perl should look in the packages listed in the @ISA array for the desired method. In this case, the server tries to invoke the method methodName to see how to handle RPC requests. The code shown starting on line 13 is what's in the XMLRPC::Lite man page, and it simply strips off anything before the left-most period and uses what's left as a method call.
Example 1. Perl XML-RPC chart server
1 #!/usr/bin/perl -w --
2 # Serve up charts hot and fresh
3
4 use strict;
5 use Chart::Bars;
6 use XMLRPC::Transport::HTTP;
7
8 @::ISA = qw(XMLRPC::Server::Parameters);
9
10 my $resp = XMLRPC::Transport::HTTP::CGI->dispatch_to('methodName');
11 $resp->handle;
12
13 sub methodName {
14 my $self = shift;
15 my $method = $_[-1]->method;
16
17 $method =~ s/\w+\.//; # ignore method prefixes
18 return $self->$method(@_);
19 }
20
21 # Implement the Chart Server API
22 sub make_chart {
23 my ($xmlrpc_obj, $x_values, $y_values) = @_;
24
25 unless( ref $x_values && ref $y_values ){
26 die "ERROR: Please send two array references";
27 }
28
29 my $chart;
30 unless( $chart = Chart::Bars->new() ){
31 die "ERROR: Can't make Chart::Bars object";
32 }
33
34 my $png = get_png($chart, [$x_values, $y_values] );
35
36 return XMLRPC::Data->type(base64 => $png);
37 }
38
39 #--helper function
40 # Given a populate Chart object,
41 # simply return the png as a scalar
42 # This is a gut of Chart::Base::png
43 sub get_png {
44 my ($chart_obj, $data) = @_;
45
46 # allocate the background color
47 $chart_obj->_set_colors();
48
49 # make sure the object has its copy of the data
50 $chart_obj->_copy_data($data);
51
52 # do a sanity check on the data, and collect some basic facts
53 # about the data
54 $chart_obj->_check_data;
55
56 # pass off the real work to the appropriate subs
57 $chart_obj->_draw();
58
59 return $chart_obj->{'gd_obj'}->png();
60 }
This server only defines one remote procedure, make_chart. It expects to be passed an array reference of values for the x values and another array reference for the y values. After a bit of error checking, the graphing function, get_png is called. The Chart module does not provide ready access to the string containing the chart graphic, which here is in Portable Network Graphics (PNG) format. Fortunately, it was easy enough to cut and paste parts needed from the Chart.pm code. This explains why, on line 59 in Example 1, the gd_obj attribute is directly accessed instead of going through a method call, as all hygienic object-oriented code should do.
The make_chart function simply passes the PNG string that get_png generates to the XMLRPC::Data method type to encode into a base64 object. When XML-RPC attempts to encode the client's response, it sees that it has data marked as base64 and encodes the data appropriately for XML transport. The XML-RPC client is expected to decode the base64 string.
The Python client is straightforward. Recall that the XML-RPC server is a CGI program. Therefore, the URL will depend on where your Web server wants to execute CGI programs from. I've configured my local Apache server to execute CGI scripts from user directories, so the URL for my chart server is http://127.0.0.1/~jjohn/chart_server.pl.
Python's XML-RPC implementation is from Fredrik Lundh and Secret Labs. You can fetch it from the PythonWare Web site. In line 8 of Example 2, a new XML-RPC client object is created, which is instantiated through a method deceptively called Server. It expects to receive the server's URL as input. In order to invoke the remote procedure call, simply call it as if it were a method of the XML-RPC object. The call returns another object, which contains the response from the server. The twist comes when the script needs to write out the data. Simple objects like strings and integers can be used immediately, but base64 data requires the data method to return the original binary data.
Example 2. Python Chart Client
1 #!/usr/bin/python
2 # Have a chart made for us
3
4 import sys, xmlrpclib
5 URL = "http://127.0.0.1/~jjohn/chart_server.pl"
6
7 # Create a new connection
8 conn = xmlrpclib.Server(URL)
9
10 # Call the RPC
11 png_string = conn.make_chart([1,3,4], [11,3,5])
12
13 # Write out the file
14 png = open("chart.png", "w")
15 png.write(png_string.data)
16 png.close();
17
18 print("done\n")
The results of this call are shown in Figure 1. (Yes, the graphic here is a GIF. Not all browsers support PNG.) While this example is less than earth-shattering, it provides a provocative indication of where Web services can go.
Web services are important because they define a standard way for programs of different platforms to not only exchange data, but to use each other's functionality. The software industry is still trying to figure out how to best leverage this truly potent idea.
In No Silver Bullet, Fred Brooks posits that creating software is fundamentally hard and that he was unlikely to see any significant breakthrough in the abatement of that primary complexity. While Web services alone isn't Brooks' silver bullet, the spirit of freedom and cooperation that they engender could go a long way toward creating a world in which all software on all platforms can play together nicely.
Joe Johnston is a software engineer at O'Reilly & Associates. A graduate of the University of Massachusetts in Boston with a B.A. in computer science, he is a teacher, a Web designer, and an author of articles for Perl Journal and for the Web. Joe coauthored O'Reilly's Programming Web Services with XML_RPC. He can be emailed at jjohn@cs.umb.edu.
|
Related Reading Programming Web Services with XML-RPC |
Copyright © 2009 O'Reilly Media, Inc.