ONC/RPC for Java package

Sun's ONC/RPC Remote Procedure Call mechanism.

This package implements Sun's ONC/RPC Remote Procedure Call specification (see RFC 1831, RFC 1832, RFC 1833).

Functionality currently supported:

- RPC calls over TCP/IP as well as UDP/IP.
- RPC client functionality.
- RPC server functionality (see package org.acplt.oncrpc.server).
- Querying the ONC/RPC portmapper.
- jrpcgen-utility for converting x-files into Java classes.
- Support for authentication types `AUTH_NONE`, `AUTH_UNIX` and `AUTH_SHORT` on both the client and the server side.

To manually convert x-files into serializable Java classes, first map the primitive rpcgen data types (and thus the data types described in RFC 1832 to some extend) onto Java's primitive data types. The following table should help you doing this.

rpcgen / RFC 1832 | Java
`char` (used as character) | 8 bits / platform dependent | `byte` | 8 bits | `xdr.xdrEncodeByte(byte)` `byte = xdr.xdrDecodeByte()`
`unsigned char` (used as character) | 8 bits / platform dependent | `byte` | 8 bits | `xdr.xdrEncodeByte(byte)` `byte = xdr.xdrDecodeByte()`
`char` (used as 8 bit quantitiy) | 8 bits / platform dependent | `byte` | 8 bits | `xdr.xdrEncodeByte(byte)` `byte = xdr.xdrDecodeByte()`
`unsigned char` (used as 8 bit quantitiy) | 8 bits / platform dependent | `byte` | 8 bits | `xdr.xdrEncodeByte(byte)` `byte = xdr.xdrDecodeByte()`
`short` | platform dependent | `short` | 16 bits | `xdr.xdrEncodeShort(short)` `short = xdr.xdrDecodeShort()`
`unsigned short` | platform dependent | `short` | 16 bits | `xdr.xdrEncodeShort(short)` `short = xdr.xdrDecodeShort()`
`int` | 32 bits | `int` | 32 bits | `xdr.xdrEncodeInt(int)` `int = xdr.xdrDecodeInt()`
`unsigned int` | 32 bits | `int` | 32 bits | `xdr.xdrEncodeInt(int)` `int = xdr.xdrDecodeInt()`
`hyper` | 64 bits | `long` | 64 bits | `xdr.xdrEncodeLong(long)` `long = xdr.xdrDecodeLong()`
`unsigned hyper` | 64 bits | `long` | 64 bits | `xdr.xdrEncodeLong(long)` `long = xdr.xdrDecodeLong()`
`enum` | 32 bits | `int` | 32 bits | `xdr.xdrEncodeInt(int)` `int = xdr.xdrDecodeInt()`
`bool` |  | `boolean` |  | `xdr.xdrEncodeBoolean(boolean)` `boolean = xdr.xdrDecodeBoolean()`
`float` | 32 bits | `float` | 32 bits | `xdr.xdrEncodeFloat(float)` `float = xdr.xdrDecodeFloat()`
`double` | 64 bits | `double` | 64 bits | `xdr.xdrEncodeDouble(double)` `double = xdr.xdrDecodeDouble()`
`quadruple` | 128 bits | `n/a` |  |
`opaque[n]` | exactly n octets | `byte[]` | n bytes | `xdr.xdrEncodeOpaque(byte[])` `byte[] = xdr.xdrDecodeOpaque(n)`
`opaque<n>` | 4 bytes size followed by at most n octets | `byte[]` | ?? bytes | `xdr.xdrEncodeDynamicOpaque(byte[])` `byte[] = xdr.xdrDecodeDynamicOpaque()`
`opaque<>` | 4 bytes size followed by ?? octets | `byte[]` | ?? bytes | `xdr.xdrEncodeDynamicOpaque(byte[])` `byte[] = xdr.xdrDecodeDynamicOpaque()`
`string<n>` | 4 bytes size followed by at most n octets | `String` | ?? bytes | `xdr.xdrEncodeString(String)` `String = xdr.xdrDecodeString()`
`string<>` | 4 bytes size followed by ?? octets | `String` | ?? bytes | `xdr.xdrEncodeString(String)` `String = xdr.xdrDecodeString()`
`void` | 0 bits | `XdrVoid` |  | `xdrvoid.xdrEncode(xdr)` `xdrvoid.xdrDecode(xdr)`

The Remote Tea library also provides method to (de-) serialize vector data types of variable length, as shown below. To (de-) derialize fixed-size vectors, use the `...FixedVector()` variants and supply the protocol-defined size of the vector -- see the next table but one.

