From: Doug O. <do...@pl...> - 2004-02-11 19:41:48
|
Greger Ohlson writes: > You can also send me the patches you've made, and I'll introduce > them in the code base. OK, here are some diffs for what I've been working on (at the bottom of this message). You should be able to feed them directly to `patch'. Summary: I'm implementing Jabber-RPC, which uses the same message syntax as XML-RPC but uses XMPP as its transport layer instead of HTTP. http://www.jabber.org/jeps/jep-0009.html There are two main problems with the Marquee XML-RPC library that prevent me from using it as-is: 1. XmlRpcClient and XmlRpcProxy are hardcoded to connect to an HTTP server, either using a URL object or a host, port, and path. Instead I need to send the message to a Jabber user ID (JID) using an XMPP connection to a Jabber server. 2. XmlRpcClient and XmlRpcDispatcher are hardcoded to prefix the XML-RPC message with "<?xml ... ?>", since the message is meant to be a complete XML document inside the HTTP post and reply. However, in Jabber-RPC the message is part of a larger XML document (the XMPP stream) and it is an error to include the "<?xml ... ?>" element. Fortunately, the first problem was easy to address, because most of the HTTP-related code is factored out and hidden behind the XmlRpcClientConnectionFactory interface. I just added a new public constructor to XmlRpcClient that takes an XmlRpcClientConnectionFactory as an argument, so that I can provide my own instance that uses XMPP instead of HTTP. I also added a new createProxy method to XmlRpcProxy that takes an XmlRpcClient object. The second problem was not too difficult to fix either, although my solution may not be the most elegant. I just added optional boolean arguments requestIsXmlDocument and responseIsXmlDocument to the XmlRpcClient constructor and the dispatch methods on XmlRpcServer and XmlRpcDispatcher, respectively, which default to true. A better solution might be to add a method to the XmlRpcClientConnection interface that determines whether messages sent using that connection should be encoded as complete XML documents or not, but I was hesitant about adding methods to an interface. Anyway, feel free to rewrite any of these changes, or to improve the comments or whatever. Let me know if you have any questions. Also, let me know if I should be sending these diffs directly to you, or if I should continue sending them to the developers list also. --...@pl... diffs start here: Index: marquee/xmlrpc/XmlRpcClient.java =================================================================== RCS file: /cvsroot/xmlrpc/xmlrpc/source/marquee/xmlrpc/XmlRpcClient.java,v retrieving revision 1.13 diff -c -r1.13 XmlRpcClient.java *** marquee/xmlrpc/XmlRpcClient.java 18 Sep 2003 14:24:14 -0000 1.13 --- marquee/xmlrpc/XmlRpcClient.java 11 Feb 2004 19:14:19 -0000 *************** *** 77,83 **** final String host, final int port, final String path) { ! connectionFactory = new SocketConnectionFactory(host, port, path); } /** --- 77,83 ---- final String host, final int port, final String path) { ! this(new SocketConnectionFactory(host, port, path), true); } /** *************** *** 88,96 **** */ public XmlRpcClient(final URL url) { ! connectionFactory = new URLConnectionFactory(url); } /** * Determines if the most previously XML-RPC response contained a * fault struct, in which case the return value of the most previous --- 88,114 ---- */ public XmlRpcClient(final URL url) { ! this(new URLConnectionFactory(url), true); } + + /** + * Creates a new client with the ability to send XML-RPC messages + * across connections created by the given connection factory. + * + * @param factory the connection factory used to create connections + * @param requestIsXmlDocument should the XML-RPC request be a + * complete XML document? + * + */ + public XmlRpcClient(final XmlRpcClientConnectionFactory factory, + boolean requestIsXmlDocument) + { + connectionFactory = factory; + this.requestIsXmlDocument = requestIsXmlDocument; + } + + /** * Determines if the most previously XML-RPC response contained a * fault struct, in which case the return value of the most previous *************** *** 279,285 **** private void beginCall(String method) { xmlBuffer.setLength(0); ! xmlBuffer.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>").append("<methodCall><methodName>") //$NON-NLS-1$ .append(method).append("</methodName><params>"); //$NON-NLS-1$ isFaultResponse = false; // Until we're proven otherwise. --- 297,305 ---- private void beginCall(String method) { xmlBuffer.setLength(0); ! if (requestIsXmlDocument) ! xmlBuffer.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>"); ! xmlBuffer.append("<methodCall><methodName>") //$NON-NLS-1$ .append(method).append("</methodName><params>"); //$NON-NLS-1$ isFaultResponse = false; // Until we're proven otherwise. *************** *** 429,434 **** --- 449,457 ---- /** The factory that creates connections to the server */ private XmlRpcClientConnectionFactory connectionFactory; + + /** Should the XML-RPC request be a complete XML document? */ + private boolean requestIsXmlDocument; /** The connection we use to talk to the server */ private XmlRpcClientConnection connection; Index: marquee/xmlrpc/XmlRpcDispatcher.java =================================================================== RCS file: /cvsroot/xmlrpc/xmlrpc/source/marquee/xmlrpc/XmlRpcDispatcher.java,v retrieving revision 1.13 diff -c -r1.13 XmlRpcDispatcher.java *** marquee/xmlrpc/XmlRpcDispatcher.java 11 Feb 2004 11:32:43 -0000 1.13 --- marquee/xmlrpc/XmlRpcDispatcher.java 11 Feb 2004 19:14:19 -0000 *************** *** 98,103 **** --- 98,125 ---- dispatchers.push(this); } + /** + * Inbound XML-RPC messages to a server are delegated to this + * method. It performs the parsing of the message, through the + * inherited parse() method, and locates and invokes the + * appropriate invocation handlers. + * + * @throw Exception When the inbound XML message cannot be parsed + * due to no available SAX driver, or when an invalid message was + * received. All other exceptions are caught and encoded within + * the XML-RPC response. + * + * @param xmlInput + * @return + * @throws Throwable + */ + + public byte[] dispatch(InputStream xmlInput) + throws Throwable + { + return dispatch(xmlInput, true); + } + /** * Inbound XML-RPC messages to a server are delegated to this *************** *** 111,122 **** * the XML-RPC response. * * @param xmlInput * @return * @throws Throwable */ ! public byte[] dispatch(InputStream xmlInput) throws Throwable { arguments.clear(); // Parse the inbound XML-RPC message. May throw an exception. --- 133,147 ---- * the XML-RPC response. * * @param xmlInput + * @param responseIsXmlDocument should the response be an XML document? * @return * @throws Throwable */ ! public byte[] dispatch(InputStream xmlInput, boolean responseIsXmlDocument) ! throws Throwable { + this.responseIsXmlDocument = responseIsXmlDocument; arguments.clear(); // Parse the inbound XML-RPC message. May throw an exception. *************** *** 352,360 **** private void writeResponse(Object value) { response.setLength(0); ! response.append( ! "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>" ! + "<methodResponse><params><param>"); if (value != null) { try { --- 377,385 ---- private void writeResponse(Object value) { response.setLength(0); ! if (responseIsXmlDocument) ! response.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>"); ! response.append("<methodResponse><params><param>"); if (value != null) { try { *************** *** 382,390 **** private void writeError(String message) { response.setLength(0); response.append( ! "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>" ! + "<methodResponse><fault><value><struct>" + "<member><name>faultCode</name><value><int>-1</int></value>" + "</member><member><name>faultString</name><value><string>"); response.append(message); --- 407,416 ---- private void writeError(String message) { response.setLength(0); + if (responseIsXmlDocument) + response.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>"); response.append( ! "<methodResponse><fault><value><struct>" + "<member><name>faultCode</name><value><int>-1</int></value>" + "</member><member><name>faultString</name><value><string>"); response.append(message); *************** *** 412,417 **** --- 438,446 ---- /** Holds the XML-RPC repsonse as it is built up */ private StringBuffer response = new StringBuffer(2048); + + /** Should the response be an XML document? */ + private boolean responseIsXmlDocument = true; } /* Index: marquee/xmlrpc/XmlRpcProxy.java =================================================================== RCS file: /cvsroot/xmlrpc/xmlrpc/source/marquee/xmlrpc/XmlRpcProxy.java,v retrieving revision 1.8 diff -c -r1.8 XmlRpcProxy.java *** marquee/xmlrpc/XmlRpcProxy.java 18 Sep 2003 12:55:00 -0000 1.8 --- marquee/xmlrpc/XmlRpcProxy.java 11 Feb 2004 19:14:20 -0000 *************** *** 116,127 **** String objectName, Class[] interfaces ) throws XmlRpcException { ! checkInterfaces( interfaces, XmlRpcException.class ); ! ! return Proxy.newProxyInstance( ! interfaces[ 0 ].getClassLoader(), ! interfaces, ! new XmlRpcProxy( host, port, path, objectName ) ); } --- 116,125 ---- String objectName, Class[] interfaces ) throws XmlRpcException { ! return createProxy( ! new XmlRpcClient( host, port, path ), ! objectName, ! interfaces ); } *************** *** 175,186 **** String objectName, Class[] interfaces ) throws XmlRpcException { checkInterfaces( interfaces, XmlRpcException.class ); return Proxy.newProxyInstance( interfaces[ 0 ].getClassLoader(), interfaces, ! new XmlRpcProxy( url, objectName ) ); } --- 173,215 ---- String objectName, Class[] interfaces ) throws XmlRpcException { + return createProxy( + new XmlRpcClient(url), + objectName, + interfaces ); + } + + + /** + * Creates a new dynamic proxy object that implements all + * supplied interfaces. This object may be type cast to any of + * the interface supplied in the call. Method calls through the + * interfaces will be translated to XML-RPC calls to the server + * via the supplied client. + * + * @param client The XML-RPC client that will send calls + * + * @param interfaces The list of interfaces the proxy should + * implement + * + * @param objectName The name under which the handler is + * reachable + * + * @return An object implementing the supplied interfaces with + * XML-RPC support + */ + + public static Object createProxy( + XmlRpcClient client, + String objectName, + Class[] interfaces ) throws XmlRpcException + { checkInterfaces( interfaces, XmlRpcException.class ); return Proxy.newProxyInstance( interfaces[ 0 ].getClassLoader(), interfaces, ! new XmlRpcProxy( client, objectName )); } *************** *** 396,423 **** */ protected XmlRpcProxy( ! String host, ! int port, ! String path, String objectName ) { ! client = new XmlRpcClient( host, port, path ); this.objectName = objectName; } - - /** - * Creates a new XmlRpcProxy whis is a dynamic proxy invocation handler with - * an encapsulated XmlRpcCLient. Not for pubilc usage -- use createProxy() - */ - - protected XmlRpcProxy( - URL url, - String objectName ) - { - client = new XmlRpcClient( url ); - this.objectName = objectName; - } /** --- 425,437 ---- */ protected XmlRpcProxy( ! XmlRpcClient client, String objectName ) { ! this.client = client; this.objectName = objectName; } /** Index: marquee/xmlrpc/XmlRpcServer.java =================================================================== RCS file: /cvsroot/xmlrpc/xmlrpc/source/marquee/xmlrpc/XmlRpcServer.java,v retrieving revision 1.9 diff -c -r1.9 XmlRpcServer.java *** marquee/xmlrpc/XmlRpcServer.java 11 Feb 2004 11:28:29 -0000 1.9 --- marquee/xmlrpc/XmlRpcServer.java 11 Feb 2004 19:14:20 -0000 *************** *** 131,139 **** public byte[] execute( InputStream xmlInput ) throws Throwable { XmlRpcDispatcher dispatcher = XmlRpcDispatcher.getDispatcher( this, "(unknown)" ); ! byte[] result = dispatcher.dispatch( xmlInput ); dispatcher.release(); return result; --- 131,163 ---- public byte[] execute( InputStream xmlInput ) throws Throwable { + return execute(xmlInput, true); + } + + + /** + * Dispatches the call contained in the supplied input stream. The stream should contain + * a proper XML message conforming to the XML-RPC specification. + * + * @param xmlInput The XML-RPC message. + * + * @param responseIsXmlDocument Should the XML-RPC response be a + * complete XML document? + * + * @throws Throwable if the input stream contains unparseable XML or if some error + * occurs in the SAX driver. + * + * @return An array of bytes representing the XML-RPC response. Any exception occuring + * in the invocation handlers are embedded in the XML-RPC response. + */ + + public byte[] execute( + InputStream xmlInput, + boolean responseIsXmlDocument) throws Throwable + { XmlRpcDispatcher dispatcher = XmlRpcDispatcher.getDispatcher( this, "(unknown)" ); ! byte[] result = dispatcher.dispatch( xmlInput, responseIsXmlDocument ); dispatcher.release(); return result; |