Sunday, January 11, 2009

Roll Your Own REST with Open Social Clients

As in the last few posts, I've been looking at the OpenSocial REST/RPC client code. This can be used to access (for example) Orkut as a back-end service. The Java clients are here: http://code.google.com/p/opensocial-java-client/. The general (programming-language neutral) API is documented here: http://code.google.com/apis/orkut/docs/rest/developers_guide_protocol.html

The Java client examples show how to do simple stuff like access a person's profile data and his/her friends' information. The primary higher level API for this is the OpenSocialClient.java class. The problem is that this class only implements a subset of the possible REST/RPC commands at this time. Peeling back the covers a little, however, you can see that this class is really just constructing REST URLs or RPC commands using lower level classes, so you can in principle do this yourself and bypass OpenSocialClient entirely.

I decided to try this out. The code below uses the REST style invocations to duplicate the DisplayProfileData.java sample code. It turned out to be a little convoluted to do this because the OpenSocialClient class itself gets passed to lower level classes (particularly OpenSocialRequstSigner.java), which is not helpful. Hopefully the developers will reorganize this in the future.

Anyway, on to the code. I think it can be modified to construct any OpenSocial REST request, including PUT and DELETE (which are not currently in OpenSocialClient), but that is a test for another day. I don't claim this is beautiful. It is just my attempt to unravel everything.

I used Revision 33 of the SVN checkout. The OAuth snapshot jar in lib can cause problems if you use a different version. See comments below.

----------------

import org.opensocial.data.*;
import org.opensocial.client.*;
import net.oauth.*;
import java.util.*;

public class MyOpenSocialClient {

public static void main(String[] args) {
MyOpenSocialClient mosc=new MyOpenSocialClient();
}

public MyOpenSocialClient() {
String REST_BASE_URI=
"http://sandbox.orkut.com/social/rest";
String CONSUMER_SECRET=
"uynAeXiWTisflWX99KU1D2q5";
String CONSUMER_KEY=
"orkut.com:623061448914";
// String VIEWER_ID=
// "03067092798963641994";
String VIEWER_ID="08354253340777199997";

try {
//Construct the request object
OpenSocialRequest req=new OpenSocialRequest("people","people.get");
req.addParameter("groupId","@self");
req.addParameter("userId","08354253340777199997");

//Construct the request URL from the request object
//This is the OpenSocialBatch.submitRest() method.
OpenSocialUrl requestUrl = new OpenSocialUrl(REST_BASE_URI);
requestUrl.addPathComponent(req.getRestPathComponent());
if (req.getParameter("userId") != null) {
requestUrl.addPathComponent(req.getParameter("userId"));
}
if (req.getParameter("groupId") != null) {
requestUrl.addPathComponent(req.getParameter("groupId"));
}
System.out.println("This is the REST URL: "+requestUrl.toString());

//Now construct and sign the request
//This is the OpenSocialRequestSigner.signRequest() code.
OpenSocialHttpRequest request = new OpenSocialHttpRequest(requestUrl);
requestUrl.addQueryStringParameter("xoauth_requestor_id", VIEWER_ID);
requestUrl.addQueryStringParameter("st", "");

String postBody = request.getPostBody();
System.out.println("Post bod: "+postBody);
String requestMethod = request.getMethod();
// OpenSocialUrl requestUrl = request.getUrl();

OAuthMessage message =
new OAuthMessage(requestMethod, requestUrl.toString(), null);

if (postBody != null) {
message.addParameter(postBody, "");
}

OAuthConsumer consumer =
new OAuthConsumer(null, CONSUMER_KEY, CONSUMER_SECRET, null);
consumer.setProperty(OAuth.OAUTH_SIGNATURE_METHOD, OAuth.HMAC_SHA1);

OAuthAccessor accessor = new OAuthAccessor(consumer);
accessor.accessToken = "";

message.addRequiredParameters(accessor);

//VERSION WARNINIG: This works with SVN version 33 and oauth-20081115.jar but other
//versions may have problems.
for (Map.Entry p : message.getParameters()) {
if (!p.getKey().equals(postBody)) {
requestUrl.addQueryStringParameter(
OAuth.percentEncode(p.getKey()),
OAuth.percentEncode(p.getValue()));
}
}

//Take a look at the signed URL
System.out.println("Signed REST URL: "+requestUrl.toString());

//Done with signing. Now back to OpenSocialBatch's submitRest()
//Finally, get the response. This is the meat of the getHttpResponse
//method, without the error checking.
request.execute();
String responseString=request.getResponseString();
OpenSocialResponse resp=OpenSocialJsonParser.getResponse(responseString, req.getId());
//A little clunky--have to add this to the OpenSocialResponse
//object's "items" map with the "people" key.
resp.addItem("people",responseString);

System.out.println("Response: "+responseString);

//Now cast the response into an OpenSocialPerson object
OpenSocialPerson person=resp.getItemAsPerson("people");

System.out.println("----------");

// Output the name and ID of the requested person
System.out.println("Info. for " + person.getDisplayName());
System.out.println("ID: " + person.getId());

// Retrieve individual fields using the getField method; fields may be
// complex (objects or arrays) or simple (Strings), which you can
// determine by querying the object using the isComplex method.
// The thumbnail URL should be a simple field, so we'll output
// the value as a String
OpenSocialField thumbnailUrlField = person.getField("thumbnailUrl");
if (!thumbnailUrlField.isComplex()) {
System.out.println("Thumbnail URL: " +
thumbnailUrlField.getStringValue());
}

System.out.println("----------");

} catch (Exception e) {
System.out.println("Request failed:" );
e.printStackTrace();
}
}
}

1 comment:

Sergei said...

Hello! Thanks for your article. But could you connect to orkut and fetch data successfully? I couldn't :(
I tried to do as you described, but 500 error is returned.
I verify my gadget and install app into my profile. However, it doesn't help.
Also should VIEWER_ID be the same as USER ID to fetch profile information. Thanks.