Wednesday 4 July 2007

DWR: Pratikte AJAX - Sorunlar ve Tüyolar

WEB 2.0 kavramıyla hayatımıza girmiş olan AJAX'ın, tasarım anlamında kısa zamanda önyüzde ne kadar kurtarıcı görev üstlendiğini bilirsiniz. Bir anda gereksiz hidden alanlardan, gereksiz server'a gidip bir parametre set edip dönüşlerden, gereksiz hidden iframe'lerden kurtardı bizi. Ama zamanla anladık ki, ajax'ta da pratik olmayan birşeyler var. Başkaları da bunu anlamış olmalı ki birçok ajax kütüphanesi hazırlanmış.

Birgün biz ajax'la boğuşurken yanımızda bizden tamamen bağımsız çalışan hatta tanımadığım bir kişi DWR'ı önerdi. Nedir bu dwr araştırayım dedim. Direct Web Remoting'in kısaltılmışı olan DWR, javascript ile browser üstünden server'daki java koduna erişmeyi ve server tarafında işlem yapabilmeyi sağlayan open source bir kütüphane.

Bir örnek yapayım dedim. Örnekteki öncelikli amacım, ekranda bir select değiştiğinde altındaki select'in dinamik olarak değişmesini sağlamaktı.

Adım adım gidiyorum.

1. Projenin web.xml'ine dwr-invoker'ı ekliyoruz.
2. Ardından aynı dizin altına dwr.xml'imizi ekliyoruz. Bu xml'de Bean'lerimizi ve bizim servlet tarafında işimizi yapacak olan metodları barındıran class'ımızı tanımlıyoruz.
3. Projemizin dwr kütüphanesini görmesini de sağladıktan sonra ayarlarımız tamamdır. dwr.jar'ı da bu linkten indirebilirsiniz.

Bunlar kodlamaya başlamadan önce yapılması gereken genel adımlar. Bu standart adımların nasıl yapılacağını daha ayrıntılı öğrenmek için aşağıdaki linke gidebilirsiniz.

Getahead'den Getting Started Yazısına Buradan Ulaşabilirsiniz

Geriye artık kod yazmak kalıyor.

Önce bir deneyelim bakalım ayarlarımız düzgün müdür.

http://serverip:port/modulename/dwr yazıp girdiğinizde DWR'ın gördüğü class'ların listesi çıkacaktır.




dwr.xml içinde tanımladığımız class'ımızı xml içinde belirttiğimiz dizinde oluşturup içine eklediğimiz metodları bu linkten yine görüp deneyebiliriz.

Jsp dosyamıza öncelikle dwr kodlarını referans ediyoruz. Aşağıdaki iki satır bu işi görecektir.

Bizim dwr.xml'de tanımlamış olduğumuz DWRCode.java class'ının referansını ekliyoruz.
<script type='text/javascript' src='/modulename/dwr/interface/DWRCode.js'></script>
Ardından DWR kodu kullanmak için olmazsa olmaz engine.js i ekliyoruz:
<script type='text/javascript' src='/modulename/dwr/engine.js'></script>

Bir select'e göre başka bir select'in opsiyonlarını boşaltıp yenilerini dolduracağım için, bu işlemde lazım olacak util.js dosyasını da ekliyorum.
<script type='text/javascript' src='/modulename/dwr/util.js'></script>

Geriye javascript kodumu yazmak kalıyor. Ekranda ilk select'in adını select1 koyuyorum, ikincisinin adı select2 olsun. select1'in onchange'ine js fonksiyonumu ekliyorum.

js fonksiyonumun içinde az bir kodum var:
DWRCode.changeSubSelect(select1.value, resultFunction);


Burada DWRCode class'ı içindeki changeSubSelect metodunu çağırıyorum. Class içindeki bu metod normalde tek parametreli ve o tek parametre de select1'in değeri. Ama yukarıda da görüldüğü gibi, js'den bu metodu çağırırken ikinci bir parametre ekliyorum. O ikinci parametre, bu metoddan dönecek data'yı işleyecek olan javascript fonksiyonu oluyor yani resultFunction diye bir javascript fonksiyonu daha yazıyorum.

