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.
Marquee e XML-RPC becomes Redstone XML-RPC

The Marquee e XML-RPC Library has been merged with Redstone XML-RPC library in an effort to further its development towards a stable and small but feature rich XML-RPC implementation.

The old library code and binary releases are still available and we intend to fix any open issues with it. At the same time Redstone XML-RPC offers a few enhancements and modifications to the old library in a new release. To be able to support the feature set offered by Redstone XML-RPC, the new library is not backwards compatible. However, the modifications required to make existing clients work with the new library is relatively simple.

Redstone XML-RPC Library is a small but versatile implementation of the XML-RPC specification. It contains the following main features:

  • An XML-RPC client for accessing XML-RPC services, and an XML-RPC servlet for publishing plain Java objects as XML-RPC services in a web server. Both the client and the server can be configured to stream messages directly over a socket without constructing the messages in memory. Although this is not compliant with the specification (which mandates the use of a Content-Length HTTP header), this allows for virtually unlimited sized messages when communicating with services and clients that do not use or rely on the Content-Length header.
  • A serialization framework allowing custom serializers to serialize values inherently unknown to the library. The serialization framework may be used outside of the XML-RPC library to encode Java objects into XML streams. The library comes with serializers for common types like Maps, Collections, Lists, object arrays, primitive arrays, and a reflective serializer for serializing any Java object.
  • A set of custom serializers generating JSON messages instead of XML-RPC messages allowing for easy access from JavaScript. The library also includes a small XML-RPC client in JavaScript for accessing services published by this library.
  • A SAX-based XML-RPC parser that may be used outside of the library to parse streams with XML-RPC messages.
  • A dynamic proxy generator to allow XML-RPC services to be used through regular Java-interfaces. This helps increase productivity and type safety while allowing IDEs to provide content assist functionality.
  • Invocation interceptors that are hooked up on the server side to intercept calls to services published though the library. Interceptors may intercept invocations based on method names or arguments sent from the client. Based on this information, interceptors may prevent invocations from completing or add, modify, or remove arguments before dispatching the call to the service. Interceptors may also redirect calls and examine and modify the result of an invocation before it is sent back to the client.

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 Applications

When 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 Proxy

The 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 Client

The 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 Services

This 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 Servlet

The 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 Servlet

To 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.jar
After 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.

Serialization

Whenever 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 JavaScript

XML-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.

Downloads

The source code and the pre-built JAR is kindly hosted by SourceForge

Thank You!

Copyright © 2006 Redstone Handelsbolag