Tapestry Facebook Integration

Continuing from the Tweeting post, lets us extend the OAuth Service to enable us to post on a user’s wall. As all the hard work has already been done, we just have to tweak things a little.

So here is the Facebook service

//interface
public interface FacebookService extends BaseOAuthService {
    
    void post(Token accessToken, String text);
    
}

//implementation
public class FacebookServiceImpl extends BaseOAuthServiceImpl implements FacebookService {

    public FacebookServiceImpl(OAuthConfiguration configuration, String apiPrefix) {
        super(configuration, FacebookApi.class, apiPrefix);
    }

    @Override
    public void post(Token accessToken, String text) {
        send(new FacebookPost(accessToken, text));
    }

    @Override
    public Token newRequestToken() {
        return null; // Empty Token
    }

    @Override
    public Token requestAccessToken(String oAuthToken, String verifier) {
        return getOAuthService().getAccessToken(newRequestToken(), new Verifier(verifier));
    }
    
}

Facebook does not need a request token, you just make a request with an empty token. The request is populated with your API key and secret but that is done by scribe-java, you don’t have to worry about that. Next is the OAuthResource for Facebook Post

public class FacebookPost extends AbstractOAuthResource {

    private String text;

    public FacebookPost(Token accessToken, String text) {
        super(accessToken, Verb.POST, "/me/feed/");
        this.text = text;
    }
    
    @Override
    public void initialize(OAuthRequest request){
        request.addBodyParameter("message", text);
    }

}

This just means we have a resource at ‘/me/feed/’ which we can request through POST method and pass the text as message parameter.

Finally the component

@Events({OAuthConstants.CONNECTION_ESTABLISHED, OAuthConstants.CONNECTION_FAILED})
public class FacebookConnect implements ClientElement {
    @Parameter(value = "prop:componentResources.id", defaultPrefix = BindingConstants.LITERAL)
    private String clientId;

    private String assignedClientId;

    @Inject
    private JavaScriptSupport javaScriptSupport;

    @Inject
    private ComponentResources resources;

    @Inject
    private FacebookService facebookService;

    void setupRender() {
        assignedClientId = javaScriptSupport.allocateClientId(clientId);
    }

    @Override
    public String getClientId() {
        return assignedClientId;
    }

    URL onConnectToFacebook() throws MalformedURLException {
        return new URL(facebookService.getAuthorizationURL());
    }

    Object onAuthorize(
         @RequestParameter(value = "code", allowBlank = true) final String code,
         @RequestParameter(value = "error_reason", allowBlank = true) final String errorReason,
         @RequestParameter(value = "error", allowBlank = true) final String error,
         @RequestParameter(value = "error_description", allowBlank = true) final String errorDescription
         ) {

        if(code != null){
            return accessGranted(code);
        }else {
            return accessDenied(error, errorReason, errorDescription);
        }
        
    }

    private Object accessGranted(String code) {
        Token accessToken = facebookService.requestAccessToken(null, code);
        CaptureResultCallback<Object> callback = new CaptureResultCallback<Object>();

        JSONObject response = facebookService.send(new FacebookAccessInfo(accessToken));
        
        boolean handled = resources.triggerEvent(OAuthConstants.CONNECTION_ESTABLISHED,
                new Object[] { accessToken, (String)response.get("id") }, callback);

        if (handled) {
            return callback.getResult();
        }

        return null;
    }

    private Object accessDenied(String error, String errorReason, String errorDescription) {
        CaptureResultCallback<Object> callback = new CaptureResultCallback<Object>();
        
        boolean handled = resources.triggerEvent(OAuthConstants.CONNECTION_FAILED,
                new Object[] { error, errorReason, errorDescription }, callback);

        if (handled) {
            return callback.getResult();
        }

        return null;
    }

}

<t:container xmlns:t='http://tapestry.apache.org/schema/tapestry_5_1_0.xsd'>

   <a href='#' t:type='eventlink' t:event='connectToFacebook'><t:body/></a>

</t:container>

Here we are using another OAuthResource to get the users details.

public class FacebookVerifyCredentials extends AbstractOAuthResource {
    
    public FacebookVerifyCredentials(Token accessToken){
        super(accessToken, Verb.GET, "account/verify_credentials");
    }

}

It is almost a copy of the TwitterConnect from the previous post. When the link is clicked, the event handler returns the authorization URL which redirects the browser to Facebook authorization. After Facebook has authorized, it redirects the browser to the callback URL(which is specified in the configuration) which is our onAuthorize() event handler. The event handler, based on the request parameters, decides which callback to call.

Finally the module contribution

public FacebookService buildFacebookService() {
        
        OAuthConfiguration configurer = new OAuthConfiguration("MY_API_KEY",
                "MY_API_SECRET",
                "http://localhost:9090/post.facebookconnect:authorize", "email,publish_stream");
        
        return new FacebookServiceImpl(configurer, OAuthConstants.DEFAULT_FACEBOOK_API_PREFIX);
    }

Using the component in a page is very similar to the previous one

public class Post {

    @Inject
    private FacebookService facebookService;

    @SuppressWarnings("unused")
    @Property
    @Persist(PersistenceConstants.FLASH)
    private String message;

    void onConnectionEstablishedFromFacebookConnect(Token token, String verifier) {

        facebookService.send(new FacebookPost(token,
                "Hello from scribe-facebook at " + new Date()));
        message = "Posted";
    }
    
    void onConnectionFailed(String error, String errorReason, String errorDescription){
        message = "Failed";
    }

}
<html xmlns:t='http://tapestry.apache.org/schema/tapestry_5_1_0.xsd'>

   <body>
         ${message}<br/>
      <a href='#' t:id='facebookconnect' t:type='facebookConnect'>Facebook Connect</a>   
   </body>
   
</html>
About these ads

Tagged: , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 91 other followers

%d bloggers like this: