Wicket ve Facebook Kullanımı

Facebook hiç kuşkusuz günümüzün en çok kullanılan sosyal ağlarının başında geliyor. Bence facebook’un bu kadar başarılı olmasının en büyük sebeplerinden biri kullanıcılarıda sisteme dahil etmeyi başarmasıdır. Yani geliştirici olarak sisteme katkıda bulunuyorsunuz. Bir eksik gördüğünüzde ya da olması gereken bir uygulama fikri geldiğinde kendiniz geliştirip sisteme ekleyebiliyorsunuz.

Ben bu yazıda facebook üzerinde çalışacak bir uygulama yapmayacağım. Benim istediğim, kendi geliştirdiğim wicket uygulamasına kullanıcıların facebook üzeirnden giriş yapabilmeleri ve kayıt olabilmeleri.

Wicket uygulamama facebook kısmını eklerken olabildiğince java API’sinden uzak durmaya çalışacağım. Bana kalırsa facebook java api’si böyle basit istekler için fazlasıyla karmaşık bir kütüphane. Onun yerine facebook developer sitesinde açıklanan http istekleriyle uygulamamı geliştirmek istiyorum.

İnternette biraz araştırma yapınca farkettim ki, bunu tek isteyen ben değilmişim. Başka geliştiricilerde benzer düşüncelerle, facebook java api’sinden olabildiğince bağımsız uygulamalar geliştirmişler. Tekerleği tekrardan icat etmek yerine bloglarında yayınladıkları örneklerden faydalanacağım. Eğer isterseniz yazının sonundaki Kaynaklar kısmından yararlandığım siteleride görebilirsiniz.

Öncelikle

Öncelikle şunu belirtmek istiyorum. Bu uygulamayı geliştirirken, wicket’ta uygulama geliştirebildiğinizi ve geliştirdiğiniz uygulamalarda kendi Session yapılarınızı kullanabildiğinizi varsayacağım. Eğer bu konuda bilginiz yoksa wicket-stuff sitesinden nasıl yapılacağını öğrenebilirsiniz.

Aynı şekilde daha önce başka platformlarda facebook uygulaması geliştirdiğinizi varsayıyorum. Ama yinede hiç bilginiz yoksa, öncelikle Facebook Developers sitesine kayıt oluyorsunuz. Siteye kayıt olduktan sonra kendinize bir uygulama yaratıyorsunuz. Uygulama yaratırken uygulamanın internet adresi kısmına “http://localhost:8080/WicketFacebookExample/” tarzı bir site ismi giriyorsunuz. Burda WicketFacebookExample benim bu gün size anlatacağım projemin adı. Geri kalanı ise adres bilgilerini içeriyor.  Bu adres bilgisi gerçekten önemli. Çünkü uygulamamızın içerisinde, biz facebook’a login isteği gönderdiğimizde, içerisine geri döneceği adres bilgisinide veriyoruz. Eğer verdiğiniz adres bilgisi ile, uygulamayı yaratırken verdiğiniz adresler benzer değilse facebook size izin vermiyor.Uygulamanızı yarattığınızda size bir API_KEY ve API_SECRET verilecek. Bu bilgileri birazdan anlatacağım sınıfta kullanacağız.

Şimdi ise facebook işlemlerimizin tamamında kullanacağımız Facebook sınıfımızı oluşturalım.

Facebook.java


package blg.bhdrkn.facebook.domain;

import com.google.code.facebookapi.FacebookException;
import com.google.code.facebookapi.FacebookJsonRestClient;
import com.visural.common.StringUtil;

