As always, you can jump straight to the RestFB Javadoc, or learn by example below.
If you're looking for help or additional examples, please check out the RestFB Google Group.
// DefaultLegacyFacebookClient is the LegacyFacebookClient implementation // that ships with RestFB. You can customize it by passing in // custom JsonMapper and WebRequestor implementations, or simply // write your own FacebookClient instead for maximum control // Use this constructor if you'd like to go with legacy authentication (not recommended) LegacyFacebookClient facebookClient = new DefaultLegacyFacebookClient(MY_API_KEY, MY_SECRET_KEY); // Use this constructor if you'd like to go with the new OAuth token authentication (preferred) LegacyFacebookClient facebookClient = new DefaultLegacyFacebookClient(MY_ACCESS_TOKEN);
access_token
parameter in your URL bar. That's it! Just copy and paste it into your code.
http://graph.facebook.com/btaylor?access_token=XXXXXXX
We'll get the UID
associated with the current Facebook API session key by calling the API method users.getLoggedInUser
.
// Last parameter specifies that this API call's result // should be returned to us as a Long value Long uid = facebookClient.execute("users.getLoggedInUser", Long.class);
Here we make the API call fql.query
to execute a custom query.
// FQL query which asks Facebook for your friends' names, // profile picture URLs, and network affiliations String query = "SELECT name, pic_big, affiliations FROM user " + "WHERE uid IN (SELECT uid2 FROM friend WHERE uid1=12345)"; // Executes an API call with result mapped to a list of classes we've defined. // Note that you can pass in an arbitrary number of Parameters - here we // send along the query as well as the "give me HTTPS URLs" flag List<User> users = facebookClient.executeForList("fql.query", User.class, Parameter.with("query", query), Parameter.with("return_ssl_resources", "true"));
The Facebook API returned JSON in response to our query. You can click here to see it.
Since RestFB doesn't know in advance what kinds of data you're requesting from the API,
you need to define your own JavaBean classes that map to API responses. Luckily, this is a simple
process - just annotate fields with @Facebook
and RestFB will automatically convert
Facebook's JSON to real Java objects.
public class User { // By default, assumes JSON attribute name is the same as the Java field name @Facebook String name; // If your Java field name is different than the JSON attribute name, // just specify the JSON attribute name @Facebook("pic_big") String pictureUrl; @Facebook List<Affiliation> affiliations; public String toString() { return String.format("Name: %s\nProfile Image URL: %s\nAffiliations: %s", name, pictureUrl, affiliations); } } public class Affiliation { @Facebook String name; @Facebook String type; public String toString() { return String.format("%s (%s)", name, type); } }
for (User user : users) System.out.println(user + "\n");
And here they are on stdout:
Name: Heather Merlino Profile Image URL: https://secure-profile.facebook.com/profile6/138/106/n28455_7662.jpg Affiliations: [Intuit (work)] Name: Steve Nolan Profile Image URL: https://secure-profile.facebook.com/v228/344/541/n4057387_1795.jpg Affiliations: [Cornell (college), Bain & Company (work)] Name: Third Person Profile Image URL: https://secure-profile.facebook.com/v2328/314/454/n137387_5491.jpg Affiliations: []
Given the unique nature of the fql.multiquery API call, RestFB provides the LegacyFacebookClient.executeMultiquery(Class, MultiqueryParameter, Parameter...) family of methods to easily perform multiquery operations with a minimum of configuration.
To use the multiquery support, simply create a map of named queries and create a class to hold the results. Here's an example:
LegacyFacebookClient facebookClient = new DefaultLegacyFacebookClient(MY_ACCESS_TOKEN); String friendsQuery = "SELECT name, pic_big FROM user " + "WHERE uid IN (SELECT uid2 FROM friend WHERE uid1=12345)"; String groupsQuery = "SELECT gid, name FROM group WHERE gid IN " + "(SELECT gid FROM group_member WHERE uid=12345)"; // The key names specified here map to field names in the result object Map<String, String> queries = new HashMap<String, String>(); queries.put("friends", friendsQuery); queries.put("groups", groupsQuery); // You can send along additional parameters if you'd like, too Results results = facebookClient.executeMultiquery(Results.class, MultiqueryParameter.with(queries));
Here's the result class and its supporting classes. By default, results are mapped to @Facebook
-annotated fields by the
logical query name specified in the map. You may override this behavior by explicitly specifying the value as shown below on the users
field.
class Results { @Facebook("friends") List<User> users; @Facebook List<Group> groups; } // The multiquery support uses the same recursive mapping as the other parts of RestFB. // It's not shown in this example, but you can create arbitrarily complex object graphs // of @Facebook-annotated fields to handle complex query results. class Group { @Facebook(value = "gid") Long id; @Facebook String name; } class User { @Facebook String name; @Facebook("pic_big") String pictureUrl; }
The stream.publish API call can accept
attachment
and privacy
(JSON objects) as well as action_links
(JSON array) as parameters.
The 1.4 release of RestFB supports mapping of Java types to JSON to simplify this use case - you no longer have to
manually create a JSON string, as the framework will take care of that for you for List
s, Map
s,
and your own Javabean types that have fields annotated with the @Facebook
annotation.
LegacyFacebookClient facebookClient = new DefaultLegacyFacebookClient(MY_ACCESS_TOKEN); ActionLink category = new ActionLink(); category.href = "http://bit.ly/KYbaN"; category.text = "humor"; Properties properties = new Properties(); properties.category = category; properties.ratings = "5 stars"; Medium medium = new Medium(); medium.href = "http://bit.ly/187gO1"; medium.src = "http://bit.ly/GaTlC"; medium.type = "image"; Attachment attachment = new Attachment(); attachment.name = "i'm bursting with joy"; attachment.href = "http://bit.ly/187gO1"; attachment.caption = "{*actor*} rated the lolcat 5 stars"; attachment.description = "a funny looking cat"; attachment.properties = properties; attachment.media = Collections.singletonList(medium); // Send the request to Facebook. // We specify the session key to use to make the call, the fact that we're // expecting a String response, and the attachment (defined above). String postId = facebookClient.execute("stream.publish", String.class, Parameter.with("attachment", attachment));
Here are the supporting classes. You may download the full example and run it locally.
Note that you don't have to write your own classes - you can just as easily populate Map
s instead
for a less strongly-typed solution.
class ActionLink { @Facebook String text; @Facebook String href; } class Medium { @Facebook String type; @Facebook String src; @Facebook String href; } class Properties { @Facebook ActionLink category; @Facebook String ratings; } class Attachment { @Facebook String name; @Facebook String href; @Facebook String caption; @Facebook String description; @Facebook Properties properties; @Facebook List<Medium> media; }
RestFB comes with a few example Java source files that you can run and tweak yourself. Don't forget to put-Daccess_token=MY_ACCESS_TOKEN
in quotes!
$ cd source/examples $ ant run-legacy-examples "-Daccess_token=MY_ACCESS_TOKEN"
Using DefaultJsonMapper
,
RestFB is able to recursively map JSON fields annotated with @Facebook
to the following Java types out of the box:
String
Integer
Boolean
Long
Double
Float
BigInteger
BigDecimal
List
s of any of the above types
For example:
public class MyClass { @Facebook String name; @Facebook BigDecimal value; @Facebook List<Integer> numbers; }
As of RestFB 1.4, Java to JSON mapping is supported by default JsonMapper.toJson(Object object)
.
You may recursively convert primitive wrapper types as specified above, List
s, Map
s with String
keys, and your own Javabean types
by applying the @Facebook
annotation to any fields you'd like to include in the conversion.
As-is, DefaultJsonMapper
should meet the needs of the vast majority of users.
If it doesn't support a feature you need, you can easily subclass it or write your own implementation of JsonMapper
instead.
All LegacyFacebookClient
methods throw the abstract FacebookException
.
These are the FacebookException
subclasses that you may catch:
FacebookJsonMappingException
@Facebook
annotation on a field with an unsupported type or
the Facebook API JSON doesn't map correctly to the fields you've annotated (e.g. attempting to map a JSON string to a Java BigDecimal
or a JSON object to a Java List
).
You generally should not explicitly catch this exception in your code, as it usually signifies programmer
error in setting up @Facebook
annotations. One valid use for catching this exception,
however, is to detect when Facebook changes what an API call returns on their end, which would break your live code.
It may be useful to catch this exception and then send a notification to you or your ops team to notify them
that your application needs to be updated.
FacebookNetworkException
OK
(200) status code.
If there's an HTTP status code available, it's included in the exception so you may take custom actions
depending on what type of error occurred.
FacebookResponseStatusException
OK
(200) response is received, but
the returned JSON signifies an error condition.
For example, below is the list of errors the Facebook API may return for the Stream.removeLike call:
Code | Description | |
100 | Invalid parameter. | |
---|---|---|
102 | Session key invalid or no longer valid (if it's a desktop application and the session is missing). | |
200 | Permissions error. The application does not have permission to perform this action. | |
210 | User not visible. The user doesn't have permission to act on that object. |
FacebookResponseStatusException
will include both the error code and error message returned by the Facebook API
so you may take custom actions depending on the type of error that occurred.
Here's some example code to illustrate the above. Keep in mind that your code doesn't need to handle every single exception the way we're doing here - this is just to demonstrate what's possible.
try { Integer result = facebookClient.execute("Stream.removeLike", sessionKey, Integer.class, Parameter.with("post_id", "12345")); } catch (FacebookJsonMappingException e) { // Looks like this API method didn't really return an Integer } catch (FacebookNetworkException e) { // Looks like an error occurred at the network level System.out.println("API returned HTTP status code " + e.getHttpStatusCode()); } catch (FacebookResponseStatusException e) { // Facebook API returned a specific error if (e.getErrorCode() == 200) System.out.println("Permission denied!"); } catch (FacebookException e) { // This is the catchall handler for any kind of Facebook exception }
In addition to LegacyFacebookClient
, RestFB provides default implementations
for WebRequestor
and
JsonMapper
, two components that
DefaultFacebookClient
depends on to do its work.
These dependencies are designed to allow for straightforward subclassing (if you only want to replace a little bit of functionality) and simple custom implementations (if you require full control).
This comes in handy when unit testing - for example, you can write your own WebRequestor
implementation
that simulates a Facebook API endpoint response. You can drop in custom data designed to exercise your application's
Facebook integration or simulate error conditions to make sure you're handling them properly.
Here's a trivial example which shows one way you might implement this:
LegacyFacebookClient facebookClient = new DefaultLegacyFacebookClient(MY_ACCESS_TOKEN, // A one-off DefaultWebRequestor for testing that returns a hardcoded JSON // list of numbers instead of hitting the Facebook API endpoint URL new DefaultWebRequestor() { @Override public Response executePost(String url, String parameters) throws IOException { return new Response(HttpURLConnection.HTTP_OK, "[123,456,789]"); } }, new DefaultJsonMapper()); // Make an API request using the mocked WebRequestor List<Integer> numbers = facebookClient.executeForList("ignored", Integer.class); // Make sure we got what we were expecting assert numbers.size() == 3; assert numbers.get(1) == 456;