Friday, 22 June 2012

Facebook and Twitter: Behind the Scenes

In my last blog I created a simple Twitter application that uses the Spring Social Twitter module to access a user’s public time line data. As I said in that blog, the example isn’t particularly complicated and I’ll be adding extra features later. Before I do that I wanted to explain a little about what a Software as a Service (SaaS) provider, such as Facebook and Twitter, is and show something of what the Spring Social API is doing for you.

And the good news is that there’s nothing magical about SaaS services, they’re just a bunch of Web servers that respond to specific HTTP / HTTPS requests with either JSON or XML instead of HTML with the format of those requests published by the SaaS providers on their web sites.

For example, Twitter publishes what it calls their REST API RESOURCES, which you can find at this address:

https://dev.twitter.com/docs/api  

The sample code to retrieve a user’s time line that I provided in my last blog contained the following two lines:

  private List<Tweet> queryForTweets(String screenName) {

   
TimelineOperations timelineOps = twitter.timelineOperations();
    List<Tweet> results = timelineOps.getUserTimeline
(screenName);
    logger.info
("Fond Twitter timeline for :" + screenName + " adding " + results.size()
       
+ " tweets to model");
   
return results;
 
}

...which I suspect, without looking at the code, is issuing a:

https://api.twitter.com/1/statuses/user_timeline.json?include_entities=true&include_rts=true&screen_name=roghughe

...request and then reading the response, which’ll look something like this:

