Making XMLRPC Requests with Google App Engine Java
I started playing with Google App Engine - Java and thought an interesting place to start would be xmlrpc requests. I soon learned though that the inability of app engine applications to open sockets imposed a severe limitation (i.e. "It won't work") on the Groovy XMLRPC module and I was left with figuring out a new way to do it. Thinking that there really isn't anything too special about xmlrpc requests and that it is still just an http post, I again wondered, "how hard could this really be?" Two frustration-filled weeks later, a solution!
Initially, I thought about using the URLFetch service built in to GAE because it is built-in but trying and failing to override the user-agent prompted a switch to HTTPBuilder, (and specifically, HttpUrlClient).
After running some wireshark traces of my bling script in action and also reviewing the specification, XMLRPC requests all contain the following elements:
- A User-Agent must be set.
- The content-type must be set to "text/xml"
- Obviously, the http request method is a POST
- content-length must be specified and correct
HTTPBuilder takes care of the first and last bullet while providing all the functionality needed to customize the request as needed. Below is a sample HttpUrlClient request:
def url = 'http://blogsearch.google.com/ping/RPC2' def http = new HttpURLClient(url: url) def resp = http.request( )
Line 3 above is where all the customization needed to go. From the bullets above, I know I need to specify that this request's method is an http post, that the content type needs to be set to 'text/xml', and to satisfy my vanity, I specify a custom User-Agent. I also want to take advantage of keep-alives if possible and I'm also specifying an Accept header.
One other item that I need to specify is the post body. The post body contains the actual method call that the XMLRPC endpoint is supposed to process. Initially, I thought it would be fine if I were to simply set a variable named xmlreq
to the string below and then set a body
property in the request.
xmlreq = """ <?xml version="1.0"?> <methodCall> <methodName>weblogUpdates.ping <params> <param> <value>Someblog</value> </param> <param> <value>http://spaces.msn.com/someblog</value> </param> </params> </methodCall> """
This, however, wound up failing because the XMLRPC servers (in many but not all cases) hated the xml in my request. Instead, I ended up using MarkupBuilder to create the XML Request. Below is the markup builder code used to generate the XML request:
def xmlreq = new StringWriter() xml = new MarkupBuilder(xmlreq).methodCall { methodName { mkp.yield ( 'weblogUpdates.ping' ) } params { param { value { string { mkp.yield ( "${blogTitle}" ) } } } param { value { string { mkp.yield ( "${blogUrl}") } } } } }
Now I have completed the customization and I am now using a request similar to the one below:
def resp = http.request(method: POST, contentType: 'text/xml', body: xmlreq, requestContentType: XML, headers:['Accept':'text/xml','User-Agent':'Bling!','Connection':'keep-alive'] )
The final version of a working XMLRPC ping request using Gaelyk and Google App Engine - Java:
def xmlreq = new StringWriter() xml = new MarkupBuilder(xmlreq).methodCall { methodName { mkp.yield ( 'weblogUpdates.ping' ) } params { param { value { string { mkp.yield ( "${blogTitle}" ) } } } param { value { string { mkp.yield ( "${blogUrl}") } } } } } def url = 'http://blogsearch.google.com/ping/RPC2' def http = new HttpURLClient(url: url) def resp = http.request(method: POST, contentType: 'text/xml', body: xmlreq, requestContentType: XML, headers:['Accept':'text/xml','User-Agent':'Bling!','Connection':'keep-alive'] )