Friday, July 31, 2009

serialVersionUID

Java Serilestirme mekanizmasi bir Java objesini alip onu bir byte[] dizisine cevirebilememizi sagliyor. Bu bayt serisini alip dosyaya yazabiliyor, network uzerinden gonderebiliyor, database'e yazabiliyoruz. Hatta Schemafree ile yaptigimiz zaten tami tamina bu aslinda; herhangi bir objeyi alip, serilestirip onu bir kimlik ile bagdastirarak o bayt serisini SFEntitiy kap objesi icine gomulmus bir sekilde veri tabanina yaziyoruz (bu class 'Serializable' arayuzunu implement ediyor olmali tabii ki).

Yanliz eger ek bazi tanimlar yapilmazsa, serilestirme ve deserilestirme islemleri, objemizin class tanimi degistiginde hata verir. Ornek: Class A icinde iki tane alan tanimladik. Bu sekilde veri tabanina yazdik. Sonra ek bir alan koymamiz gerekti, bunu yaptik, yeni kodu derledik, ama sonra hala eski tanimda olan bir onceki objeyi geri okumaya kaltik, ve hata mesaji gorduk. Niye? Cunku baytkod tanimlari artik birbiri ile uyusmuyordu.

Aslinda Java Serilestirme kutuphanesi "hala birbirine uyan" yani "okuyabilecegi kadarina okuyabilecek" "eski" alanlari cevirebilecek mekanizmalara sahiptir. Ama bu ozelligi kullanmak icin bean class icinde serialVersionUID diye bir oge tanimlamiz gerekli. Tanim soyle:
static final long serialVersionUID = [SAYI];
[SAYI] ile taninlanmis rakam nereden gelecek? Bu rakami JDK araclarinden serialver adli program uretebilir.
serialver -classpath [CLASSPATH] [paket.Class]
komutu ile gerekli sayiyi urettirmek mumkundur. Hatta bazi editorler bu rakami otomatik olarak Java kodunun icine koyabilirler. Bu rakamin onemi surada: Eger rakam degismemis ve objenizin onceki hali ile mevcut tanimi birbirine uyumlu (compatible) ise, Java Serilestirme kutuphanesi okuyabilecegi kadar alani okumaya ugrasir, ve bu okuma basari ile isler. Uyumlu olmak ne demek? Eski alanlarin taniminda "degisiklik yapilmamis" sadece yeni alanlar "eklenmis" demek. O zaman onceki ve sonraki class tanimlari birbiri ile uyumludur ve okuma islemi basari ile isleyecektir.

Gelistirme sirasinda eger class iceriginiz cok fazla degistiyse, ve uzerinde calistiginiz kod sonuc ortaminda kullanilmiyorsa, serialVersionUID'yi (tekrar) uretmek akillica olabilir. Ozellikle inner class iceren cok fazla bazi degisiklik sonrasi class'imizin uyumsuz hale geldigini bir kez gozlemledik ve problemi yeni ID ureterek tamir ettik.

Seam ve Hata Mesajlari

Seam ile validasyon (veri dogruluk kontrolu), hata bulunca hata mesajlari yazma / gosterme mekanizmasi soyle kurulabilir.

Hata kontrolu yapan her aksiyon,
  @Out
private transient List errors = new ArrayList();
ile bir hata listesi tanimlar. Validasiyon sirasinda tum hatalar errors.add("mesaj") ibaresi ile bu listeye yazilir. Bu hatalar aksiyon sonrasi ustteki @Out ile de-enjekte edilecektir ve uygulamanin geri kalani artik bu listeye erisebilir. Bu noktada eger hatalarin olup olmadigina bagli olarak bir navigasyon yapmak istiyorsak, rule-if ile bunu yapmak artik cok basittir.
  <page view-id="/page.xhtml" conversation-required="false"
