How to Make Facebook Apps Using Java – part 2

In the first part, I covered a very high-level, idiot-guide to getting started with writing a Facebook app in java. This part will cover the details of how to architect your own code for basic Facebook authentication, and include code samples you can use to get up and running more quickly. It will also explain in more detail precisely how Facebook’s servers interact with your code, and what you can expect (and what their servers expect of you!).

NB: if the quoted source code is unreadable because it disappears off the edge of the screen, select it and copy/paste (or view source of the page to see it). The most useful stuff is put together into one class you can download here – source code for FacebookLoginServlet.java.

iFrames vs FBML

Facebook apps can be implemented one of two ways, iframes, or FBML.

iFrame FBML
Standard part of HTML, embeds one webpage inside another Proprietary Facebook programming language, very similar to HTML
You can view pages through a normal web-browser to test them You cannot view anything except by running it live on Facebook
Every Facebook page view goes via your webserver, so you can debug problems All requests are internal on the Facebook servers, you cannot find ANY information if something goes wrong
Requires you to provide your own webserver Can run without an external webserver ***

*** – NB: actually, the way Facebook have designed their system, you need your own webserver anyway, they won’t let you make a Facebook App without one. So, although in theory FBML would be a faster and simpler way to get a basic app up and running, in practice, it doesn’t save you any time or effort at all.

Mostly, I’ll be assuming you want to write your app using iframes instead of FBML. FBML is great, and for some parts of your app you will want to use it, but the fact you can’t debug it makes life extremely difficult. If you can get all your code working with iframes first, then it will be relatively easy to switch some bits to FBML when the time comes. It’s much harder to try things the other way around.

Facebook Apps: Detailed explanation