function resultFunction(data){

DWRUtil.removeAllOptions("select2");

DWRUtil.addOptions("select2", data);

}

DWR kullanırken karşılaşılabilecek bir hata, APP-INF'in altına dwr.jar kütüphanesini atıp öyle kod geliştirmektir. Uygulama, APP-INF altından kütüphaneyi görünce security hatası alıp class'ları göremiyor ve dwr çalışmıyor. O yüzden, WEB-INF/lib altına konulmalı bu kütüphane ve uygulamanın da oradan görmesi sağlanmalıdır.

Bir diğer ilginç nokta da, jsp içinden javasript kodunuzla dwr class'ınızdaki ilgili metodu çağırdınız fakat HttpServletRequest'e ihtiyacınız oldu. Bunun için de güzel bir çözümleri var.

HttpServletRequest request = WebContextFactory.get().getHttpServletRequest();

diyerek request'i alabilir ve kullanabilirsiniz. Ben kullandım, oradan biliyorum :)

Benim açımdan bir diğer ince nokta da, dwr metodlarını koyduğum class içinden sadece başka class'lar içindeki static metodları çağıracak şekilde metodlar eklemektir. Böylece, dwr class'ının şişmesini engelliyorum ve release management bünyesinde sorun çıkmaması daha kolaylaşıyor.

Release management demişken, farklı ekranlar için farklı release'lerde çıkacak metodlar yazarken, production release'i esnasında sorun çıkmaması için dwr class'ı içinden çağıracağım statik metodları reflection ile çağırmak üzerinde düşünüyorum şu an. Bu da güzel bir çözüm olacaktır, derleme esnasında hata çıkma ihtimalini ortadan kaldıracaktır. Run time'da çıkacak hatalardan artık siz sorumlusunuz :)


Bir fikir daha vereyim. DWR server'a gidip bilgileri okurken, kullanıcının işlem yapmasını istemiyorsunuz. Dolayısıyla, DWR işlemini bitirene kadar beklenmesini istiyorsunuz. Bu durumda, DWR çalışırken visible olan bir Div hazırlayabilir ve bunu kullanabilirsiniz. Bunu kullanmak da çok basit.


Öncelikle görünecek olan div'imi html'in içine ekliyorum.


<div id="DWRwaiting"... >

...

</div>

Div'imin id'si DWRwaiting. Ardından, DWREngine'e bunu belirtmem lazım. Bunu da aşağıdaki javascript kodunu html'ime ekleyerek yapıyorum:


DWREngine.setPreHook(function() {
$('DWRwaiting').style.visibility = 'visible';
});
DWREngine.setPostHook(function() {
$('DWRwaiting').style.visibility = 'hidden';
});

Böylece, DWR çalışırken yani işlem yapılırken DWRwaiting div'i visible oluyor, işlem bittiğinde de tekrar hidden oluyor. DIV içine tüm ekran boyutunda saydam bir layer koyup kullanıcının ekranı değiştirmesini engelleyebilirsiniz, ya da "Bilgiler getiriyor lütfen bekleyiniz" diye bir yazı gösterip kullanıcıyı uyarabilirsiniz. Hayalgücünüze kalmış.

4 comments:

Gokmen said...

Eline sağlık Mahircim,
ne mutluki bu dersi senden bire bir almıştım;)
kolay gelsin - devam

Unknown said...

Çok iyi olmuş! Eline Sağlık

Unknown said...

hocam eline sağlık, kolay gelsin.. bi sorum olucak yanlız ben şimdi reverse ajax kullanmak istiyorum şu anki projemde ve C# da geliştiriyorum bunu c# destekliyormu?? ayrıca databasede olan bi değişiklikte bi trigger methoduyla bu bilgileri anlık alabilicekmiyim(databaseden aspx tarafına) örneğin bi veri eklendi ve akabinde benim sayfamda update olucakmı buna en güzel örnek GMAIL deki mail alınımı..

Şimdiden Teşekkür ederim

Mahir Tarlan said...

Selam,
Malesef c# bilmiyorum. yardımcı olamayacağım :(