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.
Install S-XML-RPC
Allegro: Install S-XML-RPC by downloading s-xml-rpc.zip and extracting the files into your CS325 code directory. Allegro has a very similar NET.XML-RPC package, but it uses a very different approach to defining XML-RPC services.
LispWorks: You need do nothing for this step.
Load S-XML-RPC
You will need to load S-XML-RPC whenever you restart Lisp. To do this simply, download load-s-xml-rpc.lisp to your CS325 directory and load it. It will do the appropriate thing for Allegro or LispWorks.
If you want to do this automatically,
put a command to load the file in your cs325.lisp
file.
Test calling XML-RPC functions
Start Lisp, load your 325 startup file, and load S-XML-RPC.
Try both 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
.These default
to localhost
, 80
, and /RPC2
,
respectively.
Starting an XML-RPC server
When you know you can call XML-RPC functions, it's time to set up your own server. If you have two machines with Lisp that can see each other on the network, try putting the server example on a different machine. You will need to know the network name or IP address of the server machine to call it from the client machine.
On the server machine, download
xml-rpc-server-example.lisp
to your CS325 directory. Open the file in your Lisp editor.
It shows how to
define functions that can be called as web services remotely via XML-RPC.
For example, to define lisp.GCD
to be an XML-RPC callable
greatest common divisor function:
(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)
.
Load xml-rpc-server-example.lisp
. Start the server with
(setq *xml-server* (s-xml-rpc:start-xml-rpc-server))
If you're running a web server on your machine on the default port 80, specify a different port for your XML-RPC server, e.g.,
(setq *xml-server* (s-xml-rpc:start-xml-rpc-server :port 8080))
You'll need to include :port 8080
when
calling your server. When you're done testing, be sure to stop the server with:
(s-xml-rpc:stop-server *xml-server*)
It's a security risk to leave a public service running on your computer.
Call your server from Lisp
On your client machine (which might be your server machine),
download
xml-rpc-client-example.lisp
and open it in your editor. This has code to call the functions that your server
is providing. For example, the following
function calls lisp.GCD
:
(defun call-for-gcd (m n) (xml-rpc-call (encode-xml-rpc-call "lisp.GCD" m n)))
Load this code into Lisp, and
use WITH-XML-SERVER
to specify the server:
(with-xml-server (:host "myserver.org") (call-for-gcd 36 63))
If your server doesn't have a name, find out the IP address of the host and use that, e.g.,
(with-xml-server (:host "70.84.113.44") (call-for-gcd 36 63))
What is XML-RPC good for?
An appropriate XML-RPC service is one that:
- involves modest amounts of data traffic
- requires modest computational requirements on the server
- would not be easily doable on the client without additional software
- would not be called in a tight time-sensitive loop
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.
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:
- Start a command shell.
- Change to the directory where you put
xml-rpc-test.jar
. - Type
java -jar xml-rpc-test.jar server-url method-name arguments...
.
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*)