What happens when a Facebook user tries to use your Facebook App?

  1. User opens their web browser
  2. Finds a link to your app – usually your Canvas Page URL (http://apps.facebook.com/[your app name])
  3. Clicks on the link
  4. Web browser goes to that site (part of FB) and asks for the webpage
  5. Facebook sends back the Facebook navbar, with an empty iframe in the centre
  6. Facebook redirects the browser to your site to get the contents of the iframe – AND adds extra information to the request from the user
  7. Web browser automatically goes to YOUR site, sending the extra information FB provided it
  8. Your site creates a webpage, and sends it to the user’s browser
  9. User’s browser renders your webpage inside the iframe embedded inside the page it got from FB

…but what does this look like to the user?

  1. User opens browser
  2. Clicks on the name of your app
  3. Your app appears on the screen inside the navbars etc for Facebook

So, the first thing to understand, especially if you’ve used FB a lot yourself, is that there’s a lot more going on behind the scenes than appears to the user. This will become especially clear when you make a mistake and start trying to debug your app.

Authentication

There are two “modes” in which you can create a webpage to display as part of your app:

  • Default
  • Authenticated

In the default mode, i.e. if you do nothing special, you have NO ACCESS to any of Facebook’s data. You cannot find out ANYTHING about the Facebook user – you can’t find their name, you can’t find their friends, you can’t post items on their Wall, nothing.

In the authenticated mode, you have full access to:

  • read all the data of the currently logged-in user
  • most of the data of all OTHER users of your app (people who have “added” your app to their Facebook account)
  • send messages, post newsitems, etc, from the logged-in user (but they have to OK this manually)

Authenticating in java

FB provides you with a java class, FacebookRestClient.java, which you can use to authenticate with the FB servers. Once you have authenticated an instance of this class, that class will then ALSO provide you with access to all the features of Facebook (reading data about users, sending messages to other users, etc).

FB has partially documented the way that authentication works, but there are lots of missing details. In particular, there is no documentation on how to use the FacebookRestClient class to do authentication, and several of the methods that appear to work together are actually mutually exclusive. This is because that one class is responsible both for doing Web-based apps (the only kind of app that most people have ever seen or used on Facebook) and Desktop apps (which do exist but are much less common right now).

In brief, you need to:

  1. Generate a URL for a custom login page for your app (http://www.facebook.com/login.php?api_key=[your api key goes here] )
  2. Send a redirect to the user’s web browser to force them to go to that page
  3. …Facebook will then redirect them back to your “Callback URL” page…
  4. Read the “auth_token” parameter from the HTTP Query String
  5. Instantiate an instance of FacebookRestClient using your API key and your API secret
  6. Invoke .auth_getSession( auth_token ) using the value of auth_token you received

Authenticating your servlets

Now, you may notice a problem with the above sequence of events. Traditionally, when writing a J2EE app, you have multiple servlets, one for each “page” of your website – but in order to authenticate, the servlet MUST redirect the user back to Facebook, which will then ALWAYS send the user back to your Callback URL page, which means they will be sent to the “wrong” page.

Note: just because you redirect the user to Facebook’s login page doesn’t actually mean the user SEES a login page – if they are already logged-in to Facebook, FB just automatically sends them straight back to your site (but this time, it gives your site the login information, which doesn’t otherwise do!)

So, effectively, Facebook ONLY ALLOWS ONE SERVLET to authenticate with Facebook – whichever servlet you gave the address of when you filled in the “Callback URL” field when creating your app on Facebook’s Developer page.

That’s not a big problem, though. J2EE servlets have the Session object, automatically maintained by your J2EE container, that will take care of this: if you ever want the user to be authenticated when using your app, just make the Callback URL page force the user to login once (as noted above, if already logged in to FB, the user won’t be asked to login again, but you still have to force FB to make the check), and then store the authenticated instance of FacebookRestClient.java in the J2EE Session object, like this:

protected void doGet(HttpServletRequest request, HttpServletResponse response)
{
   ...
   FacebookRestClient authenticatedClient = new FacebookRestClient( apiKey, secret );
   authenticatedClient.setIsDesktop(false);
   request.getSession().setAttribute( "auth", authenticatedClient );
}


Then, whenever you need to interrogate Facebook about any data in ANY of your servlets, fetch the authenticated FacebookRestClient instance from the J2EE session, and use it directly:

protected void doGet(HttpServletRequest request, HttpServletResponse response)
{
   FacebookRestClient authenticatedClient = (FacebookRestClient) request.getSession().getAttribute( "auth" );
   ...
}


In fact, I’d advise you to dedicate one servlet purely to logging-in to Facebook, and make that servlet your Callback URL one. For instance, here’s source code for FacebookLoginServlet.java you could use directly for that servlet. Note that the provided source code assumes you are using Apache Log4j to do your logging – you may want to change that to use Sun’s java.util.logging API. I advise you at least use some form of logging (it helps a lot when debugging!)

Old sessions

You also have to handle the situation where a player leaves their web browser open for a long time, long enough that the J2EE Session timesout and/or the Facebook authentication timesout, and then they hit “refresh”. If you’re merely starting every doGet / doPost method in your servlets with the request.getSession() call above, you’ll find that either the Session is empty and you get a null client, or you get a client but that when you attempt to invoke methods on it they all fail with FacebookException‘s. Exactly what will go wrong depends upon the implementation of your J2EE container, and how long it keeps Sessions alive for before expiring them (and whether the connected web browser kept all the Cookie’s it was given).

For this, I currently just surround all my doGet’s with a big try/catch:

protected void doGet(HttpServletRequest request, HttpServletResponse response)
{
   try
   {
      ...
   }
   catch( FacebookException e )
   {
      logger.error( "Facebook exception, probably due to timeout on authentication; sending error page to user", e );
      String loginPage = "http://www.facebook.com/login.php?api_key="+apiKey+"&v=1.0";
      response.getWriter().println( "<h1>Error: Couldn't talk to Facebook</h1>" );
      response.getWriter().println( "Your Facebook session has probably timed out" );
      response.getWriter().println( "Please <a ref=\""+loginPage+"\">click here</a> to login again");
   }
}

Testing…

And, again, time to finish off with some basic tests, so you can be sure all this stuff above is working. For my basic testing, I created a servlet that would fetch the user-id of the currently-logged-in Facebook user, and ask Facebook for a list of all their friends’ names. This is the same basic test that Facebook uses in their one and only piece of sample java code in their API.

public class TestFaceBookServlet extends HttpServlet
{
   protected void doGet( HttpServletRequest request, HttpServletResponse response ) throws IOException, FacebookException
   {
      FacebookRestClient client = (FacebookRestClient) request.getSession().getAttribute("facebookClient");
      client.setDebug(true);

      logger.info( "Fetching friends for user to prove that our auth to FB's server worked OK ... should see a list of \"uid\"s now, with NO java-exceptions!");
      Document d = client.friends_get();

      FacebookRestClient.printDom(d, "  ");
   }
}

Conclusion

By now you should have:

  • A facebook app that you can connect to via Facebook itself that displays the content from your basic servlets
  • A login-servlet as your Callback URL that automatically logs in the current user to Facebook
  • A simple test servlet that your login-servlet redirects to after a successful login, and which prints in your server log-files a list of all the uid’s of your friends

In the next part, I’ll probably stop to cover likely problems and gotchas you’ll run into, since there’s quite a few you’re likely to hit by this point.

If there’s anything else you’d like to see covered, add a comment at the bottom and I’ll try to fit it in as well.

UPDATE 21st Sept 2007: Third part now posted


18 thoughts on “How to Make Facebook Apps Using Java – part 2

  1. Ajay

    If you are using a central controller servlet as a dispatcher to your different forms authentication will work throughout your application. Let me know if you guys/girls want me to post an example.

  2. adam Post author

    Go ahead, Ajay. Any extra help on getting facebook apps working is greatly appreciated :).

  3. Ajay

    Adam
    In its core you still store the authentication in session but you can avoid the code duplication of checking the session for every servlet.
    Example:
    I am using spring and hence I can define the scope of the bean as session just by the XML configuration. see a good discussion here

    http://www.memestorm.com/blog/session-scoped-beans-in-spring-20/

    Even If you are not using it could be implemented in similar manner.
    I First extend the FacebookRestClient
    public class FaceBookClient extends FacebookRestClient {
    ..
    }

    then define its mapping

    In the constructor do everything to create and authenticate session but no utility call.

    Also I use a central servlet for all requests.
    This goes in web.xml

    app
    org.springframework.web.servlet.DispatcherServlet
    2

    app
    *.html

    In which I inject through setter depndency injection the class I created above.

    and now whenever you want to make a call get the available bean and you are ready to go.

    Now its available to my whole application authenticated

  4. Neil Benn

    Hello,

    I’m hoping someone here could help! I’ve been following the examples listed here (thanks for putting them up – they are great). However I’m running into a problem. Following is the code (which I have cut down from your givn example and hardcoded in the API key for simplicity sake) :

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
    throws IOException{

    logger.info(“Requested URL: ” + req.getRequestURL()
    + ” — with query string: ” + req.getQueryString());
    String authToken = req.getParameter(“auth_token”);

    try{
    if(authToken == null || authToken.length()

  5. Pete Hawdon

    Adam – Are you in the US ? I’m in the UK and find it very difficult to find web hosting with J2EE support.

  6. adam Post author

    Yes, the UK is generally useless for hosting of any kind. Too many people are willing to pander to the greedy and lazy hosting companies that charge twice the going rate for half the service.

    My advice: pay in dollars, and you’ll find the service improves *dramatically*. Even when running companies, we’ve nearly always done all hosting in the USA – servers in the US are so much better for the same price that you end up getting similar levels of service despite them being thousands of miles away.

  7. Joe

    I am having an issue during re-direction because the user is not logged in:

    logger.info( “User is not logged in; redirecting them to Facebook’s login page = “+defaultLoginPage );
    resp.sendRedirect(defaultLoginPage);

    The problem is that rather than logging in seamlessly and going to the canvas, when I select my application from the applications toolbar, Facebook throws an error:

    Errors while loading page from application
    Runtime errors:

    HTML tag not supported: “title”

    HTML tag not supported: “meta”
    ……

    What it appears to be doing is attempting to render the login page in the iframe instead of going there to authenticate and returning to render the canvas.

    Any ideas what I could be doing wrong?

  8. Joe

    Sorry I’ve figured this out, I had the application set to FBML rather than iFrame so it was rendering the content

    One to watch out for I guess…

  9. Peter

    Question for Ajay – did you extend the dispatcher servlet to redirect to the facebook login page if they are not logged in or if the session doesn’t have the credentials? Your method makes perfect sense if the restclient can be successfully created and then it is available throughout your app, but I don’t understand how you handle the case where the user needs to login

  10. laurent

    Ajay, I’m using Spring and I’m looking into session-scoped beans, but I do not understand how you did the “In the constructor do everything to create and authenticate session but no utility call.” part : for this, you need access to the request/response objects….. how do you get access to them within your session-scoped bean ?

    thanks

  11. Asaf

    The problem is that the redirect happens WITHIN the iFrame, thus getting the FB login form and the FB homepage (after the login) INSIDE the iFrame.

  12. Shel

    I have the same problem as Joe. I had an app set to FBML. When login is needed, the FB login page shows up in canvas, instead of the main browser page. Thus causing error because the canvas cannot show a full-blown html page. How to correct this? (I have login redirect response.sendRedirect(…)).

  13. albert

    This is a very nice article and guide, keep up the good work.
    I however, do noticed the overly used J2EE word here which might confuse your readers that you need a J2EE server. A normal java web server would’ve been able to do the job too.

  14. David

    I am trying re-do this example using the JSF framework but am having some difficulty with the
    response.sendRedirect(loginUrl)

    Facebook seems to be embedding their own page. So instead of redirecting the whole page it only redirects where it is getting my pages content from. Any ideas?

  15. Alex

    I’m tryng to download the source:
    source code for FacebookLoginServlet.java.

    But the link doesn’t work…where can I find it?
    Thanks

  16. TheGDeveloper

    Do you have any idea about why both
    String fb_session_key = request.getParameter(“fb_sig_session_key”);
    String authToken = request.getParameter(“auth_token”);

    return null value?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Current month ye@r day *