rpcgen / RFC 1832 | Java
`opaque<>` | 4 bytes size followed by ?? octets | `byte[]` | `xdr.xdrEncodeDynamicOpaque(byte[])` `byte[] = xdr.xdrDecodeDynamicOpaque()`
`char<>` `char short<>` | 4 bytes size followed by ?? chars | `byte[]` | `xdr.xdrEncodeByteVector(byte[])` `byte[] = xdr.xdrDecodeByteVector()`
`short<>` `unsigned short<>` | 4 bytes size followed by ?? shorts | `short[]` | `xdr.xdrEncodeShortVector(short[])` `short[] = xdr.xdrDecodeShortVector()`
`int<>` `unsigned int<>` | 4 bytes size followed by ?? ints | `int[]` | `xdr.xdrEncodeIntVector(int[])` `int[] = xdr.xdrDecodeIntVector()`
`long<>` `unsigned long<>` | 4 bytes size followed by ?? longs | `int[]` | `xdr.xdrEncodeIntVector(int[])` `int[] = xdr.xdrDecodeIntVector()`
`hyper<>` `unsigned hyper<>` | 4 bytes size followed by ?? hypers | `long[]` | `xdr.xdrEncodeLongVector(long[])` `long[] = xdr.xdrDecodeLongVector()`
`enum<>` | 4 bytes size followed by ?? enums | `int[]` | `xdr.xdrEncodeIntVector(int[])` `int[] = xdr.xdrDecodeIntVector()`
`bool<>` | 4 bytes size followed by ?? bools | `boolean[]` | `xdr.xdrEncodeBooleanVector(boolean[])` `boolean[] = xdr.xdrDecodeBooleanVector()`
`float<>` | 4 bytes size followed by ?? floats | `float[]` | `xdr.xdrEncodeFloatVector(float[])` `float[] = xdr.xdrDecodeFloatVector()`
`double<>` | 4 bytes size followed by ?? doubles | `double[]` | `xdr.xdrEncodeDoubleVector(double[])` `double[] = xdr.xdrDecodeDoubleVector()`

And now for (de-) serializing fixed-size vectors:

rpcgen / RFC 1832 | Java
`char[n]` `char short[n]` | 4 bytes size followed by n chars | `byte[]` | `xdr.xdrEncodeByteFixedVector(byte[], int n)` `byte[] = xdr.xdrDecodeByteFixedVector(int n)`
`short[n]` `unsigned short[n]` | 4 bytes size followed by n shorts | `short[]` | `xdr.xdrEncodeShortFixedVector(short[], int n)` `short[] = xdr.xdrDecodeShortFixedVector(int n)`
`int[n]` `unsigned int[n]` | 4 bytes size followed by n ints | `int[]` | `xdr.xdrEncodeIntFixedVector(int[], int n)` `int[] = xdr.xdrDecodeIntFixedVector(int n)`
`long[n]` `unsigned long[n]` | 4 bytes size followed by n longs | `int[]` | `xdr.xdrEncodeIntFixedVector(int[], int n)` `int[] = xdr.xdrDecodeIntFixedVector(int n)`
`hyper[n]` `unsigned hyper[n]` | 4 bytes size followed by n hypers | `long[]` | `xdr.xdrEncodeLongFixedVector(long[], int n)` `long[] = xdr.xdrDecodeLongFixedVector(int n)`
`enum[n]` | 4 bytes size followed by n enums | `int[]` | `xdr.xdrEncodeIntFixedVector(int[], int n)` `int[] = xdr.xdrDecodeIntFixedVector(int n)`
`bool[n]` | 4 bytes size followed by n bools | `boolean[]` | `xdr.xdrEncodeBooleanFixedVector(boolean[], int n)` `boolean[] = xdr.xdrDecodeBooleanFixedVector(int n)`
`float[n]` | 4 bytes size followed by n floats | `float[]` | `xdr.xdrEncodeFloatFixedVector(float[], int n)` `float[] = xdr.xdrDecodeFloatFixedVector(int n)`
`double[n]` | 4 bytes size followed by n doubles | `double[]` | `xdr.xdrEncodeDoubleFixedVector(double[], int n)` `double[] = xdr.xdrDecodeDoubleFixedVector(int n)`

For every structure you encounter in a .x file, write a Java class, which implements the `XdrAble` interface. For instance, taking this snippet from a rpcgen source file...

    struct foo {
        int bar;
        float baz;
        struct foo *next;
    };

