Sunday Aug 30, 2009

Writing Facebook Applications with Java EE (part 2)

In the first part of this blog, I showed how to register your application with facebook, create a web application and deal with Facebook authentication. In this part, I will show how to write a simple application, that shows the user their own Facebook UID and a list of all their friends. Nothing earth shatteringly useful, because this is just introduce you to the mechanics of writing a simple application.

Step 5: Write the application

Our application user is now authenticated. We can begin to make FQL calls and read/publish information to facebook. Our little sample, will use FQL to know more about the logged in user and the friends list.We told facebook that our application will run as an iframe in Facebook chrome. All we have to do is return some well formed html. We will do the "Hello World" type functionality and also show how to use FQL queries to extract information on all of the user's friends.

We will create a helper class Friend to hold the details of each facebbok user.  Later, I will show how to make this an Entity and store the data periststently, rather than make expensive and slow queries to facebook, repeatedly. The data could get stale and that is another issue to deal with later. Facebook does not encourage data caching.

public class Friend {

    private Long id;
    private String name;
    private String picurl;
    private String phone;

    public Friend() {
    }

    public Friend(Long id) {
        this.id = id;
    }

    public Friend(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Friend(Long id, String name, String pic) {
        this.id = id;
        this.name = name;
        this.picurl = pic;
    }

   // getters and setters have been omitted for brevity

}

The authenticated Facebook client is now available as a request attribute. We simply extract it and use it to make Facebook API calls. A helper method auth_getUserId provided by FacebookJsonRestClient extracts the facebook id of the logged in user. We will use this to get information on the user and friends.

 public class Canvas extends HttpServlet {

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        try {
            FacebookJsonRestClient fbc = (FacebookJsonRestClient) request.getAttribute("auth.client");

            PrintWriter out = response.getWriter();
            long myUid = 0; Friend me = null;
            try {
                myUid = fbc.auth_getUserId(request.getParameter("auth_token"));
                me = findFacebookName("" + myUid, fbc);
            } catch (FacebookException ex) {
                Logger.getLogger(Canvas.class.getName()).log(Level.SEVERE, null, ex);
            }

            ArrayList<Friend> myFriends = findFacebookFriends(fbc);

// TODO: later in the blog,  show how data is output
    }

The method findFacebookName illustrates basic use of FQL that returns one result object. 

