Over the years, there have been numerous attempts to create a standard protocol for remote procedure calls (RPC), a way for one computer program to call a procedure in another program over a network such as the Internet.
Often these protocols are completely language agnostic, enabling a client program written in a language such as C++ to call a remote database server written in Java or something else without either side knowing or caring about the implementation language of its partner.
RPC efforts are being driven at breakneck speed by web services, networking programs that use the Web to offer data in a form easily digested by other software. Web services are being employed to share password authentication information between sites, facilitate e-commerce transactions between stores, provide business-to-business information exchange, and other innovative offerings.
One of the most popular technologies in this area is XML-RPC, a protocol for using Hypertext Transfer Protocol (HTTP) and Extensible Markup Language (XML) for remote procedure calls. Today, you’ll learn how to implement it in Java as the following topics are covered:
How XML-RPC was developed
How to communicate with another computer using XML-RPC
How to structure an XML-RPC request and an XML-RPC response
How to use XML-RPC in Java programs
How to send an XML-RPC request
How to receive an XML-RPC response
Java supports one well-established technique for remote procedure calling: remote method invocation (RMI).
RMI shares a trait in common with RPC efforts such as the Common Object Request Broker Architecture (CORBA) and the Open Network Computing RPC from Sun: complexity. All three are designed to be robust solutions to a large variety of remote computing tasks.
This sophistication has been one of the hindrances to the adoption of existing RPC efforts. The complexity required to implement some of these solutions can be more than a programmer wants to take on simply to exchange information over a network.
A simpler alternative, XML-RPC, has become widely adopted for web services.
Client/server implementations of XML-RPC are available for most platforms and programming languages in widespread use. UserLand Software offers a directory of implementations at http://www.xmlrpc.com.
XML-RPC exchanges information using a combination of HTTP, the protocol of the World Wide Web, and XML, a format for organizing data independent of the software used to read and write it.
The following data types are supported by XML-RPC:
array
—. A data structure that holds multiple elements of any of the other data types, including arrays
base64
—. Binary data in Base 64 format
boolean
—. True-false values that are either 1
(true
) or 0
(false
)
dateTime.iso8601
—. A string containing the date and time in ISO8601 format (such as 20070915T19:20:15 for 7:20 p.m. (and 15 seconds) on Sept. 15, 2007)
double
—. Eight-byte signed floating-point numbers
int
(also called i4)
—. Signed integers ranging in value from –2,147,483,648 to 2,147,483,647, the same size as int
values in Java
string
—. Text
struct
—. Name-value pairs of associated data where the name is a string and the value can be any of the other data types (comparable to the HashMap
class in Java)
XML-RPC also supports the array
data type, which is used to hold arrays of any other kind of data, including arrays.
One thing noticeably absent from XML-RPC is a way to represent data as an object. The protocol wasn’t designed with object-oriented programming in mind, but you can represent reasonably complex objects with the array
and struct
types.
By design, XML-RPC is a simple remote procedure call protocol that is well suited to programming across a network. The protocol has become one of the key elements of web services implemented by many developers of software on Windows, Macintosh, Linux, and UNIX systems.
The full XML-RPC specification is available on XML-RPC.Com at http://www.xmlrpc.com/spec.
More than 75 implementations of XML-RPC are available today for a variety of languages and platforms.
After the release of XML-RPC, the specification was extended to create another RPC protocol called SOAP, the Simple Object Access Protocol.
SOAP shares some of the design goals of XML-RPC but has been expanded to better support objects, user-defined data types, and other advanced features, resulting in a significantly more complex protocol. SOAP has also become widely popular for web services and other decentralized network programming.
Because SOAP is an extension of XML-RPC, it raises the question of why the latter protocol is still in use.
When SOAP came out and was considerably more complex than XML-RPC, there was enough difference between the related protocols that an argument could be made for using either one, depending on the needs of a particular project.
To find out more about SOAP and public servers that can be used with SOAP clients, visit the website http://www.xmethods.com.
XML-RPC is a protocol transmitted via HTTP, the standard for data exchange between web servers and web browsers. The information that it transmits is not web content. Instead, it is XML data encoded in a specific way.
Two kinds of data exchanges are conducted using XML-RPC: client requests and server responses.
An XML-RPC request is XML data sent to a web server as part of an HTTP post
request.
A post
request normally is used to transmit data from a web browser to a web server—Java servlets, common gateway interface programs, and other software collect the data from a post
request and send Hypertext Markup Language (HTML) back in response. When you submit an email from a web page or vote in an online poll, you’re either using post
or a similar HTTP request called get
.
XML-RPC, on the other hand, is simply using HTTP as a convenient protocol for communicating with a server and receiving a response back.
The request consists of two parts: the HTTP headers required by the post
transmission and the XML-RPC request, which is expressed as XML.
Listing 20.1 contains an example of an XML-RPC request.
Example 20.1. An XML-RPC Request
1: POST /XMLRPC HTTP/1.0 2: Host: www.advogato.org 3: Connection: Close 4: Content-Type: text/xml 5: Content-Length: 151 6: User-Agent: OSE/XML-RPC 7: 8: <?xml version="1.0"?> 9: <methodCall> 10: <methodName>test.square</methodName> 11: <params> 12: <param> 13: <value> 14: <int>13</int> 15: </value> 16: </param> 17: </params> 18: </methodCall>
In Listing 20.1, lines 1–6 are the HTTP headers, and lines 8–18 are the XML-RPC request. This listing tells you the following:
The XML-RPC server is at http://www.advogato.org/XMLRPC (lines 1–2).
The remote method being called is test.square
(line 10).
The method is being called with one argument, an integer with a value of 13
(lines 12–16).
Unlike their counterparts in Java, method names in an XML-RPC request do not include parentheses. They consist of the name of an object followed by a period and the name of the method or simply the name of the method, depending on the XML-RPC server.
XML-RPC, which has been implemented in numerous computer-programming languages, has a few differences in terminology than Java: Methods are called procedures, and method arguments are called parameters. The Java terms are used often during today’s lesson when Java programming techniques are discussed.
An XML-RPC response is XML data that is sent back from a web server like any other HTTP response. Again, XML-RPC piggybacks on top of an established process—a web server sending data via HTTP to a web browser—and uses it in a new way.
The response also consists of HTTP headers and an XML-RPC response in XML format.
Listing 20.2 contains an example of an XML-RPC response.
Example 20.2. An XML-RPC Response
1: HTTP/1.0 200 OK 2: Date: Sun, 03 Mar 2007 19:17:11 GMT 3: Server: Apache/1.3.26 (Unix) mod_virgule/1.41 PHP/4.1.2 mod_perl/1.26 4: ETag: "PbT9cMgXsXnw52OqREFNAA==" 5: Content-MD5: PbT9cMgXsXnw52OqREFNAA== 6: Content-Length: 157 7: Connection: close 8: Content-Type: text/xml 9: 10: <?xml version="1.0"?> 11: <methodResponse> 12: <params> 13: <param> 14: <value> 15: <int>169</int> 16: </value> 17: </param> 18: </params> 19: </methodResponse>
In Listing 20.2, lines 1–8 are the HTTP headers, and lines 10–19 are the XML-RPC response. You can learn the following things from this listing:
The response is 157 bytes in size and in XML format (lines 6 and 8).
The value returned by the remote method is an integer that equals 169
(line 15).
An XML-RPC response contains only one argument, contrary to what you might expect from the params
tag in line 12. If the remote method does not return a value—for example, it might be a Java method that returns void
—an XML-RPC server still returns something.
This return value can be primitive data, strings, arrays of varying dimensions, and more sophisticated data structures such as key-value pairs (the kind of thing you would implement in Java using HashMap
or one of its subclasses).
The XML-RPC request and response examples were generated by a server run by the Advogato open source advocacy site. You can find out more about its XML-RPC server at the web address http://www.advogato.org/xmlrpc.html.
Several XML-RPC debuggers on the web can be used to call remote methods and see the full XML-RPC request and response, which makes it much easier to determine if a client or server is working correctly. One’s available at the web page http://gggeek.raprap.it/debugger.
Although you can work with XML-RPC by creating your own classes to read and write XML and exchange data over the Internet, an easier route is to use a preexisting Java class library that supports XML-RPC.
One of the most popular is Apache XML-RPC, an open source project managed by the developers of the Apache web server, Tomcat Java servlet engine, Ant build tool, and other popular open source software.
The Apache XML-RPC project, which consists of the org.apache.xmlrpc
package and three related packages, contains classes that can be used to implement an XML-RPC client and server with a short amount of your own code.
The project has a home page at the web address http://xml.apache.org/xmlrpc. Today’s projects employ release 2.0.
To use this project, you must download and install an archive file that contains a pair of JAR files: xmlrpc-2.0.jar
and xmlrpc-2.0-applet.jar
.
The installation archive is offered as a free ZIP download (suitable for Windows users) and combined TAR/GZ format download (for Linux, UNIX, and Mac OS X users).
Download and unpack the archive files matching your operating system. The main folder contains two JAR archives containing the Apache XML-RPC class libraries: xmlrpc-2.0.jar
and xmlrpc- 2.0-applet.jar
(the version number might be different at the time you install it).
After you have unpacked the files, references to the three JAR files can be added to your system’s Classpath
environment variable so that Apache XML-RPC’s packages will be found by your Java interpreter and compiler.
The full folder location and name of each file should be included somewhere in the Classpath
. For example, on Windows, if the files were in C:jdkxmlrpcxmlrpc-2.0.jar
and C:jdkxmlrpcxmlrpc-2.0-applet.jar
, the following text should be appended to the end of the Classpath
:
;C:jdkxmlrpcxmlrpc-2.0.jar;C:jdkxmlrpcxmlrpc-2.0-applet.jar
Semicolons separate each reference to a file or folder in the Classpath
. The text is similar on a Linux or UNIX system, but you must use colons between files instead of semicolons:
:C:jdkxmlrpcxmlrpc-2.0.jar:C:jdkxmlrpcxmlrpc-2.0-applet.jar
Take care not to wipe out anything that’s already in the Classpath
. More information on how to set up this environmental variable can be found in Appendix A, “Using the Java Development Kit.”
After Classpath
has been configured, you can begin using Apache XML-RPC classes in your Java programs. The easiest way to refer to these classes is to use an import
statement to make a package available, as in the following statement:
import org.apache.xmlrpc.*;
This makes it possible to refer to the classes in the main package, org.apache.xmlrpc
, without using the full package name. You’ll work with this package in the next two sections.
If Apache XML-RPC doesn’t suit your needs, you can choose from more than two dozen other implementations. XML-RPC.Com includes a directory of XML-RPC implementations in Java, C++, PHP, and other languages. To see the list, visit the website http://www.xmlrpc.com and choose the Implementations hyperlink.
An XML-RPC client is a program that connects to a server, calls a method on a program on that server, and stores the result.
Using Apache XML-RPC, the process is comparable to calling any other method in Java—you don’t have to create an XML request, parse an XML response, or connect to the server using one of Java’s networking classes.
In the org.apache.xmlrpc
package, the XmlRpcClient
class represents a client. An XmlRpcClient
object can be created in three ways, each of which requires the URL of the server:
XmlRpcClient(
String
)
—. Create a client connecting to an address specified by the String
, which must be a valid web address (such as http://www.example.com) or web address and port number (such as http://www.example.com:2274)
XmlRpcClient(
URL
)
—. Create a client connecting to the specified URL
object
XmlRpcClient(
String
,
int
)
—. Create a client connecting to the specified hostname (String
) and port number (int
)
The two constructors that require a String
argument throw java.net.MalformedURLException
exceptions if the argument is not a valid web URL.
The following statement creates a client to an XML-RPC client on the host cadenhead.org
at the port 4413:
XmlRpcClient client = new XmlRpcClient("http://cadenhead.org:4413");
If you are calling a remote method with any arguments, they should be stored in a Vector
object, a data structure that holds objects of different classes.
Vectors were covered during Day 8, “Data Structures.” They are part of the java.util
package.
To work with vectors, call the Vector()
constructor with no arguments and call its addElement(
Object
)
method with each object that should be added to the vector. Objects can be of any class and must be added to the vector in the order that they are called in the remote method.
The following data types can be arguments to a remote method:
byte[]
arrays for base64
data
Boolean
objects for boolean
values
Date
objects for dateTime.iso8601
values
Double
objects for double
values
Integer
objects for int
values
String
objects for string
values
Hashtable
objects for struct
values
Vector
objects for arrays
The Date
, Hashtable
, and Vector
classes are in the java.util
package.
For example, if an XML-RPC server has a method that takes String
and double
arguments, the following code creates a vector that holds each of the arguments:
String code = "conical"; Double xValue = new Double(175); Vector parameters = new Vector(); parameters.addElement(code); parameters.addElement(xValue);
To call the remote method on the XML-RPC server, call the XmlRpcClient
object’s execute
(String
, Vector
) object with two arguments:
The name of the method
The vector that holds the method’s arguments
The name of the method should be specified without any parentheses or arguments. An XML-RPC server usually documents the methods that it makes available to the public—for example, there really is a cadenhead.org
XML-RPC server that operates on port 4413 (it’s my own test server). It offers dmoz.getRandomSite()
, a method that returns an Object
containing information about a random website. This method has no arguments.
The following statements create an XML-RPC client and call this method:
XmlRpcClient client = new XmlRpcClient("http://cadenhead.org:4413"); Vector params = new Vector(); Object result = client.execute("dmoz.getRandomSite", params);
The execute()
method returns an Object
that contains the response. This object should be cast to one of the data types sent to a method as arguments: Boolean
, byte[]
, Date
, Double
, Integer
, String
, Hashtable
, or Vector
.
Like other networking methods in Java, execute()
throws a java.net.IOException
exception if an input/output error occurs during the connection between client and server. There’s also an XmlRpcException
exception that is thrown if the server reports an XML-RPC error.
Objects returned by the execute()
method have the following data types: Boolean
for boolean
XML-RPC values, byte[]
for base64
data, Date
for dateTime.iso8601
data, Double
for double
values, Integer
for int
(or i4) values, String
for strings
, Hashtable
for struct
values, and Vector
for arrays.
To see all this in a working program, enter the text of Listing 20.3 into your text editor and save the file as SiteClient.java
.
Example 20.3. The Full Text of SiteClient.java
1: import java.io.*; 2: import java.util.*; 3: import java.net.*; 4: import org.apache.xmlrpc.*; 5: 6: public class SiteClient { 7: String url; 8: String title; 9: String description; 10: 11: public static void main(String arguments[]) { 12: SiteClient client = new SiteClient(); 13: try { 14: Vector response = client.getRandomSite(); 15: // Report the results 16: if (response.size() > 0) { 17: client.url = response.get(1).toString(); 18: client.title = response.get(2).toString(); 19: client.description = response.get(3).toString(); 20: System.out.println("URL: " + client.url 21: + " Title: " + client.title 22: + " Description: " + client.description); 23: } 24: } catch (IOException ioe) { 25: System.out.println("IO Exception: " + ioe.getMessage()); 26: ioe.printStackTrace(); 27: } catch (XmlRpcException xre) { 28: System.out.println("XML-RPC Exception: " + xre.getMessage()); 29: } 30: } 31: 32: public Vector getRandomSite() 33: throws IOException, XmlRpcException { 34: 35: // Create the client 36: XmlRpcClient client = new XmlRpcClient( 37: "http://localhost:4413/"); 38: // Create the parameters for the request 39: Vector params = new Vector(); 40: // Send the request and get the response 41: Vector result = (Vector) client.execute("dmoz.getRandomSite", 42: params); 43: return result; 44: } 45: }
The SiteClient
application makes a connection to the XML-RPC server and calls the dmoz.getRandomSite()
method on the server with no arguments. This method returns a Vector
that contains four strings: “ok” (to show that the request was fulfilled) and the site’s URL, title, and description.
The output for the SiteClient
application should resemble the following:
URL: http://www.rdb.com/ Title: rdb Description: Makers of a very simple quasi-relational database based on Unix shell commands
These random sites are culled from the database of the Open Directory Project, a directory of more than six million sites at http://www.dmoz.org. The project’s data is available for redistribution by others at no cost under the terms of the Open Directory License. For more information, search the directory for the text “Use of ODP Data.”
An XML-RPC server is a program that receives a request from a client, calls a method in response to that request, and returns the result. The server maintains a list of methods that it allows clients to call on; these are different Java classes called handlers.
Apache XML-RPC handles all the XML and networking itself, enabling you to focus on the task you want a remote method to accomplish.
There are several ways to serve methods remotely with the classes in the org.apache.xmlrpc
package. The simplest is to use the WebServer
class, which represents a simple HTTP web server that only responds to XML-RPC requests.
This class has two constructors:
WebServer(
int
)
—. Create a web server listening on the specified port number.
WebServer(
int
,
InetAddress
)
—. Create a web server at the specified port and IP address. The second argument is an object of the java.net.InetAddress
class.
Both constructors throw IOException
exceptions if there’s an input/output problem creating and starting the server.
The following statements create an XML-RPC web server on port 4413:
int port = Integer.parseInt("4413"); WebServer server = new WebServer(port);
The web server does not contain the remote methods that clients call via XML-RPC. These reside in other Java classes, which are called handlers.
To add a handler, call the server’s addHandler(
String
,
Object
)
method with the specified handler name and handler object.
The first argument to addHandler()
is a name to give the handler, which can be anything—it’s comparable to naming a variable. Clients will use this name when calling remote methods.
The SiteClient
application created earlier today called the remote method dmoz.getRandomSite()
. The first part of this call—the text preceding the period—refers to a handler named dmoz
.
The second argument to addHandler()
is an object of the class that has public methods, which can be called remotely.
The following statements create a handler for a WebServer
object named server
:
DmozHandler odp = new DmozHandler(); server.addHandler( "dmoz", odp);
The handler in this example is a DmozHandler
object, which contains a getRandomSite()
method that returns information about a random site in the Open Directory Project. You’ll be creating this class later.
A class that handles remote method calls can be any Java class that contains public
methods that return a value, as long as the methods take arguments that correspond with data types supported by Apache XML-RPC: boolean
, byte[]
, Date
, double
, Hashtable
, int
, String
, and Vector
.
You can easily put existing Java classes to use as XML-RPC handlers without modification as long as they do not contain public
methods that should not be called and each public
method returns a suitable value.
The suitability of return values relates to the Apache XML-RPC implementation rather than XML-RPC itself. Other implementations of the protocol are likely to have some differences in the data types of the arguments they take in remote method calls and the values they return.
Using Apache XML-RPC, the web server allows any public
method in the handler to be called, so you should use access control to keep prying clients out of methods that should remain off limits.
As the first step toward the creation of an XML-RPC service, the following code creates a simple web server that takes XML-RPC requests. Enter the text of Listing 20.4 and save the file as DmozServer.java
.
Example 20.4. The Full Text of DmozServer.java
1: import java.io.IOException; 2: import org.apache.xmlrpc.WebServer; 3: import org.apache.xmlrpc.XmlRpc; 4: 5: public class DmozServer { 6: public static void main(String[] arguments) { 7: if (arguments.length < 1) { 8: System.out.println("Usage: java DmozServer [port]"); 9: System.exit(0); 10: } 11: try { 12: startServer(arguments[0]); 13: } catch (IOException ioe) { 14: System.out.println("Server error: " + 15: ioe.getMessage()); 16: } 17: } 18: 19: public static void startServer(String portString) throws IOException { 20: // Start the server 21: int port = Integer.parseInt(portString); 22: System.out.println("Starting Dmoz server ..."); 23: WebServer server = new WebServer(port); 24: 25: // Register the handler 26: DmozHandler odp = new DmozHandler(); 27: server.addHandler("dmoz", odp); 28: server.start(); 29: System.out.println("Accepting requests ..."); 30: } 31: }
This class can’t be compiled successfully until you have created the handler class DmozHandler
.
The DmozServer
application takes a port number as a command-line argument and calls the startServer()
method with this argument.
The startServer()
method creates a WebServer
object that monitors that port number for incoming XML-RPC requests. One handler is added to the server: a DmozHandler
object given the name “dmoz”; then the server’s start()
method is called to begin listening for requests.
That’s all the code required to implement a functional XML-RPC server. Most of the work is in the remote methods you want a client to call, which don’t require any special techniques as long as they are public
and they return a suitable value.
To give you a complete example you can test and modify to suit your own needs, the DmozHandler
class is provided. The techniques employed in this class were covered during Day 18, “Accessing Databases with JDBC,” and are a good review of how to use JDBC to retrieve records from a database—in this example a MySQL database called db1
.
Enter the text of Listing 20.5 and save the file as DmozHandler.java
; then compile the classes DmozServer.java
and DmozHandler.java
.
Example 20.5. The Full Text of DmozHandler.java
1: import java.sql.*; 2: import java.util.*; 3: 4: public class DmozHandler { 5: public Vector getRandomSite() { 6: Connection conn = getMySqlConnection(); 7: Vector<String> response = new Vector<String>(); 8: try { 9: Statement st = conn.createStatement(); 10: ResultSet rec = st.executeQuery( 11: "SELECT * FROM cooldata ORDER BY RAND() LIMIT 1"); 12: if (rec.next()) { 13: response.addElement("ok"); 14: response.addElement(rec.getString("url")); 15: response.addElement(rec.getString("title")); 16: response.addElement(rec.getString("description")); 17: } else { 18: response.addElement("database error: no records found"); 19: } 20: } catch (SQLException sqe) { 21: response.addElement("database error: " + sqe.getMessage()); 22: } 23: return response; 24: } 25: 26: private Connection getMySqlConnection() { 27: Connection conn = null; 28: String data = "jdbc:mysql://localhost/cool"; 29: try { 30: Class.forName("com.mysql.jdbc.Driver"); 31: conn = DriverManager.getConnection( 32: data, "username", "password"); 33: } catch (SQLException s) { 34: System.out.println("SQL Error: " + s.toString() + " " 35: + s.getErrorCode() + " " + s.getSQLState()); 36: } catch (Exception e) { 37: System.out.println("Error: " + e.toString() 38: + e.getMessage()); 39: } 40: return conn; 41: } 42: }
Lines 28–32 of the DmozHandler
application should be changed to reflect your own database drive, username, and password. You also might need to change the rest of the string used to connect to the database, depending on your driver.
The server is run by specifying the port number at a command line, as in this example:
java DmozServer 4413
After the server is up and running, you can modify the SiteClient
application to connect to a different XML-RPC server. Change lines 36–37 of Listing 20.3 to replace localhost:4413
with a reference to the server’s computer followed by a colon and port number, as in the following:
XmlRpcClient client = new XmlRpcClient( "http://cadenhead.org:4413/");
Running this particular XML-RPC server also requires a database. To download a Microsoft Access database containing information on 1,000 websites from the Open Directory Project, visit this book’s website at http://www.java21days.com and open the Day 20 page. The database is in a file named dmozdata.mdb
.
XML-RPC has been described as the “lowest common denominator” of remote procedure call protocols, but this isn’t considered an insult by its originators. Most attempts to facilitate software communication over a network have been extremely sophisticated, scaring off developers who have more simple needs.
The XML-RPC protocol can be used to exchange information with any software that supports HTTP, the lingua franca of the Web, and XML, a highly popular, structured format for data.
By looking at XML-RPC requests and responses, you should be able to figure out how to use the protocol even without reading the protocol specification.
However, as implementations such as Apache XML-RPC become more extensive, you can begin using it quickly without ever looking at the protocol at all.
Review today’s material by taking this three-question quiz.
The following question is the kind of thing you could expect to be asked on a Java programming certification test. Answer it without looking at today’s material or using the Java compiler to test the code.
Given:
public class Operation { public static void main(String[] arguments) { int x = 1; int y = 3; if ((x != 1) && (y++ == 3)) y = y + 2; } }
The answer is available on the book’s website at http://www.java21days.com. Visit the Day 20 page and click the Certification Practice link.
To extend your knowledge of the subjects covered today, try the following exercises:
The programming site Advogato offers an XML-RPC interface to read member diaries at http://www.advogato.org/xmlrpc.html. Write an application that reads a member’s last 10 diary entries.
The XML-RPC interface for the weblog update service Weblogs.Com is at http://www.weblogs.com/api.html. Write a client and server that can send and receive the weblogUpdates.ping
method.
Where applicable, exercise solutions are offered on the book’s website at http://www.java21days.com.