Spring Social Integration

So, you may notice that I am something of a Spring fan, and generally like to try out different Spring projects where I get the chance. I have been aware of the Spring Social project for a little while (was looking at integrating LinkedIn with flutterby but decided against it), so recently, when a competition was announced in work to create a Java web app in the cloud for a chance to win a new Android tablet I thought this would be a good chance to put together something of a showcase of technology.

As always, the web app is a straight forward Spring MVC project built with the current latest Spring libraries (3.1 - also making use of declarative caching with ehcache, which was nice) and also includes the Spring Data MongoDB integration (again - primarily because it seems to be the most commonly supported NoSQL db in the cloud.. well CloudFoundry and OpenShift anyways). I also took the opportunity to integrate some Spring Social stuff, so I will go through that, as its pretty easy (will post again on some thoughts about Redhat's OpenShift platform and further SpringSocial stuff).



Maven Configuration

First of all, there is obviously some Maven configuration needed in the pom. It took me a little while to find the correct repositories to get all the different Spring Social libraries from (some implementations are less mature than others so not all have full proper releases yet)


	 spring-snapshot
	 Spring Maven Snapshot Repository
	 http://maven.springframework.org/snapshot



	 spring-milestone
	 Spring Maven Milestone Repository
	 http://maven.springframework.org/milestone




 Next we need to add the libraries we need:


	org.springframework.social
	spring-social-web
	${spring-social.version}
	
		
			spring-core
			org.springframework
		
		
			spring-web
			org.springframework
		
		
			spring-webmvc
			org.springframework
		
	



	org.springframework.social
	spring-social-github
	${spring-social-github.version}



	org.springframework.social
	spring-social-twitter
	${spring-social-twitter.version}


As you can see, we take the core Spring Social library, and for my project I also used Twitter and GitHub integration (I also had LinkedIn early on, but removed that as the LinkedIn terms of use on the API are pretty limiting). I added the exclusions to ensure that the latest Spring libraries were being used, as these were declared elsewhere in the Pom.



Storing User Connection Details

Spring Social supports both OAuth1 and OAuth2, and provides the underlying mechanisms to perform the "OAuth Dance" which enables your application (once approived by a user) to capture the access token required to enable your application to access the third party API. Usually your application would capture the user details including access token and store them (encrypted of course!) so the user does not need to approve the application every time they use it. There are examples in the Spring Social showcase on GitHub of doing this with a standard JDBC connection implementation, but as I was capturing my user details in Hibernate I decided to implement a Hibernate version.

To implement your own storage mechanism you need two classes, a ConnectionRepository and a UsersConnectionRepository and must implement the same interfaces:

public class HibernateUsersConnectionRepository implements UsersConnectionRepository { 

public class HibernateConnectionRepository implements ConnectionRepository {


You can see my hibernate implementations here. Obviously I also needed a Hibernate entity to represent this connection details, but that was a straight forward entity as follows:

@Entity
@Table(name = "CV_CONNECTION")
public class SocialConnection {

       @Id
       private Long id;
       private String providerId;
       private String providerUserId;
       private String displayName;
       private String profileUrl;
       private String imageUrl;
       private String accessToken;
       private String secret;
       private String refreshToken;
       private Long expireTime;
       private Integer rank;


Configuring the Beans

The next step is to define the  Spring Social beans to use (such as Twitter, GitHub etc) so they can be used throughout the application.

For this I used a Configuration class rather than adding the config directly to my application context xml (this just involves creating a POJO and annotating it with the @Configuration annotation - this then allows you to create code equivalents of xml config that would normally exist in app context xml - we will see more in the details below).

@Configuration
public class SocialConfiguration {


The beans we need to define are the ConnectionRepository, UsersConnectionRepository, ConnectionFactoryLocator and then any Social interface that we are using, in our case Twitter and GitHub.

ConnectionFactoryLocator:


@Bean
public ConnectionFactoryLocator connectionFactoryLocator() {
	  ConnectionFactoryRegistry registry = new ConnectionFactoryRegistry();
	  registry.addConnectionFactory(new GitHubConnectionFactory(gitHubKey, gitHubSecret));
	  registry.addConnectionFactory(new TwitterConnectionFactory(twitterKey, twitterSecret));
	  return registry;
}

The ConnectionFactoryLocator is simply used to initialise the UsersConnectionRepository, for every SpringSocial third party you want to use, you need to add a ConnectionFactory instance as per above - all Spring Social implementations will provide a ConnectionFactory implementation.

The @Bean annotation is like defining a <bean> in the application context xml.

UsersConnectionRepository:


@Bean
@Scope(value = "singleton", proxyMode = ScopedProxyMode.INTERFACES)
public UsersConnectionRepository usersConnectionRepository() {
	  HibernateUsersConnectionRepository repository = new HibernateUsersConnectionRepository(connectionFactoryLocator(), Encryptors.noOpText());
	  return repository;
}

This is to configure the UsersConnectionRepository - as you can see, in this case we are telling Spring to instantiate a HibernateUsersConnectionRepository.


ConnectionRepository:

This one is straight forward as well - again, using the Hibernate implementation of the interface and

@Bean
@Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
public ConnectionRepository connectionRepository() {
	  Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
	  if (authentication == null) {
			 throw new IllegalStateException("Unable to get a ConnectionRepository: no user signed in");
	  }
	  ApplicationUser user = (ApplicationUser) authentication.getPrincipal();
	  return usersConnectionRepository().createConnectionRepository(String.valueOf(user.getAccountId()));
}

You will note that this method also includes an @Scope annotation the value is set to "request" - this is because for every individual user that accesses the applciation, we want them to have access to their own connection (so the application connects to their twitter/github accounts etc, so this bean is scoped to each individual user request (but for user session life). 

ThirdParty Interfaces:

Finally we need a config method for every third party interface that we want to use - they all take a common form as follows:

@Bean
@Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
public Twitter twitter() {
	if (connectionRepository().findPrimaryConnection(Twitter.class) != null) {
		return connectionRepository().findPrimaryConnection(Twitter.class).getApi();
	}
	return null;
}

You will note once again these are scoped to  a user's request and simply gets the connection from the ConnectionRepository for the given interface.



Using the Interfaces

Every implementation exposes the third party API, so each will have a specific API available to it to allow you to perform common tasks (depending on the maturity of the implementation, it may only offer a limited subset of the overall API) .

Below is an example of using the Twitter interface in the Spring-Social-Twitter library to post an update:

@Autowired
private Twitter twitter;
public void postTweet(String content) {
	  twitter.timelineOperations().updateStatus(content);
}

As you can see, once the Twitter interface has been configured and injected, it becomes very simple to utilise the API.



On the whole, it is a relatively simple set of libraries to use and offers (an ever growing) set of integrations with third party sites so worth having a look at.


As always, the entire code base is on my GitHub account (if this link fails, then check the GitHub link on the right) and for the time being the application is being hosted on the CloudFoundry platform (although this is still in development, so it may come up and down, and data may be cleared out intermittently - it is not intended as a live production site and you should not put any data in there that you want to keep!) - The application is a dynamic online resume/CV application, that allows users to create a CV as well as pull in data from their GitHub accounts to show off actual examples of code and skills etc.

2 comments:

  1. Hi,
    Can u plz provide me the working example?
    Thanks in advance

    ReplyDelete