/*
* JBoss, Home of Professional Open Source
* Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual
* contributors by the @authors tag. See the copyright.txt in the
* distribution for a full listing of individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.quickstarts.portal.social.oauth.facebook;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.portlet.PortletContext;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import com.restfb.Connection;
import com.restfb.DefaultFacebookClient;
import com.restfb.FacebookClient;
import com.restfb.Parameter;
import com.restfb.exception.FacebookException;
import com.restfb.json.JsonObject;
import com.restfb.types.NamedFacebookType;
import com.restfb.types.StatusMessage;
import com.restfb.types.User;
import org.gatein.api.oauth.AccessToken;
import org.gatein.api.oauth.OAuthProvider;
/**
* Wrapper around {@link FacebookClient}, which handles error situations
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class FacebookClientWrapper {
private final RenderRequest request;
private final RenderResponse response;
private final PortletContext portletContext;
private final OAuthProvider oauthProvider;
private final FacebookClient facebookClient;
FacebookClientWrapper(RenderRequest request, RenderResponse response, PortletContext portletContext, OAuthProvider oauthPr,
String accessToken) {
this.request = request;
this.response = response;
this.portletContext = portletContext;
this.oauthProvider = oauthPr;
this.facebookClient = new DefaultFacebookClient(accessToken);
}
/**
* @return Info about me (current user)
*/
public UserWithPicture getMe() throws IOException, PortletException {
return this.fetchObject("me", UserWithPicture.class, Parameter.with("fields", "id,name,picture"));
}
/**
* @return Info about me (current user) with all the details (id, name, firstName, lastName, ...)
*/
public User getMeWithDetails() throws IOException, PortletException {
return this.fetchObject("me", User.class,
Parameter.with("fields", "id,name,username,first_name,last_name,gender,timezone,locale,email"));
}
/**
* @return number of all your friends on Facebook or -1 if error occured
*/
public int getFriendsCount() throws IOException, PortletException {
Connection<NamedFacebookType> myFriends = this.fetchConnection("me/friends", NamedFacebookType.class);
return myFriends != null ? myFriends.getData().size() : -1;
}
/**
* Filter string, which will be used for filtering of users by "contains" rule.
*
* <p>
* For example: You have 3 friends on Facebook with names: "John Woo", "Marc Boo", "Susie Cole" and filter will be "oo" Then
* output of this method will return "John Woo" and "Marc Boo"
* </p>
*
* @param filter Filter string, which will be used for filtering of users by "contains" rule. For example
* @return ids of filtered friends
*/
public List<String> getIdsOfFilteredFriends(String filter) throws PortletException, IOException {
// Not good to obtain all friends within each request, but we don't have better way atm (limitation of facebook search
// api...)
Connection<NamedFacebookType> connection = this.fetchConnection("me/friends", NamedFacebookType.class);
if (connection == null) {
return new ArrayList<String>();
}
List<NamedFacebookType> allFriends = connection.getData();
List<String> result = new ArrayList<String>();
for (NamedFacebookType current : allFriends) {
if (current.getName().contains(filter)) {
result.add(current.getId());
}
}
return result;
}
/**
* get Page of friends according to given offset and limit
*
* @param offset
* @param limit
* @return friends starting from offset and number of returned friends will be limit
*/
public List<NamedFacebookType> getPageOfFriends(int offset, int limit) throws PortletException, IOException {
Connection<NamedFacebookType> con = this.fetchConnection("me/friends", NamedFacebookType.class,
Parameter.with("offset", offset), Parameter.with("limit", limit));
if (con != null) {
return con.getData();
} else {
return new ArrayList<NamedFacebookType>();
}
}
/**
* Return list of friends based on list of given ids
*
* @param idsOfFriendsToDisplay of friends, which we want to obtain from facebook
* @return list of friends
*/
public List<FacebookUserBean> getFriends(final List<String> idsOfFriendsToDisplay) throws IOException, PortletException {
List<FacebookUserBean> fbFriends = new ArrayList<FacebookUserBean>();
// Render friends with their pictures
if (idsOfFriendsToDisplay.size() > 0) {
// Fetch all required friends with obtained ids
JsonObject friendsResult = this.fetchObjects(idsOfFriendsToDisplay, JsonObject.class,
Parameter.with("fields", "id,name,picture"));
if (friendsResult == null) {
return fbFriends;
}
for (String id : idsOfFriendsToDisplay) {
JsonObject current = friendsResult.getJsonObject(id);
UserWithPicture friend = facebookClient.getJsonMapper().toJavaObject(current.toString(), UserWithPicture.class);
fbFriends.add(new FacebookUserBean(friend));
}
}
return fbFriends;
}
/**
* return FacebookUserBean with filled status messages
*
* @param friendId
* @param myId This parameter is used only to recognize Facebook scope, which we need
* @param accessToken
* @return FacebookUserBean with filled status messages
*/
public FacebookUserBean getStatusesOfPerson(String friendId, String myId, AccessToken accessToken) throws PortletException,
IOException {
Connection<StatusMessage> statusMessageConnection = this.fetchConnection(friendId + "/statuses", StatusMessage.class,
Parameter.with("limit", 5));
if (statusMessageConnection == null) {
return null;
}
List<StatusMessage> statuses = statusMessageConnection.getData();
FacebookUserBean ffb = new FacebookUserBean();
ffb.setId(friendId);
ffb.setScope(false);
ffb.setStatuses(statuses);
if (statuses.size() == 0) {
// Different scope is needed for me and different for my friends
String neededScope = friendId.equals(myId) ? "user_status" : "friends_status";
if (accessToken.isScopeAvailable(neededScope)) {
ffb.setScope(true);
} else {
ffb.setNeededScope(neededScope);
}
} else {
NamedFacebookType currentFriendToDisplay = this.fetchObject(friendId, NamedFacebookType.class,
Parameter.with("fields", "id,name"));
if (currentFriendToDisplay != null) {
ffb.setName(currentFriendToDisplay.getName());
}
}
return ffb;
}
private <T> T fetchObject(final String object, final Class<T> objectType, final Parameter... parameters)
throws PortletException, IOException {
FacebookPortletRequest<T> facebookRequest = new FacebookPortletRequest<T>(request, response, portletContext,
oauthProvider) {
@Override
protected T invokeRequest() throws FacebookException {
return facebookClient.fetchObject(object, objectType, parameters);
}
};
return facebookRequest.executeRequest();
}
private <T> Connection<T> fetchConnection(final String connection, final Class<T> connectionType,
final Parameter... parameters) throws PortletException, IOException {
FacebookPortletRequest<Connection<T>> facebookRequest = new FacebookPortletRequest<Connection<T>>(request, response,
portletContext, oauthProvider) {
@Override
protected Connection<T> invokeRequest() throws FacebookException {
return facebookClient.fetchConnection(connection, connectionType, parameters);
}
};
return facebookRequest.executeRequest();
}
private <T> T fetchObjects(final List<String> objects, final Class<T> objectType, final Parameter... parameters)
throws PortletException, IOException {
FacebookPortletRequest<T> facebookRequest = new FacebookPortletRequest<T>(request, response, portletContext,
oauthProvider) {
@Override
protected T invokeRequest() throws FacebookException {
return facebookClient.fetchObjects(objects, objectType, parameters);
}
};
return facebookRequest.executeRequest();
}
}