Introduction
Simple HTTP server meets Redstone XML-RPC. We have uploaded a new package which
combines Redstone XML-RPC Library with the Simple HTTP server to simplify deployment
of XML-RPC services. It can be started directly from the command line, or can be
embedded in you own main() easily. Check out the releases and the wiki
for more information.
Don't forget to also check out the wiki
space on Source Forge which contains more information about the library. The wiki is currently
being setup and documentation on this page will be migrated over to the wiki.
Redstone XML-RPC Library is a small but versatile implementation of the XML-RPC specification. It contains the following main features:
All of this is packed into a 39Kb JAR (server and client) or a 20Kb JAR (client only) Credit goes go to Hannes Waln�ffer (for the initial Helma library), and all people involved in the Marqu�e XML-RPC Library by which this implementation is heavily influenced. Developing XML-RPC Client ApplicationsWhen developing XML-RPC clients that access XML-RPC services there are two alternatives. The application may either use the redstone.xmlrpc.XmlRpcClient class or the redstone.xmlrpc.XmlRpcProxy. It is normally recommended that the XmlRpcProxy is used where you first define the XML-RPC interface of the service you want to use in a plain Java interface. Using an XML-RPC ProxyThe following example demonstrates how to use the XML-RPC proxy to log into and out of the online Atlassian JIRA service using login credentials supplied on the command line (exceptions are just propagated out of main() for brevity): Example:import redstone.xmlrpc.XmlRpcProxy; import redstone.xmlrpc.XmlRpcFault; static interface Jira { public String login( String username, String password ) throws XmlRpcFault; public void logout( String loginToken ) throws XmlRpcFault; } public static void main( String[] args ) throws Exception { Jira jira = ( Jira ) XmlRpcProxy.createProxy( "http://jira.atlassion.com/RPC2", new Class[] { Jira.class } ); String token = jira.login( args[ 0 ], args [ 1 ] ); jira.logout( token ); } First we define the service interface in a regular Java interface. This interface is normally defined in its own .java file. We the create a dynamic proxy that implements the Java interface and that includes an XmlRpcClient behind the scenes that will access the XML-RPC service found at http://jira.atlassion.com/RPC2. When invoking methods on the proxy, XML-RPC invocations towards the backing XML-RPC service are made. This can make the code easier to read and more type safe, allowing IDEs to help out with code completion and other content assist functionality since we are basically using a regular Java interface. Using the XML-RPC ClientThe following example demonstrates how to use the XML-RPC client which require that we use the XmlRpcClient.invoke() method for any call that we want to make, and that we manually create tha arguments array (or List) that we want to include in the call.import redstone.xmlrpc.XmlRpcClient; public static void main( String[] args ) throws Exception { XmlRpcClient client = new XmlRpcClient( "http://jira.atlassion.com/RPC2" ); Object token = client.invoke( "jira1.login", new Object[] { args[ 0 ], args [ 1 ] } ); client.invoke( "jira1.logout", new Object[] { token } ); } Compared with the XmlRpcProxy example above, this code is not type safe. Developing XML-RPC ServicesThis section assumes that you are somewhat familiar with developing web applications. In the example, a servlet and a web.xml file is produced that should be registered in your servlet container of choice. If you have never deployed an application in a servlet container (like Jetty or Tomcat) it is recommended that you first check out the documentation for either of these or any of the other servlet containers available. As opposed to the original Marqu�e XML-RPC Library, Redstone XML-RPC does not include a lightweight web server. Instead, the library includes a servlet that can be used in web applications to host XML-RPC services. If demand for a stand-alone web server should arise, we might add that later. However, with excellent servlet containers like Jetty and others we feel that there is no reason to maintain another HTTP server implementation. The easiest way to create an XML-RPC service is to create a servlet that extend the redstone.xmlrpc.XmlRpcServlet and which registers the Java objects that will be serving the XML-RPC requests made by the XML-RPC clients. These Java objects are called invocation handlers but can be any plain Java object. Implementing the XML-RPC ServletThe following example shows how an XML-RPC service can be implemented. package example; import java.util.Random; import java.util.HashMap; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import redstone.xmlrpc.XmlRpcServlet; public class ExampleService extends XmlRpcServlet { public void init( ServletConfig servletConfig ) throws ServletException { super.init( servletConfig ); getXmlRpcServer().addInvocationHandler( "SimpleDatabase", new HashMap() ); getXmlRpcServer().addInvocationHandler( "RandomNumberGenerator", new Random() ); } } We extend redstone.xmlrpc.XmlRpcServlet so that our new servlet has built-in support for handling XML-RPC requests. We the override init() to register which invocation handlers that we want to provide for our clients. In this case we have two handlers, SimpleDatabase and RandomNumberGenerator. As you can see you can use any type of Java object. In this example we registered a HashMap and a Random object. Clients will be able to invoke methods on these objects by invoking "SimpleDatabase.put", "SimpleDatabase.get", and "RandomNumberGenerator.nextInt" for instance. To publish this service you will need to have a servlet container like Tomcat, Jetty, Resin, or any of the other containers out there. For information on how to configure the servlet see the section below. Configuring the XML-RPC ServletTo register the XML-RPC servlet, use the following servlet definition in your web.xml file: <servlet> <servlet-name>xml-rpc</servlet-name> <servlet-class>example.ExampleService</servlet-class> <init-param> <param-name>streamMessages</param-name> <param-value>1</param-value> </init-param> <init-param> <!-- Optional! Defaults to text/xml and ISO-8859-1 --> <param-name>contentType</param-name> <param-value>text/xml; charset=ISO-8859-1</param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>xml-rpc</servlet-name> <url-pattern>/xml-rpc/*</url-pattern> </servlet-mapping> With the following servlet definition in your web.xml the XmlRpcServlet responds with JSON messages and is to be invoked using the XML-RPC JavaScript client supplied with this library. <servlet> <servlet-name>ajax</servlet-name> <servlet-class>example.ExampleService</servlet-class> <init-param> <param-name>streamMessages</param-name> <param-value>1</param-value> </init-param> <init-param> <param-name>contentType</param-name> <param-value>text/javascript+json; charset=ISO-8859-1</param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>ajax</servlet-name> <url-pattern>/ajax/*</url-pattern> </servlet-mapping> The final application to be deployed should look like this: /WEB-INF web.xml classes/ example/ExampleService.class lib/ xmlrpc.jarAfter registering your web application (which in this case contains a single servlet) in your servlet container, clients will be able to access the services on the "http://yourhost/xml-rpc" or "http://yourhost/ajax" respectively. SerializationWhenever an argument for an invocation or a return value from an invocation is to be serialized into its XML-RPC counterpart, the XmlRpcSerializer class is used behind the scenes. The XmlRpcSerializer knows how to serialize primitive values like integers, doubles, booleans, dates, byte arrays, and strings. When an object not recognized by the serializer is supplied it asks any custom serializers added to XmlRpcSerializer.The library contains several custom serializers that are automatically added to XmlRpcSerializer. These allow collections, maps, primitive arrays, and other object to be serialized. Custom serializers are easy to implement if an application wants to use objects of other types in the XML-RPC interfaces. Unless an application needs to use custom serializers it rarely accesses the XmlRpcSerializer class directly. The XmlRpcSerializer and custom serializers implementing XmlRpcCustomSerializer write the XML-RPC values to a java.io.Writer. The serialization code may therefor be used outside of the library if a need should exist; import java.io.Writer; import java.io.OutputStreamWriter; import redstone.xmlrpc.XmlRpcSerializer; public static void main( String[] args ) throws Exception { Writer writer = new OutputStreamWriter( System.out ); XmlRpcSerializer.serialize( "Hello!", writer ); XmlRpcSerializer.serialize( new Integer( 42 ), writer ); writer.flush(); } Outputs: <value><string>Hello!</string></value><value><i4>42</i4></value> Note: Although the serialization mechanism allows custom serializers to serialize any kind of object the original type information is not conveyed in the XML-RPC message since XML-RPC only supports generic structs and arrays. Once the message is deserialized by whatever implementation is used at the terminating end, it will be converted to the data types used to represents <struct> and <array>s. For Redstone XML-RPC, this means that the invocation handlers will accept java.util.List and java.util.HashMap (in addition to the basic datatypes supported by XML-RPC, like integers, doubles, Strings, Dates, booleans, and byte[]s. XML-RPC and JavaScriptXML-RPC is perfectly suited to support basic AJAX functionality in web applications. The Redstone XML-RPC library contains a minimal XML-RPC client written in JavaScript that may be used to invoke XML-RPC services published using this library. The client is small due to the fact that it relies on a set of custom serializers supplied by the library that serializes return values from invocation handlers using the JSON notation. This means that XML-RPC messages are sent to the server whereas JSON messages are returned to the client which can be immediately evaluated to JavaScript objects: <html> <head> <script src="ajax.js"></script> </head> <body> <p>Result from invocation:</p> <script> var client = new AjaxService( 'http://example.net/ajax', 'HandlerName' ); var result = client.invoke( 'methodName', [ 10, 20.5, '42' ] ); document.write( '<p>' + result + '<p>' ); </script> </body> </html> Tips
The serializers create XmlRpcArray and XmlRpcStruct objects to handle arrays
and structs. Instead of having invocation handler parameters accepting Collection
or List (or Map in case of structs) these classes may be used. An XmlRpcArray is
in fact an ArrayList and an XmlRpcStruct is a HashMap with a few utility methods
for exctracting values from the array without the need for typecasts and conversion
to primitives:
public void doSomething( XmlRpcArray array ) { boolean aBoolean = array.getStruct( 0 ).getBoolean( "someBoolean" ); }The old way of doing this would be the following, which is perfectly alright if dependencies on the Redstone XML-RPC library is to be avoided in the invocation handlers. public void doSomething( List array ) { boolean aBoolean = ( ( Boolean ) ( ( Map ) array.get( 0 ) ).get( "someBoolean" ) ).booleanValue(); }Both examples are legal for invocation handlers, but for nested arguments the former is without question shorter and more readable. DownloadsThe source code and the pre-built JAR is kindly hosted by SourceForge Thank You! |
|
|