|| Home  ||  Programming Guide  ||  Reference  ||  Class Hierarchy  ||  Index  || JRPCGen  || XML Extensions || Samples  ||

Distinct ONC RPC/XDR for Java - XML Extensions

1. Overview

In contrast to ONC RPC that has been designed for local networks, XML-RPC is a remote procedure call protocol that works over the Internet. An XML-RPC message is an HTTP-POST request. The body of the request is in XML. 

The XML Extensions of Distinct ONC RPC/XDR for Java (in the following referred as JRPC-XML) turn an ONC RPC server into a web service. They provide the following features:

  • XML-RPC clients and servers can be generated from ONC RPC (XDR)-Files.
  • Client and server-side APIs are unchanged (i.e. it enables an extremely simple transition from ONC RPC to XML RPC)
  • Each JRPC-XML server is accessible simultaneously via ONC RPC and XML-RPC.
  • Automatic generation of an XML-RPC-to-ONC-RPC-translator (i.e. direct integration of a legacy ONC RPC server into an XML-based infrastructure)

 JRPC-XML does this by extending the Distinct ONC RPC for Java with additional support for XML. These extensions include:

  • The ability of Jrpcgen to generate XML-RPC client stubs for a given XDR-description (.x-file) of an ONC RPC service and to extend the existing ONC RPC client and server classes with new methods that allows you to use them as XML-RPC services.
  • A new serialization scheme and a type mapping that allows to encode the RPC types not only to XDR transfer syntax but also to XML.

 There are a number of different scenarios in which JRPC-XML provides a unique tool for developing up-to-date Web-Services reusing existing code and skills:

Scenario 1: Exposing an ONC-RPC server to the Web

In many cases a required functionality is implemented for many years by a robust and well-tested ONC RPC server. The only new requirement is that this functionality should be exposed to the Internet, possibly to clients that are not yet known and possibly designed and deployed by other organizations. This reveals two problems: ONC RPC is a protocol that is inherently hard to be run on the Internet. XML-RPC would be a better protocol for this job. It fits perfectly in the Internet and client libraries are available for nearly any programming environment. JRPC-XML can expose an existing ONC RPC server to the Internet as an XML-RPC server with just 10 lines of java code.

Scenario 2: Easy replacement of ONC RPC by XML-RPC

If you have existing ONC RPC code that is mature and known to work, JRPC-XML allows you simply to replace the protocols without further porting effort.

Scenario 3: Smooth introduction of new XML-RPC-based clients

JRPC-XML also allows you to write new XML-RPC based client applications that interact with the same ONC RPC server that also services older ONC RPC clients.

Scenario 4: Easy development of new XML-RPC-based applications

Finally, JRPC-XML is just an easy way of writing XML-RPC client/server applications. While the main idea of XML-RPC is its simplicity, it lacks two major features that are important for any more larger client/server project: it has no interface definition language and, thus, no automatic generation of client and server stubs. This handicap becomes obvious as soon as more complex data-types are involved and hand-coding the subs turn out to be an error-prone and time-consuming task. Here JRPC-XML provides an easy solution, as it reuses the well-accepted XDR language for exactly this purpose. Moreover, with JRPC-XML you don't even need to know about the details of XML and XML-RPC to write an XML-RPC service. If you are used to writing ONC RPC applications with Java, you can use your skills to write an XML-RPC service with no additional effort.

 The following sections will explain the new Jrpcgen compiler options, and give a step-by-step illustration of the development of a Web-Service (and a client to it), and describe the new type-mapping.

2. New Jrpcgen Options

With the introduction of XML-RPC in JRPC-XML Jrpcgen now has two additional options:

-x   

Enables XML-RPC support. Without this option only standard ONC RPC stubs and types are generated. With this option set, Jrpcgen add the following features to the generated classes:
o A new class <interfacename>_xml.java is generated. It contains the client stub for the XML-RPC Web-Service.
o Each standard ONC RPC server stub now also implements the org.apache.xmlrpx.XmlRpcHandler interface, i.e. provides an implementation for its execute() method. I.e. it can be used directly for providing the server functionality not only over ONC RPC but also at the same time over XML-RPC.
o Each standard ONC RPC client stubs now also implements the org.apache.xmlrpx.XmlRpcHandler interface, i.e. provides an implementation for its execute() method. I.e. it can be used as a handler that receives an XML-RPC and calls out using ONC RPC.
o Each generated interface type class now implements the com.distinct.rpc.XMLRPCType interface, i.e. it provides an implementation for its obj_encode() and obj_decode() methods that convert the Java class to an XML-structure and vice versa.

