/*--------------------------------------------------------------------------+ | Copyright (c) 2006 Facebook, Inc. | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions | | are met: | | | | 1. Redistributions of source code must retain the above copyright | | notice, this list of conditions and the following disclaimer. | | 2. Redistributions in binary form must reproduce the above copyright | | notice, this list of conditions and the following disclaimer in the | | documentation and/or other materials provided with the distribution. | | | | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | +---------------------------------------------------------------------------+ | For help with this library, contact api-help@facebook.com | +--------------------------------------------------------------------------*/ package com.moochspot.util; import java.io.IOException; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.facebook.api.FacebookException; import com.facebook.api.FacebookRestClient; /** * Wrapper around the Facebook API to encapsulate operations on friends. * This allows application code to deal with a couple of high-level objects, * {@link FacebookFriend} (representing information about a * single friend) and {@link FacebookFriends} (representing a * collection of friends). *

* To customize which fields are fetched from Facebook, first edit the * {@link FacebookFriend} class to add whatever fields you need. * Then modify this class's {@link #extractFriend(Node) * extractFriend()} method (and optionally {@link #getFriend(String) * getFriend()} to extract the new fields. */ public class FacebookApi { public static String DEFAULT_API_URL = "http://api.facebook.com/restserver.php"; private Logger _log = Logger.getLogger(getClass().getName()); protected FacebookRestClient _client; /** * Constructs a new Facebook API wrapper. * * @param serverUrl * URL of API server to connect to. * @param sessionKey * Session key as returned by facebook.auth.getSession (which * is wrapped by this class's {@link #fetchCredentials(String) * fetchCredentials()} method). Null if this wrapper should not * be associated with a session, e.g. because it is being used to handle * a new login. */ public FacebookApi(String serverUrl, String sessionKey) { try { _client = new FacebookRestClient(serverUrl, sessionKey, Constants.FACEBOOK_KEY, Constants.FACEBOOK_SHARED_SECRET); } catch (MalformedURLException e) { _log.severe("Bad server URL " + serverUrl); throw new RuntimeException(e); } } /** * Constructs a new Facebook API wrapper. * * @param sessionKey * Session key as returned by facebook.auth.getSession (which * is wrapped by this class's {@link #fetchCredentials(String) * fetchCredentials()} method). Null if this wrapper should not * be associated with a session, e.g. because it is being used to handle * a new login. */ public FacebookApi(String sessionKey) { this(DEFAULT_API_URL, sessionKey); } /** * Fetches the user ID for a session key. * * @param authToken * Authentication token as passed in on query string after redirection * from Facebook login page. * @return null if the session key was invalid or Facebook couldn't be * contacted. */ public FacebookCredentials fetchCredentials(String authToken) { if (authToken == null) return null; FacebookCredentials creds = new FacebookCredentials(); Document document = null; try { List params = new ArrayList(); params.add("auth_token=" + authToken); document = _client.callMethod("facebook.auth.getSession", params); NodeList nodes = document.getElementsByTagName("session_key"); Node node = nodes.item(0); if (node != null) creds.setSessionKey(node.getTextContent()); nodes = document.getElementsByTagName("uid"); node = nodes.item(0); if (node != null) creds.setUid(node.getTextContent()); return creds; } catch (Exception e) { _log.log(Level.SEVERE, "Can't fetch session data for " + authToken, e); if (document != null) { _log.info(document.toString()); } return null; } } /** * Returns specific pieces of friend info for a specific user. * * @param userId * Facebook user ID of the friend whose information is to be returned. * @param fields * Comma-delimited list of fields to request. */ public FacebookFriend getFriend(String userId, String fields) { List uids = new ArrayList(1); uids.add(userId); try { FacebookFriends friends = getInfo(uids, fields); if (friends.getAll().size() == 0) return null; return friends.getAll().first(); } catch (Exception e) { _log.log(Level.SEVERE, "Can't look up user " + userId, e); return null; } } /** * Returns all the defined friend info for a specific user. */ public FacebookFriend getFriend(String userId) { return getFriend(userId, "first_name,name"); } /** * Returns a list of selected pieces of information about the user's friends. * * @param fields * Comma-delimited list of fields to query from Facebook. */ public FacebookFriends getFriends(String fields) { try { return getInfo(getFriendIds(), fields); } catch (Exception e) { _log.log(Level.SEVERE, "Can't look up friends", e); return null; } } /** * Returns a list of the user's friends with full names. */ public FacebookFriends getFriendsName() { return getFriends("name"); } /** * Returns a list of the user's friends' Facebook user IDs. */ public Collection getFriendIds() throws FacebookException, IOException { Collection userIds = new LinkedList(); Document document = _client.callMethod("facebook.friends.get", new ArrayList()); NodeList nodes = document.getElementsByTagName("result_elt"); /* Walk the list of result elements; each one's contents is a user ID. */ for (int i = nodes.getLength() - 1; i >= 0; i--) { Node node = nodes.item(i); userIds.add(node.getTextContent().trim()); } return userIds; } /** * Returns information for a list of Facebook user IDs. * * @param userIds * User IDs to fetch information for. All these users must be friends of * the current user. * @param fieldList * Comma-delimited list of fields to fetch. */ public FacebookFriends getInfo(Collection userIds, String fieldList) throws FacebookException, IOException { Document document; StringBuilder sb = new StringBuilder("users="); /* The call takes a comma-delimited list of user IDs. */ for (String userId : userIds) { if (sb.length() > 6) sb.append(','); sb.append(userId); } /* Make the actual call to get the information we want. */ List params = new ArrayList(); params.add(sb.toString()); params.add("fields=" + fieldList); document = _client.callMethod("facebook.users.getInfo", params); /* Turn the resulting XML into Java objects. */ return extractFriendsFromGetInfo(document); } /** * Extracts a FacebookFriends object from the results of a * "facebook.users.getInfo" API call. */ private FacebookFriends extractFriendsFromGetInfo(Document document) { FacebookFriends friends = new FacebookFriends(); NodeList nodes; nodes = document.getElementsByTagName("result_elt"); for (int i = nodes.getLength() - 1; i >= 0; i--) { friends.add(extractFriend(nodes.item(i))); } return friends; } /** * Extracts a FacebookFriend object from an XML node. The FacebookFriend * object has a superset of the fields that might be contained in the * node; only extracts those fields that are actually present. */ private FacebookFriend extractFriend(Node node) { FacebookFriend friend = new FacebookFriend(); friend.uid = node.getAttributes().getNamedItem("id").getTextContent(); for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { if (child.getNodeType() == Node.ELEMENT_NODE) { if (child.getNodeName().equals("name")) friend.name = child.getTextContent(); else if (child.getNodeName().equals("first_name")) friend.firstName = child.getTextContent(); } } return friend; } }