...`struct foo` is translated into Java ONC/RPC babble as:

    class foo implements XdrAble {
       // members of foo structure
       public int bar;
       public float baz;
       public foo next;

       // serialize / encode foo
        public void xdrEncode(XdrEncodingStream xdr)
               throws OncRpcException, IOException {
            xdr.xdrEncodeInt(bar);
            xdr.xdrEncodeFloat(baz);
            if ( next == null ) {
                xdr.xdrEncodeBoolean(false);
            } else {
                xdr.xdrEncodeBoolean(true);
                next.xdrEncode(xdr);
            }
        }

        // deserialize / decode foo
        public void xdrDecode(XdrDecodingStream xdr)
               throws OncRpcException, IOException {
            bar = xdr.xdrDecodeInt();
            baz = xdr.xdrDecodeFloat();
            if ( !xdr.xdrDecodeBoolean() ) {
                next = null;
            } else {
                next = new foo();
                next.xdrDecode(xdr);
            }
        }
    }

The previous example also shows how to deal with pointers (eeek!) in .x files. The are simply transformed into references -- quite a difference, really.

Tanslating a descriminated union is typically done the simple and silly way. Silly, because unions can not really be represented in the Java language. You need to transform them into a class (structure) instead, and this will lead to a rather ugly form.

    union foo switch (FOOTYPE type) {
        case BAR_CLASS:
            bar u_bar;
        case BAZ_CLASS:
            baz u_baz;
    }

should become

    class foo implements XdrAble {
        // descriminant value
        public int type;
        // arm declarations
        public bar u_bar;
        public baz u_baz;

        // descriminant-arm values
        public static int BAR_CLASS = 1;
        public static int BAZ_CLASS = 2;

        // serialize / encode foo
        public void xdrEncode(XdrEncodingStream xdr)
               throws OncRpcException, IOException {
            xdr.xdrEncodeInt(type);
            switch ( type ) {
            case BAR_CLASS:
                u_bar.xdrEncode(xdr); break;
            case BAZ_CLASS:
                u_baz.xdrEncode(xdr); break;
            }
        }

        // deserialize / decode foo
        public void xdrDecode(XdrDecodingStream xdr)
               throws OncRpcException, IOException {
            type = xdr.xdrDecodeInt();
            switch ( type ) {
            case BAR_CLASS:
                u_bar = new bar(); u_bar.xdrDecode(xdr); break;
            case BAZ_CLASS:
                u_baz = new bar(); u_bar.xdrDecode(xdr); break;
            }
        }
    }

You can also take advantage of polymorphism when translating descriminated unions into a class hierarchy. You should then write an abstract base class with only one static member function capable of constructing the appropriate instance from the XDR stream. The tricky part about this is getting the descriminant value into the stream or out of it without having to duplicate the decoding code into the constructor. With the skeleton below you should be able to do this easily, but you should remember to never recycle an object reference and never deserialize the state of the object a second time from a XDR stream!

    abstract class foobase implements XdrAble {
        // construct a bar, baz,... class from XDR stream
        public static foobase xdrNew(XdrDecodingStream xdr)
               throws OncRpcException, IOException {
            foobase obj = null;
            switch ( xdr.xdrDecodeInt() ) {
            case BAR_CLASS:
                obj = new bar(); break;
            case BAZ_CLASS:
                obj = new baz(); break;
            }
            obj.xdrDecode(xdr);
            return obj;
        }

        public abstract void xdrEncode(XdrEncodingStream xdr)
               throws OncRpcException, IOException;
        public abstract void xdrDecode(XdrDecodingStream xdr)
               throws OncRpcException, IOException;

        // descriminant values
        public static int BAR_CLASS = 1;
        public static int BAZ_CLASS = 2;
    }

    class bar extends foobase {
        public void xdrEncode(XdrEncodingStream xdr)
               throws OncRpcException, IOException {
            // encode your members here...
            // dont forget to encode the descriminant value
            xdr.xdrEncodeInt(BAR_CLASS);
            xdr.xdrEncodeFloat(noah);
        }
        public void xdrDecode(XdrDecodingStream xdr)
               throws OncRpcException, IOException {
            // decode your members here...
            // but *NEVER* decode the descriminant value
            noah = xdr.xdrDecodeFloat();
        }
    }

This package is part of the ACPLT/KS ACPLTea Java Library package. (c) 1999, 2006 Harald Albrecht. (c) 1999, 2002 Lehrstuhl für Prozeßleittechnik, Aachen University of Technology, Germany. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program (see the file LICENSE.txt for more details); if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.