    private Friend findFacebookName(String fid, FacebookJsonRestClient fbc) {
        String name = null;
        String pic = null;
        try {
            String query = "SELECT name,pic FROM user WHERE uid=" + fid;
            org.json.JSONArray fa = null;
            fa = (org.json.JSONArray) fbc.fql_query(query);
            name = fa.getJSONObject(0).getString("name");
            pic = fa.getJSONObject(0).getString("pic");
        } catch (FacebookException ex) {
            Logger.getLogger(Canvas.class.getName()).log(Level.SEVERE, null, ex);
        } catch (JSONException ex) {
            Logger.getLogger(Canvas.class.getName()).log(Level.SEVERE, null, ex);
        }
        return new Friend(new Long(fid), pic, name);
    }

The method findFacebookFriends illustrates friends_get() that returns an array of Facebook Ids of all the user's friends. 

private ArrayList<Friend> findFacebookFriends(FacebookJsonRestClient fbc) {
        org.json.JSONArray resultArray = null;
        ArrayList friends = new ArrayList();
        try {
            resultArray = fbc.friends_get();
            for (int i = 0; i < resultArray.length(); i++) {
                try {
                    Long fid = resultArray.getLong(i);
                    String query = "SELECT name,pic FROM user WHERE uid=" + fid;
                    org.json.JSONArray fa = null;
                    try {
                        fa = (org.json.JSONArray) fbc.fql_query(query);
                        String pic = fa.getJSONObject(0).getString("pic");
                        String name = fa.getJSONObject(0).getString("name");
                        friends.add(new Friend(fid, name, pic));
                    } catch (FacebookException ex) {
                        Logger.getLogger(Canvas.class.getName()).log(Level.SEVERE, null, ex);
                    }
                } catch (JSONException ex) {
                    Logger.getLogger(Canvas.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        } catch (FacebookException ex) {
            Logger.getLogger(Canvas.class.getName()).log(Level.SEVERE, null, ex);
        }
        return friends;
    }

Spitting it all out

After getting all the data we need, it is time to output something that will be shown to the end user, as the output a HTML page is shown in an iframe.

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        try {

            // Data Extraction: See Above
            PrintWriter out = response.getWriter();
            out.println("<html>");
            out.println("<body>");
            out.println("Your Facebook Id:" + me.getId());

            out.println("Your friends:");
            out.println("<ol>");
            Iterator it = myFriends.iterator();
            while (it.hasNext()) {
                Friend f = (Friend) it.next();
                out.println("<li>Friend Name" + f.getName() + "</li>");
            }
            out.println("");
            out.println("</ol>");
            out.println("</body>");
            out.println("</html>");
            return;
        } catch (Throwable t) {
            t.printStackTrace();
        } finally {
            return;
        }
    }

Conclusion

Deploy the web application to a Java EE server that is visible to the internet and is accessible at the Canvas callback URL you used when the application was registered at Facebook.

The sample application can be accessed directly specifying the Canvas Callback URL specified at registration (http://sailfin.webhop.net/FacebookSample/Canvas)  or  http://apps.facebook.com/glassbook.  It produces output as expected showing my Facebook Id and the names of all my friends. Real applications are of course, going to be a lot more interesting... This should get you, the Java EE developer, going..


Friday Aug 28, 2009

Facebook Applications on Glassfish (part 1)

During this summer, I explored writing Facebook applications using Java EE, something I have wanted to look into for a while, but never got around to. Facebook had stopped official support for it's Java API, in May 2008, which is not so nice to the millions of Java programmers out there. Fortunately, the API has since evolved as Facebook Java API.  It is very usable and there are many blogs and examples on how to get started. After looking at various posts, it was relatively simple to write a basic facebook application and make it run on Glassfish application server. I will share what I have learned along the way.

I used a recent NetBeans Dev build (NetBeans IDE Dev (Build 200908070201))  and GlassFish v2.1, but feel free to use an setup you are comfortable with. The steps should work the same way with Eclipse or GlassFish v3.

Step 1

I am assuming you are a Facebook user. Who isn't these days :-). If not, go sign up first.

The next task is to get yourself facebook developer privileges, which you do by "installing" facebook developer application. The developer application allows you to register your new application, provide some basic details to facebook on where it runs, generate an Application Identifier, API key and Secret Key etc. which identify your application to facebook users and to facebook platform.

To get started, click on Setup New Application at the top of the page. 

Provide a name for your sample, Agree to the Facebook Terms and Save Changes.


After Step 1, facebook assigns an API key that identifies your Application's requests and also a secret key that must be supplied with every request This is what you will see after you hit Save Changes above.

I have whited out the App Id, API Key and Secret code that I obtained. You will see long hex strings. You have taken the first big step already.  There is an application now registered.

Step 2

Now provide some essential information about where your application is hosted, the main canvas page etc. If you are not familiar already, your facebook application will not run on facebook's servers. You have to host it somewhere. You can use a Glassfish or Tomcat hosting provider or do it from home, which is what I do. I use dyndns.org to get a public domain name that points back to my home server or laptop. This is obviously not recommended if your application becomes wildly popular, but it is good enough for this sample!

In Step 2, we will provide some more crucial information about your application.The bits of information that we plan to provide are under Canvas and Connect.


On this page, we will make some important choices.

  1. Canvas Page URL: Choose the common name that you want for your application's users. I chose glassbook. When facebook receives a request for this URL, it will map it to your application.
  2. Canvas Callback URL: Provide the full URL path to where your facebook application will eventually be running. I am hosting my sample on a domain called sailfin.webhop.net.  The domain is registered at dyndns.org, which offers free DNS services, with some limitations. Note the following:
    1. I used FacebookSample as the context root for my web application that will handle the application logic. You could choose anything you want for this. Just make sure to use the same context root when you develop the web application later.
    2. I declared that the receiver of facebook traffic is a servlet or JSP that is mapped to Canvas. You can use any name you want here and do the corresponding thing while developing the application.
  3. Use the defaults for all others. We will not use FBML in this sample.

 Now, click on Connect on the  left hand panel and supply the same path we provided for Canvas call back URL.


Thats it! You have registered an application. Now all that is left is to write it!

Step 3

Now we will develop the web application, called FacebookSample. In NetBeans, create a new project, called FacebookSample.

Note how I used the context root: FacebookSample


Then I created a servlet called Canvas:

And a ServletFilter called FacebokAuthFilter


 At the point, in NetBeans there should be two source files in the project structure.


You already know that we intend to put the application logic in Canvas.java servlet class.We have not filled in anything there yet. We will return to it soon.

Before we start writing code, need to add some Facebook Java API jar files to your web application. In NetBeans, you can do this by right clicking on the FacebookSample application, choosing Properties and then Libraries. You must add the following 3 jar files:

  1. commons-logging-1.1.1.jar
  2. facebook-java-api-2.1.1.jar
  3. json-20070829.jar

All of these are in the facebook Java API bundle you downloaded at the top of the tutorial.

Macintosh-202:lib Sreeram$ pwd
/Users/Sreeram/facebook-java-api-2.1.1/lib
dhcp-usca14-133-138:lib Sreeram$ ls -l 
total 3568
-rw-r--r--  1 Sreeram  Sreeram   62983 Dec 31  2007 activation-1.1.jar
-rw-r--r--  1 Sreeram  Sreeram  243016 Jan 15  2009 commons-lang-2.2.jar
-rw-r--r--  1 Sreeram  Sreeram   60686 Sep 20  2008 commons-logging-1.1.1.jar
-rw-r--r--  1 Sreeram  Sreeram  137560 May  1 08:26 facebook-java-api-2.1.1.jar
-rw-r--r--  1 Sreeram  Sreeram  278382 May  1 08:25 facebook-java-api-schema-2.1.1.jar
-rw-r--r--  1 Sreeram  Sreeram   89967 Sep 27  2008 jaxb-api-2.1.jar
-rw-r--r--  1 Sreeram  Sreeram  856752 Nov 24  2008 jaxb-impl-2.1.9.jar
-rw-r--r--  1 Sreeram  Sreeram   41829 Dec 31  2007 json-20070829.jar
-rw-r--r--  1 Sreeram  Sreeram   15949 Jan 15  2009 runtime-0.4.1.3.jar
-rw-r--r--  1 Sreeram  Sreeram   23346 Sep 27  2008 stax-api-1.0-2.jar

Now we are ready to start coding, but there is one other topic I need to introduce: why do we need FacebookAuthenticationFilter.java. That is the topic of the next section.

Step 4: Facebook Authentication

Only registered Facebook users can access your application. We need to authenticate users and force login if necessary. Login is handled by Facebook. Our application will make calls to Facebook APIs in the context of authenticated user.

Our application, may sometimes need permissions to do some things, such as read and publish to a stream. We need to request the user to grant such permissions.

We will use a Servlet Filter for check whether the user is logged in and request necessary permissions. 

Now lets dissect FacebookAuthenticationFilter.java

   /\*\*
     \* Init method for this filter
     \*/
    public void init(FilterConfig filterConfig) {
        this.filterConfig = filterConfig;
        if (this.filterConfig != null) {
            _apiKey = filterConfig.getInitParameter("api_key");
            _secretKey = filterConfig.getInitParameter("secret_key");
            if (debug) {
                log("FaceBookAuthFilter:Initializing filter");
            }
        }
    }

You need to supply the API Key and Secret Key received from facebook in the web.xml, so the Servlet Filter can use then in its requests redirected to facebook. This is what your web.xml would look like. Plugin the API key and Secret code you got from Facebook.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <filter>
        <filter-name>FacebookAuthFilter</filter-name>
        <filter-class>org.glassfish.samples.facebook.FacebookAuthFilter</filter-class>
        <init-param>
            <param-name>api_key</param-name>
            <param-value>Your API Key Here</param-value>
        </init-param>
        <init-param>
            <param-name>secret_key</param-name>
            <param-value>Your Secret Key Here</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>FacebookAuthFilter</filter-name>
        <url-pattern>/\*</url-pattern>
    </filter-mapping>
    <servlet>
        <servlet-name>Canvas</servlet-name>
        <servlet-class>org.glassfish.samples.facebook.Canvas</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Canvas</servlet-name>
        <url-pattern>/Canvas</url-pattern>
    </servlet-mapping>
</web-app>

Back to the Filter

     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        try {
            FacebookJsonRestClient authClient = getAuthenticatedClient((HttpServletRequest) request,_apiKey, _secretKey);
            request.setAttribute("auth.client", authClient);
            filterConfig.getServletContext().setAttribute("fbc", request.getAttribute("auth.client"));
            chain.doFilter(request, response);
        } catch (FailedLoginException fle) {
            //user not logged in
            request.setAttribute("auth.client", null);
            forceLogin((HttpServletResponse) response);
        } catch (Exception e) {
            //handle exception
        }
    }

We first try to create a FacebookJsonRestClient object. This logic is encapsulated in the getAuthenticatedClient method. We will check the incoming request for the presence of two request parameters: auth_token and session_key. If a session key is present and is valid, the client object is instantiated. If only an auth_token is present, a client is instantiated and a new session created. The facebook session lasts about an hour. Infinite sessions are a different beast and one needs to request the user to grant offline access privileges. I have not played with it yet.

private FacebookJsonRestClient getAuthenticatedClient(HttpServletRequest request, String apiKey, String secretKey) throws FailedLoginException, FacebookException {
        String authToken = request.getParameter("auth_token");
        String sessionKey = request.getParameter(FacebookParam.SESSION_KEY.toString());
        FacebookJsonRestClient fbClient = null;
        if (sessionKey != null) {
            fbClient = new FacebookJsonRestClient(apiKey, secretKey, sessionKey);
        } else if (authToken != null) {
            fbClient = new FacebookJsonRestClient(apiKey, secretKey);
            //establish session
            fbClient.auth_getSession(authToken);
        } else {
            throw new FailedLoginException("Session key not found");
        }
        //fbClient.setIsDesktop(false);
        return fbClient;
    }

If there is no valid session established for the user, a FailedLoginException is thrown and we call forceLogin to cplete the Login process.  Otherwise, the client is objected is inserted into the request attributes for later retrieval.

     private void forceLogin(HttpServletResponse response) {
        try {
            String redirect = "http://www.facebook.com/login.php?" + "api_key=" + _apiKey + "&connect_display=popup" + "&v=1.0" + "&next=http://apps.facebook.com/glassbook" + "&cancel_url=http://www.facebook.com/connect/login_failure.html" + "&fbconnect=true" + "&return_session=true"
            response.sendRedirect(redirect);
        } catch (Exception ioe) {
            //handle exception
        }
    }

I used next=http://apps.facebook.com/glassbook to point to my test program. You will need to change the  next parameter to point to your application canvas URL.

 In the next part, I will walk through the rest of the application, the part that implements the canvas. Won't be long.

About

Various things I do at Sun Microsystems.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today