Вы находитесь на странице: 1из 11

XML-RPC

XML-RPC is an XML application designed to enable remote procedure calls (RPC) over the
Internet. In Java lingo, a procedure call is just a method invocation. In some other languages like C
it might be called a function call. However, it just means that some named chunk of code
somewhere is invoked with a list of argument values of particular types. The procedure may or may
not return a single value of a known type; i.e. in Java terms it may or may not return void. A remote
procedure call is one in which the called procedure is not necessarily running on the same host as
the calling procedure.
XML-RPC was hardly the first effort to invent a syntax for remote procedure calls. There have been
numerous attempts previously including CORBA and Java’s own Remote Method Invocation (RMI).
However, prior to XML-RPC none of these technologies lived up to their hype. The reason is they
were too complex, too binary, too opaque, and too platform dependent.
XML-RPC is a clear case of the triumph of worse is better. It bit off the 90% of the problem that
gave developers the features they actually needed. It ignored the 10% of the problem that caused
90% of the complexity in previous RPC systems. Features it ignores completely include garbage
collection, object stubs and skeletons, callbacks, activation, object serialization, and more. What it
provides is a simple, easy-to-comprehend means of sending a method name and a list of arguments
from one system to another. And it turns out that this may be the only thing that’s needed in many,
many actual systems.
At a very high level, the fundamental idea of XML-RPC is this: An XML document that contains a
method name and some arguments is sent to a Web server using HTTP POST. The server invokes
the method with the specified arguments. Then it wraps up the return value of the method in another
XML document, and sends that back to the client.
For example, let’s suppose you want to invoke the getQuote() method in the program running at
the URL http://stocks.cafeconleche.org/quotes.cgi. This method takes as an
argument a string containing a stock symbol and returns a double containing the current price of the
stock. That is, in Java parlance its signature looks like this:
public static double getQuote(String symbol);
Encoded as an XML-RPC document, the request looks quite different, but the same information is
still present. Example 2.6 demonstrates.
Example 2.6. An XML-RPC request document
<?xml version="1.0"?>
<methodCall>
<methodName>getQuote</methodName>
<params>
<param>
<value><string>RHAT</string></value>
</param>
</params>
</methodCall>

The root element of an XML-RPC request document is methodCall. This has a methodName
child element whose content is the ASCII name of the method to invoke. The methodCall
element also has a params child element containing the arguments to pass to the method. Each
argument is encoded as a param element that contains a value element. The value element
contains a child element identifying the type of the param, and this child element contains the
actual value passed, as an ASCII string. Here the type of the single argument is string. Other
possibilities include int, boolean, double, dateTime.iso8601, and base64. i4 is allowed
as an alias for int, but is not a different type.
The XML-RPC client (which is normally not a web browser) will post this document to the server
as shown in Example 2.7 using the MIME media type text/xml. It must provide the correct content
length so that the server knows when the client has finished sending.
Example 2.7. POSTing an XML-RPC request document
POST /quotes.cgi HTTP/1.0
Host: stocks.cafeconleche.org
Content-Type: text/xml
Content-length: 167

<?xml version="1.0"?>
<methodCall>
<methodName>getQuote</methodName>
<params>
<param>
<value><string>RHAT</string></value>
</param>
</params>
</methodCall>

The server then responds with an HTTP header and a response document as shown in Example 2.8.
The HTTP header is the standard sort of HTTP header you’d see with any successful web request.
Example 2.8. An XML-RPC response
HTTP/1.0 200 OK
Date: Mon, 16 Jul 2001 20:12:37 GMT
Server: Apache/1.3.12 (Unix) mod_perl/1.24
Last-Modified: Mon, 16 Jul 2001 20:12:37 GMT
Content-Length: 140
Connection: close
Content-Type: text/xml

<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value><double>4.12</double></value>
</param>
</params>
</methodResponse>

This is the basic format of all successful XML-RPC responses. A methodResponse root element
contains a single params child element which contains a single param child element which
contains a single value element. The value element contains a single value of a type indicated
by its child element, double in this example. The only thing that can change is the type and
content of the response data.
So far I’ve been talking as if the program on the server that receives and processes this request is a
Java class with the appropriately named methods, but that isn’t necessarily so. The server program
could be written in Perl, Python, C, C#, C++, AppleScript, Rexx, or any of dozens of other
languages. Whatever language the program is written in, it may or may not have an actual
procedure named “getQuote”. All that’s required is that the server which receives this document has
some way of dispatching the request to an appropriate process. Sometimes the server may parse the
XML and send the raw data to the processor. Other times, it may send the entire XML document. It
might even transform the XML document into another form and send that. What happens on the
server doesn’t really matter as long as it eventually sends back its answer as an XML document in
the proper format. XML very neatly and almost completely decouples the implementation from the
interface. In this respect, at least, XML-RPC is a huge improvement over competing technologies
like RMI, DCOM, and CORBA, all of which make way too many assumptions about how the
remote end of the connection is implemented.