-X <packagename>

Specifies the exact name of the xmlrpc-package. This enables you to select from the various available implementations. Default is "org.apache.xmlrpc" and it is a requirement that the package is functionally equivalent to this package. But there are other implementations available, like the well-known "helma.xmlrpc" that are derived from the same source and can be used instead.

3. Development of a Web-Service

Given the following simple XDR interface description "admin.x" of something like a rudimentary membership administration system, we now want to create a client and a server that use XML-RPC for communication. Note that the Search procedure in this sample illustrates two advanced features of Distinct ONC RPC for Java, namely the ability to handle multiple arguments in an RPC call and the attributes [in], [out], and [inout] that specify the direction in which the parameter is used for data transfer (both features are not present in many other implantations, but they are fully conforming to the standard protocol). These features are seamlessly supported also by JRPC-XML.

 enum stat {active, passive, dont_know};

 struct member
{
    string name<>;
    int born_in;
   
stat status;
};

struct member_list
{
       member m;
       member_list *next;
};

program MEMBER_SERV
{
    version MEMBER_SERV_VERSION
    {
              void Add(member) = 1;
              bool Search(string<> name, [out] member m) = 2;
              member_list List(void) = 3;
    } = 1;
} = 100000;

Compiling this file with

Java Jrpcgen -n -S -x -X helma.xmlrpc admin.x

results in the creation of eight new files: admin.java, adminServer.java, admin_xml.java, stat.java, member.java, member_list.java, Search_1_argument.java, and Search_1_return.java. The general usage of these files for creating an ONC RPC client/server application has been described before and has not changed. What is new is the file admin_xml.java and some additional methods in the other classes.

In order to write a XML-RPC client instead of an ONC RPC client there is one marginal modification required at the client:

