RoX
Anyone using this library probably has rox in their headers ...

Overview

RoX (RPC over XML) is a complete XML-RPC implementation for Java 1.4 Java 1.5 and higher.

RoX has passed the XML-RPC validation suite using this server implementation.

Its aims are:

  • To provide a fully functional implementation with as few external dependencies as possible.
  • To build on the Java NIO libraries in a bid to provide the best scalability the platform can offer.
  • To simplify value marshalling by marshalling between plain old Java objects (POJO's) and XML-RPC <struct> values using reflection.
  • To simplify the client side by building on Java's dynamic proxies to provide relatively seamless remote XML-RPC method invocation.
  • To simplify the server side by supporting dynamic method discovery and invocation using reflection.

Some of the original aims which no longer apply (but influenced some my design decisions) include:

  • To target a Java 1.4 runtime. I've finally given up on this and dragged Rox into the 21st century (Java 1.5 ooh, ahh).
  • To provide an implementation sufficiently extensible to support other wire protocols. Originally I intende to support YAML-RPC and JSON-RPC but after taking a closer look and reflecting I've decided that they're all of sufficiently different ilk that it doesn't entirely make sense and would just take away from what Rox is: an XML-RPC implementation. That said, I have included support for a simple CGI based RPC format and part of that work meant making HTTP method handlers a little more pluggable. So if you feel driven ...
  • To provide support for XML-RPC over an SSL channel on Java 1.4. After putting it off for over a year I finally sat down and gave SSLEngine the time it needed to get it working under NIO. This was a large part of the motivation for moving to 1.5. That, and the fact that I didn't feel like maintaining two diverging code-bases.

Motivation

RoX was motivated largely by frustration. After using the Apache XML-RPC implementation and growing increasingly frustrated by the poor API design, the complete lack of documentation and the feeling that a much more seamless fit with the Java language was not only possible, but relatively simple, I made the decision to write a "replacement".

I considered getting involved with that particular project but the API design changes I felt were necessary would have been pretty extreme, and I wanted to avoid the dependency soup that seems to clutter the Jakarta projects these days.

I also wanted to be free to target newer versions of Java without backwards compatibility constraints, primarily so I could make use of the NIO libraries.

But it wasn't only about what existing solutions couldn't provide. I was also motivated in part by the challenge of an interesting project that seemed within the reach of a single programmer in a short period of time. It seemed like fun and, more importantly, it seemed reasonably contained.

Limitations

RoX has a few limitations in it's current incarnation. Specifically

  • The following encodings are not yet supported (and probably won't be until they're asked for)
    • EBCDIC
    • UTF-7
    • UCS-4 in little-endian octet order
    • UCS-4 in big-endian octet order
    • UCS-4 in "2134" octet order
    • UCS-4 in "3412" octet order
  • Although HTTPS is supported using the SSLEngine class it may not scale to large numbers of new connections. RoX's connection pooling should help a bit here but in the interest of full disclosure this aspect of RoX has not been tested at scale (yet).

Building RoX from source

Building from source is pretty straightforward. The only wrinkle is likely to show up on a Windows platform when junit.jar isn't available in the classpath Ant uses when building the source. The easiest (but not really the cleanest) solution is to copy the missing jar into the Ant lib directory.

Beyond that it should just be a case of unzipping the source distributable and running ant from within the project directory. Easy peasy.

Rox targets Java 1.5. Earlier releases would run (but not build) on Java 1.4 but this is no longer the case as of version 1.1.

Using earlier versions of RoX on J2SE 1.4

Versions pre-dating 1.1 can be used on J2SE 1.4. At present, all testcases pass on J2SE 1.4.2 (release 11). However, you'll need two additional JAR files (packaged with the Rox binary and source zip files):

  • sax2.jar: The SAX 2.0 Java bindings, available from saxproject.org
  • dom.jar: The DOM Level 3 Java bindings, available from w3.org (although only in source form and you'll need to do a bit of hunting around on the site)

To convince yourself of this you'll need to build Rox using a 1.5 compatible JDK and then run the junit ant task under 1.4. This ensures that all of the code is built upfront, satisfying the junit task's dependencies (thereby avoiding attempting to build the source under 1.4).

The only caveat regarding running on 1.4 is a performance related issue. By default Rox uses an internal SAX parser. This parser is not fully functional but should cope with the 90% case. If you run into problems you can switch Rox over to a DOM parser (in which case you'll need the NanoXML library included with the Rox distribution. Alternatively you can configure an alternative SAX Parser (Rox honours the javax.xml.parsers.SAXParserFactory system property). However, on 1.4 the SAX API does not support resetting a parser which means Rox can't pool parser implementations. This will result in a performance loss (I haven't measured how much of a loss yet) but should not impair Rox's functionality in any way.

Although it originally targeted Java 1.4, Rox does correctly support 1.5 specific features like Enum types and generics. Enums are marshalled using their string literal name (as returned by java.lang.Enum.name()) and are unmarshalled using this name (more specifically, using java.lang.Enum.valueOf()). Generics are handled just like non-generic collections. This is the upside of Sun's decision to implement generics using type erasure. The downside is that Rox can't validate that the target field is parameterized correctly during unmarshalling (type erasure means just that, type information is 'erased' at compile time) and will happily unmarshal a list of string values into a field declared as java.util.List<Integer>. When you try to access it later on you'll almost certainly hit a ClassCastException. But assuming you do match your types up correctly Rox will correctly populate fields that use a parameterized type.