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 simply a method invocation. In some other languages like C, it might be called a function call. However, it simply 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; that is, in Java terms it may or may not return void. A remote procedure call is a procedure call 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 previous attempts, including Common Object Request Broker Architecture (CORBA) and Java's own Remote Method Invocation (RMI). However, prior to XML-RPC, none of these technologies lived up to their hype. 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 [http://www.ai.mit.edu/docs/articles/good-news/subsection3.2.1.html] It bites off the 90 percent of the problem that gives developers the features they actually need. And it ignores 10 percent of the problem that caused 90 percent 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. 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 present, as 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 things that can change are the type and content of the response data.

So far I've been talking as though the program on the server that receives and processes this request were a Java class with the appropriately named methods , but that isn't necessarily so. The server program could be written in Perl, C, C++, AppleScript, 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". The only requirement 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 such as 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 serversyou most certainly canjust 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 data types than Java 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. The following array contains a string and two doubles:

 <array>    <data>     <value><string>RHAT</string></value>     <value><double>4.12</string></double>     <value><double>4.25</string></double>   </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, because 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 system organized very differently.

Structs

A struct is a collection of variables . For those of you 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 of 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 at which you're willing to buy, 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 also can 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 identical to 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, such as 404 Not Found or 500 Internal Server Error, are returned only if something goes wrong with the request at the HTTP level, not if it goes wrong 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 understood 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 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 , date Time.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    (i4intstringdateTime.iso8601doublebase64structarray)> <!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 that 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 can, however.

This DTD is informative, not normative. There is no official DTD for XML-RPC. I 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 outas is the case for all of 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 schema of medium complexity 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; therefore new, more restricted types were derived from the standard base types. In addition the complex types are context dependent. For example, structs normally contain one or more members, but the struct inside a fault 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">     <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="01" />     </xsd:restriction>   </xsd:simpleType> </xsd:schema> 

This schema is informative, not normative. There is no official schema for XML-RPC. I 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 example, 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, use it 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 gets only 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 say include the following:

[1] XML-RPC requirements that can't be specified in the W3C XML Schema Language are first, that 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, and second, that a value element can contain either an ASCII string or a type element such as int , but not a type element and an ASCII string.

  • The only two legal root elements are methodCall and methodResponse .

  • A params element that is a child of a methodCall can have any number of param child elements, but a params element that is a child of a methodResponse element must have exactly one param child element (that is, 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 say, but this gives you the idea. Schemas are both more descriptive and more proscriptive than DTDs.



Processing XML with Java. A Guide to SAX, DOM, JDOM, JAXP, and TrAX
Processing XML with Javaв„ў: A Guide to SAX, DOM, JDOM, JAXP, and TrAX
ISBN: 0201771861
EAN: 2147483647
Year: 2001
Pages: 191

Similar book on Amazon

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net