login-required="true">
<navigation from-action="#{userHandler.someAction}">
<rule if="#{errors.size() == 0}">
<redirect view-id="/home.xhtml"/>
</rule>
</navigation>
</page>
Dikkat edelim: Hata olmadigi durumda hangi sayfaya gidilecegini tanimlamadik, cunku bunu tanimlamadiysak, Seam otomatik olarak bizi ayni sayfaya yonlendirir. Bu bizim icin uygun, zaten hata varsa o sayfayi terk etmek istemiyorduk. Simdi, bu ayni sayfada hatalari basmak icin bir JSF kodu soyle yazilabilir.
  <font color="#FF0000">
<s:div rendered="#{errors.size() > 0}">
#{errors}
</s:div>
</font>
Bu sayfayi bir errors.xhtml diye tanimladik ve gereken her sayfadan include ile dahil ediyoruz.

Tabii bu kodu surekli kullanabilmek icin hata verebilecek her aksiyonumuzun errors adli ayni degisken ismini kullanmasi gerekiyor.

Sayfa Oncesi Aksiyon

Seam'in kullandigi sayfa -> aksiyon -> sayfa modelinden bahsettik. pages.xml adli bir dosyada tum navigasyon kurallari tanimlanmaktadir, ve X sayfasinda iken 'a' aksiyonu alinca Y sayfasina yonlendir gibi kurallar burada tanimlanabilir.

Yanliz bazi durumlarda sayfa yuklenmeden once bir aksiyon isletmek gerekebilir. Bu durum icin view-id tanimladigimiz ayni satirda bir aksiyon tanimlamamiz mumkundur.
<page view-id="/main.xhtml" conversation-required="false"
login-required="false" action="#{userHandler.someAction}">
<rule if="#{user == null}">
<redirect view-id="/home.xhtml"/>
</rule>
...
</page>
Bu ornekte main.xhtml sayfasi icin (aslinda boyle bir sayfanin mevcut olmasi bile gerekli degil, tarayiciya main.seam girince ustteki navigasyon kurallari isler) userHandler.someAction komutu isletilsin istiyorsak, tanim yukaridaki gibidir... Ayrica, diger her tur navigasyon kurali icin de, bu isletimden sonraki yonlendirmeyi "eger kuralina" baglayabiliriz. Mesela, bir aksiyondan @Out ile de-enjekte edilmis bir user objesinin olup olmadigina bagli bir karar almak istiyorsak, rule-if ile bunu yapariz. Ustte, eger user null ise, "home.xhtml adli bir sayfaya yonlendir" diye bir tanim yapmisiz.

Tuesday, July 28, 2009

Facebook Import

Bir kullanicinin arkadas listesini, onlarin bilgisini kendi uygulamaniza yuklemek istiyorsaniz, alttaki kodlar yardimci olabilir. Once temel kodlar:
  public Collection<String> getFriendIds() throws FacebookException, IOException {
Collection<String> userIds = new LinkedList<String>();

JSONArray friends = _client.friends_get();
if (friends == null || friends.length() == 0) {
return userIds;
}

/* Walk the list of result elements; each one's contents is a user ID. */
for (int i = 0; i < friends.length(); i++) {
try {
userIds.add(friends.getString(i));
}
catch (JSONException e) {
throw new IOException("friend list can't be extracted");
}
}

return userIds;
}

public JSONArray getInfo(Collection <String> userIds) {
Object result = null;
try {
// two fields
Set<ProfileField> fields = new HashSet<ProfileField>(2);
fields.add(ProfileField.UID);
fields.add(ProfileField.BIRTHDAY);
fields.add(ProfileField.FIRST_NAME);
fields.add(ProfileField.LAST_NAME);
fields.add(ProfileField.PIC_SMALL);
fields.add(ProfileField.SEX);
fields.add(ProfileField.RELATIONSHIP_STATUS);
fields.add(ProfileField.SIGNIFICANT_OTHER_ID);
fields.add(ProfileField.ACTIVITIES);
fields.add(ProfileField.INTERESTS);
fields.add(ProfileField.MUSIC);
fields.add(ProfileField.TV);
fields.add(ProfileField.MOVIES);

Collection<Long> longUserIds = new ArrayList<Long>(userIds.size());
for (String uid : userIds) {
longUserIds.add(Long.valueOf(uid));
}
result = _client.users_getInfo(longUserIds, fields);

} catch (Exception e) {
Util.log(e);
}

return (JSONArray)result;

}
Bu kodlari su sekilde cagirabiliriz (uid degiskeni kullanicinin facebook kimligini tasiyor olsun):
  public void importUsers() {
try {
Collection<String> plist = new LinkedList<String>();
plist.add(uid);
JSONArray parr = getInfo(plist);
importUsers(parr);

Collection<String> list = getFriendIds();
JSONArray arr = getInfo(list);
importUsers(arr);
} catch (Exception e) {
Util.log(e);
}
}

