Search code examples
apachetclxml-rpc

Java XmlRpc Client POST issue with TCL XMLRPC Server


I am having a simple problem, my Java XMLRPC Client cant seem to properly speak to the XMLRPC Server that is written in TCL

(Using TCL XMLRPC SERVER OPEN SOURCE implementation)

Summary: XMLRPC Clients in TCL/Python etc, can/do send/receive messages to the TCL XMLRPC Server, but my Java XMLRPC client doenst seem to work.

Java Client Side Code:

   /*
    * try's, catches, comments removed to show code-flow w/ out mess.
    * host/port/target all same as whats set in Python
    */
    //show imports / package used, this is using apache's xmlrpc v3.1.3
    import org.apache.xmlrpc.XmlRpcException;
    import org.apache.xmlrpc.client.XmlRpcClient;
    import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
    //... host and port are the same as whats used in working tcl/python clients. (remoteHostName / 5555)
    //... method is the same as well, 'fooBar123', and args is just 1 string passed to it.
    XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
    target = "RPC2";
    String targetUrl = "http://"+host+":"+port+"/" + target;
    TestNgUtil.ReportInfo("config.SetServerUrl("+targetUrl+")");
    config.setServerURL(new URL(targetUrl));
    XmlRpcClient client = new XmlRpcClient();
    client.setConfig(config);
    String result = null;
    /*
    * This Result Never Returns from TCL XMLRPC Server
    */
    result = (String) client.execute(command, params);

TCL Server's Debug ERROR Response to Java:

    //(notice unlike Python example below, no proper Header, Content-Type, etc)

    TCL Server Side of the Java Error
    in serveOnce: addr: 10.21.69.13
    in serveOnce: port: 64522
    Unknown type: fooBar123</value></param></params>
    bgerror failed to handle background error.
    Original error: 
    Error in bgerror: can't read "xmlcall": no such variable**

Python example works, however, also note I print out the XML-debug to look at successful requests:

However, If I attempt to use the TCL client, or even a simple Python XMLRPC client, it works. I even use Python to print out the XMLRPC request:

(from Python client, nothing fancy, )

    import xmlrpclib
    server_url = "http://remoteHostName:5555";
    server = xmlrpclib.Server(server_url, verbose=True);
    result = server.hello('hello world')
    ## DEBUG INFO PRINTED FROM REQUEST POST ##
    send: "POST /RPC2 HTTP/1.1\r\nHost: remoteHostName:5555\r\nAccept-Encoding: gzip\r\nUser-Agent: xmlrpclib.py/1.0.1 (by www.pythonware.com)\r\nContent-Type: text/xml\r\nContent-Length: 160\r\n\r\n<?xml version='1.0'?>\n<methodCall>\n<methodName>hello</methodName>\n<params>\n<param>\n<value><string>hello world</string></value>\n</param>\n</params>\n</methodCall>\n"

    reply: 'HTTP/1.1 200 OK\n'
    header: Content-Type: text/xml
    header: Content-length: 162
    body: '<?xml version="1.0"?>\n<methodResponse>\n\t<params>\n\t\t<param>\n\t\t\t<value>     <string>hello(hello world) yaaaah?!</string></value>\n\t\t</param>\n\t</params>\n</methodResponse>\n'

TCL Server's Debug/Response to Python, before pushing back proper response:

    send: "POST /RPC2 HTTP/1.1
    Host: remoteHostName:5555
    Accept-Encoding: gzip
    User-Agent: xmlrpclib.py/1.0.1 (by www.pythonware.com)
    Content-Type: text/xml
    Content-Length: 156

here's the TCL XMLRPC Server code for hello( arg ), works for tcl, python, not java. (java client configuration issue probably)

    #using the TCL XMLRPC Server ( http://sourceforge.net/projects/xmlrpctcl/ ) 
    package require xmlrpc
    xmlrpc::serv 5555

    proc hello { world } {
  puts "IN HELLO WORLD!"
  set res "hello(${world}) yaaaah?!"
  return [list string $res]
    }

    vwait forever

