my foray into xmlrpc - calling perl from java
XMLRPC is supposedly the new RPC mechanism to handle remote method calls independent of programming language, platform type, or location. Basically, it is how CORBA was supposed to be. Of course, it formats data into xml packaged into an HTTP message. This is not SOAP though, and I still don't really know the difference, other than SOAP is a the packaging for WebServices and has its own strict way of defining method and method arguments(?). Anyways, XML-RPC sounds perfect to get some crappy Perl to Java method calls working!
The problem as I see it, is that the XMLRPC mechanism is usually "extended" to handle language specific needs. This makes things a lot easier when you are using it to call remote methods in a uniform environment. The problem is that thats not what I want, as extensions in Java will make the Perl methods die. A horrible death.
In Perl there are two cpan modules that facilitate the XMLRPC mechanism that I tried: RPC::XML and XMLRPC::Transport in the SOAP::Lite package. I tried the XMLRPC::Transport package with no results (meaning I couldn't get it to work), but after installing the RPC::XML package and using the examples provided at this page (beware the text ads!). It seemed to get things working via his provided Client (on Page 3) and Server (Page 2) examples. His provided sample client and server perl uses only the non-extended XMLRPC mechanisms. Nice. [Note: I did not include links to Mr. Yang's perl because he might care about licenses and stuff (he copyrights his examples! ]
So anyways, what I did was write up some Java code to interface with Mr. Yang's Perl. Here is the example class, using the apache xmlrpc jars. The jar files** can be found at the Apache Web Service Page and are licensed under the Apache License.
import org.apache.xmlrpc.client.XmlRpcClient; import org.apache.xmlrpc.client.XmlRpcClientConfigImpl; import org.apache.xmlrpc.client.XmlRpcCommonsTransportFactory; import java.net.URL; import java.util.HashMap; /** * 03/2007 hello from tmarthal :: tmarthal.blogspot.com * * sample xmlrpc client using Apache's common xmlrpc-client classes to interface with a sample perl xmlrpc * server provided on the interwebs at http://www.herongyang.com/perl_b/rpc_xml_3.html * * The methods are (from the webpage): * It offers 3 methods: com.herong.hello, com.herong.getCelsius, and com.herong.getInfo. - com.herong.hello has one signature: return type of string, and no input argument. Its handler function is defined inside the add_method call. - com.herong.getCelsius has two signatures. The first one requires no input argument, and the second one requires one "double" argument. - com.herong.getInfo has one signature of returning an 'array'. The corresponding handler function returns a reference to a list. The server knows how to convert the content of the reference to an 'array'. * * There are also two XML:RPC system calls that get the list of available methods and method arguments: - system.listMethods returns an array of strings which are the names of the methods - system.methodSignature returns the HashMap of method name to string arguments * */ public class XmlRpcClientExample { public XmlRpcClientExample() { } private static final String SERVER_URL = "http://localhost:8001"; public double getCelcius(double temp) { double value = 0.0; try { XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl(); config.setServerURL(new URL(SERVER_URL)); 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 Double(temp) }; //TM Calculator is the name of the handler on the server side // and add is the method call Object result = (Object) client.execute("com.herong.getCelsius", params); // autobox / Java 5.0 value = (Double) result; System.out.println("result = " + value); } catch (Exception e) { e.printStackTrace(); } return value; } public String[] listMethods() { String[] methods = null; try { XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl(); config.setServerURL(new URL(SERVER_URL)); 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); // Null input Object[] params = null; // RPC Object result = client.execute("system.listMethods", params); // Cast the result as a list of objects try { Object[] methodsObjects = (Object[]) result; methods = new String[methodsObjects.length]; for (int i=0; i < methodsObjects.length; i++) { methods[i] = (String) methodsObjects[i]; System.out.println(i + " " + methods[i]); } } catch (ClassCastException e) { e.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); } return methods; } public HashMap<Integer, String[]> methodSignature(String method) { HashMap<Integer,String[]> signature = new HashMap<Integer,String[]>(); try { XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl(); config.setServerURL(new URL(SERVER_URL)); 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); // method Object[] params = new Object[] { method }; // RPC Object result = client.execute("system.methodSignature", params); // Cast the result as a list of objects try { Object[] returns = (Object[]) result; for (int k=0;k<returns.length;k++) { Object[] methodsObjects = (Object[]) returns[k]; String[] methods = new String[methodsObjects.length]; for (int i=0; i< methodsObjects.length; i++) { methods[i] = (String) methodsObjects[i]; //System.out.println(i + " " + methods[i]); } signature.put(k,methods); } } catch (ClassCastException e) { e.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); } return signature; } public String hello() { String returnString = ""; try { XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl(); config.setServerURL(new URL(SERVER_URL)); 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 = null; //TM Calculator is the name of the handler on the server side // and add is the method call Object result = (Object) client.execute("com.herong.hello", params); returnString = (String) result; } catch (Exception e) { e.printStackTrace(); } return returnString; } public String[] getInfo() { String[] info = null; try { XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl(); config.setServerURL(new URL(SERVER_URL)); 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); // Null input Object[] params = null; // RPC Object result = client.execute("com.herong.getInfo", params); // Cast the result as a list of objects try { Object[] infoObjects = (Object[]) result; info = new String[infoObjects.length]; for (int i=0; i< infoObjects.length; i++) { if (infoObjects[i].getClass().equals(String.class)) { info[i] = (String) infoObjects[i]; } else if (infoObjects[i].getClass().equals(Integer.class)){ Integer castedInteger = (Integer) infoObjects[i]; info[i] = (String) castedInteger.toString(); } else { continue; } System.out.println(i + " " + infoObjects[i]); } } catch (ClassCastException e) { e.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); } return info; } public static void main(String[] args) { XmlRpcClientExample x = new XmlRpcClientExample(); x.listMethods(); x.getCelcius(100.0); x.methodSignature("system.listMethods"); //System.out.println(x.hello()); x.getInfo(); } }
I hope the <code> tag CSS works! (Just in case, I used the outdated pre tag as well). The for
loop iterators and Java Generic markup had to be replaced. :(
The reason that I published this, instead of just keeping it is that XML-RPC resources from the web are lacking. Example code to interface between the Apache xmlrpc and Perl's XML::RPC package are non-existent. At least, with my limited google-fu, nothing turned up that would help me, so maybe Blogger will spyder this into a google search and be helpful to the one developer that finds this blog when looking for an example. Note that an IBM Web Development link "Utilize XML-RPC in Perl" was also helpful as a reference. If you read that and still can't figure out how to implement the methods in the linked code, comment here or find me and I can help.
** The jar files used are xmlrpc-client-3.0.jar, xmlrpc-common-3.0.jar, commons-codec-1.3.jar, common-logging-1.1.jar, ws-commons-util.1.0.1.jar, commons-httpclient-3.0.1.jar
Comments
also, you should blog about your editor. i'm sure it will be sweet.
** The jar files used are xmlrpc-client-3.0.jar, xmlrpc-common-3.0.jar, commons-codec-1.3.jar, common-logging-1.1.jar, ws-commons-util.1.0.1.jar, commons-httpclient-3.0.1.jar
----
The latest version of Apache XMLRPC seems to not include the "commons-codec" or "commons-httpclient" JAR files. The rest are there.
Do you still have a copy of these, or know where I can find them?
Using your code, I'm getting NoClassDefFoundError error messages looking for 'org/apache/commons/httpclient/'.
Am now getting "Connection Refused" errors - it never ends! ;-)
You're right - the documentation on anything relating to XML-RPC with Java is frustratingly useless - so thanks for this blog post. :)