Monday, 18 June 2012

Getting Started with Spring Social

Like me, you will not have failed to notice the current rush to ‘socialize’ applications, whether it’s adding a simple Facebook ‘Like’ button, a whole bunch of ‘share’ buttons or displaying timeline information. Everybody’s doing it including the Guys at Spring, and true to form they’ve come up with a rinky-dinky API called Spring Social that allows you to integrate your application with a number of Software as a Service (SaaS) feeds such as Twitter, Facebook, LinkedIn etc.

This, and the following few blogs, takes a look at the whole social scene by demonstrating the use of Spring Social, and I’m going to start by getting very basic.

If you’ve seen the Spring Social Samples you’ll know that they contain a couple of very good and complete ‘quickstart’ apps; one for Spring 3.0.x and another for Spring 3.1.x. In looking into these apps, the thing that struck me was the number of concepts you have to learn in order to appreciate just what’s going on. This includes configuration, external authorization, feed integration, credential persistence etc... Most of this complexity stems from the fact that your user will need to login to their Software as a Service (SaaS) account, such as Twitter, Facebook or QZone, so that your application can access their data1. This is further complicated by the large number of SaaS providers around together with the different number of authorization protocols they use.

So, I thought that I’d try and break all this down into the various individual components explaining how to build a useful app; however, I’m going to start with a little background.

The Guys at Spring have quite rightly realized that there are so many SaaS providers on the Internet that they’ll never be able to code modules for all of them, so they’ve split the functionality into two parts, with the first parting comprising the spring-social-core and spring-social-web modules that provide the basic connectivity and authorization code for every SaaS provider. Providing all this sounds like a mammoth task but it’s simplified in that there aren't that many published security protocols and in any case most SaaS providers implement what’s known as the OAuth protocol, which seems to be the de-facto standard. I’m not going into OAuth details just yet, but in a nutshell the OAuth protocol performs a complicated little jig that allows the user to share their SaaS data (i.e. stuff they have on Facebook etc) with your application without the user handing out their credentials to your application. There are at least three versions: 1.0, 1.0a and 2.0 and SaaS providers are free to implement any version they like, often adding their own proprietary features.

The second part of this split consists of the SaaS provider modules that know how to talk to the individual service provider servers at the lowest levels. The Guys at Spring currently provide the basic services, which to the Western World are Facebook, LinkedIn and Twitter. The benefit of taking the extensive modular approach is that there’s also a whole bunch of other community led modules that you can use:


This, however, is only fraction of the number of services available: to see how large this list is visit the AddThis web site and find out what services they support.



Back to the Code


Now, if you’re like me, then when it comes to programming you’ll hate security: from a development view point it’s a lot of faff, stops you from writing code and makes your life difficult, so I thought I’d start off by throwing all that stuff away and write a small app that displays some basic SaaS data. This, it turns out, is possible as some SaaS providers, such as Twitter, serve both private and public data. Private data is the stuff that you need to login for, whilst public data is available to anyone.

In today’s scenario, I’m writing a basic app that displays a Twitter user’s time line in an application using the Spring Social Twitter Module and all you’ll need to do this is the screen name of a Twitter user.

To create the application, the first step is to create a basic Spring MVC Project using the template section of the SpringSource Toolkit Dashboard. This provides a webapp that’ll get you started.

The second step is to add the following dependencies to your pom.xml file:

<!-- Twitter API -->
    <dependency>
        <groupId>org.springframework.social</groupId>
        <artifactId>spring-social-twitter</artifactId>
        <version>${org.springframework.social-twitter-version}</version>
    </dependency>

     <!-- CGLIB, only required and used for @Configuration usage: could be removed in future release of Spring -->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib-nodep</artifactId>
        <version>2.2</version>
    </dependency>

The first dependency above is for Spring Social’s Twitter API, whilst the second is required for configuring the application using Spring 3’s @Configuration annotation. Note that you’ll also need to specify the Twitter API version number by adding:

<org.springframework.social-twitter-version>1.0.2.RELEASE</org.springframework.social-twitter-version>

...to the <properties> section at the top of the file.

Step 3 is where you need configure Spring. If you look at the Spring Social sample code, you’ll notice that the Guys at Spring configure their apps using Java and the Spring 3 @Configuration annotation. This is because Java based configuration allows you a lot more flexibility than the original XML based configuration.

@Configuration
public class SimpleTwitterConfig {

 
private static Twitter twitter;

 
public SimpleTwitterConfig() {

   
if (twitter == null) {
     
twitter = new TwitterTemplate();
   
}
  }

 
/**
   * A proxy to a request-scoped object representing the simplest Twitter API
   * - one that doesn't need any authorization
   */
 
@Bean
  @Scope
(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
 
public Twitter twitter() {
   
return twitter;
 
}

}

All that the code above does is to provide Spring with a simple TwitterTemplate object via its Twitter interface. Using @Configuration is strictly overkill for this basic application, but I will be building upon it in future blogs.

For more information on the @Configuration annotation and Java based configuration, take a look at:

