Mule
  1. Mule
  2. MULE-945

Investigate new XML-RPC provider/transport

    Details

    • Type: New Feature New Feature
    • Status: Closed
    • Priority: Major Major
    • Resolution: Won't Fix or Usage Issue
    • Affects Version/s: 1.3-rc4
    • Fix Version/s: None
    • Labels:
      None
    • Similar Issues:
      MULE-3632Look into onEvent() method in VMMessageReceiver and investigate whether synchronized block is needed
      MULE-2434Investigate/convert remaining old, unconverted tests to schema
      MULE-639Investigate JDK 1.5 as base platform for upcoming Mule versions
      MULE-519Investigate options for a ListenerContainer in Spring to pass in Mule events to beans
      MULE-2167Investigate Email Exception Handling
      MULE-4639Consider making mimeType a first class property of MuleMessage
      MULE-200Investigate ways of externalising Mule theading
      MULE-2879File connector fails to move file when moveToDirectory is specified and autoDelete=true
      MULE-2055Investigate using Spring custom scopes for Mule ObjectFactories
      MULE-1675Investigate applicability of Embedded JBoss for JCA distro test

      Description

      The Apache XML-RPC project (http://ws.apache.org/xmlrpc/) is finally coming out of its hibernation, with a new upcoming and much improved 3.0 release. The servlet-based server integration looks promising, and XML-RPC in general would provide a very lightweight, multi-language access point with very low barrier to entry since it is so much easier to handle than SOAP (a perl/python/ruby xmlrpc call takes 2-3 lines of code).

        Activity

        Hide
        Andrew Perepelytsya added a comment -

        Version 3.0 has now been officially released (almost a complete rewrite). They are using m2 build, so that part is easy at least: http://www.ibiblio.org/maven2/org/apache/xmlrpc/xmlrpc/3.0/

        Show
        Andrew Perepelytsya added a comment - Version 3.0 has now been officially released (almost a complete rewrite). They are using m2 build, so that part is easy at least: http://www.ibiblio.org/maven2/org/apache/xmlrpc/xmlrpc/3.0/
        Hide
        Aleksandar Vidakovic added a comment -

        I got this working with a HttpConnector and two Transformers (XmlRpcRequestToObject and ObjectToXmlRpcRequest). Some polishing is still needed, but it works with Apache XML-RPC 3.0 on the client side and a Service Component provided by Spring on the server side. The method that I invoked had a non-trivial signature (i. e. no base types like int, String etc.), but had a quite complex Object as input parameter and returned an object of the same type.

        Is there still an interest for this?

        Show
        Aleksandar Vidakovic added a comment - I got this working with a HttpConnector and two Transformers (XmlRpcRequestToObject and ObjectToXmlRpcRequest). Some polishing is still needed, but it works with Apache XML-RPC 3.0 on the client side and a Service Component provided by Spring on the server side. The method that I invoked had a non-trivial signature (i. e. no base types like int, String etc.), but had a quite complex Object as input parameter and returned an object of the same type. Is there still an interest for this?
        Hide
        Andrew Perepelytsya added a comment -

        Absolutely. Note, if you'd like to submit a patch, do so against the trunk, as the connectors have been refactored, sometimes more than slightly.

        Show
        Andrew Perepelytsya added a comment - Absolutely. Note, if you'd like to submit a patch, do so against the trunk, as the connectors have been refactored, sometimes more than slightly.
        Hide
        Holger Hoffstaette added a comment -

        Hi Aleksandar - while I had something a little bit different in mind when I filed this item (e.h. not relying on the HTTP transport) your contribution would still be greatly appreciated. Just to make sure - your transformers still rely on the Apache XML-RPC library for parsing etc., right?

        Show
        Holger Hoffstaette added a comment - Hi Aleksandar - while I had something a little bit different in mind when I filed this item (e.h. not relying on the HTTP transport) your contribution would still be greatly appreciated. Just to make sure - your transformers still rely on the Apache XML-RPC library for parsing etc., right?
        Hide
        Aleksandar Vidakovic added a comment -

        Salut Holger. My first intention was to make a XmlRpcConnector, but as a total Mule greenhorn I had to scale down my ambitions a little bit
        I'll try to make a patch of my solution by tonight (GMT), but here is already a rough sketch what I did.

        My configuration looks like this:

        [config]
        ...
        <connector name="xmlrpc" className="org.mule.providers.http.HttpConnector"/>

        <endpoint-identifiers>
        <endpoint-identifier name="service.xmlrpc" value="http://localhost:9093"/>
        </endpoint-identifiers>

        <transformers>
        <transformer name="XmlRpcRequestToObject" className="org.mule.providers.http.xmlrpc.transformers.XmlRpcRequestToObject"/>
        <transformer name="ObjectToXmlRpcRequest" className="org.mule.providers.http.xmlrpc.transformers.ObjectToXmlRpcRequest"/>
        </transformers>

        <interceptor-stack name="xmlrpc-stack">
        <interceptor className="org.mule.providers.http.xmlrpc.interceptors.XmlRpcInterceptor"/>
        </interceptor-stack>

        <model name="Service">
        <mule-descriptor name="XmlRpcService" implementation="service">
        <inbound-router>
        <endpoint address="service.xmlrpc" transformers="XmlRpcRequestToObject" responseTransformers="ObjectToXmlRpcRequest"/>
        </inbound-router>
        <interceptor name="xmlrpc-stack"/>
        <properties>
        <list name="serviceInterfaces">
        <entry value="my.package.Service"/>
        </list>
        </properties>
        </mule-descriptor>
        </model>
        ...
        [/config]

        First the xml-rpc method call passes my interceptor. I need this to extract the method name which is passed then to Mule ("invocation.getMessage().setProperty(MuleProperties.MULE_METHOD_PROPERTY, method)"). I tried to set it in the transformer, but it seems "too late" in Mule's lifecycle (i. e. Mule doesn't recognize my mehtod hint if I set it there). The transformer for incoming xml-rpc requests deserializes the xml-rpc method call parameters into an Object array using the SOJO lib (http://sojo.sf.net). Then the deserialized parameters are passed to my service. The return value of the service method invocation is passed again through a transformer, but now SOJO serializes the object into a xml-rpc struct.

        Issues:

        • you need a member "class" set in your xml-rpc struct to give SOJO a hint how to deserialize the struct into an Object
        • a connector would be nicer
        • on the client side you only can choose the method you want to call ("createIssue"), but not the service ("IssueService.createIssue")
        • I'm not sure if the interceptor is really necessary. This can certainly be done in a more elegant way.
        • Mule's DynamicEntryPoint had a problem to find one method in my service class (configured with spring). The method has this signature: getIssues(List<Long>). SOJO deserializes the input parameter to an ArrayList.

        Dependencies:

        • SOJO 0.30 (only because it has already a working xml-rpc serializer/deserializer)

        I tested this configuration with Apache's XML-RPC lib as a client. And it works better than I thought (see issues).

        Show
        Aleksandar Vidakovic added a comment - Salut Holger. My first intention was to make a XmlRpcConnector, but as a total Mule greenhorn I had to scale down my ambitions a little bit I'll try to make a patch of my solution by tonight (GMT), but here is already a rough sketch what I did. My configuration looks like this: [config] ... <connector name="xmlrpc" className="org.mule.providers.http.HttpConnector"/> <endpoint-identifiers> <endpoint-identifier name="service.xmlrpc" value="http://localhost:9093"/> </endpoint-identifiers> <transformers> <transformer name="XmlRpcRequestToObject" className="org.mule.providers.http.xmlrpc.transformers.XmlRpcRequestToObject"/> <transformer name="ObjectToXmlRpcRequest" className="org.mule.providers.http.xmlrpc.transformers.ObjectToXmlRpcRequest"/> </transformers> <interceptor-stack name="xmlrpc-stack"> <interceptor className="org.mule.providers.http.xmlrpc.interceptors.XmlRpcInterceptor"/> </interceptor-stack> <model name="Service"> <mule-descriptor name="XmlRpcService" implementation="service"> <inbound-router> <endpoint address="service.xmlrpc" transformers="XmlRpcRequestToObject" responseTransformers="ObjectToXmlRpcRequest"/> </inbound-router> <interceptor name="xmlrpc-stack"/> <properties> <list name="serviceInterfaces"> <entry value="my.package.Service"/> </list> </properties> </mule-descriptor> </model> ... [/config] First the xml-rpc method call passes my interceptor. I need this to extract the method name which is passed then to Mule ("invocation.getMessage().setProperty(MuleProperties.MULE_METHOD_PROPERTY, method)"). I tried to set it in the transformer, but it seems "too late" in Mule's lifecycle (i. e. Mule doesn't recognize my mehtod hint if I set it there). The transformer for incoming xml-rpc requests deserializes the xml-rpc method call parameters into an Object array using the SOJO lib ( http://sojo.sf.net ). Then the deserialized parameters are passed to my service. The return value of the service method invocation is passed again through a transformer, but now SOJO serializes the object into a xml-rpc struct. Issues: you need a member "class" set in your xml-rpc struct to give SOJO a hint how to deserialize the struct into an Object a connector would be nicer on the client side you only can choose the method you want to call ("createIssue"), but not the service ("IssueService.createIssue") I'm not sure if the interceptor is really necessary. This can certainly be done in a more elegant way. Mule's DynamicEntryPoint had a problem to find one method in my service class (configured with spring). The method has this signature: getIssues(List<Long>). SOJO deserializes the input parameter to an ArrayList. Dependencies: SOJO 0.30 (only because it has already a working xml-rpc serializer/deserializer) I tested this configuration with Apache's XML-RPC lib as a client. And it works better than I thought (see issues).
        Hide
        Aleksandar Vidakovic added a comment -

        Added the patch mentioned in my last comment. But I realize now what should be done and have to admit that this is not the perfect solution.

        I started to write a XmlRpcConnector and a XmlRpcMessageReceiver. The tricky thing is to write a more tolerant type converter so that "real" objects can be passed to the underlying services and not only HashMaps. Basically objects have to be translated to HashMaps and back.

        This is what I have until now:

        [code]
        package org.mule.providers.xmlrpc;

        import org.apache.xmlrpc.server.PropertyHandlerMapping;
        import org.apache.xmlrpc.server.XmlRpcServer;
        import org.apache.xmlrpc.server.XmlRpcServerConfigImpl;
        import org.apache.xmlrpc.webserver.WebServer;
        import org.mule.config.i18n.Message;
        import org.mule.config.i18n.Messages;
        import org.mule.providers.AbstractMessageReceiver;
        import org.mule.providers.http.xmlrpc.converter.JibxTypeConverterFactory;
        import org.mule.umo.UMOComponent;
        import org.mule.umo.UMOException;
        import org.mule.umo.endpoint.UMOEndpoint;
        import org.mule.umo.lifecycle.InitialisationException;
        import org.mule.umo.lifecycle.LifecycleException;
        import org.mule.umo.provider.UMOConnector;

        public class XmlRpcMessageReceiver extends AbstractMessageReceiver
        {
        public static final String XMLRPC_SERVLET_CONNECTOR_NAME= "_xmlrpcConnector";

        private WebServer webServer;

        public XmlRpcMessageReceiver(UMOConnector connector, UMOComponent component, UMOEndpoint endpoint)
        throws InitialisationException

        { super(connector, component, endpoint); }

        public void doConnect() throws Exception

        { webServer = new WebServer(endpoint.getEndpointURI().getPort()); XmlRpcServer xmlRpcServer = webServer.getXmlRpcServer(); PropertyHandlerMapping phm = new PropertyHandlerMapping(); //phm.setAuthenticationHandler(arg0); //phm.setRequestProcessorFactoryFactory(arg0); phm.setTypeConverterFactory(new MoreTolerantTypeConverterFactory()); xmlRpcServer.setHandlerMapping(phm); phm.addHandler("Service", getComponent().getClass()); XmlRpcServerConfigImpl serverConfig = (XmlRpcServerConfigImpl)xmlRpcServer.getConfig(); serverConfig.setEnabledForExtensions(true); // TODO: configuration serverConfig.setContentLengthOptional(false); // TODO: configuration }

        public void doDisconnect() throws Exception
        {
        }

        protected void doDispose()
        {
        }

        public void doStart() throws UMOException
        {
        try

        { webServer.start(); }

        catch (Exception e)

        { throw new LifecycleException(new Message(Messages.FAILED_TO_START_X, "XML-RPC Http Receiver"), e, this); }

        }

        public void doStop() throws UMOException
        {
        try

        { webServer.shutdown(); }

        catch (Exception e)

        { throw new LifecycleException(new Message(Messages.FAILED_TO_STOP_X, "XML-RPC Http Receiver"), e, this); }

        }
        }

        [/code]

        The interesting part starts - as I understand it - in the line "phm.setTypeConverterFactory(new MoreTolerantTypeConverterFactory());". For "simple" services (method parameters only have basic types or HashMaps as input/output values) this could already work (didn't try it though; this is just an idea).

        Is this the way to go? Did I miss anything (I left the XmlRpcConnector, because the necessary code seems to be quite straight forward)?

        Show
        Aleksandar Vidakovic added a comment - Added the patch mentioned in my last comment. But I realize now what should be done and have to admit that this is not the perfect solution. I started to write a XmlRpcConnector and a XmlRpcMessageReceiver. The tricky thing is to write a more tolerant type converter so that "real" objects can be passed to the underlying services and not only HashMaps. Basically objects have to be translated to HashMaps and back. This is what I have until now: [code] package org.mule.providers.xmlrpc; import org.apache.xmlrpc.server.PropertyHandlerMapping; import org.apache.xmlrpc.server.XmlRpcServer; import org.apache.xmlrpc.server.XmlRpcServerConfigImpl; import org.apache.xmlrpc.webserver.WebServer; import org.mule.config.i18n.Message; import org.mule.config.i18n.Messages; import org.mule.providers.AbstractMessageReceiver; import org.mule.providers.http.xmlrpc.converter.JibxTypeConverterFactory; import org.mule.umo.UMOComponent; import org.mule.umo.UMOException; import org.mule.umo.endpoint.UMOEndpoint; import org.mule.umo.lifecycle.InitialisationException; import org.mule.umo.lifecycle.LifecycleException; import org.mule.umo.provider.UMOConnector; public class XmlRpcMessageReceiver extends AbstractMessageReceiver { public static final String XMLRPC_SERVLET_CONNECTOR_NAME= "_xmlrpcConnector"; private WebServer webServer; public XmlRpcMessageReceiver(UMOConnector connector, UMOComponent component, UMOEndpoint endpoint) throws InitialisationException { super(connector, component, endpoint); } public void doConnect() throws Exception { webServer = new WebServer(endpoint.getEndpointURI().getPort()); XmlRpcServer xmlRpcServer = webServer.getXmlRpcServer(); PropertyHandlerMapping phm = new PropertyHandlerMapping(); //phm.setAuthenticationHandler(arg0); //phm.setRequestProcessorFactoryFactory(arg0); phm.setTypeConverterFactory(new MoreTolerantTypeConverterFactory()); xmlRpcServer.setHandlerMapping(phm); phm.addHandler("Service", getComponent().getClass()); XmlRpcServerConfigImpl serverConfig = (XmlRpcServerConfigImpl)xmlRpcServer.getConfig(); serverConfig.setEnabledForExtensions(true); // TODO: configuration serverConfig.setContentLengthOptional(false); // TODO: configuration } public void doDisconnect() throws Exception { } protected void doDispose() { } public void doStart() throws UMOException { try { webServer.start(); } catch (Exception e) { throw new LifecycleException(new Message(Messages.FAILED_TO_START_X, "XML-RPC Http Receiver"), e, this); } } public void doStop() throws UMOException { try { webServer.shutdown(); } catch (Exception e) { throw new LifecycleException(new Message(Messages.FAILED_TO_STOP_X, "XML-RPC Http Receiver"), e, this); } } } [/code] The interesting part starts - as I understand it - in the line "phm.setTypeConverterFactory(new MoreTolerantTypeConverterFactory());". For "simple" services (method parameters only have basic types or HashMaps as input/output values) this could already work (didn't try it though; this is just an idea). Is this the way to go? Did I miss anything (I left the XmlRpcConnector, because the necessary code seems to be quite straight forward)?
        Hide
        Holger Hoffstaette added a comment -

        Hi - I read all your comments and just wanted to say that all the serialization problems are more or less exactly what I wanted to avoid. The idea of XML-RPC is not to allow for general object mashalling but only for the supported types: arrays, strings, numbers etc. The motivation was to be able to kick off service component methods from perl/python/ruby scripts or other external systems; nothing more. As you can see once you try to add generic object serialization, you just end up reinventing the smashing "success" that is SOAP.
        Also starting the xml-rpc web server in the receiver is not really the right thing to do; this is the role of the connector. I don't remember all the details but ideally there should only be a single server per connector; the endpoints could then act as the various "methods" for xml-rpc requests. But again I haven't looked at the whole thing in a very long time, and do not have much time for this in the near term either.
        We will have more examples/better docs etc. for writing connectors in the near future.

        Show
        Holger Hoffstaette added a comment - Hi - I read all your comments and just wanted to say that all the serialization problems are more or less exactly what I wanted to avoid. The idea of XML-RPC is not to allow for general object mashalling but only for the supported types: arrays, strings, numbers etc. The motivation was to be able to kick off service component methods from perl/python/ruby scripts or other external systems; nothing more. As you can see once you try to add generic object serialization, you just end up reinventing the smashing "success" that is SOAP. Also starting the xml-rpc web server in the receiver is not really the right thing to do; this is the role of the connector. I don't remember all the details but ideally there should only be a single server per connector; the endpoints could then act as the various "methods" for xml-rpc requests. But again I haven't looked at the whole thing in a very long time, and do not have much time for this in the near term either. We will have more examples/better docs etc. for writing connectors in the near future.
        Hide
        Aleksandar Vidakovic added a comment -

        Salut Holger. I totally agree with your comments about SOAP. The only reason why I made an attemept to make the xml-rpc calls more general is because a collegue of mine has a Qt client and the only webservice lib he can use (i. e. compile with MinGW on Windows) is an xml-rpc lib. And I didn't want to rewrite and maintain an extra service component for him (I admit, I'm lazy .

        Anyway, now that I started the thing, I'd like to finish it in the way that you suggested. I'll drop my plans to rewrite SOAP and will look more into the details of the existing connectors. Maybe I'll come up with something better next time. Thanks for the hints and your patience.

        Cheers.

        Show
        Aleksandar Vidakovic added a comment - Salut Holger. I totally agree with your comments about SOAP. The only reason why I made an attemept to make the xml-rpc calls more general is because a collegue of mine has a Qt client and the only webservice lib he can use (i. e. compile with MinGW on Windows) is an xml-rpc lib. And I didn't want to rewrite and maintain an extra service component for him (I admit, I'm lazy . Anyway, now that I started the thing, I'd like to finish it in the way that you suggested. I'll drop my plans to rewrite SOAP and will look more into the details of the existing connectors. Maybe I'll come up with something better next time. Thanks for the hints and your patience. Cheers.
        Hide
        Ross Mason added a comment -

        If this is still desirable it should be continued as a MuleForge project

        Show
        Ross Mason added a comment - If this is still desirable it should be continued as a MuleForge project

          People

          • Assignee:
            Ross Mason
            Reporter:
            Holger Hoffstaette
          • Votes:
            1 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development