On a recent project I had to create an XML-RPC interface to some of the services we had in the business-layer of our application. The first thing to do is of course check out the Spring manual on remoting but to my surprise Spring doesn’t support XML-RPC natively. Googling for “Spring XML-RPC” I found a lot of blogposts and articles about integrating the Apache XML-RPC library with Spring, but part of them describe methods that just don’t work and others take a rather complicated approach where I would like to be able to just map a service-name to a Spring bean.
After this rather unsatisfactory search I decided to checkout how hard it would be to integrate Apache XML-RPC with Spring in the way I just described. The target is to be able to write something like the following Spring configuration to be able to export some services:
<bean name="serviceExporter" class="...">
<property name="services">
<map>
<entry key="ExampleService" value-ref="exampleServiceBean" />
</map>
</property>
</bean>
Perry Nguyen has written a post about integrating Apache XML-RPC 3 with Spring but he has left some open ends in my opinion. His solution is like mine based on a map of beans to be used in a ProcessorFactoryFactory. The first step in handling an XML-RPC request is mapping the request to a simple controller:
public class XmlRpcController extends AbstractController {
private XmlRpcServletServer server = new XmlRpcServletServer();
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest req, HttpServletResponse res) throws Exception {
// Delegate the request to the XmlRpcServletServer
server.execute(req, res);
// And stop processing of the request
return null;
}
@Required
public void setMapping(XmlRpcHandlerMapping mapping) {
server.setHandlerMapping(mapping);
}
}
As you can see this is just a simple wrapper around the XmlRpcServletServer to pass the request and response to the execute method. Apart from that we have a mapping property to set the handlerMapping property on the server. The next step is to create our own mapping class to be able to map service-names to a class:
public class SimpleHandlerMapping extends AbstractReflectiveHandlerMapping {
public void setServices(Map<String, Object> services) throws XmlRpcException {
// Check parameter
Assert.notNull(services, “Services map should not be null.”);
// Setup a RequestProcessorFactoryFactory
SimpleRequestProcessorFactoryFactory factory = new SimpleRequestProcessorFactoryFactory();
// Register all service beans with the factory
for (Object serviceBean : services.values()) {
factory.registerServiceBean(serviceBean.getClass(), serviceBean);
}
// Set the RequestFactoryFactory to be used for mapping
setRequestProcessorFactoryFactory(factory);
// Loop through the set
Iterator<Entry<String, Object>> it = services.entrySet().iterator();
while (it.hasNext()) {
// Fetch from the map
Entry<String, Object> entry = it.next();
String serviceName = entry.getKey();
Object serviceBean = entry.getValue();
// Register service in the handler mapping
registerPublicMethods(serviceName, serviceBean.getClass());
}
}
}
The input of this mapper is a simple Map of Strings and Objects describing the service-names associated with the objects (Spring beans). First we create a RequestProcessorFactoryFactory to be able to map classnames to Spring beans the code for this SimpleRequestProcessorFactoryFactory is given below. Then we register all services in the Map with the factory and set the factory to be used by the methods from our superclass.
The last step is to register all service-names with their respective classes using the registerPublicMethods method from our superclass. This tells Apache XML-RPC to register all public methods and call our SimpleRequestProcessorFactoryFactory to get a Factory returning the servicebean. This SimpleRequestProcessorFactoryFactory is based on a Map of Class and Objects to map the Class Apache XML-RPC asks for to a Spring bean:
public class SimpleRequestProcessorFactoryFactory implements RequestProcessorFactoryFactory {
private Map<Class, Object> serviceBeans = new HashMap<Class, Object>();
public RequestProcessorFactory getRequestProcessorFactory(Class pClass) throws XmlRpcException {
// Get the serviceBean from the map
final Object serviceBean = serviceBeans.get(pClass);
// Check if the bean is actually found
if (serviceBean == null) {
throw new XmlRpcException(”Handler \”" + pClass.getCanonicalName() + “\” not found.”);
}
// Create a factory just returning the lookedup bean
return new RequestProcessorFactory() {
public Object getRequestProcessor(XmlRpcRequest pRequest) throws XmlRpcException {
return serviceBean;
}
};
}
public void registerServiceBean(Class clazz, Object serviceBean) {
serviceBeans.put(clazz, serviceBean);
}
}
And then just a few lines of XML to wire it all together:
<bean id="xmlRpcHandlerMapping" class="nl.celerity.example.xmlrpc.SimpleHandlerMapping">
<property name="services">
<map>
<entry key="ExampleService" value-ref="exampleService" />
</map>
</property>
</bean>
<bean id="xmlRpcController" class="nl.celerity.example.xmlrpc.XmlRpcController">
<property name="mapping" ref="xmlRpcHandlerMapping" />
</bean>
Where exampleService is of course the name of a Spring bean with some public methods to be exported. With these two simple helper classes and an extremely simple controller we have the ability to export Spring beans as XML-RPC services by just adding a simple line of XML configuration.





Great article. Just what I was looking for. Thank you very much. But one more thing about it, spring’s DispatcherServlet now throws some:
at javax.servlet.ServletException.getRootCause(ServletException.java:96)
at org.springframework.web.util.NestedServletException.getCause(NestedServletException.java:74)
at javax.servlet.ServletException.getRootCause(ServletException.java:96)
at org.springframework.web.util.NestedServletException.getCause(NestedServletException.java:74)
at javax.servlet.ServletException.getRootCause(ServletException.java:96)
at org.springframework.web.util.NestedServletException.getCause(NestedServletException.java:74)
at javax.servlet.ServletException.getRootCause(ServletException.java:96)
at org.springframework.web.util.NestedServletException.getCause(NestedServletException.java:74)
at javax.servlet.ServletException.getRootCause(ServletException.java:96)
at org.springframework.web.util.NestedServletException.getCause(NestedServletException.java:74)
Any ideas?
Hi Max,
Maybe you could post the first lines of the stacktrace? It should list the actual exception-name.
Tomas
Hey Tomas,
Very well written. Can you give an example how to actually use the exampleService from an xmlrpc client?
Or at least how you have setup your servlets?
Hi Thijs,
The servlet is a normal Spring DispatcherServlet just like almost every Spring MVC application has. The controller is not different from other controllers, so you can just use the same HandlerMapping (for example a SimpleUrlHandlerMapping) you use for all controllers to assign a URL to this controller.
Calling the service from a client depends on which client you use. The apache project has an XML-RPC client with some usage examples.
Tomas
Thanks Tomas I got it working!
Thijs Thiessens
Hi Thomas, this is useful article, but I receive a strange exception:
Error creating bean with name ‘xmlRpcHandlerMapping’ defined in ServletContext resource [/WEB-INF/xmlrpc-servlet.xml]: Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property ’services’ threw exception; nested exception is java.lang.IllegalStateException: Invalid parameter or result type: org.springframework.aop.Advisor
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1278)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1010)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:472)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
at java.security.AccessController.doPrivileged(Native Method)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:221)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:429)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:729)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:381)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:402)
at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:316)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:282)
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:126)
at javax.servlet.GenericServlet.init(GenericServlet.java:215)
at org.mortbay.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:431)
at org.mortbay.jetty.servlet.ServletHolder.doStart(ServletHolder.java:263)
at org.mortbay.component.AbstractLifeCycle.start(AbstractLife
Can somebody help me on this issue?
Hi and,
I don’t recognize the exception as one I have encountered before, it seems like the Spring AbstractAutowireCapableBeanFactory is unable to set your services property because one of the arguments is not of the correct type. Maybe you can try to get it working with a very simple service-bean first? (Something like HelloWorldService?) If that works the problem is with the service you’re trying to export, which makes your debugging easier and at lot more directed.
Tomas
I mapped proxies to service interfaces, and it works.
Thanks Tomas.
How can I send custom object parameters to the XML-RPC service handler?
Hi and,
Please read the Apache XML-RPC documentation: http://ws.apache.org/xmlrpc/
Tomas
I used Redstone’s XML RPC library, and did it like this:
http://mikeski.net/site/node/117
I found that instead of adding a class name for the handler (i.e. the properties file for Apache’s version) adding the class itself lets you get it from the Spring factory and be done with it.