The client object is now initialized like this:

 public static void main (String args[]) throws Exception
{
       try
       {
              admin_xml cl = new admin_xml("http://aubach:8080/");
              …..................
              .....................

The new class <interfacename>_xml.java provides the same RPC call interface to the client application as the standard client class <interfacename>.java, but it calls using the XML-RPC protocol. It is initialized via the URL (as String or URL type) plus optionally with a string, giving the handler name at the server. In the Java implementation of XML-RPC a method-name in a call usually consists of a pair handler.procedure. This allows one server to host and distinguish multiple, possibly unrelated RPC services at the same time. The handler name used by default is always the <interfacename>, "admin" in this sample.

The remainder of this client sample is straight forward and has no differences compared to an ONC RPC client application.

import java.net.*;
import helma.xmlrpc.*;
import java.util.Vector;
import com.distinct.rpc.*;

public class MemberClient
{
    public static void main (String args[]) throws Exception
    {
        try
        {
            admin_xml cl = new admin_xml("http://aubach:8080");
            member m1 = new member("Joe", 1965, new stat(stat.active));
            cl.Add_1(m1);
            member m2 = new member("Bill", 1973, new stat(stat.active));
            cl.Add_1(m2);
            member m3 = new member("Bob", 1942, new stat(stat.passive));
            cl.Add_1(m3);
            member m_ret = new member();
            if (cl.Search_1(args[0], m_ret))
                   System.err.println (m_ret.name+", born in "+m_ret.born_in);
            else
                   System.err.println ("can't find "+args[0]);
            member_list ml = cl.List_1();
       }
        catch (Exception x)
        {
            System.err.println (x);
       }
    }
}

For implementing an XML-RPC server, there are three different possibilities in JRPC-XML:

1) Built-in HTTP server

The XML-RPC library provides its own built-in HTTP server. This is not a general purpose web server, its only purpose is to handle XML-RPC requests. The HTTP server can be embedded in any Java application with a few simple lines:

WebServer webserver = new WebServer (server port);
webserver.addHandler ("examples", someHandler);

A special feature when using the built in Web server is that you can set the IP addresses of clients from which to accept or deny requests. This is done via the following methods:

webserver.setParanoid (true); // deny all clients
webserver.acceptClient ("192.168.0.*"); // allow local access
webserver.denyClient ("192.168.0.3"); // except for this one ...
webserver.setParanoid (false); // disable client filter

If the client filter is activated, entries to the deny list always override those in the accept list. Thus, webserver.denyClient ("*.*.*.*") would completely disable the web server.

For the sample from above, a server that uses the built-in HTTP server can be initialized like this:  

import com.distinct.rpc.*;
import helma.xmlrpc.*;

public class MemberServer extends adminServer
{
    member_list my_list;
    public static void main (String args[])
    {
        XmlRpc.setKeepAlive (true);
        try
        {
            WebServer webserver = new WebServer (8080);
            MemberServer serv = new MemberServer(false);
  
         webserver.addHandler ("admin", serv);
  
         System.err.println ("started web server on port 8080");
  
     }
        catch (Exception x)
        {
  
         System.err.println ("Error creating web server: "+x);
        }
}

//     Constructor that creates the RPC server

public MemberServer(boolean doONC) throws RPCError
{
   super(0, doONC, doONC, true);
}

........
........

The code first initializes a generic XML-RPC WebServer object, then a MemberServer (the user-derived RPC server that implements the "admin" interface), and finally it adds the RPC to the list of handlers for the WebServer. This can be done, as each server class now implements the XmlRpcHandler interface and provides an execute() method that decodes and dispatches the incoming XML-RPC calls to the remote procedure call implementations.

The sample implements a constructor with one Boolean that selects whether an ONC RPC server should be started. If so, the same server object handles both RPCs coming from XML-RPC and those coming over the classic ONC RPC protocol (using the setMultithreaded() method of each server it can be controlled whether these calls should be handled concurrently or should be serialized before entering the remote procedure implementation). The full example server is included in the sample directory.

2) Servlet

An JRPC-XML component can be embedded into any Web server framework that supports the servlet API. The full code that is required to run the MemberServer sample service from a servlet is given below:

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import com.distinct.rpc.RPCError;
import helma.xmlrpc.*;

public class MemberServlet extends HttpServlet
{
    public XmlRpcServer xmlrpc;

    public void init(ServletConfig config) throws ServletException
    {
        try
        {
            MemberServer serv = new MemberServer(false);
   
         xmlrpc = new XmlRpcServer ();
   
         xmlrpc.addHandler ("admin", serv);
   
     }
   
     catch (RPCError e)
        {
            throw new ServletException(e);
        }
    }

    public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
    {
        byte[] result = xmlrpc.execute (req.getInputStream ());
   
     res.setContentType("text/xml");
   
     res.setContentLength (result.length);
       
OutputStream output = res.getOutputStream();
       
output.write (result);
       
output.flush ();
    }
}

 

 

Similar to the constructor in the previous stand-alone version in this sample the server is initialized in the init() method and than added as a handler to the XmlRpcServer object. The code in the doPost() method is pretty generic. It simply forwards the input to the XmlRpcServer object's execute() method and writes its output to the servlet's OutputStream.

3) Using Proxies

Often it is not the ONC RPC server itself, that should be implemented by a stand-alone server or a servlet, but a proxy or a gateway to the actual server. This proxy takes XML-RPC requests, translates them to ONC RPC protocol and forwards it to the real server. The reply from the server is then processed in a similar way in the other direction. The server itself might be implemented using Distinct ONC RPC for Java, but it also might be a legacy ONC RPC server written in an arbitrary language running on arbitrary operating system.

This typical case is supported by JRPC-XML in quite the same manner as shown in the previous servlet sample. The only difference is that the handler that is added to the XmlRpcServer object is not an ONC RPC server but a client, as shown in the following sample:

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.net.*;
import com.distinct.rpc.RPCError;
import helma.xmlrpc.*;

public class MemberProxyServlet extends HttpServlet
{
    public XmlRpcServer xmlrpc;