[{"created_at":"Fri Jun 15 19:58:49 +0000 2012","id":213722250153697280,"id_str":"213722250153697280","text":"RT @joshbloch: Incredibly depressing (to me) article on the state and future of Apple hardware http:\/\/t.co\/Mwv7MZFz . If I can't hack it ...","source":"\u003ca href=\"http:\/\/www.echofon.com\/\" rel=\"nofollow\"\u003eEchofon\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":117413536,"id_str":"117413536","name":"Roger Hughes","screen_name":"roghughe","location":"UK","description":"","url":"http:\/\/www.captaindebug.com","protected":false,"followers_count":11,"friends_count":18,"listed_count":0,"created_at":"Thu Feb 25 13:51:47 +0000 2010","favourites_count":87,"utc_offset":0,"time_zone":"London","geo_enabled":false,"verified":false,"statuses_count":19,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"B2DFDA","profile_background_image_url":"http:\/\/a0.twimg.com\/images\/themes\/theme13\/bg.gif","profile_background_image_url_https":"https:\/\/si0.twimg.com\/images\/themes\/theme13\/bg.gif","profile_background_tile":false,"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/729683837\/Head1_normal.jpg","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/729683837\/Head1_normal.jpg","profile_link_color":"93A644","profile_sidebar_border_color":"EEEEEE","profile_sidebar_fill_color":"FFFFFF","profile_text_color":"333333","profile_use_background_image":true,"show_all_inline_media":false,"default_profile":false,"default_profile_image":false,"following":null,"follow_request_sent":null,"notifications":null},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweeted_status":{"created_at":"Fri Jun 15 19:19:51 +0000 2012","id":213712443388600320,"id_str":"213712443388600320","text":"Incredibly depressing (to me) article on the state and future of Apple hardware http:\/\/t.co\/Mwv7MZFz . If I can't hack it, I don't want it.","source":"web","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":61135090,"id_str":"61135090","name":"Joshua Bloch","screen_name":"joshbloch","location":"Silicon Valley","description":"Effective Java author, API Designer, Swell guy","url":null,"protected":false,"followers_count":15836,"friends_count":94,"listed_count":942,"created_at":"Wed Jul 29 06:50:55 +0000 2009","favourites_count":3,"utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","geo_enabled":false,"verified":false,"statuses_count":1017,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"BADFCD","profile_background_image_url":"http:\/\/a0.twimg.com\/images\/themes\/theme12\/bg.gif","profile_background_image_url_https":"https:\/\/si0.twimg.com\/images\/themes\/theme12\/bg.gif","profile_background_tile":false,"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/1778383824\/joshbloch.original-1326394501_normal.jpg","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1778383824\/joshbloch.original-1326394501_normal.jpg","profile_link_color":"FF0000","profile_sidebar_border_color":"F2E195","profile_sidebar_fill_color":"FFF7CC","profile_text_color":"0C3E53","profile_use_background_image":true,"show_all_inline_media":false,"default_profile":false,"default_profile_image":false,"following":null,"follow_request_sent":null,"notifications":null},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":66,"entities":{"hashtags":[],"urls":[{"url":"http:\/\/t.co\/Mwv7MZFz","expanded_url":"http:\/\/goo.gl\/jmkfM","display_url":"goo.gl\/jmkfM","indices":[80,100]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false},"retweet_count":66,"entities":{"hashtags":[],"urls":[{"url":"http:\/\/t.co\/Mwv7MZFz","expanded_url":"http:\/\/goo.gl\/jmkfM","display_url":"goo.gl\/jmkfM","indices":[95,115]}],"user_mentions":[{"screen_name":"joshbloch","name":"Joshua Bloch","id":61135090,"id_str":"61135090","indices":[3,13]}]},"favorited":false,"retweeted":false,"possibly_sensitive":false},{"created_at":"Fri Jun 15 17:54:37 +0000 2012","id":213690991708880897,"id_str":"213690991708880897","text":"RT @bobbyllew: RT @jeffjarvis: In solidarity, every schoolkid everywhere should start tweeting pix of their meals:  http:\/\/t.co\/NWeROWBo","source":"\u003ca href=\"http:\/\/www.echofon.com\/\" rel=\"nofollow\"\u003eEchofon\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":117413536,"id_str":"117413536","name":"Roger Hughes","screen_name":"roghughe","location":"UK","description":"","url":"http:\/\/www.captaindebug.com","protected":false,"followers_count":11,"friends_count":18,"listed_count":0,"created_at":"Thu Feb 25 13:51:47 +0000 2010","favourites_count":87,"utc_offset":0,"time_zone":"London","geo_enabled":false,"verified":false,"statuses_count":19,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"B2DFDA","profile_background_image_url":"http:\/\/a0.twimg.com\/images\/themes\/theme13\/bg.gif","profile_background_image_url_https":"https:\/\/si0.twimg.com\/images\/themes\/theme13\/bg.gif","profile_background_tile":false,"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/729683837\/Head1_normal.jpg","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/729683837\/Head1_normal.jpg","profile_link_color":"93A644","profile_sidebar_border_color":"EEEEEE","profile_sidebar_fill_color":"FFFFFF","profile_text_color":"333333","profile_use_background_image":true,"show_all_inline_media":false,"default_profile":false,"default_profile_image":false,"following":null,"follow_request_sent":null,"notifications":null},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweeted_status":{"created_at":"Fri Jun 15 12:37:47 +0000 2012","id":213611259055181826,"id_str":"213611259055181826","text":"RT @jeffjarvis: In solidarity, every schoolkid everywhere should start tweeting pix of their meals:  http:\/\/t.co\/NWeROWBo","source":"\u003ca href=\"http:\/\/www.tweetdeck.com\" rel=\"nofollow\"\u003eTweetDeck\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":1077971,"id_str":"1077971","name":"Robert Llewellyn","screen_name":"bobbyllew","location":"London","description":"Conflicted wet liberal, amateur nerd, every few years Kryten in Red Dwarf. Electric vehicle fan and Stone Cold Fox (apparently)","url":"http:\/\/www.llewtube.com","protected":false,"followers_count":85654,"friends_count":668,"listed_count":3418,"created_at":"Tue Mar 13 09:20:36 +0000 2007","favourites_count":20,"utc_offset":0,"time_zone":"London","geo_enabled":false,"verified":true,"statuses_count":37079,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"253132","profile_background_image_url":"http:\/\/a0.twimg.com\/profile_background_images\/366726010\/NFGsmall.jpg","profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/366726010\/NFGsmall.jpg","profile_background_tile":true,"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/1183945291\/twitterpic2_normal.jpg","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1183945291\/twitterpic2_normal.jpg","profile_link_color":"4242DA","profile_sidebar_border_color":"87BC44","profile_sidebar_fill_color":"A3DF0D","profile_text_color":"000000","profile_use_background_image":true,"show_all_inline_media":true,"default_profile":false,"default_profile_image":false,"following":null,"follow_request_sent":null,"notifications":null},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":27,"entities":{"hashtags":[],"urls":[{"url":"http:\/\/t.co\/NWeROWBo","expanded_url":"http:\/\/bit.ly\/LrgTtP","display_url":"bit.ly\/LrgTtP","indices":[101,121]}],"user_mentions":[{"screen_name":"jeffjarvis","name":"Jeff Jarvis","id":11435642,"id_str":"11435642","indices":[3,14]}]},"favorited":false,"retweeted":false,"possibly_sensitive":false},"retweet_count":27,"entities":{"hashtags":[],"urls":[{"url":"http:\/\/t.co\/NWeROWBo","expanded_url":"http:\/\/bit.ly\/LrgTtP","display_url":"bit.ly\/LrgTtP","indices":[116,136]}],"user_mentions":[{"screen_name":"bobbyllew","name":"Robert Llewellyn","id":1077971,"id_str":"1077971","indices":[3,13]},{"screen_name":"jeffjarvis","name":"Jeff Jarvis","id":11435642,"id_str":"11435642","indices":[18,29]}]},"favorited":false,"retweeted":false,"possibly_sensitive":false}]

...which I’ve truncated to save space. Once the Twitter module has the above data, it’ll then parse it into a list of Tweet beans using the Jackson Java JSON-processor and a few other classes. This list of Tweet beans is what your application receives as the return value to its call.

Similarly, Facebook have an API that they call the Graph API; a good name because it allows the caller to access a user’s social graph. Details of the Graph API are available here - although you do need to login to see it:

https://developers.facebook.com/docs/reference/api/

If you’ve seen the Spring Social QuickStart sample you’ll know that it demonstrates how to get hold of a list of your Facebook friends using the following code:

  @RequestMapping(value = "/", method = RequestMethod.GET)
 
public String home(Model model) {
   
List<Reference> friends = facebook.friendOperations().getFriends();
    model.addAttribute
("friends", friends);
   
return "home";
 
}

and, like the Twitter code above, I’ll bet that Keith Donald’s code maps to a Facebook API request that looks something like this:

https://graph.facebook.com/me/friends?access_token=AAAAAAITEghMWiBBlEC5OGXjotoIjzn7r3KinZAWIbbleoHX1rYFlMMr9cvUmAEYEtQ1ZCHPFSbop2gXQ1ibbleeIWAbrZChwfrcywlYGTWIBBLeAVbjNnWjIlE

...the result of which is similarly parsed and returned to your app as a list of POJOs.

However, note the access token, or to give it its full name, the OAuth access token. This is known only by your application and Facebook and is used to grant your application access to your user’s private data. This is what OAuth is all about: your application obtaining an access token so that it can access your user's Facebook, LinkedIn, Sina Weibo or other private SaaS data.

So, how does you app get hold of an access token. It turns out that there are a few hoops to jump through, but more on that next time...


No comments: