Not so long ago, I already
wrote about the new project SpringSource:
spring-social . Today I want to tell (or rather show an example) how to use this library. As an example, we use the simplest application that allows you to log in to all three services and update the status at the same time in all three (caution - there are a lot of letters and code under the cut and there are no pictures at all).




The status update was taken as an example because:
* it is simple and clear;
* This function is not supported by spring-social for LinkedIn, that is, you can see how to add the implementation of API methods of services that are not implemented in spring-social by default.
In principle, any other functionality can be in place of the status update.
The developers themselves are advised to look at their GreenHouse application as an example of using the library - but the application is quite large and isolating the “social” part is not so easy. It was also published a couple of examples - but rather primitive - not giving a "complete picture." That's why I decided to write a small example (to figure it out myself and show it to others) which, on the one hand, would represent with myself some complete example from login to service to sending status, and on the other hand it would be as simple as possible. Because - I'm sorry that html is naked without any styles - plus the java-code is not “beautiful” everywhere - in some places the code could be done “more correctly”, for example, by inheritance, but it would “zymylylo” example - would have to deal with additional class hierarchy.
This post is a free translation of my
English-language post .
All sources are available in svn:
http://svn.emforge.net/svnroot/akakunin-experiments/update-statusSources can also be viewed on
the EmForge website.')
Basic application
As a “base” we use the simplest application on the spring framework and spring-mvc. This application uses the standard gentleman's set:
* Maven to build;
* Spring Framework 3.0.5;
* Spring-Security 3.0.5;
* JPA (via Hibernate);
* HSQL as a base.
In principle, everything is as standard and simple as possible. There is a single Entity -
net.emforge.updatestatus.entity.User - which stores users (userName, password). For each user, the tokens are also saved, which are used to work with each of the services.
There is a DAO class
UserDao , and a
UserServiceImpl service.
The application has two pages - the main (index) with a form for login and registration (processed by
UserController ), and the status page is accessible only to logged in users - we'll talk about it later.
All together, this is the simplest application where a user can register or log in, and on the status page connect with one of the services and update the status.
Application registration
In order for this example to work, you will first need to register your application with the services you intend to use:
* Registration on Facebook is carried out on page
http://developers.facebook.com/setup/ - only please use
localhost : 8080 / update-status / as SiteURL - otherwise the callback will not work;
* Sign up for Twitter:
http://dev.twitter.com/apps/new ;
* Register on LinkedIn:
https://www.linkedin.com/secure/developerIn each case, you will receive two keys - the application key (or application id) & security-token - they should be saved in
src / main / resources / config.propertiesRun Quick
If you want to quickly see how it works and you have java & maven (I hope that this is so :)), then just take the source from svn and execute (after you write your keys in config.properties)
mvn tomcat:run
After that, the application should be available at
http: // localhost: 8080 / update-statusCreate a new user - and go ahead, and we will see how it is all implemented
Connect on LinkedIn and Twitter
To work with both services, OAuth is used and the connection procedure is similar - so I'll show you with a twitter example.
To work with the service, we write the simplest class
TwitterProvider the main method in which is getOAuthService:
public OAuthService getOAuthService() {
OAuthConfig config = new OAuthConfig();
config.setRequestTokenEndpoint( "https://api.twitter.com/oauth/request_token" );
config.setAccessTokenEndpoint( "https://api.twitter.com/oauth/access_token" );
config.setAccessTokenVerb(Verb.POST);
config.setRequestTokenVerb(Verb.POST);
config.setApiKey(apiKey);
config.setApiSecret(apiSecret);
config.setCallback(callbackUrl);
return new OAuth10aServiceImpl(
new HMACSha1SignatureService(),
new TimestampServiceImpl(),
new BaseStringExtractorImpl(),
new HeaderExtractorImpl(),
new TokenExtractorImpl(),
new TokenExtractorImpl(),
config);
}
apiKey, apiSecret &
callbackUrl are injected from config.properties (where we first write from).
For the connection, add the link
/ connet / twitter and hang the handler (in
SocialController ):
@RequestMapping(value = "/connect/twitter" , method = RequestMethod.GET)
public String requestConnectionToTwitter(WebRequest request) {
// get request token
Token requestToken = twitterProvider.getOAuthService().getRequestToken();
// store request token in session
request.setAttribute( "twitter_request_token" , requestToken, WebRequest.SCOPE_SESSION);
return "redirect:" + twitterProvider.getAuthorizeUrl() + "?oauth_token=" + requestToken.getToken();
}
In essence, this handler redirects to Twitter in order to pass the login in it. In the case of a successful login, Twitter will redirect to callbackUrl and transfer the user's keys there. Let's hang up the handler for callBack in SocialController:
/** Callback from twitter on success login
*
* @param verifier
* @param request
* @return
*/
@RequestMapping(value = "/callback/twitter" , method = RequestMethod.GET, params = "oauth_token" )
public String authorizeTwitterCallback(@RequestParam(value = "oauth_verifier" , defaultValue = "verifier" ) String verifier,
WebRequest request) {
// get request token from session
Token requestToken = (Token)request.getAttribute( "twitter_request_token" , WebRequest.SCOPE_SESSION);
// get access token
Token accessToken = twitterProvider.getOAuthService().getAccessToken(requestToken, new Verifier(verifier));
String userName = getCurrentUser().getName();
userService.updateTwitterAuthentication(userName, accessToken.getToken(), accessToken.getSecret());
return "redirect:/status" ;
}
In this handler, we get the tokens and save them to the user for future use.
Facebook Connect
On Facebook, things are a little trickier. In order to connect to Facebook we place the button “Connect to Facebook” on our
status.jsp page using the following code:
< form id ="fb_signin" action ="<c:url value=" / connect / facebook " /> " method="post" >
< div class ="formInfo" >
</ div >
< div id ="fb-root" ></ div >
< p >< fb:login-button perms ="email,publish_stream,offline_access" onlogin ="$('#fb_signin').submit();" v ="2" length ="long" > Connect to Facebook </ fb:login-button ></ p >
</ form >
< facebook:init />
It is important to pay attention to the following points:
*
facebook: init tag is implemented in the spring-social library and it needs a
$ {facebookProvider} bin in the context of the spring context - we have it implemented in the
FacebookProvider class. In fact, the tag takes only keys from this bin;
*
fb: login-button generates the "Connect to Facebook" button;
* form-action
/ connect / facebook will be used as a callback in case of a successful login.
Just for this code to work it is necessary to connect tagLib
<%@ taglib uri="http://www.springframework.org/spring-social/facebook/tags" prefix="facebook" %>
and connect jQuery - in my case I just did
Well, you need to write a callback handler that will come to / connect / facebook:
@RequestMapping(value= "/connect/facebook" , method=RequestMethod.POST)
public String connectAccountToFacebook(@FacebookAccessToken String accessToken,
@FacebookUserId String facebookUserId) {
if (facebookUserId != null && accessToken != null ) {
// store facebook information
String userName = getCurrentUser().getName();
userService.updateFacebookAuthentication(userName, accessToken, facebookUserId);
}
return "redirect:/status" ;
}
* This source code was highlighted with Source Code Highlighter .
In this example, we will see two interesting annotations: @FacebookAccessToken and @FacebookUserId is one of the spring-social features, but in order for it to work, we need to add a special WebArgResolver to the project. To do this, add to
applicationContext.xml :
< bean id ="facebookWebArgResolver" class ="org.springframework.social.facebook.FacebookWebArgumentResolver" >
< constructor-arg name ="apiKey" value ="${facebook.appId}" />
</ bean >
* This source code was highlighted with Source Code Highlighter .
Handling callback is similar to Tiwtter & Linkedin - saving keys (in our case, facebookUserId & facebookToken) for further use.
Posting status on Twitter & Facebook
Log out, logged in, now you can update the status. On the status page, we have a textArea where the user enters text and sends a message. The form handler looks like this:
@RequestMapping(value = "/status" , method = RequestMethod.POST)
public String sendStatus(@Valid StatusForm statusForm, BindingResult result, ModelMap modelMap) {
User user = getCurrentUser();
LinkedInTemplateExt linkedInTemplate = linkedInProvider.createTemplate(user);
FacebookTemplate facebookTemplate = facebookProvider.createTemplate(user);
TwitterTemplate twitterTemplate = twitterProvider.createTemplate(user);
// send message to LinkedIn
if (linkedInTemplate != null ) {
linkedInTemplate.updateStatus(statusForm.getStatus());
}
// send message to Facebook
if (facebookTemplate != null ) {
facebookTemplate.updateStatus(statusForm.getStatus());
}
// send message to Twitter
if (twitterTemplate != null ) {
twitterTemplate.updateStatus(statusForm.getStatus());
}
return "redirect:/status" ;
}
* This source code was highlighted with Source Code Highlighter .
In this handler, we get the
FacebookTemplate &
TwitterTemplate (both classes from spring-social) using the keys of the current user and call their
updateStatus method
Posting LinkedIn Status
LinkedIn got a little more complicated - the LinkedInTemplate implemented in spring-social does not contain a method for updating the status - so we have to write it ourselves (using the REST-API call
api.linkedin.com/v1/people/~/person -activities ). To do this, we write the class
LinkedInTemplateExt - the successor of the “original” LinkedInTemplate in which:
* Initialize our own object of the
RestOperations class - using the special OAuth1RequestSignerFactory.getRequestSigner for “correct” call coding (another spring-social feature);
* Implement the updateStatus method itself:
public void updateStatus(String message) {
LinkedInPersonActivity personActivity = new LinkedInPersonActivity(message);
restOperationsExt.postForLocation( "http://api.linkedin.com/v1/people/~/person-activities" , personActivity);
}
* This source code was highlighted with Source Code Highlighter .
we also need to write a class
LinkedInPersonActivity - to pass the status:
@XmlRootElement(name = "activity" )
public class LinkedInPersonActivity {
public LinkedInPersonActivity() {
}
public LinkedInPersonActivity(String body) {
this .body = body;
}
@XmlElement(name = "content-type" )
String contentType = "linkedin-html" ;
@XmlElement(name = "body" )
String body;
}
* This source code was highlighted with Source Code Highlighter .
Everything - the method is ready - as you can see - despite the fact that LinkedInTemplate initially supports very few functions from the API - adding new functions is not such a difficult task.
That's all - I hope this example will be useful.