I appreciate any pointers, I've gone so far to attempt to use Java or Python with embedded TCL interpretters to avoid this, but because of the large amount of TCL that this application has to use, source, and share, I have to get a TCL XMLRPC server up and working.

I've also attempted to use the webservices httpd with XMLRPC, but did not have much sucess even with getting it to work with tcl/python clients.

Killed a whole weekend on this already.

Thanks for reading, and any pointers/help.


SOLUTION FOUND (POSTED HERE)

  1. the problem boils down to the XML of the old tcl xmlrpc server not including on the data types. They're implied, so in order to get apache's XMLRPC client to send the implied data types around strings, just implement the 'custom data type' to put the tag back.

CODE IS HERE:

import java.net.URL;

import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
import org.apache.xmlrpc.common.TypeFactoryImpl;
import org.apache.xmlrpc.common.XmlRpcController;
import org.apache.xmlrpc.common.XmlRpcStreamConfig;
import org.apache.xmlrpc.serializer.StringSerializer;
import org.apache.xmlrpc.serializer.TypeSerializer;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;

public class XMLRPCClient {
    public static void main(String[] argv) throws Exception {
        XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
        config.setServerURL(new URL("http://127.0.0.1:6800/rpc"));
        XmlRpcClient client = new XmlRpcClient();
        client.setConfig(config);
        client.setTypeFactory(new MyTypeFactoryImpl(client));
        Object[] params = new Object[] {
            new String[] { "http://www.google.com" }
        };
        String result = (String)client.execute("aria2.addUri", params);
    }

    static private class MyStringSerializer extends StringSerializer {
        public void write(ContentHandler pHandler, Object pObject)
            throws SAXException {
            // Write <string> tag explicitly
            write(pHandler, STRING_TAG, pObject.toString());
        }
    }

    static private class MyTypeFactoryImpl extends TypeFactoryImpl {
        public MyTypeFactoryImpl(XmlRpcController pController) {
            super(pController);
        }

        public TypeSerializer getSerializer(XmlRpcStreamConfig pConfig, Object pObject) throws SAXException {
            if(pObject instanceof String) {
                return new MyStringSerializer();
            } else {
                return super.getSerializer(pConfig, pObject);
            }
        }
    }
}

Solution

  • The problem boils down to the XML of the old tcl XMLRPC server not including on the data types. They're implied, so in order to get Apache's XMLRPC client to send the implied data types around strings just implement the 'custom data type' to put the tag back.

    import java.net.URL;
    
    import org.apache.xmlrpc.client.XmlRpcClient;
    import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
    import org.apache.xmlrpc.common.TypeFactoryImpl;
    import org.apache.xmlrpc.common.XmlRpcController;
    import org.apache.xmlrpc.common.XmlRpcStreamConfig;
    import org.apache.xmlrpc.serializer.StringSerializer;
    import org.apache.xmlrpc.serializer.TypeSerializer;
    import org.xml.sax.ContentHandler;
    import org.xml.sax.SAXException;
    
    public class XMLRPCClient {
        public static void main(String[] argv) throws Exception {
            XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
            config.setServerURL(new URL("http://127.0.0.1:6800/rpc"));
            XmlRpcClient client = new XmlRpcClient();
            client.setConfig(config);
            client.setTypeFactory(new MyTypeFactoryImpl(client));
            Object[] params = new Object[] {
                new String[] { "http://www.google.com" }
            };
            String result = (String)client.execute("aria2.addUri", params);
        }
    
        static private class MyStringSerializer extends StringSerializer {
            public void write(ContentHandler pHandler, Object pObject)
                throws SAXException {
                // Write <string> tag explicitly
                write(pHandler, STRING_TAG, pObject.toString());
            }
        }
    
        static private class MyTypeFactoryImpl extends TypeFactoryImpl {
            public MyTypeFactoryImpl(XmlRpcController pController) {
                super(pController);
            }
    
            public TypeSerializer getSerializer(XmlRpcStreamConfig pConfig, Object pObject) throws SAXException {
                if(pObject instanceof String) {
                    return new MyStringSerializer();
                } else {
                    return super.getSerializer(pConfig, pObject);
                }
            }
        }
    }