  1. Spring's Java Based Dependency Injection
  2. More Spring Java based DI

Having written the configuration class the next thing to do is to sort out the controller. In this simple example, I’ve used a straight forward @RequestMapping handler that deals with URLs that look something like this:

<a href=timeline?id=roghughe>Grab Twitter User Time Line for @roghughe</a><br />

...and the code looks something like this:

@Controller
public class TwitterTimeLineController {

 
private static final Logger logger = LoggerFactory.getLogger(TwitterTimeLineController.class);

 
private final Twitter twitter;

 
@Autowired
 
public TwitterTimeLineController(Twitter twitter) {
   
this.twitter = twitter;
 
}

 
@RequestMapping(value = "timeline", method = RequestMethod.GET)
 
public String getUserTimeline(@RequestParam("id") String screenName, Model model) {

   
logger.info("Loading Twitter timeline for :" + screenName);

    List<Tweet> results = queryForTweets
(screenName);

   
// Optional Step - format the Tweets into HTML
   
formatTweets(results);

    model.addAttribute
("tweets", results);
    model.addAttribute
("id", screenName);

   
return "timeline";
 
}

 
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;
 
}

 
private void formatTweets(List<Tweet> tweets) {

   
ByteArrayOutputStream bos = new ByteArrayOutputStream();
    StateMachine<TweetState> stateMachine = createStateMachine
(bos);

   
for (Tweet tweet : tweets) {

     
bos.reset();
      String text = tweet.getText
();
      stateMachine.processStream
(new ByteArrayInputStream(text.getBytes()));

      String out = bos.toString
();
      tweet.setText
(out);
   
}
  }

 
private StateMachine<TweetState> createStateMachine(ByteArrayOutputStream bos) {

   
StateMachine<TweetState> machine = new StateMachine<TweetState>(TweetState.OFF);

   
// Add some actions to the statemachine
   
machine.addAction(TweetState.OFF, new DefaultAction(bos));
    machine.addAction
(TweetState.RUNNING, new DefaultAction(bos));
    machine.addAction
(TweetState.READY, new ReadyAction(bos));
    machine.addAction
(TweetState.HASHTAG, new CaptureTag(bos, new HashTagStrategy()));
    machine.addAction
(TweetState.NAMETAG, new CaptureTag(bos, new UserNameStrategy()));
    machine.addAction
(TweetState.HTTPCHECK, new CheckHttpAction(bos));
    machine.addAction
(TweetState.URL, new CaptureTag(bos, new UrlStrategy()));

   
return machine;
 
}

}

The getUserTimeline method contains three steps: firstly it gets hold of some tweets, does a bit of formatting and then puts the results into the model. In terms of this blog, getting hold of the tweets in the most important point and you can see that this is done in the List<tweet> queryForTweets(String screenName) method. This methods has two steps: use the Twitter object to get hold of a TimelineOperations instance and then use that object to query a time line using using screen name the argument.

If you look at the Twitter interface, it acts as a factory object returning other objects that deal with different Twitter features: timelines, direct messaging, searching etc. I guess that this is because the developers realized that Twitter itself encompasses so much functionality that if all the required methods were in one class, then they’d have a God Object on their hands.

I’ve also included the optional step of converting the Tweets into HTML. To do this I’ve used the JAR from my State Machine project and blog and you can see how this is done in the formatTweets(...) method.

After putting the list of Tweets in to the model as an attribute, the final thing to accomplish is to write a JSP to display the data:

<ul>
    <c:forEach items="${tweets}" var="tweet">
        <li><img src="${tweet.profileImageUrl}" align="middle"/><c:out value="${tweet.createdAt}"/><br/><c:out value="${tweet.text}" escapeXml="false"/></li>
    </c:forEach>
</ul> 

If you implement the optional anchor tag formatting then the key thing to remember here is to ensure that the formatted Tweet’s HTML is picked up by the browser. This is achieved by either using the escapeXml="false" attribute of the c:out tag or to place ${tweet.text} directly into the JSP.

I haven’t included any styling or a fancy front end in this sample, so if you run the code2 you should get something like this:



And that completes my simple introduction to Spring Social, but there’s still a lot of ground to cover. In my next blog, I’ll be taking a look at what’s going on in the background.


1I'm guessing that there's lots of privacy and data protection legality issues to consider here, especially if you use this API to store your users' data and I'd welcome comments and observations on this.

2The code is available on GitHub at https://github.com/roghughe/captaindebug/tree/ce33615b3581dc0d35693e46235b3cca422b88ec/social in the social project.





1 comment:

Unknown said...

Hi Roger,

Little fix: "Spring Social Flickr" and "Spring Social Foursquare" links are replaced.

Thanks.