    public void init(ServletConfig config) throws ServletException
    {
       
try
        {
            admin cl = new admin(InetAddress.getByName("aubach"), true);
            xmlrpc = new XmlRpcServer ();
            xmlrpc.addHandler ("admin", cl);
        }
   
     catch (RPCError e)
        {
            throw new ServletException(e);
        }
   
     catch (UnknownHostException e)
        {
            throw new ServletException(e);
        }
}

public void doPost(HttpServletRequest req, HttpServletResponse res)
...........
...........

Of course, the same approach is possible also for a stand-alone server. Again, simply replace the ONC RPC server object by a client and add it as a handler to the XmlRpcServer.

4. Type Mapping

XML-RPC has a very simple type-system. The XML-RPC specification says:

Payload format

The payload is in XML, a single <methodCall> structure.

The <methodCall> must contain a <methodName> sub-item, a string, containing the name of the method to be called. The string may only contain identifier characters, upper and lower-case A-Z, the numeric characters, 0-9, underscore, dot, colon and slash. It's entirely up to the server to decide how to interpret the characters in a methodName.

For example, the methodName could be the name of a file containing a script that executes on an incoming request. It could be the name of a cell in a database table. Or it could be a path to a file contained within a hierarchy of folders and files.

 

If the procedure call has parameters, the <methodCall> must contain a <params> sub-item. The <params> sub-item can contain any number of <param>s, each of which has a <value>.

Scalar <value>s

<value>s can be scalars, type is indicated by nesting the value inside one of the tags listed in this table:

   

Tag   Type Example
<i4> or <int>   four-byte signed integer   -12  
<boolean>   0 (false) or 1 (true)   1  
<string>   ASCII string   hello world  
<double>   double-precision signed floating point number   -12.214  
<dateTime.iso8601>   date/time   19980717T14:08:55  
<base64>   base64-encoded binary  eW91IGNhbid0IHJlYWQgdGhpcyE=  

If no type is indicated, the type is string.

<struct>s

A value can also be of type <struct>.  A <struct> contains <member>s and each <member> contains a <name> and a <value>. Here's an example of a two-element <struct>:  

<struct>
    <member>
        <name>lowerBound</name>
        <value><i4>18</i4></value>
    </member>
    <member>
        <name>upperBound</name>
        <value><i4>139</i4></value>
    </member>
</struct>  

