OverviewThis text contains a user's guide and a quick walkthrough of the current implementation. Some of the classes in the library requires JDK 1.2 and 1.3, but the core functionality can be used with JDK 1.1. The documentation clearly states which JDK is required for each class. This document is incomplete and will be updated during April 2001 The library consists of four major parts:
Users will come in contact with the serializer when specifying which custom serializers to be used during the serialization process. The library contains several custom serializers for serializing Java 2 collections, arrays, and other types of objects. Users will also come in contact with the parser when specifying which SAX driver to use during parsing. All other interaction occurs through the server and the client. SerializersWhen sending XML-RPC messages from an XmlRpcClient to an XML-RPC server and from an XmlRpcServer to an XML-RPC client, Java objects need to be translated into their XML-RPC representations. Servers need to translate return values from their handlers, and clients need to translate arguments supplied in method calls. This is accomplished by using serializers. You do not need to know how the serialization mechanism works in order to use the XML-RPC server or client. Unless you're interested in creating your own custom serializers, you may skip the rest of this section. XmlRpcSerializerAll serialization occurs initially through the XmlRpcSerializer class which has support for basic object types like java.lang.String, java.lang.Integer, and so forth (see list below). If the XmlRpcSerializer is passed an object which it does not recognize, it keeps a list of XmlRpcCustomSerializers which it tries to use instead. Each custom serializer reports which kind of Java object it knows how to serialize.
public static void serialize( Object value, StringBuffer output ); public static void registerCustomSerializer( XmlRpcCustomSerializer serializer ); public static void unregisterCustomSerializer( XmlRpcCustomSerializer serializer ); XmlRpcCustomSerializerThe following class represents a custom serializer that may handle any kind of Java 2 collection which is serialized into an XML-RPC array. This serializer is included in the Marquee XML-RPC library. public class CollectionSerializer implements XmlRpcCustomSerializer { public Class getSupportedClass() { return Collection.class; } public void serialize( Value, StringBuffer output ) { Collection c = ( Collection ) value; Iterator iter = c.iterator(); output.append( " getSupportedClass()The getSupportedClass() method returns the class indicating which types of objects it knows how to serialize. This method is called in two situations by the XmlRpcSerializer. First when the serializer is installed by calling XmlRpcSerializer.registerCustomSerializer(), and second when an actual object has been requested for serialization by calling the XmlRpcSerializer.registerCustomSerializer() method. When registering a custom serializer, XmlRpcSerializer investigates the supported class so that it can determine where in the list of custom serializers the serializer belongs. For instance, if the serializer list already contains a serializer that knows how to handle java.util.Vector objects, the generic CollectionSerializer, above, will end up after the java.util.Vector serilizer. That is, the Vector serializer will override the generic CollectionSerializer, as it operates on a more specialized kind of collection. When an object has been requested for serialization, the XmlRpcSerializer class queries every custom serializer in the list until it finds a serializer that knows how to handle the supplied value. For instance, when passing the XmlRpcSerializer.serialize() method a java.util.ArrayList object, the CollectionSerializer, if registered with XmlRpcSerializer, will catch the object (as it is an instance of java.util.Collection) and convert it into an XML-RPC array. serialize()Notice how the serialize() method in the CollectionSerializer above reuses the default serialization mechanism introduced in XmlRpcSerializer. The supplied collection may contain any kind of object which the XmlRpcSerializer knows how to handle, or for which a custom serializer has been registered. Theoretically, this may result in a recursive call to the serialize() method above if any of the elements in the collection is another collection. Included custom serializersThe Marquee XML-RPC library supplies a few useful implementations of the XmlRpcCustomSerializer interface for converting generic collections and maps, as well as a few other more specialized types. It also contains a reflective serializer which can serialize any type of object by using Java Reflection.
The generic collection serializer, presented in the example above, requires JDK 1.2, as does the generic map serializer. If you don't support Java 1.2, the VectorSerializer and HashtableSerializer classes may be used instead. The XML-RPC ServerWhen setting up an XML-RPC server you supply a set of objects that will receive the method calls parsed by the server dispatchers. These objects must implement the XmlRpcInvocationHandler interface which can be achieved by extending the ReflectiveInvocationHandler class or wrapping the object in a ReflectiveInvocationHandler instance, or implementing it from scratch. The methods that are to be invoked through XML-RPC must only use parameters of the following types:
An exception to this rule is when using invocation processors that modify the list of call arguments according to some set of rules. For instance, an invocation processor may add an additional transaction object for methods with names starting with "tx_". These arguments are not transported using XML-RPC and may be of any type. Return values may be of any type supported by the built-in serializer or any of the registered custom serializers. That is, if the CollectionSerializer supplied with the XML-RPC library is registered with the server, invocation handlers may return any type of object implementing the Java 2 Collection interface. Custom serializers are ordered by specialization. That is, the reflective serializer will always be placed last and the MapSerializer will always be placed before the CollectionSerializer, and so on. This ensures that the most appropriate serializer will be used for an object during serialization -- for instance, a HashMap will not be serialized using the generic Collection serializer if a MapSerializer is available. ExampleSetting up an XML-RPC serverThis example shows how to set up a server by registering invocation handlers and processors. It also shows how to specify which SAX driver and custom serializers to use.
import marquee.xmlrpc.*; import marquee.xmlrpc.serializers.*; class SampleHandler extends ReflectiveInvocationHandler { String getNameOfMonth( int month ) throws IllegalArgumentException { if ( month > 0 && month < 13 ) { return months[ month ]; } throw new IllegalArgumentException( "Invalid month." ); } String[] getAllMonths() { return months; } private final static String[] months = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" } } public class SampleServer { public static void main( String[] args ) { XmlRpcParser.setDriver( "com.sun.xml.parser.Parser" ); XmlRpcSerializer.registerCustomSerializer( new ObjectArraySerializer() ); XmlRpcServer server = new XmlRpcServer(); server.registerInvocationHandler( new SampleHandler() ); server.runAsService( 80 ); } } Invocation Handlers
XML-RPC messages are parsed by the XmlRpcServer and dispatched to an XmlRpcInvocationHandler
corresponding to the handler name contained in the The XmlRpcInvocationHandler interface contains a single method that invocation handlers must implement. The XmlRpcReflectiveInvocationHandler class supplies a default implementation of this interface that you'll use most of the times. public Object invoke( String methodName, Vector arguments ) throws Throwable; XmlRpcReflectiveInvocationHandlerThe easiest way to create an invocation handler is to inherit the XmlRpcReflectiveInvocationHandler class which implements the XmlRpcInvocationHandler using Java Reflection to find the method targeted by the call. The XmlRpcReflectiveInvocationHandler class may also be used to wrap a Java object in a reflective handler, if your class is already inheriting from another class. If you look at the SampleServer above, the SampleHandler represents a complete invocation handler that extends the reflective invocation handler. This is the most common way of creating invocation handlers. If, for some reason, you do not wish to inherit XmlRpcReflectiveInvocationHandler, you may wrap your object in a new XmlRpcReflectiveInvocationHandler. This gives the overhead of an additional object being created (although with a single reference data member); XmlRpcReflectiveInvocationHandler handler = new XmlRpcReflectiveInvocationHandler( myObject ); Writing your own handler from scratchIf you do not wish to use the reflective handler wich uses Java Reflection to identify which method to call, you may write your own invocation handler. This may increase performance slightly if you have a single method or only a few methods in your class. public class MyInvocationHandler implements XmlRpcInvocationHandler { public Object invoke( String methodName, Vector arguments ) throws Throwable { if ( methodName.equals( "doSomethingAndReturnString" ) ) { return doSomethingAndReturnString(); } throw new Exception( "Method not found in handler." ); } public String doSomethingAndReturnString() { return "All done!"; } } The XML-RPC ClientComing soon...Dynamic ProxiesThis feature requires JDK 1.3.
An alternative way of calling server procedures is to use a dynamic XmlRpcProxy.
When creating an instance of the XmlRpcProxy you specify the URL of the server
which should be proxied for, and a list of interfaces the proxy should implement.
The proxy may be typecast to and called through any of these interfaces, which
will convert calls to XML-RPC messages that are sent to the server using the
" ExampleWhen developing an application using XML-RPC servers located somewhere on the Internet, you start by expressing the services available on the servers in Java interfaces. interface mailToTheFuture { /** * Adds a message to username's queue. * * @param username The email address of a registered user. * * @param password must be that user's password. * * @param message A hashtable containing the following elements; dateTime, * messageBody, receiverMailAddress, subject. * * @return The number of messages in username's queue. */ int addMessage( String username, String password, Hashtable message ) throws Throwable; /** * Deletes a message from username's queue. * * @param username The email address of a registered user. * * @param password must be that user's password. * * @param ordinal The number of the message to remove. * * @return An empty string. */ String deleteMessage( String username, String password, int ordinal ) throws Throwable; /** The rest of the methods are omitted for brevity */ } Object o = XmlRpcProxy.createProxy( "www.mailtothefuture.com", "/RPC2", 80, new Class[] { mailToTheFuture.class } ); mailToTheFuture mttf = (mailToTheFuture) o; System.out.println( mttf.addMessage( "usr@mailtothefuture.com", "secret", aMessageTable ) ); |