Data Structures
The implicit XML-RPC model of how services are implemented is a lot closer to C than to Java.
This is not to say you can’t use Java to write XML-RPC clients or servers — you most certainly can
— just that an XML-RPC procedure call is more like a C function call than a Java method call and
that XML-RPC data types are more like C’s data types than Java’s object types. Fortuitously, XML-
RPC doesn’t have any concept of pointers (the root of all C’s evils); but it does have structs and
arrays for handling combinations of values and lists of values. These constructs can nest. That is, a
struct member can be another struct or an array; and an array element can be a struct or another
array. This enables you to pass fairly complex data structures to remote procedures.

Arrays
An XML-RPC array is represented as an array element. The array element contains a single
data element which contains zero or more value elements. These are the same value elements
used in param elements. Thus each value element contains a type element such as int or
string which contains the actual data. For example, here’s an array that contains four stock
symbols, each of which is a string:
<array>
<data>
<value><string>RHAT</string></value>
<value><string>SUNW</string></value>
<value><string>ASKJ</string></value>
<value><string>COVD</string></value>
</data>
</array>

Unlike arrays in C or Java, the elements of an XML-RPC array do not have to share the same type.
Here’s an array that contains a string and two doubles:
<array>
<data>
<value><string>RHAT</string></value>
<value><double>4.12</double></value>
<value><double>4.25</double></value>
</data>
</array>

You can use an array element wherever you would use an int or string or other type element.
For example, Example 2.9 is a request for four stock quotes:
Example 2.9. An XML-RPC request that passes an array as an argument
<?xml version="1.0"?>
<methodCall>
<methodName>getQuote</methodName>
<params>
<param>
<value>
<array>
<data>
<value><string>RHAT</string></value>
<value><string>SUNW</string></value>
<value><string>ASKJ</string></value>
<value><string>COVD</string></value>
</data>
</array>
</value>
</param>
</params>
</methodCall>

The response might also contain an array with four prices, as shown in Example 2.10:
Example 2.10. An XML-RPC response document that returns an array
<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value>
<array>
<data>
<value><double>4.12</double></value>
<value><double>13.68</double></value>
<value><double>1.93</double></value>
<value><double>0.78</double></value>
</data>
</array>
</value>
</param>
</params>
</methodResponse>

In some sense this indicates an overloaded method since in one example the getQuote() method
is taking a string and in another it’s taking an array of strings. However, this may not necessarily
map to an overloaded method on the server. Indeed, it may not map to a method named
getQuote() at all. This could all just be an illusion perpetrated by the server to provide an easy
to understand client interface to a very differently organized system.

Structs
A struct is a collection of variables. For those readers who went straight to Java and never had the
misfortune of struggling with C, a struct is a poor man’s class. It has fields but no methods, and all
the fields are public.
An XML-RPC struct is represented by a struct element. Each member of the struct is
represented by a member element. Each member element has a name child and a value child.
For example, this struct lists a stock symbol and a limit price:
<struct>
<member>
<name>symbol</name>
<value><string>RHAT</string></value>
</member>
<member>
<name>limit</name>
<value><double>2.25</double></value>
</member>
</struct>

As with arrays, you can use a struct anywhere you’d use one of the simple type elements such as
int or string. For example, Example 2.11 is an XML-RPC request that represents a limit order.
In a limit order, three values are required: the stock to buy, the price you’re willing to buy at, and
the expiration date of the order.
Example 2.11. An XML-RPC Request that passes a struct as an argument
<?xml version="1.0"?>
<methodCall>
<methodName>bid</methodName>
<params>
<param>
<value>
<struct>
<member>
<name>symbol</name>
<value><string>RHAT</string></value>
</member>
<member>
<name>limit</name>
<value><double>2.25</double></value>
</member>
<member>
<name>expires</name>
<value><dateTime.iso8601>20020709T20:00:00</dateTime.iso8601></value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>

Responses can also contain structs. You’ll see an example of this in the next section when we talk
about faults.

