XML-RPC is a very simple standard XML-based protocol for calling web services. XML-RPC is a much smaller protocol than the other leading XML-based web service protocol, SOAP. The complete specifications for XML-RPC in HTML is less than 20K bytes. The specifications for SOAP 1.2 in HTML is 8 times as large. As a result, there are many implementations of XML-RPC in many different languages, including Perl, Python, C++, Java, Lisp, etc. A number of examples of using XML-RPC in various languages is given at the XML-RPC How To site.

XML-RPC is very stable. XML-RPC's inventor, Dave Winer, has resisted changes, even quite modest and reasonable ones, because they would break existing XML-RPC servers and clients.

A personal history by Winer of how XML-RPC and SOAP relate is here.

In Lisp, an easy way to get started with XML-RPC is to use the The S-XML-RPC library. This document describes how to set up the library, and call and define XML-RPC services.

Download S-XML and S-XML-RPC

S-XML is an XML parser, like the CLOCC XML parser. S-XML-RPC is an XML-RPC library that uses the S-XML parser. The S-XML parser isn't as complete as the CLOCC parser, but it's sufficient for our needs.

Instructions on getting both S-XML and S-XML-RPC are at their respective web sites. Unfortunately, the standard distribution omits the code needed for Allegro Common Lisp, so Allegro users should use my s-xml-rpc.zip.

Compile and load ASDF, S-XML and S-XML-RPC

To compile and load these libraries, you need ASDF. ASDF (A System Definition Facility) is a Lisp equivalent of make in Unix or Ant. ASDF lets you define how code files depend on other code files so that, with one operation, you can load, and compile if necessary, a library and all the files it depends on. ASDF uses the same syntax and concepts as an earlier package called DEFSYSTEM.

ASDF comes with several Lisps, including SBCL, OpenMCL, and Allegro. In those cases, you should be able to just say (require :asdf) to load ASDF.

For other Lisps, including Lispworks, you can use the version of ASDF included in s-xml-rpc.zip. Load asdf.lisp before you do the remaining steps.

To use ASDF to load a library like S-XML or S-XML-RPC, you:

For CS 325 students, I've put all these steps into the file load-s-xml.lisp. The code in this file runs in the cs325-user package. Put this file in the same directory that contains the S-XML and S-XML-RPC directories. Load load-s-xml.lisp. to compile and load both S-XML and S-XML-RPC, and add S-XML-RPC to the packages used by cs325-user.

Calling XML-RPC Methods

The S-XML-RPC web site gives several examples of web services available to the public for testing the XML-RPC installations. In addition, I found another site that has XML-RPC methods to do some trivial math. Try all three of these XML-RPC method calls:

(xml-rpc-call
   (encode-xml-rpc-call "examples.getStateName" 41)
   :host "betty.userland.com")

(xml-rpc-call
  (encode-xml-rpc-call "math.SumAndDifference" 5 2)
  :host "www.cookcomputing.com"
  :url "/xmlrpcsamples/math.rem")

The function encode-xml-rpc-call constructs a string with the XML to send to the server, e.g.,

 > (encode-xml-rpc-call "examples.getStateName" 41)
"<methodCall><methodName>examples.getStateName</methodName>
<params><param><value><int>41</int></value></param>
</params></methodCall>"

xml-rpc-call then sends this XML to the server specifed. The server consists of a host, a port, and a path, specified by the keywords host, :port and :url, which default to localhost, 80, and /RPC2, respectively.

Defining XML-RPC Methods

An appropriate XML-RPC service is one that:

For example, a web service that retrieve book information given an ISBN number would make sense, but one that retrieved the contents of the book, would be inappropriate as well as illegal. Also inappropriate would be a web service to calculate square roots, because this is so easily done on the client machine.

For demonstration purposes, I'll define a method, lisp.GCD, that takes two numbers and returns their greatest common divisor. This is trivial in Lisp and is just to show how the services are defined.

To define a web service method in S-XML-RPC, you just need to define a function in the s-xml-rpc-exports package. Because XML-RPC is case-sensitive, but Lisp isn't, we need to protect the name of our method by using Lisp's vertical bar notation. So the full name of our method, including the package, will be s-xml-rpc-exports::|lisp.GCD|. In some languages, this name might imply that GCD is a method of some class or package called lisp, but here it's just a name, designed to look like a normal XML-RPC method name.

Since Lisp already defines a function to calculate greatest common divisor, we're done:

(defun s-xml-rpc-exports::|lisp.GCD| (m n)
  (gcd m n))

S-XML-RPC will take care of converting the input parameters from the strings that were sent over the network into numbers, and converting the returned value back into a string to send back to the client.

The parameters for XML-RPC methods should be kept simple, i.e., numbers, strings, and simple lists and structures, because of that's what the XML-RPC protocol allows. Similarly, the function should return either a number, string, simple list, or structure.

"Structure" here means an XML-RPC structure, which is a list of name-value pairs. In S-XML-RPC client code in Lisp, you create an XML-RPC structure with (s-xml-rpc:xml-rpc-struct key value key value ...). In XML-RPC server code in Lisp, you get at parts of an XML-RPC structure with (s-xml-rpc:get-xml-rpc-struct-member struct key). The XML-RPC method math.SumAndDifference shown in the examples above returns an XML-RPC structure to hold the sum and difference.

Running an S-XML-RPC Server

To start the S-XML-RPC server:

(setq *xml-server* (start-xml-rpc-server :port 8080))

We store the name of the server in a variable so that we can easily stop it later.

If you've defined the lisp.GCD method above, then you can now call it from Lisp, like this:

> (xml-rpc-call
    (encode-xml-rpc-call "lisp.GCD" 12155 130130)
    :port 8080)
715

Testing your server from other languages

Of course, using Lisp to call Lisp doesn't prove much. More interesting is to call your XML-RPC method from a totally different client, in another language, perhaps on a different machine. Two examples are given below.

Testing your server with Python

If you have Python installed, then it's easy to test XML-RPC because Python comes with an XML-RPC library. Just start your Python interpreter, import the library xmlrpclib, and call the function xmlrpclib.ServerProxy to create an object that can be then used to call your XML-RPC methods. How it works may be a bit confusing, but the calling pattern is quite simple. For example, to call our GCD function above:

# get the library
import xmlrpclib  

# get the server proxy and use it to call lisp.GCD()
xmlrpclib.ServerProxy("http://localhost:8080/RPC2").lisp.GCD(12155, 130130)

More about Python and XML-RPC can be found here.


Testing your server with Java

The easiest way test to XML-RPC with Java is to use xml-rpc-test.jar, a Java Archive I created by merging version 1.3 of the Apache Commons Codec library, and version 2 of the Apache XML-RPC library. (Note: version 3 of this library does not provide a utility class for calling XML-RPC directly.)

To install, download xml-rpc-test.jar to some directory, e.g., c:\java\xml-rpc\.

To test an XML-RPC method:

For example, to test the state name service,

C:\java\xml-rpc>java -jar xml-rpc-test.jar http://betty.userland.com/RPC2 examples.getStateName 32
New York

To test the GCD service above:

C:\java\xml-rpc>java -jar xml-rpc-test.jar http://localhost:8080/RPC2 lisp.GCD 12155 130130
715

When you're done, for safety, be sure to stop the XML-RPC server in Lisp, with

(stop-server *xml-server*)

checked with XENU button Valid HTML 4.01 Strict

Comments? Comments icon Send mail to Chris Riesbeck.