public void importUsers(JSONArray arr) {
..
for (int i = 0; i < arr.length(); i++) {
JSONObject obj = (JSONObject)arr.get(i);
...
value = ""+obj.get(ProfileField.BIRTHDAY.toString());
...
}
}
Session id uzerinden facebook kimliginin nasil alinacagini bir onceki yazida islemistik.

Thursday, July 23, 2009

Google Aramasi ve Urllib

Google aramalarina scriptlemek istiyorsak, wget, hatta urllib ile ilk denememiz basarisiz olabilir. Anlasiliyor ki wget ve urllib baglantilari, kullanicilari Google'in izin verdigi robotlardan degil. O zaman baglananin 'kim oldugunu' degistirerek, yani Google'i yaniltarak, izin verilen bir robot ortaya cikartabiliriz. Alttaki 'version' tanimi bunu yapiyor. Sanki bir Windows makinasindan baglanan Firefox tarayicisi gibi gozukuyoruz.
from urllib import FancyURLopener

class MyOpener(FancyURLopener):
version = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; it; ' + \
'rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11'

myopener = MyOpener()
page = myopener.open('http://www.google.com.tr/search?q=tomatoes')
content = page.read()
print content
Bu kadar!

Detexify

Bazen bir Latex sembolunun komutunu hatirlayamiyor olabiliriz; boyle durumlar icin Detefixy web programi faydali oluyor. Elle cizdiginiz sembol oruntu eslestirme algoritmasi ile arka planda size bir Latex sembolu bulup gosteriyor. Altta bizim theta icin yaptigimiz arama..

Wednesday, July 22, 2009

HTML Toplamak (Scraping)

Baska bir sitenin icerigini otomize olarak (bir script yardimiyla) alip diskte saklamak istiyorsak, bu tekrar yayin amacli, baglantisiz bakma amacli vs. olabilir, o zaman HTML toplama tekniklerini bilmek gerekli. Bu gibi isler icin biz Unix wget komutunu kullaniriz; fakat eger wget herhangi bir sebeple ise yaramazsa, bir alternatifi bilmekte yarar var. Python kutuphanelerinden urllib. Alttaki ornekte Google Insights for Search sayfalarindan Google'da son 7 gun icinde en cok aranan kelimelerin listesini almak icin kullandigimiz kodlar bulunabilir. Ayni sayfalar uzerinde wget ise yaramadi, urllib FancyURLopener calisti.
from urllib import FancyURLopener

myopener = FancyURLopener()
insightsURL = 'http://www.google.com/insights/search/overviewReport'
page = myopener.open(insightsURL + '?q=&date=today+7-d&cmpt=q')
print page.read()

Saturday, July 18, 2009

Ajax, Seam ve Facebook Connect

Facebook Connect mekanizmasini Java servis tarafi kodlarina baglama cabasinin iyilestirmeleri suruyor. Bir onceki yazida, Facebook FQL sorgusunu Javascript'e yaptirarak gelen verileri servis tarafina aktarmistik. Buradaki bir problem, Uzun suren Ajax cagrilarinin geri donuste problem yasamasi. Bu tur uzun sureli islem kodlarini servis tarafina gondermek daha iyi.

Ek olarak, simdiye kadar Javascript -> FB baglantisini kurmak icin kullanici UID (yani Facebook kullanici kimligi) Ajax ile set etmek bir cozum gorulebilirdi. Fakat bu cozum FB Connect kullanan uygulama acisindan guvenli bir cozum olmayacaktir - Facebook UID cok gizli bir bilgi degildir, 1'den baslayarak birer birer artan bir tam sayi sadece (kuruculari 1,2,3,4 diye kimlikler almislar mesela!), o zaman sadece Facebook tarafindan "uretilen", ve hem client hem servis tarafinda ayri ayri kontrol edilebilen, ve rutin olarak degisen bir kimlige ihtiyacimiz var.

