I had a really hard time implementing Facebook with Wicket, and mostly that was because the examples were out dated. This example is for using Facebook connect with wicket and the facebook-java-api found here: http://code.google.com/p/facebook-java-api/ The docs were not much help, so I feel obligated to put what I did to get things working.
For starters, I added the dependency to maven:
<dependency> <groupId>com.google.code.facebookapi</groupId> <artifactId>facebook-java-api-schema</artifactId> <version>2.1.1</version> </dependency>
We use a Panel to display login information.
<html xmlns:wicket> <body> <wicket:panel> <!-- facebook api as of 8/11/09 --> <script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php" type="text/javascript"></script> <!-- function for facebook to execute on login--> <script type="text/javascript" wicket:id="fbcallback"> function callWicket() { var wcall = wicketAjaxGet('$url$' + '$args$', function() { }, function() { }); } </script> <div id="loginform"> <div wicket:id="fbloginDiv" style="display:none;"> <span wicket:id="fblogin"> <!-- facebool login button --> <fb:login-button onlogin='callWicket();'></fb:login-button> </span> <!-- facebook api --> <script type="text/javascript"> FB.init("your-api-key", "/xd_receiver.htm"); </script> </div> </div> </wicket:panel> </body> </html>
The panel caused me a slight issue when setting up the AbstractDefaultAjaxBehavior. I found that I had to add my Panel to my Page before I could get the callbackUrl from the behavior. Once I did that, everything was ok. Here is a simple panel example that illustrates one way to add the <fb:login to your page and handle a callback from Facebook.
import java.util.ArrayList; import java.util.HashSet; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.wicket.Page; import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.behavior.SimpleAttributeModifier; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.protocol.http.WebResponse; import org.apache.wicket.protocol.http.servlet.ServletWebRequest; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.springframework.security.GrantedAuthority; import org.springframework.security.GrantedAuthorityImpl; import org.springframework.security.context.SecurityContext; import org.springframework.security.context.SecurityContextHolder; import org.springframework.security.context.SecurityContextImpl; import org.springframework.security.providers.UsernamePasswordAuthenticationToken; import com.google.code.facebookapi.FacebookException; import com.google.code.facebookapi.FacebookJsonRestClient; import com.google.code.facebookapi.FacebookWebappHelper; import com.google.code.facebookapi.ProfileField; import com.pigspigot.model.User; /** * An example Panel that shows one way to output a facebook login button using fbml * and getting a callback from Facebook. * * @author russellsimpkins@hotmail.com * */ public class SimplePanel extends Panel { private static final Log log = LogFactory.getLog(SimplePanel.class); private WebMarkupContainer fbloginDiv; private Label fblogin; public SimplePanel(String id) { super(id); // TODO Auto-generated constructor stub } /** * This method will the panel */ public void createPanel() { fbloginDiv = new WebMarkupContainer("fbloginDiv"); fbloginDiv.setOutputMarkupId(true).setMarkupId("fbloginDiv"); fblogin = new Label("fblogin", "<fb:login-button onlogin='callWicket();'></fb:login-button>"); fblogin.setEscapeModelStrings(false); fblogin.setOutputMarkupId(true); if (isAuthenticated()) { fbloginDiv.add(new SimpleAttributeModifier("style", "display:none;")); } fbloginDiv.add(fblogin); addOrReplace(fbloginDiv); /** * This will only be called after they're logged in via facebook */ final AbstractDefaultAjaxBehavior behave = new AbstractDefaultAjaxBehavior() { protected void respond(final AjaxRequestTarget target) { // deal with facebook handleFacebookCallback(target.getPage()); fbloginDiv.add(new SimpleAttributeModifier("style", "display:none;")); target.addComponent(fbloginDiv); } }; add(behave); CharSequence url = behave.getCallbackUrl(); StringBuffer sb = new StringBuffer(); sb.append("function callWicket() { \n"); sb.append(" var wcall = wicketAjaxGet('"); sb.append(url); sb.append("', function() { }, function() { });"); sb.append(" }"); Label fbcallback = new Label("fbcallback", sb.toString()); fbcallback.setOutputMarkupId(true); fbcallback.setEscapeModelStrings(false); add(fbcallback); } /** * All that we do to log you in from facebook. I put my fbook.key and fbook.secret in the * properties file. * @param thePage */ public void handleFacebookCallback(Page thePage) { HttpServletRequest req = ((ServletWebRequest) thePage.getRequest()).getHttpServletRequest(); HttpServletResponse res = ((WebResponse) thePage.getResponse()).getHttpServletResponse(); String api = getLocalizer().getString("fbook.key", this); String secret = getLocalizer().getString("fbook.secret", this); FacebookWebappHelper<Object> helper = FacebookWebappHelper.newInstanceJson(req, res, api, secret); // make sure the login worked if (helper.isLogin()) { FacebookJsonRestClient facebookClient = (FacebookJsonRestClient) helper.getFacebookRestClient(); long id; try { // grab the logged in user's id id = facebookClient.users_getLoggedInUser(); // you can bundle ajax calls... facebookClient.beginBatch(); // i'm going to call the users.getInfo fb api call, just to make sure it works ArrayList<Long> ids = new ArrayList<Long>(); ids.add(new Long(id)); // put together a set of fields for fb to return HashSet<ProfileField> fields = new HashSet<ProfileField>(); fields.add(ProfileField.FIRST_NAME); fields.add(ProfileField.LAST_NAME); // get the user data facebookClient.users_getInfo(ids, fields); // execute the batch (which also terminates batch mode until beginBatch is called again) List<? extends Object> batchResponse = facebookClient.executeBatch(false); JSONArray userInfo = (JSONArray) batchResponse.get(0); JSONObject user = userInfo.getJSONObject(0); // a pojo user object User theUser = new User(); String username = user.getString("first_name"); theUser.setUsername(username); // fb emails are proxy, my app needs some kind of holder theUser.setEmail("noreply@facebook.com"); theUser.setUserId(new Integer(0)); theUser.setFacebook(true); theUser.setFacebookId(id); // we use spring, so here we give basic access to the facebook user. List<GrantedAuthority> gaList = new ArrayList<GrantedAuthority>(); gaList.add(new GrantedAuthorityImpl("STANDARD")); theUser.setAuthorities(gaList.toArray(new GrantedAuthority[] {})); GrantedAuthority[] ga = theUser.getAuthorities(); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(theUser, theUser, ga); SecurityContext context = new SecurityContextImpl(); context.setAuthentication(authentication); SecurityContextHolder.setContext(context); } catch (FacebookException e) { log.error("facebook issues: " + e); } catch (JSONException e) { log.error("facebook json issues: " + e); } } } /** * Do your own kind of auth check * @return */ public boolean isAuthenticated() { return SecurityContextHolder.getContext().getAuthentication() != null; } }
The Panel is added to your Page like so:
SimplePanel myPanel = new SimplePanel("your-wicket-id"); // make sure you add the panel first add(myPanel); // now you can create the panel contents myPanel.createPanel();