public class Facebook {
private static final String api_key = "[API_KEY]";
private static final String secret = "[SECRET]";
private static final String client_id = "[API_KEY_?]";

private static final String redirect_uri = "http://localhost:8080/WicketFacebookExample/fbauth";
private static final String[] perms = new String[] {"publish_stream", "email",  "offline_access"};

public static String getAPIKey() {
return api_key;
}

public static String getSecret() {
return secret;
}

public static String getLoginRedirectURL() {
return "https://graph.facebook.com/oauth/authorize?client_id=" +
client_id + "&display=page&redirect_uri=" +
redirect_uri+"&scope="+StringUtil.delimitObjectsToString(",", perms);
}

public static String getAuthURL(String authCode) {
return "https://graph.facebook.com/oauth/access_token?client_id=" +
client_id+"&redirect_uri=" +
redirect_uri+"&client_secret="+secret+"&code="+authCode;
}

public static void sendToWall(String message, long userId){
FacebookJsonRestClient client = new FacebookJsonRestClient(Facebook.getAPIKey(), Facebook.getSecret());
try {
client.stream_publish(message, null, null, null, userId);
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (FacebookException e) {
System.out.println("FACEBOOK EX: M: " + e.getMessage() + " C: " + e.getCause());
e.printStackTrace();
}
}
}

Bu sınıf ile ilgili olarak ilk dikkat etmeniz gereken kısım, API_KEY, SECRET ve CLIENT_IDkısımlarını kendinizinkiyle değiştirmeniz gerekiyor. Client id’nin tam olarak ne olduğunu bilmediğimden ben API_KEY kullandım ve başarılı sonuç aldım. Bir diğer dikkat etmeniz gereken kısım, facebook java api’yi sadece, giriş işlemini yaptıktan sonra kullanacak olmam. Son olarak dikkat etmeniz gereken yer redirect adresim, “http://localhost:8080/WicketFacebookExample/fbauth“. Farkettiyseniz normal site adresimden farklı. Sonundaki fbauth ileride yazacağım filtre için gerekiyor. Tam olarak ne işe yarayacağını kafa karışıklığına sebep olmamak için anlatmayacağım.

Login Komutunun Gönderilmesi

Şimdi bundan sonra bir link üzerinden, getLoginRedirectURL() metoduyla istek göndermemiz gerekiyor. Ben bunu LoginPage.java sayfasının içerisine aşağıdaki linki ekleyerek giderdim. Siz isterseniz başka yöntemlerlede bu adrese istek gönderebilirsiniz.

LoginPage.java


package blg.bhdrkn.facebook.pages;

import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.link.Link;

import blg.bhdrkn.facebook.domain.Facebook;

public class LoginPage extends WebPage{

public LoginPage() {
add(new Link<String>("myLink") {

@Override
protected CharSequence getURL() {
return Facebook.getLoginRedirectURL();
}

@Override
public void onClick() {
System.out.println(getRequest().getUrl().toString());
}
});
}
}

Bu basit bir web sayfası. Uygulamam açıldıktan sonra bu sayfaya yönleniyor. Farkettiyeseniz link direk olarak benim getLoginRedirectURL() metodumun sonucuna yöneleniyor. Bu adrese yapacağımız isteğin cevabı ise ileride bir filtreden geçecek. Geçeren gelen isteği inceleyeceğiz gereken kısımlarını alıp yeni bir istek göndereceğiz. Yani kullanıcı bir istek yapmış olacak fakat biz facebook’a senkron olarak iki istek göndereceğiz.

Filtre

Şimdi gelen isteğin nasıl filitreleneceğine bakalım. Bunun için bir Servlet yazacağız. Yazacağımız bu servlet ise /fbauth uzantılı istekleri filitreleyecek. Bu filtrelemenin amacı gelen isteği kullanarak ikinci bir istek üretip, facebook’tan asıl ihtiyacımız olan access_token bilgisini almak.

