> If we decide to go ahead and make an XmlRpcClient interface, > we either have to decide to use some NonImplementedException > framework for the features of the interface that > XmlRpcClientLiteImpl does not implement, or we need to > use an abstract class with the bare minimum for the root > interface and inherit XmlRpcClientWithMoreHttpFeatures from it. > > Are there other features XmlRpcClient provides now that > XmlRpcClientLite does not, like multithreading? > If so, these should be addressed as well.
I've been looking into this myself. I've finished integrating and testing the pre 1.2 release of Apache XML-RPC with parts of our own software (we use a home-grown HTTP implementation for various reasons). It works nicely with our C client. I now need to write a Java client to talk to our Java server and a C server. But, I need to be able to do XML-RPC as well as other HTTP PUT and GET request on the same connection to the server (ie. the same socket). So, I would like to rearrange the client classes so that XML-RPC encoding and decoding was separated from the HTTP send and receive. This should simplify client implementation for the three clients: Lite, Current, and Jakarta Commons. As I see it, there are the steps involved in making an XML-RPC request, in the case of a persistent HTTP connection: 1. Create and configure the default options for a HTTP connection. 2. Make the socket connection to the server. 3. Prepare an XML-RPC request (an actual ASCII byte stream) 4. Set any per-request HTTP options. Send the HTTP request (POST), headers and terminating newline. 5. Send the XML-RPC request byte stream. 6. Receive the HTTP response headers. 7. Receive the XML-RPC response. 8. Parse the XML-RPC response. 9. If the XML-RPC succeded: return the received object, otherwise: throw the received exception. Further requests on a persistent HTTP connection start at 3. I would like to extract code to perform step 3 into: XmlRpcClientRequestProcessor void encodeRequest(XmlRpcRequest req, String encoding, OutputStream out) throws XmlRpcClientException byte [] encodeRequestBytes(XmlRpcRequest req, String encoding) throws XmlRpcClientException And step 7 and 8 into: XmlRpcClientResponseProcessor (extends XmlRpc) // Step 7: Decode a response, return an Object or an Exception Object decodeResponse(InputStream in, int contentLength) throws XmlRpcClientException // Step 7 & 8: Decode a response. If the response is an exception, // throw it as an XmlRpcException, otherwise return it. Object handleResponse(InputStream in, int contentLength) throws XmlRpcException, XmlRpcClientException also, {decode|handle}RequestBytes(byte []) that take a byte[] instead of an InputStream. Then, based on a similar refactoring to the one performed on the server, have an abstract XmlRpcClient that manages a thread pool. The main entry points are the execute and executeAsync methods: Object execute(XmlRpcRequest request, XmlRpcClientContext context) throws XmlRpcClientContext, XmlRpcException this method gets a worker (or creates one), then calls the worker's execute method. It then returns the result in the current thread. void executeAsync(XmlRpcRequest request, XmlRpcClientContext context) throws XmlRpcClientContext, XmlRpcException this returns immediately after enqueuing the request. The response will be returned in a separate thread via the XmlRpcClientContext (see later). The XmlRpcClient also has an abstract factory method: XmlRpcClientWorker createWorker(). that returns an appropriately configured XmlRpcClientWorker. XmlRpcClientWorker has as its major method: Object execute(XmlRpcRequest request, XmlRpcClientContext context) throws XmlRpcClientException, XmlRpcException This method is responsible for doing steps 3-8 (with the help of the XmlRpcClient*Processor classes). The context object can be used by the worker in a multitude of ways to handle connection persistence, multiplexing, HTTP headers, encryption, drug dealing and the like. The XmlRpcClientContext object has at least the following methods: ? getHTTPHeaders() returns the HTTP headers for this request. This would probably return String [], maybe Vector? String getUsername() String getPassword() - possibly, probably just part of getHTTPHeaders AsyncCallback getCallback() returns the callback to respond to - null means discard the response. This is only called after the response has been received, but may be called before or after it has been decoded. Workers are free to cast the XmlRpcContext to some other class or interface that may define extra methods. The XmlRpcClientException is an exception to indicate that something went wrong in the processing of the XML-RPC as part of client processing. eg. an invalid encoding was specified, the specified host could not be contacted, etc. It has a Throwable getCause() method in the Java 1.4 style, with appropriate constructors. I am naturally willing to code this all up, test it and put it as a patch in bugzilla for general consumption. I'm 90% sure it can be done without too much breakage from the 1.1 API. I have time available to work on this now and in the near future. Let me know your opinions ;) Andrew.