Oturum kimligi (session id) bu isi basarabilir. Eger Facebook'a login edilmis haldeyseniz, ki degilseniz bir ziplama ekrani sonrasi login edilmis olursunuz, artik Facebook size bir session id uretebilir. Bu id Java servis kodlarina Ajax ya da form post uzerinden gonderilir, ve arka planda tekrar kontrol edilir. Bunun icin servis tarafinda FB cagrisi yapabilmek lazim tabii. Servis tarafi bunu yapiyorsa, User objesinin set edilmesi @Out ile de-enjente edilmesi, vs. gerceklestirilmis olur. O noktadan sonra isler tamamen Seam'in kontrolunde olacaktir.

Servis tarafinda Facebook baglantisi yapmak icin REST arayuzu var.

FB Session id almak icin Javascript kullanacagiz. Javascript illa ki biraz oluyor, cunku baska turlu FB ile ilk baglantiyi kurmak mumkun degil. Ama bu kod cok az olacak.

<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:fb="http://www.facebook.com/2008/fbml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">

<head>
<script type="text/javascript" src="seam/resource/remoting/resource/remote.js"></script>
<script type="text/javascript" src="seam/resource/remoting/interface.js?userHandler"></script>
</head>

<script type="text/javascript">
FB_RequireFeatures(["XFBML"], function()
{
FB.Facebook.init("[API KEY BURAYA]",
"xd_receiver.htm");
});

function flogin() {
sessionKey = FB.Facebook.apiClient.get_session().session_key ;
Seam.Component.getInstance("userHandler").facebookLogin(sessionKey);
}
</script>

<body>
<h:form>
<a href="#" onclick="flogin()">Login</a>
</h:form>
</body>

</html>
Ustteki HTML gosterilip Login baglantisina tiklaninca userHandler objesi uzerinde facebookLogin cagrisi yapilacaktir. Servis tarafi kodlari soyle:
import javax.ejb.Local;
import org.jboss.seam.annotations.remoting.WebRemote;
import com.google.code.facebookapi.FacebookException;
@Local
public interface UserHandler {
@WebRemote
public void facebookLogin(String sessionKey) ;
}


import com.google.code.facebookapi.FacebookJsonRestClient;
import com.google.code.facebookapi.FacebookException;
import java.util.LinkedList;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

@Stateful
@Scope(SESSION)
@Name("userHandler")
public class UserHandlerBean implements UserHandler {

protected FacebookJsonRestClient _client;

public void facebookLogin(String sessionKey) {
try {
_client = new FacebookJsonRestClient("[APP KEY BURAYA]",
"[GIZLI APP KEY BURAYA]",
sessionKey);

Collection list = getFriendIds();
log.debug("list=" + list);
} catch (Exception e) {
Util.log(e);
}
}

public Collection getFriendIds() throws FacebookException, IOException {
Collection userIds = new LinkedList();

JSONArray friends = _client.friends_get();
if (friends == null || friends.length() == 0) {
return userIds;
}

for (int i = 0; i < friends.length(); i++) {
try {
userIds.add(friends.getString(i));
}
catch (JSONException e) {
log.debug("Can't fetch friend " + i + " from list");
throw new IOException("friend list can't be extracted");
}
}

return userIds;
}

Ustteki kodlar facebook-java-api kodlarina ihtiyac duyuyor. Kodlari indirip kurun. Ayrica MoochSpot sitesinin ornek kodlari java-api kodlarini sarmalayan yardimci bazi kodlar sunmakta.

Bu konu hakkinda ek yazilar gelebilir; Sonuc olarak bir kere java-api uzerinden baglanti kurulunca, _client uzerinden Facebook uid gibi bilgiler alinarak, Seam User objesini set etmek icin kullanilabilir.

Lineer Cebir Karikaturu