  1. Önce getRedirectURL() üzerinden ilk isteğimizi göndereceğiz. Gerekirse kullanıcı burada facebook girişi yapacak. İstediğimiz izinleri verecek.
  2. Kullanıcı işlemleri bittiğinde, facebook, sayfayı, bizim gönderdiğimiz isteğin içinde bulunan redirect_page adresine yönlendirecek. Ki o adreste bizim uygulamamız için http://localhost:8080/WicketFacebookExample/fbauth adresi olacak.
  3. Filitremiz /fbauth uzantılı istekleri filtreleyeceğinden. Bu istek doğruca filtremize gidecek. Filtremiz içerisinde, gelen cevabı kullanarak yeni bir istek oluşturacağız.
  4. İkinci istek sonucunda erişmek istediğimiz access_token bilgisine ulaşmış olacağız.

Filtre yaratmanın iki adımı var. İlk önce filtre sınıfımızı yazacağız. Sonra bu sınıfın neyi filtreleyeceğini web.xml içerisinde belirteceğiz.

FacebookAuthentication.java


package blg.bhdrkn.facebook.filter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.wicket.Session;

import blg.bhdrkn.facebook.application.LoginSession;
import blg.bhdrkn.facebook.domain.Facebook;

import com.visural.common.StringUtil;

public class FacebookAuthentication implements Filter{

private static final long serialVersionUID = -460291618325615633L;

public void doFilter(ServletRequest sr, ServletResponse sr1, FilterChain fc)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) sr;
HttpServletResponse res = (HttpServletResponse) sr1;
String code = sr.getParameter("code");
if (StringUtil.isNotBlankStr(code)) {
String authURL = Facebook.getAuthURL(code);
URL url = new URL(authURL);
try {
String result = readURL(url);
String accessToken = null;
Integer expires = null;
String[] pairs = result.split("&");
for (String pair : pairs) {
String[] kv = pair.split("=");
if (kv.length != 2) {
throw new RuntimeException("Unexpected auth response");
} else {
if (kv[0].equals("access_token")) {
accessToken = kv[1];
}
if (kv[0].equals("expires")) {
expires = Integer.valueOf(kv[1]);
}
}
}
if (accessToken != null){
LoginSession session = (LoginSession)Session.get();
if(session.authenticateFacebookLogin(accessToken))
res.sendRedirect("http://localhost:8080/WicketFacebookExample/SignedIn");
else
res.sendRedirect("http://localhost:8080/WicketFacebookExample/");
} else {
System.out.println("ACCESSTOKEN: " + accessToken + " EXPIRES: " + expires);
throw new RuntimeException(
"Access token and expires not found");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

private String readURL(URL url) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = url.openStream();
int r;
while ((r = is.read()) != -1) {
baos.write(r);
}
return new String(baos.toByteArray());
}

public void destroy() {
}

@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub

}
}

Ve web.xml dosyamızın son hali aşağıdaki gibi olmalı.

web.xml


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>Wicket Facebook Application</display-name>
<filter>
<filter-name>Application</filter-name>
<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
<init-param>
<param-name>applicationClassName</param-name>
<param-value>blg.bhdrkn.facebook.application.FacebookApplication</param-value>
</init-param>
</filter>
<filter>
<filter-name>FBOAuth</filter-name>
<filter-class>blg.bhdrkn.facebook.filter.FacebookAuthentication</filter-class>
</filter>
<filter-mapping>
<filter-name>Application</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>FBOAuth</filter-name>
<url-pattern>/fbauth</url-pattern>
</filter-mapping>
</web-app>

Tek eksik olan kısım LoginSession içerisindeki siteye giriş yaptıracak olan metod. LoginSession’ıda verip durumu sonlandırıyorum.

LoginSession.java


package blg.bhdrkn.facebook.application;

import java.net.URL;

import org.apache.wicket.protocol.http.WebSession;
import org.apache.wicket.request.Request;
import org.json.JSONObject;

import blg.bhdrkn.facebook.domain.User;

import com.visural.common.IOUtil;

public class LoginSession extends WebSession {

private static final long serialVersionUID = 1L;

private User user;

public LoginSession(Request request) {
super(request);
}

public boolean isSignedIn() {
return user != null;
}

public User getUser() {
return user;
}

public boolean authenticateFacebookLogin(String accessToken) {
try{
if(user == null){
JSONObject resp = new JSONObject(
IOUtil.urlToString(new URL("https://graph.facebook.com/me?access_token=" + accessToken)));
if(resp == null){
return false;
}
user = new User();
System.out.println(resp.getString("id"));
user.setFirstName(resp.getString("first_name"));
user.setLastName(resp.getString("last_name"));
user.setId(Long.valueOf(resp.getString("id")));
user.setEmail(resp.getString("email"));
}
}catch(Exception e){
return false;
}
return true;
}

}

Burada kullanılan User sınıfı, bildiğiniz sırada bir POJO, dört tane alanı var içerisinde. Filtremizden anlayacağınız üzere, eğer kullanıcının giriş işlemi başarılıysa yani access_token bilgisini alabildiysek SignInPage.java isimli başka bir sayfaya yönlenecek. Eğer başarılı değilse tekrardan ana sayfaya dönüş yapacak.

Alınan Bilgilerin Yorumlanması

Şimdi ise SignInPage.java sınıfımıza bakalım. Bu sınıfta giriş yapanan kullanıcı bilgilerini ekrana basacağız. Hatta bir link koyup kullanıcıya hali hazırda bulunan bir mesajı duvarlarında yayınlamalarını sağlayacağız. Aslında bunu yapacak metodu Facebook.java sınıfımızda yazmıştık. Burada yapacağımız tek şey kullanıcının facebook id’sini kullanıp bu metodu çağırmak olacak.

SignInPage.java


package blg.bhdrkn.facebook.pages;

import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.Link;

import blg.bhdrkn.facebook.application.LoginSession;
import blg.bhdrkn.facebook.domain.Facebook;
import blg.bhdrkn.facebook.domain.User;

public class SignedInPage extends WebPage implements AuthenticatedWebPage {

private static final long serialVersionUID = -5476595548698214837L;
private User signedInUser;

private static final String Message = "Facebook Test Message...";

public SignedInPage() {
publishUserInformation();
createLogOutLink();
createSendMessagetoFacebookWallLink();
}

private void createSendMessagetoFacebookWallLink() {
add(new Link<String>("send") {

@Override
public void onClick() {
Facebook.sendToWall(Message, signedInUser.getId());
}

});

}

private void createLogOutLink() {
add(new Link<String>("logout") {

@Override
public void onClick() {
getSession().invalidate();
setResponsePage(LoginPage.class);
}

});
}

private void publishUserInformation() {
signedInUser = ((LoginSession) getSession()).getUser();
add(new Label("userId", String.valueOf(signedInUser.getId())));
add(new Label("firstName", signedInUser.getFirstName()));
add(new Label("lastName", signedInUser.getLastName()));
add(new Label("email", signedInUser.getEmail()));
}
}

Ve aynı sayfanın markup kısmı…

SignInPage.html


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=windows-1254">
<title>Sign In Page</title>
</head>
<body>
<fieldset>
<legend>Facebook User Information</legend>
FACEBOOK ID: <span wicket:id="userId"></span><br />
FIRST NAME: <span wicket:id="firstName"></span><br />
LAST NAME: <span wicket:id="lastName"></span><br />
EMAIL: <span wicket:id="email"></span><br />
</fieldset>
<br />
<br />
<br />
<fieldset>
<legend>Operations</legend>
<a wicket:id="logout">LOG OUT</a> <br />
<a wicket:id="send">SEND TEST MESSAGE</a><br />
</fieldset>
</body>
</html>

Eğer bilgileriniz doğru bir şekilde geldiyse ve kullanıcının duvarına yazabiliyorsanız başardınız demektir.

Kaynaklar

GitHub Adres Bilgileri

Projenin tamamına aşığadaki GitHub Adresinden ulaşabilirsiniz.

https://github.com/bhdrkn/Wicket-Examples

End Of Line