Faults
It’s not at all uncommon for a procedure call to fail. In Java this causes an exception. In XML-RPC
this causes a fault. For example, getQuote() might fail if the client passes in a nonexistent stock
symbol. A fault response is almost exactly like a successful response except that a fault element
replaces the params element. The value of the fault is always a struct containing two members:
faultCode, an int, and faultString, a string. Example 2.12 demonstrates a fault for an
unknown stock symbol.
Example 2.12. An XML-RPC fault
<?xml version="1.0"?>
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>23</int></value>
</member>
<member>
<name>faultString</name>
<value><string>Unknown stock symbol ABCD</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>

When the server faults, the HTTP request still succeeds. The server still uses the 200 OK response
code in the HTTP header. Other HTTP error codes like 404 Not Found or 500 Internal Server Error
are returned only if something goes wrong with the request at the HTTP level, not in the XML-RPC
invocation.

Validating XML-RPC
There’s no official DTD or schema for XML-RPC. Nonetheless, it’s straightforward to write one or
both. Indeed such a DTD may be a more easily understandable description of what is and isn’t
allowed than the prose specification.

A DTD for XML-RPC


Example 2.13 is a simple DTD for XML-RPC. It states that a methodCallExample 2.13 is a simple
DTD for XML-RPC. It states that a methodCall contains one Example 2.13 is a simple
DTD for XML-RPC. It states that a methodCall contains one
methodNameExample 2.13 is a simple DTD for XML-RPC. It states that
a methodCall contains one methodName and one Example 2.13 is a
simple DTD for XML-RPC. It states that a methodCall contains one
methodName and one paramsExample 2.13 is a simple DTD for XML-RPC.
It states that a methodCall contains one methodName and one params
in that order, that a Example 2.13 is a simple DTD for XML-RPC. It
states that a methodCall contains one methodName and one params in
that order, that a methodResponseExample 2.13 is a simple DTD for
XML-RPC. It states that a methodCall contains one methodName and
one params in that order, that a methodResponse contains one
Example 2.13 is a simple DTD for XML-RPC. It states that a
methodCall contains one methodName and one params in that order,
that a methodResponse contains one paramsExample 2.13 is a simple
DTD for XML-RPC. It states that a methodCall contains one
methodName and one params in that order, that a methodResponse
contains one params or one Example 2.13 is a simple DTD for XML-
RPC. It states that a methodCall contains one methodName and one
params in that order, that a methodResponse contains one params or
one faultExample 2.13 is a simple DTD for XML-RPC. It states that
a methodCall contains one methodName and one params in that order,
that a methodResponse contains one params or one fault, that a
Example 2.13 is a simple DTD for XML-RPC. It states that a
methodCall contains one methodName and one params in that order,
that a methodResponse contains one params or one fault, that a
valueExample 2.13 is a simple DTD for XML-RPC. It states that a
methodCall contains one methodName and one params in that order,
that a methodResponse contains one params or one fault, that a
value element can contain an Example 2.13 is a simple DTD for XML-
RPC. It states that a methodCall contains one methodName and one
params in that order, that a methodResponse contains one params or
one fault, that a value element can contain an i4Example 2.13 is a
simple DTD for XML-RPC. It states that a methodCall contains one
methodName and one params in that order, that a methodResponse
contains one params or one fault, that a value element can contain
an i4, Example 2.13 is a simple DTD for XML-RPC. It states that a
methodCall contains one methodName and one params in that order,
that a methodResponse contains one params or one fault, that a
value element can contain an i4, intExample 2.13 is a simple DTD
for XML-RPC. It states that a methodCall contains one methodName
and one params in that order, that a methodResponse contains one
params or one fault, that a value element can contain an i4, int,
Example 2.13 is a simple DTD for XML-RPC. It states that a
methodCall contains one methodName and one params in that order,
that a methodResponse contains one params or one fault, that a
value element can contain an i4, int, stringExample 2.13 is a
simple DTD for XML-RPC. It states that a methodCall contains one
methodName and one params in that order, that a methodResponse
contains one params or one fault, that a value element can contain
an i4, int, string, Example 2.13 is a simple DTD for XML-RPC. It
states that a methodCall contains one methodName and one params in
that order, that a methodResponse contains one params or one
fault, that a value element can contain an i4, int, string,
datetime.iso8601Example 2.13 is a simple DTD for XML-RPC. It
states that a methodCall contains one methodName and one params in
that order, that a methodResponse contains one params or one
fault, that a value element can contain an i4, int, string,
datetime.iso8601, Example 2.13 is a simple DTD for XML-RPC. It
states that a methodCall contains one methodName and one params in
that order, that a methodResponse contains one params or one
fault, that a value element can contain an i4, int, string,
datetime.iso8601, doubleExample 2.13 is a simple DTD for XML-RPC.
It states that a methodCall contains one methodName and one params
in that order, that a methodResponse contains one params or one
fault, that a value element can contain an i4, int, string,
datetime.iso8601, double, Example 2.13 is a simple DTD for XML-
RPC. It states that a methodCall contains one methodName and one
params in that order, that a methodResponse contains one params or
one fault, that a value element can contain an i4, int, string,
datetime.iso8601, double, base64Example 2.13 is a simple DTD for
XML-RPC. It states that a methodCall contains one methodName and
one params in that order, that a methodResponse contains one
params or one fault, that a value element can contain an i4, int,
string, datetime.iso8601, double, base64, Example 2.13 is a simple
DTD for XML-RPC. It states that a methodCall contains one
methodName and one params in that order, that a methodResponse
contains one params or one fault, that a value element can contain
an i4, int, string, datetime.iso8601, double, base64,
structExample 2.13 is a simple DTD for XML-RPC. It states that a
methodCall contains one methodName and one params in that order,
that a methodResponse contains one params or one fault, that a
value element can contain an i4, int, string, datetime.iso8601,
double, base64, struct, or Example 2.13 is a simple DTD for XML-
RPC. It states that a methodCall contains one methodName and one
params in that order, that a methodResponse contains one params or
one fault, that a value element can contain an i4, int, string,
datetime.iso8601, double, base64, struct, or arrayExample 2.13 is
a simple DTD for XML-RPC. It states that a methodCall contains one
methodName and one params in that order, that a methodResponse
contains one params or one fault, that a value element can contain
an i4, int, string, datetime.iso8601, double, base64, struct, or
array, and so forth.
Example 2.13. A DTD for XML-RPC
<!ELEMENT methodCall (methodName, params)>
<!ELEMENT methodName (#PCDATA)>
<!ELEMENT params (param*)>
<!ELEMENT param (value)>
<!ELEMENT value
(i4|int|string|dateTime.iso8601|double|base64|struct|array)>

<!ELEMENT i4 (#PCDATA)>
<!ELEMENT int (#PCDATA)>
<!ELEMENT string (#PCDATA)>
<!ELEMENT dateTime.iso8601 (#PCDATA)>
<!ELEMENT double (#PCDATA)>
<!ELEMENT base64 (#PCDATA)>

<!ELEMENT array (data)>


<!ELEMENT data (value*)>
<!ELEMENT struct (member+)>
<!ELEMENT member (name, value)>
<!ELEMENT name (#PCDATA)>

<!ELEMENT methodResponse (params | fault)>


<!ELEMENT fault (value)>

There are also many things this DTD does not say. For example, it does not say that the value
inside a fault must be a struct or that each i4 element contains an integer between
-2,147,483,648 and 2,147,483,647. DTDs cannot make statements such as these. Schemas,
however, can.
This DTD is informative, not normative. There is no official DTD for XML-RPC. I just made this
one up after reading the XML-RPC specification. You can include a document type declaration in
your XML-RPC documents, but it’s much more common to leave it out as is the case for all the
examples in this chapter. Furthermore many parsers used in practice for reading XML-RPC
documents are not validating and will not consider the contents of any DTD.

A Schema for XML-RPC


Example 2.14 is a medium complex schema for XML-RPC. It says everything the DTD says and
then some. All elements are strictly typed. In the case of string and boolean,
the XML-RPC types don’t quite match the schema types so new, more
restricted types were derived from the standard base types. In
addition the complex types are context dependent. For instance,
structs normally contain one or more members, but the struct
inside a faultExample 2.14 is a medium complex schema for XML-RPC.
It says everything the DTD says and then some. All elements are
strictly Example 2.14 is a medium complex schema for XML-RPC. It
says everything the DTD says and then some. All elements are
strictly always contains exactly two members, one of which has
the name Example 2.14 is a medium complex schema for XML-RPC. It
says everything the DTD says and then some. All elements are
strictly always contains exactly two members, one of which has
the name faultCodeExample 2.14 is a medium complex schema for XML-
RPC. It says everything the DTD says and then some. All elements
are strictly always contains exactly two members, one of which
has the name faultCode and the other of which has the name
Example 2.14 is a medium complex schema for XML-RPC. It says
everything the DTD says and then some. All elements are strictly
always contains exactly two members, one of which has the name
faultCode and the other of which has the name
faultStringExample 2.14 is a medium complex schema for XML-RPC. It
says everything the DTD says and then some. All elements are
strictly always contains exactly two members, one of which has
the name faultCode and the other of which has the name
faultString.
Example 2.14. A Schema for XML-RPC
<?xml version="1.0"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<!-- The only two possible root elements are methodResponse and
methodCall so these are the only two I use a top-level
declaration for. -->

<xsd:element name="methodCall">
<xsd:complexType>
<xsd:all>
<xsd:element name="methodName">
<xsd:simpleType>
<xsd:restriction base="ASCIIString">
<xsd:pattern value="([A-Za-z0-9]|/|\.|:|_)*" />
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="params" minOccurs="0" maxOccurs="1">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="param" type="ParamType"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:all>
</xsd:complexType>
</xsd:element>

<xsd:element name="methodResponse">
<xsd:complexType>
<xsd:choice>
<xsd:element name="params">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="param" type="ParamType"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="fault">
<!-- What can appear inside a fault is very restricted -->
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="struct">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="member"
type="MemberType">
</xsd:element>
<xsd:element name="member"
type="MemberType">
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>

<xsd:complexType name="ParamType">
<xsd:sequence>
<xsd:element name="value" type="ValueType"/>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="ValueType" mixed="true">


<!-- I need to figure out how to say that this
is either a simple xsd:string type or that
it contains one of these elements; but that otherwise
it does not have mixed content -->
<xsd:choice>
<xsd:element name="i4" type="xsd:int"/>
<xsd:element name="int" type="xsd:int"/>
<xsd:element name="string" type="ASCIIString"/>
<xsd:element name="double" type="xsd:decimal"/>
<xsd:element name="Base64" type="xsd:base64Binary"/>
<xsd:element name="boolean" type="NumericBoolean"/>
<xsd:element name="dateTime.iso8601" type="xsd:dateTime"/>
<xsd:element name="array" type="ArrayType"/>
<xsd:element name="struct" type="StructType"/>
</xsd:choice>
</xsd:complexType>

<xsd:complexType name="StructType">
<xsd:sequence>
<xsd:element name="member" type="MemberType"
maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="MemberType">
<xsd:sequence>
<xsd:element name="name" type="xsd:string" />
<xsd:element name="value" type="ValueType"/>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="ArrayType">
<xsd:sequence>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="ValueType"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>

<xsd:simpleType name="ASCIIString">
<xsd:restriction base="xsd:string">
<xsd:pattern value="([ -~]|\n|\r|\t)*" />
</xsd:restriction>
</xsd:simpleType>

<xsd:simpleType name="NumericBoolean">
<xsd:restriction base="xsd:boolean">
<xsd:pattern value="0|1" />
</xsd:restriction>
</xsd:simpleType>

</xsd:schema>

This schema is informative, not normative. There is no official schema for XML-RPC. I just wrote
this one up after reading the XML-RPC specification. You should not include
xsi:noNamespaceSchemaLocation attributes in your XML-RPC documents, but you might
be able to use some other extra-document means of attaching the schema. For instance, the Xerces-J
parser lets you set the http://apache.org/xml/properties/schema/external-
noNamespaceSchemaLocation property to the location of the schema. However, very few
XML-RPC services would be likely to support this. If you use a schema with XML-RPC, it should
be used purely for validation, not for attaching default attributes to elements or anything else that
might affect the document’s content.
This example does effectively demonstrate the relative power of DTDs and schemas. The schema
identifies all possible, legal XML-RPC documents. Almost anything that satisfies this schema is a
legal XML-RPC document, and anything that does not satisfy the schema is not a legal XML-RPC
document. [1] The DTD only gets half that far. All documents that do not satisfy the DTD are not
legal XML-RPC documents. However, not all documents that do satisfy the DTD are legal XML-
RPC documents. Things the schema says that the DTD does not include:
 The only two legal root elements are methodCall and methodResponse
 A params element that’s a child of a methodCall can have any number of
param child elements, but a params element that’s a child of a methodResponse element
must have exactly one param child element (i.e. context dependent content models)
 i4 and int elements must contain an integer between -2,147,483,648 and
2,147,483,647.
 String values and method names can only use ASCII characters.
 The value of a fault must be a struct with exactly two members.
There are other things the schema says that the DTD doesn’t, but this gives you the idea. Schemas
are both more descriptive and proscriptive than DTDs.

[1] The three XML-RPC requirements that can't be specified in the W3C XML Schema Language
are:
 One of the two members in a fault struct must have the name faultCode
and an int value and the other must have the name faultString and a string value.
 A value element can contain either an ASCII string or a type element such
as int, but not a type element and an ASCII

Вам также может понравиться