 <struct>s can be recursive, any <value> may contain a <struct> or any other type, including an <array>, described below.

<array>s

A value can also be of type <array>.  An <array> contains a single <data> element, which can contain any number of <value>s. Here's an example of a four-element array:  

<array>
    <data>  
        <value><i4>12</i4></value>  
        <value><string>Egypt</string></value>  
        <value><boolean>0</boolean></value>  
        <value><i4>-31</i4></value>  
    </data>  
</array>  

<array> elements do not have names. You can mix types as the example above illustrates. <arrays>s can be recursive, any value may contain an <array> or any other type, including a <struct>, described above.

XDR Type

XML-RPC Transfer

Java Type

(unsigned) int

<i4>

int

(unsigned) long

<i4>

int

(unsigned) short

<i4>

short

(unsigned) char

<i4>

char

(unsigned) hyper

<struct> of two <i4>s
(high and low)

long

Float

<double>

float

Double

<double>

double

Bool

<boolean>

Boolean

String

<string>

String

Opaque

<base64>

byte array

fixed length array

<array>

Java array

variable length array

<array>

Java array

optional data (pointer-like *x)

optional filed

reference to an object of class x. If x is a basic type a special wrapper class XDRx is used instead

enum x

<i4>

class x with member variable int value and one constant int per enum constant

struct x

<struct> with components for each struct member.

class x with member variables for each struct member

union x

<struct> with components for each union member including discriminant.

class x with member variables for each union member including discriminant. No overlaying of members is supported (neither for type conversion nor for saving space)

typedef x y

Replacement of types

class x with member variable “value” of the redefined type y

Compared with ONC RPC's XDR it is apparent that XML-RPC has a shortage of all "short" integer types, all sorts of unsigned values, hypers, single precision floats and unions. Most of these limitations do already apply when mapping the XDR-types to Java (e.g. no unsigned values) and encoding the "smaller" type in the "bigger" one does (e.g. a char in a 4-byte integer) not cause a problem as long as the stubs decode it correctly. The only new severe problem arises with hypers. In order not to lose the complete upper four bytes we decode it into a <struct> with two <i4> members that represent the "high" and the "low" part of the value.

Given this mapping the usage of XML-RPC as the transport instead of ONC RPC's XDR doesn't change anything in the usage of the types from the Java source. The only visible difference is that all types (other than the basic types) that are used in RPCs now also implement the com.distinct.rpc.XMLRPCType interface (in addition to inheriting from com.distinct.rpc.XDRType). The interface com.distinct.rpc.XMLRPCType has two methods:

public interface XMLRPCType 
{

/**
* Decodes an object of this class from the XMLRPC standard format.
* @exception RPCError When the call fails for any reason.
*/
   
public void obj_decode(Object obj) throws RPCError;

/**
* Encodes an object of this class to the XMLRPC standard format.
* @exception RPCError When the call fails for any reason.
*/
   
public Object obj_encode();
}

Similar to xdr_encode() and xdr_decode() are controlling the serialization of a type to and from and XDRStream, obj_encode() and obj_decode() are controlling the serialization to XML. By using the Apache XML-RPC implementation for Java as a basis for the XML-coding, it is enough to provide the right Java object for encoding it correctly to a stream. In the other direction the Apache XML-RPC implementation directly provides the objects parsed from an XML-input. This mapping of Java objects to XML-RPC types is described in the following table:

 

XML-RPC data type

Types in  xdr_encode()/xdr_decode()

<i4> or <int>

java.lang.Integer

<boolean>

java.lang.Boolean

<string>

java.lang.String

<double>

java.lang.Double

<dateTime.iso8601>

java.util.Date

<struct>

java.util.Hashtable

<array>

java.util.Vector

<base64>

byte[ ]

 

This means obj_encode() and obj_decode() basically have to map basic types to their wrapper objects (i.e. int -> java.lang.Integer), structs and unions to java.util.Hashtable (of "name"- "value" pairs for each member), and arrays to java.util.Vector.

A sample of the encoding and decoding of a more complex RPC interface type (as created by Jrpcgen) is given below for the class member.java as defined by the example:

/**
 * Encodes an object of class member to the XML-RPC standard representation.
 * @return    The object that contains the encoded variable.
 */

public Object obj_encode()
{
    Hashtable h__oncint = new Hashtable();
    h__oncint.put("name", name);
    h__oncint.put("born_in", XDRObj.obj_encode_int(born_in));
    h__oncint.put("status", status.obj_encode());
    return h__oncint;
}

/**
 * Decodes an object of class member from the XML-RPC standard representation.
 * @param     obj The object that contains the encoded variable.
 * @exception RPCError When the calls fails for any reason.
 */

public void obj_decode(Object obj) throws RPCError
{
    try 
    {
        Hashtable h__oncint = (Hashtable)obj;
        name = ((String)h__oncint.get("name"));
       
born_in = XDRObj.obj_decode_int(h__oncint.get("born_in"));
   
     status = new stat();
       
status.obj_decode(h__oncint.get("status"));
   
}
    catch (Exception e) 
    {
       
throw new RPCDecodeError("Wrong Parameter Type");
    }
}

 

As a sample of the XML data-structures generated by JRPC-XML, here the request of a Search_1() call from the membership administration system in the previous section is given. Please note, that this is already an advanced example, as the second parameter of Search_1() is defined in the .x-file to be an [out] parameter. This is the reason why JRPC-XML handles is in the reply rather than in the request message:

member m_ret = new member();
boolean b = cl.Search_1(args[0], m_ret));

results in a call:

 

  <?xml version="1.0" encoding="ISO-8859-1" ?>

  <methodCall>

    <methodName>admin.Search_1</methodName>

          <params>

              <param>

                  <value>Bob</value>

              </param>

          </params>

  </methodCall>  

 

The reply looks like this:

<?xml version="1.0" encoding="ISO-8859-1" ?>

  <methodResponse>

  <params>

  <param>

  <value>

  <struct>

  <member>

  <name>ret2</name>

  <value>

  <struct>

  <member>

  <name>born_in</name>

  <value>

  <int>1942</int>

  </value>

  </member>

   <member>

  <name>status</name>

  <value>

  <int>1</int>

  </value>

  </member>

 <member>

  <name>name</name>

  <value>Bob</value>

  </member>

  </struct>

  </value>

  </member>

  <member>

  <name>ret1</name>

  <value>

<boolean>1</boolean>

</value>

  </member>

  </struct>

  </value>

  </param>

  </params>

  </methodResponse>

The Search_1() call actually replies a structure consisting of two components: the real boolean return value (ret1) and the member output parameter (ret1). This conversion of output parameters to return structs is already a feature of Distinct ONC RPC for Java and it is supported by the XML-RPC support as well.


This product includes software developed by the Apache Software Foundation (http://www.apache.org/).

Copyright © 1997 - 2002 by Distinct Corporation
Last Modified: May 28, 2002.