Skip to content

Instantly share code, notes, and snippets.

@vrudikov
Forked from sody/FacebookConnect.java
Created May 21, 2012 07:24
Show Gist options
  • Save vrudikov/2760935 to your computer and use it in GitHub Desktop.
Save vrudikov/2760935 to your computer and use it in GitHub Desktop.
spring-social and tapestry5
package com.example.components;
import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.EventConstants;
import org.apache.tapestry5.Link;
import org.apache.tapestry5.annotations.Cached;
import org.apache.tapestry5.annotations.OnEvent;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.annotations.RequestParameter;
import org.apache.tapestry5.corelib.base.AbstractComponentEventLink;
import org.apache.tapestry5.internal.util.CaptureResultCallback;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.ioc.annotations.InjectService;
import org.springframework.social.facebook.api.Facebook;
import org.springframework.social.oauth2.AccessGrant;
import org.springframework.social.oauth2.GrantType;
import org.springframework.social.oauth2.OAuth2Parameters;
import org.springframework.social.oauth2.OAuth2ServiceProvider;
import java.net.MalformedURLException;
import java.net.URL;
/**
* This component represents link that connects users to their facebook accounts using OAuth2 protocol. After clicking
* this link users will be redirected to facebook authorization page and then returned back to the page where they come
* from. After facebook authorization this component triggers {@link EventConstants#SUCCESS} or
* {@link EventConstants#FAILURE} events according to authorization result. Success event comes with access token
* as event context, so developers can then use it to access facebook api.
*
* @author Ivan Khalopik
* @since 1.0
*/
public class FacebookConnect extends AbstractComponentEventLink {
private static final String CONNECT_EVENT = "connect";
private static final String CONNECTED_EVENT = "connected";
/**
* This parameter defines OAuth2 application scope.
*/
@Parameter(defaultPrefix = BindingConstants.LITERAL)
private String scope;
@Inject
private ComponentResources resources;
/**
* Injected spring-social OAuth2 service for facebook. It should be configured somewhere in application modules.
*/
@InjectService("facebookService")
private OAuth2ServiceProvider<Facebook> facebookService;
/**
* Generates OAuth2 redirectUri as event link to current component with internal {@code 'connected'} event.
* This event will be processed inside this component later.
*
* @return OAuth2 redirectUri
*/
@Cached
protected String getRedirectUri() {
return resources.createEventLink(CONNECTED_EVENT).toAbsoluteURI();
}
/**
* Generates component link that will present on html markup as a connect URL. This link will produce internal
* {@code 'connect'} event. Then this event will be processed by this component to generate correct facebook
* authorization URL.
*
* @param eventContext event context
* @return link that will present on html markup as a connect URL
*/
@Override
protected Link createLink(final Object[] eventContext) {
return resources.createEventLink(CONNECT_EVENT);
}
/**
* Event handler for internal {@code 'connect'} event. Generates correct facebook authorization URL with all needed
* parameters.
*
* @return facebook authorization URL
* @throws MalformedURLException for incorrect URL produced by facebook service
*/
@OnEvent(CONNECT_EVENT)
URL connect() throws MalformedURLException {
// generate oauth parameters with redirect uri
final OAuth2Parameters parameters = new OAuth2Parameters();
if (scope != null) {
parameters.setScope(scope);
}
parameters.setRedirectUri(getRedirectUri());
// build and return connection url as redirect response
return new URL(buildConnectURL(parameters));
}
/**
* Event handler for internal {@code 'connected'} event. Processes reply came from facebook authorization page. If
* access was granted then it tries to get OAuth2 access token and triggers {@link EventConstants#SUCCESS} event with
* access token as event context. If access was denied or error occurs while getting access token then
* {@link EventConstants#FAILURE} event will be triggered.
*
* @param code authorized OAuth token came from facebook
* @param error error came from facebook
* @param errorReason error reason came from facebook
* @param errorDescription error description came from facebook
* @return result came from container for triggered events or null if was not processed.
*/
@OnEvent(CONNECTED_EVENT)
Object connected(
@RequestParameter(value = "code", allowBlank = true) final String code,
@RequestParameter(value = "error", allowBlank = true) final String error,
@RequestParameter(value = "error_reason", allowBlank = true) final String errorReason,
@RequestParameter(value = "error_description", allowBlank = true) final String errorDescription) {
final CaptureResultCallback<Object> callback = new CaptureResultCallback<Object>();
// null code means that access was not granted
if (code != null) {
final AccessGrant accessGrant;
try {
// request for access token from twitter
accessGrant = facebookService.getOAuthOperations().exchangeForAccess(code, getRedirectUri(), null);
// create success even context
final Object[] context = {accessGrant.getAccessToken()};
// trigger success event
final boolean handled = resources.triggerEvent(EventConstants.SUCCESS, context, callback);
// if event was processed return result
if (handled) {
return callback.getResult();
}
// return null if not processed
return null;
} catch (Exception e) {
// error occurs, so failure event will be triggered
}
}
final Object[] context = {errorDescription};
// handle failure event if access was denied or error occurs while requesting access token
final boolean handled = resources.triggerEvent(EventConstants.FAILURE, context, callback);
// if event was processed return result
if (handled) {
return callback.getResult();
}
// return null if not processed
return null;
}
/**
* Generates OAuth2 authorization URL according to specified parameters.
*
* @param parameters OAuth2 parameters
* @return OAuth2 authorization URL
*/
private String buildConnectURL(final OAuth2Parameters parameters) {
return facebookService.getOAuthOperations().buildAuthorizeUrl(GrantType.AUTHORIZATION_CODE, parameters);
}
}
facebookService.getApi(accessToken).feedOperations().updateStatus("My new status");
<html t:type="layout" title="message:page.index.title"
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd"
xmlns:p="tapestry:parameter">
<br/>
<div class="unit c-3">
<div class="column">
<t:button>
<t:facebookconnect t:id="facebook">FACEBOOK</t:facebookconnect>
</t:button>
<br/>
<br/>
<t:button>
<t:twitterconnect t:id="twitter">TWITTER</t:twitterconnect>
</t:button>
</div>
<div class="column s-2">
<t:if test="socialProfile">
<table>
<tbody>
<tr>
<th align="right">${message:label.id}:</th>
<td width="5"/>
<td><a href="${socialProfile.get('link')}">${socialProfile.get('id')}</a></td>
</tr>
<tr>
<th align="right">${message:label.name}:</th>
<td width="5"/>
<td>${socialProfile.get('name')}</td>
</tr>
<tr>
<th align="right">${message:label.gender}:</th>
<td width="5"/>
<td>${socialProfile.get('gender')}</td>
</tr>
<tr>
<th align="right">${message:label.locale}:</th>
<td width="5"/>
<td>${socialProfile.get('locale')}</td>
</tr>
</tbody>
</table>
<p:else>
${errorMessage}
</p:else>
</t:if>
</div>
</div>
</html>
public class Social {
@Inject
private Messages messages;
@InjectService("facebookService")
private OAuth2ServiceProvider<Facebook> facebookService;
@InjectService("twitterService")
private OAuth1ServiceProvider<Twitter> twitterService;
@Persist
private Map<String, String> socialProfile;
@Persist
@Property
private String errorMessage;
public Map<String, String> getSocialProfile() {
return socialProfile;
}
@OnEvent(value = EventConstants.SUCCESS, component = "facebook")
void facebookConnected(final String accessToken) {
final FacebookProfile profile = facebookService.getApi(accessToken).userOperations().getUserProfile();
errorMessage = null;
socialProfile = new HashMap<String, String>();
socialProfile.put("id", profile.getId());
socialProfile.put("name", profile.getName());
socialProfile.put("gender", profile.getGender());
socialProfile.put("locale", String.valueOf(profile.getLocale()));
socialProfile.put("link", profile.getLink());
}
@OnEvent(value = EventConstants.FAILURE, component = "facebook")
void facebookFailure() {
socialProfile = null;
errorMessage = messages.format("message.connection-denied", "Facebook");
}
@OnEvent(value = EventConstants.SUCCESS, component = "twitter")
void twitterConnected(final String accessToken, final String accessTokenSecret) {
final TwitterProfile profile = twitterService.getApi(accessToken, accessTokenSecret).userOperations().getUserProfile();
errorMessage = null;
socialProfile = new HashMap<String, String>();
socialProfile.put("id", String.valueOf(profile.getId()));
socialProfile.put("name", profile.getName());
socialProfile.put("gender", "");
socialProfile.put("locale", profile.getLanguage());
socialProfile.put("link", profile.getProfileUrl());
}
@OnEvent(value = EventConstants.FAILURE, component = "twitter")
void twitterFailure() {
socialProfile = null;
errorMessage = messages.format("message.connection-denied", "Twitter");
}
}
package com.example.components;
import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.EventConstants;
import org.apache.tapestry5.Link;
import org.apache.tapestry5.annotations.*;
import org.apache.tapestry5.corelib.base.AbstractComponentEventLink;
import org.apache.tapestry5.internal.util.CaptureResultCallback;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.ioc.annotations.InjectService;
import org.springframework.social.oauth1.AuthorizedRequestToken;
import org.springframework.social.oauth1.OAuth1Parameters;
import org.springframework.social.oauth1.OAuth1ServiceProvider;
import org.springframework.social.oauth1.OAuthToken;
import org.springframework.social.twitter.api.Twitter;
import java.net.MalformedURLException;
import java.net.URL;
/**
* This component represents link that connects users to their twitter accounts using OAuth protocol. After clicking
* this link users will be redirected to twitter authorization page and then returned back to the page where they come
* from. After twitter authorization this component triggers {@link EventConstants#SUCCESS} or
* {@link EventConstants#FAILURE} events according to authorization result. Success event comes with access token
* (token + secret) as event context, so developers can then use it to access twitter api.
*
* @author Ivan Khalopik
* @since 1.0
*/
@Events({EventConstants.SUCCESS, EventConstants.FAILURE})
public class TwitterConnect extends AbstractComponentEventLink {
private static final String CONNECT_EVENT = "connect";
private static final String CONNECTED_EVENT = "connected";
/**
* This parameter defines OAuth application scope.
*/
@Parameter(defaultPrefix = BindingConstants.LITERAL)
private String scope;
@Inject
private ComponentResources resources;
/**
* Injected spring-social OAuth service for twitter. It should be configured somewhere in application modules.
*/
@InjectService("twitterService")
private OAuth1ServiceProvider<Twitter> twitterService;
/**
* Generates OAuth callbackUrl as event link to current component with internal {@code 'connected'} event.
* This event will be processed inside this component later.
*
* @return OAuth callbackUrl
*/
@Cached
protected String getCallbackUrl() {
return resources.createEventLink(CONNECTED_EVENT).toAbsoluteURI();
}
/**
* Generates component link that will present on html markup as a connect URL. This link will produce internal
* {@code 'connect'} event. Then this event will be processed by this component to generate correct twitter
* authorization URL.
*
* @param eventContext event context
* @return link that will present on html markup as a connect URL
*/
@Override
protected Link createLink(final Object[] eventContext) {
return resources.createEventLink(CONNECT_EVENT);
}
/**
* Event handler for internal {@code 'connect'} event. Generates correct twitter authorization URL with all needed
* parameters.
*
* @return twitter authorization URL
* @throws MalformedURLException for incorrect URL produced by twitter service
*/
@OnEvent(CONNECT_EVENT)
URL connect() throws MalformedURLException {
// generate oauth parameters with callback url
final OAuth1Parameters parameters = new OAuth1Parameters();
parameters.setCallbackUrl(getCallbackUrl());
// build and return connection url as redirect response
return new URL(buildConnectUrl(parameters));
}
/**
* Event handler for internal {@code 'connected'} event. Processes reply came from twitter authorization page. If
* access was granted then it tries to get OAuth access token and triggers {@link EventConstants#SUCCESS} event with
* access token as event context. If access was denied or error occurs while getting access token then
* {@link EventConstants#FAILURE} event will be triggered.
*
* @param token authorized OAuth token came from twitter
* @param verifier authorized OAuth token secret came from twitter
* @param denied error came from twitter
* @return result came from container for triggered events or null if was not processed.
*/
@OnEvent(CONNECTED_EVENT)
Object connected(
@RequestParameter(value = "oauth_token", allowBlank = true) String token,
@RequestParameter(value = "oauth_verifier", allowBlank = true) final String verifier,
@RequestParameter(value = "denied", allowBlank = true) String denied) {
final CaptureResultCallback<Object> callback = new CaptureResultCallback<Object>();
if (verifier != null) {
try {
// create authorized OAuth request token
final AuthorizedRequestToken requestToken =
new AuthorizedRequestToken(new OAuthToken(token, verifier), verifier);
// request for access token from twitter
final OAuthToken accessToken = twitterService.getOAuthOperations()
.exchangeForAccessToken(requestToken, null);
// create success even context
final Object[] context = {accessToken.getValue(), accessToken.getSecret()};
// trigger success event
final boolean handled = resources.triggerEvent(EventConstants.SUCCESS, context, callback);
// if event was processed return result
if (handled) {
return callback.getResult();
}
// return null if not processed
return null;
} catch (Exception e) {
// error occurs, so failure event will be triggered
}
}
// handle failure event if access was denied or error occurs while requesting access token
final boolean handled = resources.triggerEvent(EventConstants.FAILURE, new Object[]{}, callback);
// if event was processed return result
if (handled) {
return callback.getResult();
}
// return null if not processed
return null;
}
/**
* Generates OAuth authorization URL according to specified parameters.
*
* @param parameters OAuth parameters
* @return OAuth authorization URL
*/
private String buildConnectUrl(final OAuth1Parameters parameters) {
final OAuthToken token = twitterService.getOAuthOperations().fetchRequestToken(getCallbackUrl(), null);
return twitterService.getOAuthOperations().buildAuthorizeUrl(token.getValue(), parameters);
}
}
public class UIModule {
private static final String FACEBOOK_CLIENT_ID = "facebook.client-id";
private static final String FACEBOOK_CLIENT_SECRET = "facebook.client-secret";
private static final String TWITTER_CONSUMER_KEY = "twitter.consumer-key";
private static final String TWITTER_CONSUMER_SECRET = "twitter.consumer-secret";
public void contributeApplicationDefaults(final MappedConfiguration<String, String> configuration) {
configuration.add(SymbolConstants.SUPPORTED_LOCALES, "ru,en");
configuration.add(SymbolConstants.APPLICATION_VERSION, "1.0-SNAPSHOT");
configuration.add(FACEBOOK_CLIENT_ID, "_key_");
configuration.add(FACEBOOK_CLIENT_SECRET, "_key_");
configuration.add(TWITTER_CONSUMER_KEY, "_key_");
configuration.add(TWITTER_CONSUMER_SECRET, "_key_");
}
public OAuth2ServiceProvider<Facebook> buildFacebookService(@Symbol(FACEBOOK_CLIENT_ID) final String clientId,
@Symbol(FACEBOOK_CLIENT_SECRET) final String clientSecret) {
return new FacebookServiceProvider(clientId, clientSecret);
}
public OAuth1ServiceProvider<Twitter> buildTwitterService(@Symbol(TWITTER_CONSUMER_KEY) final String consumerKey,
@Symbol(TWITTER_CONSUMER_SECRET) final String consumerSecret) {
return new TwitterServiceProvider(consumerKey, consumerSecret);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment