About
XchemeRPC is a library for PLT Scheme (Mz/DrScheme) 200 and above that implements both an XML-RPC client and server. It is copyright Matt Jadud 2002, and released under the LGPL.
Thanks to Matthew Flatt for his assistance in the porting of IU's matcher, and Paul Graunke for assistance with the PLT webserver framework.
If you are using version 103, I recommend you check out Pete Hopkins's CapnRPC.
Installation
- Download xmlrpc.plt. (You must have DrScheme
v200 or greater installed first. If you are using the 200a12 binaries, you must also download and install this file.)
- Double-click (on Windows/Mac) the PLT file. This will install the libraries. Unix/Linux users should know what they're doing.
- Discard the PLT file, if you wish.
This PLT file installs three files:
- collects/web-server/xmlrpc.ss
This is the XML-RPC core library for both clients and servers.
- collects/eopl/iu-match.ss and collects/eopl/iu-exptime.ss
This is a port of the IU matcher from Chez to PLT Scheme v200. It is current as of March 28th, and does not contain the latest code supporting multiple instances of pattern variables. There currently is very little documentation on the use of these libraries; this may change. Note this code carries a different license than the XML-RPC library itself.
Usage: Client
Begin by including the XML-RPC library.
(require (lib "xmlrpc.ss" "web-server"))
|
The client-side interface is identical to that of CapnRPC ; this was done to ensure maximum portability of previously written code. That is, all clients must begin with a call to xml-rpc-server:
;;CONTRACT
;; xml-rpc-server : string number string -> procedure
;;PURPOSE
;; Takes a string representing the hostname or IP address of
;; a server, a number representing the port, and a string
;; representing the path on the remote machine. Returns a
;; procedure of one argument for mapping local procedures
;; to remote procedures.
UserLand provides a number of XML-RPC services to the public for testing and development. If I were to write a client to connect to one of their machines, I would start by defining an xml-rpc-server that specifies the name, port, and path to the remote host.
(define userland
(xml-rpc-server "betty.userland.com" 80 "/RPC2"))
|
Once the server is defined, we can map any number of local procedures to remote calls. Here, I'm mapping the local procedure get-state-name to the procedure "examples.getStateName" on the server "userland".
(define get-state-name (userland 'examples.getStateName))
|
And, once defined, the calls can be used as if they were a local procedure call:
(if (equal? "Mississippi" (get-state-name 42))
"Correct" "Incorrect")
|
Of course, a more Schemely test might look like:
(letrec ((mlon (lambda (n)
(if (zero? n) '()
(cons n (mlon (sub1 n)))))))
(map get-state-name (mlon 50)))
|
If you have installed the XchemeRPC library correctly (and are connected to the internet), you should be able to copy-and-paste each of the shaded regions in sequence into the DrScheme interactions window and get a result.
Usage: Server
XML-RPC servers are implemented as servlets under the existing PLT Scheme webserver. This allows XML-RPC applications to benefit from the speed and robustness of the PLT webserver, and allow the programmer to develop in a manner consistent with their normal coding practice.
Because XML-RPC code is implemented as a servlet, all the standard practices for PLT webserver servlets apply. For example, XML-RPC code is only loaded once, when the server is launched; changes must be refreshsed on the webserver for them to take effect. More information is available in the documentation for the PLT web-server.
The XML-RPC library introduces one syntactic form for aiding in the development of servers. handle-xml-rpc is a binding form, similar to let, with the form
(handle-xml-rpc initial-request
([identifier procedure] ...) bodies ...)
|
where
-
intial-request is the XML-RPC POST from the client; this variable is provided by the servlet^ unit/sig import, and is required by the handle-xml-rpc form. The first line of every server should (generally) always look like the first line of the form above.
-
identifiers are the names that will be made accessible to the outside world for invocation on the server.
procedures are bound to the identifiers. These behave very much like the RH sides of a let; they must evaluate to Scheme procedures.
-
bodies ... are any forms that are allowed in the body of a let. Any code that will be executed on the server must be defined within the scope of the handle-xml-rpc form.
Example: Server and Client
Memo to self: get a better (read: Schemely) example
Another note: This is a really dumb example...
For no good reason at all, I've decided that I need to farm out some of my mathematics to a remote server (really Big Iron, capable of doing exponentiation in Scheme really, really fast). The number crunching I have to do is so intensive that it's worth the time and effort to ship XML documents around the net to get the speedup on the remote machine. Yeah, right.
My server needs to define the procedures that can be called, and provide the names they are accessed by. Note that any code you want the server to be allowed to execute must be defined within the handle-xml-rpc form. As depicted in the example, procedures can be named or anonymous.
(require (lib "unitsig.ss")
(lib "xmlrpc.ss" "web-server"))
(unit/sig ()
(import servlet^)
(handle-xml-rpc initial-request
([math.square (lambda (x) (* x x))]
[math.cube (lambda (x) (pow x 3))]
[math.power pow])
(define pow
(lambda (x y)
(if (zero? y)
1
(* x (pow x (sub1 y))))))
)
)
|
To turn this piece of code into an XML-RPC server, it must be saved somewhere under the "servlets" directory in the "default-web-root" of the PLT Scheme webserver collection. If your webserver is already running, you will need to "refresh-servlets" to force the webserver to load your code. For the moment, assume I have created an "rpc" subdirectory of "servlets," and saved this code as "math.ss".
A Scheme client that connects to this server would look like
(require (lib "xmlrpc.ss" "web-server"))
(define remote (xml-rpc-server "some.host.org"
8080
"/servlets/rpc/math.ss"))
(define square (remote 'math.square))
(define cube (remote 'math.cube))
(define pow (remote 'math.power))
(display "Square: ")
(display (square 9)) (newline)
(display "Cube: ")
(display (cube 1000)) (newline)
(display "Power: ")
(display (pow 2 32)) (newline)
|
Naturally, the name of the host and port number would be different. Depending on where you store your RPC servlets, you might need to change the path as well.
A more complete server example can be found in the XML-RPC validator source for XchemeRPC.
Marshalling
To go from XML-RPC to Scheme, native datatypes are forced into the XML-RPC typing, and marshaled back into Scheme upon return. The following mapping has been employed:
| Scheme | XML-RPC |
| lists | arrays |
| vectors | arrays |
| hash tables | structs |
| Integers from -231 to 231 | integers |
| all other numbers | doubles |
| strings | strings |
| (make-date ...) | dateTime.iso8601 |
| (make-b64 ...) | base64 |
Interop
XchemeRPC is known to interop with:
- Frontier 7.0/Radio 8.0 [Usertalk]
- Advogato's mod_virguile interface [Apache C module]
- XMLRPC::Lite (in the SOAP::Lite collection) [Perl]
Caveats
As I discover things that aren't fixed (or that I want to implement/improve) I'll put them here. All the standard disclaimers (caveat emptor) regarding code you find on the 'net apply to this library.
- Exceptions seem to be handled, but the error messages are currently not very useful. I throw back the text from the exception (in general), but a good deal more could be done to provide better information to the client-side developer in the case of a problem. (20020328)
- I have to do quite a bit of cleanup on the X-expressions from the
xml collection. I currently traverse the XML document tree at least twice for each client- or server-side call. Handling the "messy" X-expressions in the match would at least bring me down to one traversal of the tree total. (20020328) Porting to Oleg's SSAX parser may take care of some of these issues (20020329) A port using Oleg's SSAX parser has been made, and once (if?) it becomes standard in PLT Scheme, I will move the entire package to SSAX (20020405).
- Arity checking could be nice. (20020328)
- Adding the ability to auto-define procedures by way of introspection would be nice (client-side). (20020328)
- The
xexpr->scheme and scheme->xexpr code could use some more guards and sanity checks, in general. (20020329)
- Oops! I forgot vectors! (20020329)
Should be fixed. (20020405)
Other caveats, as they are remembered, will be added. As they are fixed, they will be removed. Suggestions welcome.
|