Wednesday, October 14, 2009

Java API for accessing Bugzilla

It just happened one day that my manager came to me and said we should automate our build process to make our life easier. It is then we started analyzing what exactly we want to automate among the zillion steps we do as part of the build process.

One of steps is that we should be able to programatically access bugzilla and do some operations with the bug. Probably search for a list of bugs and verify the comments etc.. It turns out Bugzilla only supports XML-RPC and JSON-RPC protocols, where as XML-RPC being the stable one we went ahead and picked it.

Since its the first time I'm going to use XML-RPC, I did some research on net about it and found that there's one cool API framework available from apache, its called 'The Apache XML-RPC client'.

Just like any other API from apache, it came with loads of documentation and Javadocs.., a big thanks to those geeks for maintaining code.

Coming back to problem at hand, All I wanted to do was to access bugzilla and login and do some operations in it with a list of bugs. Following is the code snippet that I simply copied and modified from the apache's examples for XML-RPC 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;


public class Bugzilla {
public static void main(String[] args) throws MalformedURLException, XmlRpcException {
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
config.setServerURL(new URL("http://bug-tracker.yyyy.com/Bugzilla/xmlrpc.cgi"));

XmlRpcClient client = new XmlRpcClient();
client.setConfig(config);

Object[] params = new Object[]{"xxx@yyy.com", "password"};

Object result = client.execute("User.login", params);
System.out.println("Result = "+result);
}
}


When i ran this, it started throwing this exception..


Exception in thread "main" java.lang.ClassCastException: java.lang.String
at org.apache.xmlrpc.parser.XmlRpcResponseParser.addResult(XmlRpcResponseParser.java:61)
at org.apache.xmlrpc.parser.RecursiveTypeParserImpl.endValueTag(RecursiveTypeParserImpl.java:78)
at org.apache.xmlrpc.parser.XmlRpcResponseParser.endElement(XmlRpcResponseParser.java:186)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(AbstractSAXParser.java:633)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanEndElement(XMLNSDocumentScannerImpl.java:719)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(XMLDocumentFragmentScannerImpl.java:1685)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:368)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:834)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:764)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:148)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1242)
at org.apache.xmlrpc.client.XmlRpcStreamTransport.readResponse(XmlRpcStreamTransport.java:186)
at org.apache.xmlrpc.client.XmlRpcStreamTransport.sendRequest(XmlRpcStreamTransport.java:156)
at org.apache.xmlrpc.client.XmlRpcHttpTransport.sendRequest(XmlRpcHttpTransport.java:115)
at org.apache.xmlrpc.client.XmlRpcSunHttpTransport.sendRequest(XmlRpcSunHttpTransport.java:69)
at org.apache.xmlrpc.client.XmlRpcClientWorker.execute(XmlRpcClientWorker.java:56)
at org.apache.xmlrpc.client.XmlRpcClient.execute(XmlRpcClient.java:167)
at org.apache.xmlrpc.client.XmlRpcClient.execute(XmlRpcClient.java:137)
at org.apache.xmlrpc.client.XmlRpcClient.execute(XmlRpcClient.java:126)
at Bugzilla.main(Bugzilla.java:21)

The error message is confusing and doesn't really point me to the root cause.

After an hour of search, found the mystery with the error above. It turns out Bugzilla follows certain rules with the XML-RPC implementation.
  1. All the parameters that are passed to Bugzilla must be a named parameter.
  2. All the parameters are treated as 'struct' tag by Bugzilla, so we must pass all the parameters as keys and values in a HashMap.
Following is modified code...



import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;


public class Bugzilla {
public static void main(String[] args) throws MalformedURLException, XmlRpcException {
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
config.setServerURL(new URL("http://bug-tracker.yyyy.com/Bugzilla/xmlrpc.cgi"));

XmlRpcClient client = new XmlRpcClient();
client.setConfig(config);

Map map = new HashMap();
map.put("login","xxxx@yyyy.com");
map.put("password","password");

Map result = (Map) client.execute("User.login", new Object[]{map});
System.out.println("Result = "+result);
}
}


The response from bugzilla is again a Map object, so I had to cast it to make sure I can access elements in it.

Now comes the next hurdle. Bugzilla's XML-RPC apis also assume that the client maintains the session with the server using cookies. The problem now is to figure out a way to do that with the apache XML-RPC client APIs. I'm going to write about it in my next blog post.