If you have read and understood the previous document about the Apache XML-RPC client, then the server isn't too much news.
First of all, there is an object, called the XmlRpcServer. This objects purpose is to receive and execute XML-RPC calls by the clients. The XmlRpcServer can be embedded into a servlet container, or another HTTP server (for example, the minimal web server, that comes with XML-RPC), but it doesn't need to. Take the local transport as an example: In that case the XML-RPC server is simply embedded into the client application.
Like the XmlRpcClient, the XmlRpcServer needs a configuration, which is given by the XmlRpcServerConfigImpl object.
The easiest way to create an XML-RPC Server is the XmlRpcServlet, which has an automatically embedded instance of XmlRpcServer. This servlet allows you to create a server within 10 minutes or so:
package org.apache.xmlrpc.demo; public class Calculator { public int add(int i1, int i2) { return i1 + i2; } public int subtract(int i1, int i2) { return i1 - i2; } }
This class has two public, non-static methods, which should be available to the clients.
Calculator=org.apache.xmlrpc.demo.Calculator
The property file must be called XmlRpcServlet.properties, and it must be located in the package org.apache.xmlrpc.webserver. In other words, you would typically put it into the directory org/apache/xmlrpc/webserver and add it to your jar file.
<servlet> <servlet-name>XmlRpcServlet</servlet-name> <servlet-class>org.apache.xmlrpc.webserver.XmlRpcServlet</servlet-class> <init-param> <param-name>enabledForExtensions</param-name> <param-value>true</param-value> <description> Sets, whether the servlet supports vendor extensions for XML-RPC. </description> </init-param> </servlet> <servlet-mapping> <servlet-name>XmlRpcServlet</servlet-name> <url-pattern>/xmlrpc</url-pattern> </servlet-mapping>
That's it! You have just created your first XML-RPC server. :-)
Unlike in the case of the clients configuration, there isn't much to configure on the server. The reason is, that most things depend on the client and the HTTP headers, which are received by the client. There is one very important property to configure, though:
Property Name | Description |
enabledForExceptions | If the server catches an exception, and this property is set, then the server will convert the exception into a byte array (by using an ObjectOutputStream) and return the exception to the client. Note, that this may have privacy or even security implications, because Exceptions may contain arbitrary Java objects, which you possibly do not want to be sent to the client. |
enabledForExtensions | Whether the vendor extensions of Apache XML-RPC should be enabled. By default, Apache XML-RPC is strictly compliant to the XML-RPC specification. Enabling this property doesn't indicate, that the server is unable to serve requests by standard clients: In contrary, the servers behaviour depends on the client. Setting this property to true will only advice the server, that it may accept requests, which ask for vendor extensions. For example, if a client sends a content-length header, then the server assumes, that the client wants a content-length header in the request and disables the streaming mode. |
Basic authentication is frequently used to authenticate and authorize users. Within Apache XML-RPC, basic authentication is done by the XmlRpcHandler. The handler receives an instance of XmlRpcRequest. This object has a method getConfig(), which returns an instance of XmlRpcRequestConfig.
If you are running within a HTTP server, then the request configuration may be casted to an instance of XmlRpcHttpRequestConfig. This object has methods getBasicUserName(), and getBasicPassword(), which provide the necessary details.
In other words: Your task is to provide your own instance of XmlRpcHandlerMapping, which creates your own handlers. And your own handlers are responsible to validate the basic authentication details.
Here's an example servlet, which overrides the default PropertyHandlerMapping.
public class MyServlet extends XmlRpcServlet { private boolean isAuthenticated(String pUserName, String pPassword) { return "foo".equals(pUserName) && "bar".equals(pPassword); } protected XmlRpcHandlerMapping newXmlRpcHandlerMapping() throws XmlRpcException { PropertyHandlerMapping mapping = (PropertyHandlerMapping) super.newXmlRpcHandlerMapping(); AbstractReflectiveHandlerMapping.AuthenticationHandler handler = new AbstractReflectiveHandlerMapping.AuthenticationHandler(){ public boolean isAuthorized(XmlRpcRequest pRequest){ XmlRpcHttpRequestConfig config = (XmlRpcHttpRequestConfig) pRequest.getConfig(); return isAuthenticated(config.getBasicUserName(), config.getBasicPassword()); }; }; mapping.setAuthenticationHandler(handler); return mapping; } }
The WebServer is a minimal HTTP server, that might be used as an embedded web server.
Use of the WebServer has grown very popular amongst users of Apache XML-RPC. Why this is the case, can hardly be explained, because the WebServer is at best a workaround, compared to full blown servlet engines like Tomcat or Jetty. For example, under heavy load it will almost definitely be slower than a real servlet engine, because it does neither support proper keepalive (multiple requests per physical connection) nor chunked mode (in other words, it cannot stream requests).
If you still insist in using the WebServer, it is recommended to use its subclass, the ServletWebServer instead, which offers a minimal subset of the servlet API. In other words, you keep yourself the option to migrate to a real servlet engine later.
Use of the WebServer goes roughly like this: First of all, create a property file (for example "MyHandlers.properties") and add it to your jar file. The property keys are handler names and the property values are the handler classes. Once that is done, create an instance of WebServer.
package org.apache.xmlrpc.demo.webserver; import java.net.InetAddress; import org.apache.xmlrpc.common.TypeConverterFactoryImpl; import org.apache.xmlrpc.demo.webserver.proxy.impls.AdderImpl; import org.apache.xmlrpc.server.PropertyHandlerMapping; import org.apache.xmlrpc.server.XmlRpcServer; import org.apache.xmlrpc.server.XmlRpcServerConfigImpl; import org.apache.xmlrpc.webserver.WebServer; public class Server { private static final int port = 8080; public static void main(String[] args) throws Exception { WebServer webServer = new WebServer(port); XmlRpcServer xmlRpcServer = webServer.getXmlRpcServer(); PropertyHandlerMapping phm = new PropertyHandlerMapping(); /* Load handler definitions from a property file. * The property file might look like: * Calculator=org.apache.xmlrpc.demo.Calculator * org.apache.xmlrpc.demo.proxy.Adder=org.apache.xmlrpc.demo.proxy.AdderImpl */ phm.load(Thread.currentThread().getContextClassLoader(), "MyHandlers.properties"); /* You may also provide the handler classes directly, * like this: * phm.addHandler("Calculator", * org.apache.xmlrpc.demo.Calculator.class); * phm.addHandler(org.apache.xmlrpc.demo.proxy.Adder.class.getName(), * org.apache.xmlrpc.demo.proxy.AdderImpl.class); */ xmlRpcServer.setHandlerMapping(phm); XmlRpcServerConfigImpl serverConfig = (XmlRpcServerConfigImpl) xmlRpcServer.getConfig(); serverConfig.setEnabledForExtensions(true); serverConfig.setContentLengthOptional(false); webServer.start(); } }
The Calculator class can be found above. The Adder and AdderImpl classes can be found in the proxy example.
Jimisola Laursen, who provided the above example, has also supplied an example for the client:
package org.apache.xmlrpc.demo.client; import java.net.MalformedURLException; import java.net.URL; import org.apache.xmlrpc.XmlRpcException; import org.apache.xmlrpc.client.XmlRpcClient; import org.apache.xmlrpc.client.XmlRpcClientConfigImpl; import org.apache.xmlrpc.client.XmlRpcCommonsTransportFactory; import org.apache.xmlrpc.client.util.ClientFactory; import org.apache.xmlrpc.demo.proxy.Adder; public class Client { public static void main(String[] args) throws Exception { // create configuration XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl(); config.setServerURL(new URL("http://127.0.0.1:8080/xmlrpc")); config.setEnabledForExtensions(true); config.setConnectionTimeout(60 * 1000); config.setReplyTimeout(60 * 1000); XmlRpcClient client = new XmlRpcClient(); // use Commons HttpClient as transport client.setTransportFactory( new XmlRpcCommonsTransportFactory(client)); // set configuration client.setConfig(config); // make the a regular call Object[] params = new Object[] { new Integer(2), new Integer(3) }; Integer result = (Integer) client.execute("Calculator.add", params); System.out.println("2 + 3 = " + result); // make a call using dynamic proxy ClientFactory factory = new ClientFactory(client); Adder adder = (Adder) factory.newInstance(Adder.class); int sum = adder.add(2, 4); System.out.println("2 + 4 = " + sum); } }
This is a subclass of the standalone WebServer, which offers a minimal servlet API. It is recommended to use this class, rather than the WebServer, because it offers you a smooth migration path to a full blown servlet engine.
Use of the ServletWebServer goes like this: First of all, create a servlet. It may be an instance of @link XmlRpcServlet or a subclass thereof. Note, that servlets are stateless: One servlet may be used by multiple threads (aka requests) concurrently. In other words, the servlet must not have any instance variables, other than those which are read only after the servlets initialization.
The XmlRpcServlet is by default using a property file named org/apache/xmlrpc/server/webserver/XmlRpcServlet.properties. See the PropertyHandlerMapping for details on the property file.
package org.apache.xmlrpc.demo.webserver; import java.net.InetAddress; import org.apache.xmlrpc.common.TypeConverterFactoryImpl; import org.apache.xmlrpc.demo.webserver.proxy.impls.AdderImpl; import org.apache.xmlrpc.server.PropertyHandlerMapping; import org.apache.xmlrpc.server.XmlRpcServer; import org.apache.xmlrpc.server.XmlRpcServerConfigImpl; import org.apache.xmlrpc.webserver.ServletWebServer; public class ServletServer { private static final int port = 8080; public static void main(String[] args) throws Exception { XmlRpcServlet servlet = new XmlRpcServlet(); ServletWebServer webServer = new ServletWebServer(servlet, port); webServer.start(); } }