Friday, November 1, 2002

XPath İle Çocuk Düğümleri Toplamak

Farzedelim ki, aşağıda örneği gösterilen dosya içinden eCRM baslığı taşıyan etiketlerin altındaki bütün çocuk düğümleri toplamak istiyoruz.

<?xml version="1.0" encoding="iso-8859-9"?>
<kategoriler>
<kategori baslik="eCRM">
<yazi>a_etl.xml<tarih>19 Nisan 2002</tarih></yazi>
<yazi>a_hangi_verileri_alalim.xml<tarih>19 Nisan 2002</tarih></yazi>
<yazi>a_internet_veri_ambari.xml<tarih>19 Nisan 2002</tarih></yazi>
<yazi>a_musteri_kayitlari.xml<tarih>19 Nisan 2002</tarih></yazi>
<yazi>a_oracle_kavramlari.xml<tarih>19 Nisan 2002</tarih></yazi>
<yazi>a_oracle_kullanim.xml<tarih>19 Nisan 2002</tarih></yazi>
<yazi>a_oracle_parallel.xml<tarih>19 Nisan 2002</tarih></yazi>
<yazi>a_oracle_query_optimize.xml<tarih>19 Nisan 2002</tarih></yazi>
<yazi>a_veri_madenciligi.xml<tarih>19 Nisan 2002</tarih></yazi>
</kategori>
<kategori baslik="Genel">
<yazi>a_acik_anahtar_sifreleme.xml<tarih>19 Nisan 2002</tarih></yazi>
<yazi>a_continous_integration.xml<tarih>19 Nisan 2002</tarih></yazi>
<yazi>a_cvs.xml<tarih>19 Nisan 2002</tarih></yazi>
</kategori>
</kategoriler>



Bunun için şöyle bir XPath komutu vermemiz gerekecek.

/kategoriler/kategori[@baslik='"eCRM"']/yazi


Bu komut kategoriler, kategori altına gidip, eCRM baslığı taşıyan bütün yazı etiketlerinin değerlerini getirecektir. Java ile şöyle kodlama yapabiliriz.

  public ArrayList kategoriAltindakiYazilar(String kategori)
{
Document doc = belgeOku();
String xpath = "/kategoriler/kategori[@baslik='" + kategori + "']/yazi";
NodeList liste = null;
ArrayList sonListe = new ArrayList();

try {

liste = org.apache.xpath.XPathAPI.selectNodeList(doc, xpath);

} catch (javax.xml.transform.TransformerException e) { }


for (int i=0; i < liste.getLength(); i++) {
Element elem = (Element)liste.item(i);
sonListe.add(elem.getFirstChild().getNodeValue());
}

return sonListe;

}



getFirstChild (ilk çocuğu getir) çağrısına dikkatinizi çekmek istiyorum. Bu çağrı ile, zaten başka çocuğu olmayan yazı etiketinin alt değerini almış oluyoruz. Tabii getNodeValue (dügüm değerini getir) dememiz de gerekti. Anlamamız gereken nokta, 'yazı' altında bulunan değerin etiketin kendisinde değil, alt düğüm olarak başka bir düğümde durmasıdır.

Hemen JUnit testimizi yazalım:

  public void testYaziListe() throws Exception {
ArticleList liste = new ArticleList();
ArrayList l = liste.kategoriAltindakiYazilar("eCRM");
assertTrue(l.size() > 0);
}

İnternet Yayıncılığı ve XML

XML en çok İnternet yayıncılığı için uygun olmuştur. Bir önceki yazıda XML içeriğinin hiyerarşik, yani "ağaç-dalları" yapısında olduğunu söylemiştik. Genelde yazılar, yani dökümanlar boyle yapıya çok uygundurlar. Her yazının başlığı, başlık altında paragrafları, paragraf altında kelimeler, resimler ve İnternet linkleri olabilir.

Ayrıca XML istenilen "başlama/bitiş" işaretlerini kullanmanıza izin verir. Böyle olunca herkes kendine uyan XML yapısını önceden planlayıp, onu kullanarak yazılarını yazabilir.

Daha önemlisi, XML kayıdını kullanarak, görsel "çevirmen program" XSLT kullanıp, XML içinden HTML, PDF gibi görsel sayfalar yaratabilirsiniz. Yani "içerik" ile görsel HTML dosyaları birbirinden ayrı tutmak mümkün olur. Bunun sayısız faydaları var.


* İçerik değişiklikleri daha rahat yapılabilir. Eğer yeni bir cümle eklemek istiyorsanız, nasıl gözükeceği ile kafa yormanıza gerek kalmaz.
* Yazılarınızın görsel olarak değiştirmek isterseniz, görsel çevirmen programı değiştirmeniz yeterlidir.

Mesela bu yazıyı ele alalım. Bu yazının basligi önce XML, sonra XSL (çevirmen) en son HTML olarak geçirdiği değişimleri görebilirsiniz.

XML
 XML ne işe yarar

XSL
  <xsl:template match="title">
<tr><td class="head">
<xsl:apply-templates/>
<br></br><img kaynak="images/spacerc1.gif" height="2" width="400"
border="0"></img></td></tr> <tr><td height="15"></td></tr>
</xsl:template>

HTML
<td class="head">XML ne ise yarar<br>
<img border="0" width="400" height="2" kaynak="images/spacerc1.gif"></td>
</tr><tr>
<td height="15"></td>

Gördüğünüz gibi XML dosyasında görsel hiçbir şey yok. XSL dosyası sadece "baslik" komutu nasıl HTML çevirisi yapılmalı, onu tarif ediyor. Bu ikisini (XML, XSL) beraber işlemden geçirdikten sonra (xalan adlı bir program kullanarak) sonuç HTML olarak gözüküyor.

Eğer bu yazının başlığını değistirmek istesem, XML dosyasından istediğim değisikliği yapıp, tekrar çevirmen programı işletirim. XSL dosyasına hiç dokunmama gerek yok. Zaten içerik, XML içinde ne olduğu daha belli: yani daha rahat okunabiliyor.

Eğer yazının gözükme şeklini değiştirmek istersem, XSL dosyasından yapabilirim bu değişikliği, "içerik" dosyasına dokunmama gerek yok. Bu iki şeyi birbirinden ayırmak, bize büyük rahatlık sağlayacaktır. Büyük siteleri kontrol eden programcılar/idareciler muhakkak bunun değerini anlayacaktır.

Ek:
* Projesinde bizzat çalıştığım Martha Stewart Omnimedia sitesi için, XML bazlı içerik idaresi kurduk. XML Spy ile yazı yazan içerik üreticileri, J2EE uyumlu bir ürün olan ATG Dynamo ile XSL uyguluyarak, HTML sonucunu görüyor, ve makaleyi gene XML olarak sonuç sitesine gönderebiliyorlar.
* Ünlü tasarımcı ve programcı Martin Fowler en son kitabının tamamını XML ile yazmıştır.

Kontrol Edilmeyen (Unchecked) Exception'lar

Java'nın sözdizim bağlamında çoğu programcıyı gereksiz yere uğraştıran ve vakit kaybına neden olan özelliklerinden biri, exception atan bir kod parçasını kullandığınızda kullanan tarafın ya try/catch koymaya, ya da bulunduğu metotun tanımına "throws xxException" gibi bir ifade eklemeye mecbur kalmasıdır. Tabii hepimizin bildiği gibi "throws xxException" ekleme yoluna giderseniz, bu eklemenin etkisi zincirleme olarak o metotu çağıran metota, ve oradan başka yerlere sıçrar, ve bir süre sonra bir bakmışız ki metotlarımızın yarısı aynı asalak ifadeyi içeriyor: method_x() throws Exception!

"En iyi kod, yazmadığım koddur" desturundan yola çıkarak, bu yazının geri kalanında kontrollü Exception kullanımını tavsiye etmeyeceğiz. Kurumsal Java programcılığı esnasında zaten fazla olan teknoloji çorbası ile uğraşırken "sadece istenilen şeyi söylemek, geri ile gerekince ilgilenmek" en iyisidir. Daha kısa zamanda, daha basit/bakımlı kod yazmak bunu gerektirir.

Şimdi, final tavsiyeyi vermeden önce Java exception mimarisine bir bakalım.

Java ve Exception'lar

Java sözdizim yapısında iki tür exception vardır. Kontrollü (checked) exception, ve kontrolsüz (unchecked) exception.

Çoğumuzun bildiği gibi kontrollü exception durumunda, eğer bir kod parçası içinde 'throw new xxxException' gibi bir ibare mevcutsa, kullanan kod try/catch "yakalayıcıları" bulundurmaya mecburdur. Bu gereklilik bizzat Java derleyicisi üstümüzde zorlanan (enforce) bir gerekliliktir, kaçış yoktur.

Kontrolsüz exception kullanımında ise herhangi bir servis kod parçası kontrolsüz exception fırlatabilir, fakat çağırım yapan taraf bu exception'ı yakalamaya (ya da bulunduğu metot tanımına "throws xxException" eklemekte) mecburiyeti yoktur. RuntimeException bunun güzel bir örneğidir.

Java tasarımcıları baştan beri kontrolsüz yanında kontrollü exception kullanımını da bir seçenek olarak sunmayı seçmişlerdi. Ayrıca, zamanın ortak aklı da (conventional wisdom) bir şekilde kontrollü exception kullanımını ön plana çıkarmıştır (çünkü ilk Java kod örneklerinde böyleydi). Fakat aslında, Java tasarımcılarının bu seçiminin arkasında müthiş zeka aramamak gerekir. İstekleri sadece şuydu:

Programcının, çağırdiğı kodun ne yaptığından "haberdar olması", ve o kodu kullanırken "bunun sonuçlarına katlanıyorum :)" ibaresini yazılı olarak kodunun içine koyması.

Fakat bu saf istek, kurumsal Java bağlamında artık bir zul olmaya başlamıştır. Kurumsal Java kodları bir API "denizi" içinde yüzer, ve 3-4 dış kütüphane kullanan bir kodda artık çok fazla sayıda try/catch ibaresi görmek mümkündür, ya da metot tanımı bağlamında gereksiz bir ekleme enflasyonu ortaya çıkmaktadır.

Bu sebeple yazımızın tavsiyesi kurumsal uygulamalarda (web/telekom/vs) kontrolsüz Exception kullanılmasıdır. Bu Exception yöntemi ile, bir Exception'ı, "sadece istediğinizde" try/catch ile yakalamanız yetiyor, eğer hiçbir Exception ile ilgilenmiyorsanız, derleyici size bir hatırlatmada/zorlamada bulunmuyor. Bu sayede kodunuz daha temiz kalıyor.

Tabii eklemek gerekir ki, içinde Exception atılması muhtemel bir çağrıyı yapan kodun, o kontrolsüz Exception'ı yakalamaması demek, bu Exception'ın daha üst seviyelerde yakalanmayacağı demek değildir. Exception kurallarına göre yakalanmayan bir Exception zaten çağrı sıralamasında üste doğru çıkacaktır, ve kontrolsüz şartında sadece ilgilenen bir yakalayıcı tarafından yakalanabilir. Zaten, her uygulama bir "en üst seviye" yakalayıcı genelde olduğu için bu, o Exception'ın eninde sonunda bir yerde yakalanacağı anlamına gelir. Ama bu yakalayıcı bir tane, ve her yerde olmayan bir ekleme olduğu için kod temizliği için bir tehlike arz etmez.

Bu tavsiye ışığında Exception muamele sistemimiz daha temiz ve bakımı rahat hâle gelekcektir. İlginçtir ki, kontrollü Exception seçeneği, diğer "pür" nesnesel dillerde mevcut değildir: Ada, Eiffel, Ruby, Phyton dillerinin hepsi kontrolsüz Exception atarlar. Bu dillerden Ada, gerçek zamanlı (real-time) ve kritik misyon (mission critical) sistemlerde kullanıldığına göre (askeri, uzay sistemlerinde), kontrolllü Exception'ların pek o kadar da hayati olmadığı ortaya çıkmaktadır.

Thursday, October 31, 2002

Yazılım Çeşitleri

Yazilim cesitleri ve metodlari tek degildir. Bir metodu ogrenerek butun problemleri cozeceginizi sanmayin. Kariyerimiz boyunca "Rational Metodu", "Extreme Metodu" ogrenip Istanbul'u fethedecigini zanneden cok programci tanidik. Bu metodlar, her turlu yazilim problemine deva olacaklarini reklam ederler, fakat degisik yazilim problemlerinin ve cozumlerinin ne oldugunu bilmezseniz, hangi metodu ogrenirseniz ogrenin, basariya ulasamazsiniz.

Ana software "problem alanlarini" sayalim..


* Internet "site" programlari.
* "Anlik" calismasi gereken programlar.
* Gunluk, "toplu" halde calisabilen programlar.
* Gorsel Programlar

Butun bu problemlerin kendine gore "teknolojik" cozumleri vardir. Mesela, veri tabani paket programiniz Oracle; yani SQL dili kullanabileceginiz bir paket. Musteriniz dedi ki: "Bana oyle bir progrem yazki, her gun ABC kayitlarini isleyip XYZ kayidi haline getirsin. Ne kadar cabuk yapabilirse o kadar iyi. Sadece islesin. Gorsel falan hic bir sey istemem."

Yukaridaki program 'gunluk' program kategorisine girer. Boyle programlar surekli 'uyanik' durmak zorundadir, ve isleyecek veri beklerler. Sanki ac bir hayvan gibidirler, veri buldugu anda yerler. Yoksa gelmesini beklerler. Gorsel program orasina burasina klik edilsin diye bekler, o yuzden daha degisik programlama gerektirir.

Anlik programlarin degisik ihtiyaclari vardir. Mesela bir uzay mekigini kontrol eden, ya da son model arabanizda benzin pompasini ayarlayan program, anlik programdir. 1~2 milisaniye arasinda karar vermesi gerekir, o yuzden kod ona gere yazilir. Bilgisayarlar tabii ki herseyi yapabilecekleri kadar hizli yapmaya ugrasirlar, fakat birden fazla, ayni anda islem gerektiren olaylar oluyorsa, belki bazi seylerin birbirini beklemesi gerekebilir. Mesela benzin pompasi dogru olcude benzin vermekle gorevlidir, ama ayni anda fren sinyallerini dinleyen bir bolumude vardir. Fren sinyali geldigi anda herseyi birakmasi mecburdur. Bunlar "anlik" program problemleridir. Gunluk programlarin problemleri yukarida gordugunuz gibi degisik.

Site programlari bir baska hikaye: Internet siteleri ayni anda birden fazla kisiye hizmet versin diye yazilir. Yani, ayni anda degisik kullanicilara hizmet verir, ama bu isler aslinda birbirinin kopyasidir, ve kullanicilar birbirlerini "aninda" etkileyemezler. "Anlik" program kategorisine bu yuzden benzemesine ragmen, aslinda cok ayri bir metod gerektirirler. Mesela amazon.com sitesine girdiniz, uye oldunuz, kitap satin aldiniz. Fakat ayni anda, amazon.com sitesinde ayni islemleri yapan belki binlerce kullanici vardir. O kullanicilarin yaptiklari sizi etkilemez, herkes sanki kendi odasinda, izole bir sekilde isini yapmaya ugrasir. Bu tip programlar "kapasite" icin yazilirlar, problemler veri tabanina daha hizli erisim, sayfa hizli yukleme gibi seyler etrafinda doner. Son zamanin Internet Java paketleri, sanki siteleri bir kisi icin yaziyormus gibi yardim eder size; ondan sonra kopya kagidindan cikarmis gibi 10,000 kisi icin ayni kodu kullanabilirsiniz.

Gorsel programlar Windows programlari gibidir. Unlu windows 'fal' oyunu bir gorsel programdir. Klik edilebilen nesneler vardir, bu nesnelerin bazilari her zaman kliklenemez, kullanici hatasi verilir bu zamanlarda, yada kullanici o isi yapamaz. Bu tip programlarin temelinde bir 'hadise/vaka dongusu' vardir. Bir gorsel hadise oldugu anda, (nesne uzerine klik) hadise icin yapilan gorevler teker teker, onceden programa kayitlanir. Bu stili ilginc yapan, programi tamamen 'gorsel hadiseler' uzerine kurulmasidir. Oteki tip programlarda sanki duz bir sira yoktur. 'Su olursa sunu yap, bu olursa bunu yap' seklinde programlardir.

Görsel Uygulamalar (Swing) Nasıl Test Edilir?

Java bazlı, görsel olmayan kodlarımız için, JUnit ile test yazabiliyoruz. Bu tip kodlar için, aşağıdaki gibi bir ifade yeterli oluyordu...

..
IslemNesnesi i = new IslemNesnesi();
assertEquals(2, i.topla(1, 1));
...


Fakat diyelim ki, elimizde Swing bazlı görsel bir uygulama var. Extreme programcılık dahilinde, müşteri rolünde olan kişinin yeni kodlanan sistem özelliğini kabul edebilmesi (acceptance) için, kabul testleri denilen görsel testleri işletmesi gerekir. Kabul testleri, ya kağıt üzerinde yazılı "önce A'ya bas, sonra B'ye, çıkan metinin X olup olmadığını kontrol et" şeklinde direktifler olur, ya da daha iyisi, bilgisayar tarafından arka arkaya çok hızlı bir şekilde işletilebilen XML bazlı dosyalar olur.

Otomatik olmayan testler için bir altyapıya gerek yok. Otomatik olanlar için, Swing Tıklayıcı adlı geliştirdiğimiz altyapı yardımcı olabilir.

Swing Tıklayıcı

Swing Tıklayıcı programını indirip kurduktan sonra, kullanmaya başlamadan önce birkaç kavramını anlamamız yerinde olur.

Swing Tıklayıcı mimarisi üç bölümden oluşuyor.


* XML testleri (swing-tikla/test/acceptance/tests altındaki dosyalar)
* Görsel birim etiketleri (swing-tikla/test/acceptance/tags)
* Görsel, Java arasında tutkal kodlar (swing-tikla/test/gui)

XML Testleri

Kabul testleri için düğmeye basmak, görsel listeden seçim yapmak gibi işlevleri yapmamızı sağlayan bir "dil" olması lazım. Bu dil söyle olabilir.

<AcceptanceTest>
<startApplication/>
<lookAtWindow name="Stylepad"/>
<enter name="Fatura" value="1000" />
</AcceptanceTest>


Bu teste göre Swing uygulamamızı başlatıyoruz, sonra ekrandaki pencerelerin arasından, başlığı "Stylepad" olan uygulamayı buluyoruz (seçiyoruz). "Fatura" adlı metin kutusuna (TextBox) içine 1000 değerini yerleştiriyoruz.

Etiketlerin Gerçekleştirimi

lookAtWindows (pencereyeBak) olarak gözüken etiketin Java altyapısını tags dizini altında bulabilirsiniz. Kendi etiketlerinizi de bu dizin altına bırakın. Swing Tıkla etiket mimarisinin ilginç bir özelliği, Java Değişken Çâğırım (Introspection) özelliğini kullanarak, etiketlerin içerdiği değerleri tekâbül eden Java nesneleri üzerine direk aktarma yapabilmesidir.

Tutkal Kodlar

Bu dizin altında her Swing nesnesi üzerinde işlem yapabilen tutkal kodlar var. Eğer uygulamanıza özel yeni bir görsel Swing nesnesi mevcut ise, o zaman bu yeni birimi test altyapısına "tutkallamanız" gerekir. GuiAdapter.java dosyası altında bunu gerçekleştirebilirsiniz. Bizim projemiz dahilinde JCTable, vs gibi standart Swing'e dahil olmayan birçok nesne mevcut idi. Bu kodları ayreten tutkallamamamız gerekti.

Kullanılan Örnek

Swing Tıklayıcı ile beraber, bir demo sâglamak açısından Stylepad adlı JDK sürümüne zaten dahil olan bir programı kullandık. Fakat bu programı aslında sıkı bir şekilde testten geçirmiyoruz. Tam teferruatlı testler ileri yazılarda gösterilecek. Şimdilik test altyapısına acil ihtiyacı olanlar için burada sunmayı uygun gördük.

Diğer Mevcut Test Ürünleri

Serbest yazılımlar arasında Maraton programı oldukça kullanışlı. Bu programa da alternatif olarak bakmanızı tavsiye ederim. Maraton, testler için dil olarak bizim XML'imiz yerine, Phyton adlı bir dilde test betikleri üretiyor. Yaklaşımın oldukça benzediğini belirtmeliyim. Bizim projemiz Maraton'a eklenti yapmak bakımından biraz karışık buldu, fakat demo'u için fazla zamanımız yoktu. Bu yüzden Maraton'un içyapısını öğrenip şirket içinde standart haline getirmeye ortam var ise, kullanılabilir olacağını zannediyoruz.

Not: Swing tıklayıcı, yakın zamanda tamamen Türkçeleştirilecektir.

Kaynaklar

arayüzlerini kullanıyor.

* Swing Tıklayıcı
* Maraton
* Görsel tıklayıcı kodlarımız, perde arkasında Jemmy

Uygulama Servisi Başlatmak ve Beklemek

Farzedelim ki bazı testlerimizin (entegre testleri) işlemesi için, Weblogic, JBoss ya da Tomcat'in çalışıyor olması gerekiyor. Entegre testleri, birim testlerinin aksine direk komut satırından ya da müşteri tarafında (client side) çalıştırılamaz, meselâ, yazdığımız bir EJB modülünü test etmek için, EJB'nin kesinlikle çalışıyor olması, yâni Weblogic üzerine konmuş ve Weblogic'in çalışıyor durumda olması gerekecektir.

Biz de bunun üstüne entegre testlerini otomize olarak çalıştırmak istersek, ki Extreme Programcılık dahilinde bunu yapmamız gerekecektir, o zaman bir problemle karşılaşırız. Diyelim ki Weblogic'i startup.bat gibi bir betik ile ateşledik. Arkasından hemen entegre testlerini işleteceğiz. Fakat, Weblogic'in başlamış olduğunu nasıl anlayacağız ki hemen arkasından entegre testlerine devam edebilelim? Weblogic bâzen 30 saniye, bâzen 1 dakikada ayağa kalkıyor olabilir. Zamanlama ile bu işi çözemeyiz.

Demek ki, bir yere bakarak Weblogic'in durumunu öğrenmemiz gerekiyor. Bunu her uygulama servisinin dışarı afişe ettiği port üzerinden öğrenebiliriz. Fakat daha da kolayı, her uygulama servisinin muhakkak yazdığı bir log dosyasında belli aralarla Perl ile tarama yaparak bir anahtar kelimenin gelmesini beklemektir. Bu, Weblogic için "listenin on port 7001" dir, Tomcat için başka bir şey olabilir.

Alttaki Perl programı ve Ant komutları, bu amaç için kullanılabilir. İlk önce, bir uygulama servisi log dosyasını okuyan ve belli bir kelime bekleyen programı gösterelim.

while (! /$ARGV[1]/) {
open IN, $ARGV[0];
undef $/;
$_ = <IN>;
print "\nServisin ba�lamasını bekliyoruz ... $ARGV[0] taranıyor\n";
sleep (2);
close(IN);
}


Bu program, Perl programına $ARGV[0] (ilk parametre) ile gönderilen log dosya ismini okuyarak, $ARGV[1] kelimesininde belirtilen kelimeyi tarayarak, beklemektedir. Kelime bulunmazsa, 2 saniye uyuyarak tarama tekrar yapılır. Bu betik, genelde build.xml içinden çalıştırılacaktır, çünkü entegre testlerini Ant içinden işletiriz. Ant içinde bu çağırımın nasıl yapıldığını görelim. Dikkat, bu çağırım sadece beklemek için; Servisi başlatmak için değil.

 <target name="servis-bekle">
<exec executable="perl">
<arg line="./servisBasladimi.pl"/>
<arg line="${bea.dizin}/gmlcdomain.log"/>
<arg line="${sunucu.hazir.kelimesi}"/>
</exec>
</target>


Ant içinde kullandığımız değişkenleri, build.properties içinde tutuyoruz.

bea.dizin=D:/bea/user_projects/domains/gmlcdomain
bea.dizin.win=D\:\\bea\\user_projects\\domains\\gmlcdomain
cygwin=d:/cygwin
sunucu.hazir.kelimesi="listening on port 7001"

Ant içine aşağıdaki ibareyi ekleyerek, bu değişkenleri okuyabiliriz

    


Komut satırından servis-bekleyi çağırdığımız zaman, ve Weblogic başlar başlamaz bu komut geri dönecektir. Geri dönmesi de bizim için yeterli, çünkü "ant filan falan servis-bekle entegre-testleri" komutu verince, servis-bekle geri dönünce, entegre-testlerine geçilebilecektir.

Şimdi, servisi başlatmayı da otomize hâle getirelim. Fakat ondan da önce, o anda çalışıyor olan servisi öldürmemiz gerekiyor.

Bu, Unix programcıları için gereksiz bir paragraf olabilir. Sonuçta pkill ile isi vererek Linux ve Solaris üzerinde istediğiniz süreci öldürebiliyorsunuz. Windows için bazı ekler yapmak gerekecek.

Cygwin ve ps

Windows üzerinde Unix komutlarını kullanabilmek için Cygwin programını kullanıyoruz. Cygwin içinde ps mevcut,. Seçeneksiz olarak kullanılan ps komutu, sadece Cygwin tabanlı süreçleri göstermektedir. Windows süreçlerini de görmek istiyorsak, ps -W komutunu kullanmamız gerekiyor. O zaman şöyle bir betik yazabiliriz.

01:   $_ = `ps -W | grep bea`;
02: /^\s*(\d*)\s*/;
03: exit if ($1 eq "");
04:
05: `$ARGV[0]/bin/kill --force -9 $1`;

Tamam. Bu kodda neler oluyor? İlk satırda (#1) grep ile "bea" kelimesini taradığımızı görüyoruz. Bu kelime yerine istediğiniz anahtar kelimeyi kullanabilirsiniz. ps -W ile tüm listeye bakın, öldürmek istediğiniz sürecin satırında kendine has bir kelime var mı? O kelimeyi kullanın.

ps -W çıktısı şöyle gözükecek:

1064       0       0       1064    ?    0 19:04:43 C:\emacs-21.3\bin\emacs.exe
1448 0 0 1448 ? 0 19:31:43 C:\Program Files\Mozilla
1596 0 0 1596 ? 0 19:04:24 C:\WINDOWS\Explorer.EXE

Şimdi, ps komutu çıktısından PID (process no'su) çıkartılması/alınması gerekiyor. Bunu yapmamız lâzım, çünkü, kill komutuna verecek PID numarasını bir şekilde bulmamız gerekiyor. Yâni yukarıdaki örnekte 1596 sayısı... Bu işlemi basit bir düzenli ifade ile (#2) hemen yapabiliyoruz. Bundan sonra tek yapmamız gereken kill komutu ile bu PID'i öldürmektir. Ama dikkat! Direk Unix kill, Windows süreçleri üzerinde çalışmıyor, --force seçeneğini de geçmemiz gerekecek.

(#5)'de $ARGV[0] kullanımına dikkat; Bu ARGV Cygwin programının kurulduğu dizini gösteriyor. Ps ve kill komutlarının PATH'te olmama şansına karşı bunu yapmak daha uygun oldu.

Bu betiğin Ant'ten nasıl çağırıldığına gelelim.

        ...
<target name="weblogic-durdur">
<exec executable="perl">
<arg line="./servisOldur.pl"/>
<arg line="${cygwin}"/>
</exec>
<exec executable="${cygwin}/bin/rm">
<arg line="${bea.dizin.win}/gmlcdomain.log"/>
</exec>
</target>



Artık servis başlatmaya hazırız! Bunun için birkaç takla daha atmamız lazım: Ant'in başlatma işlemine "kitlenmemesi" yâni ona takılıp kalmaması için, exec etiketinde spawn=yes seçeneğini kullanmamız gerekiyor. Bekleme işini exec'de değil, Perl betiği ile dışarıdan yapıyoruz, unutmayalım. Spawn seçeneği exec'de verilen komutu sallar, ve sonucunu beklemeden geri döner. Bizim istediğimiz de bu zaten, çünkü hemen arkasından servis-bekle komutunu işletecegiz.

 <target name="weblogic-baslat" depends="weblogic-durdur">
<exec executable="./run.bat" spawn="yes">
<arg line="${bea.dizin.win}"/>
<arg line="${bea.dizin.win}/startWebLogic.cmd"/>
</exec>
</target>


Run.bat, exec için bir sıçrama tahtasından ibarettir. Tek amacı dizin değiştirmek aslında, yoksa direk startWebLogic komutunu da işletebilirdik. Run.bat aşağıda gösteriliyor.

cd %1
start %2

Güzel. Artık tek kalan, herşeyi işletmek.

$ ant weblogic-baslat servis-bekle entegre-testleri-islet

Wednesday, October 30, 2002

İnternet Yazılımları için Örnek Mimari

İnternet bazlı program yazmaya karar verdiniz. Müşteriniz kapıda, ya da patronunuz soruyor "Hangi teknolojileri kullanacaksın bu iş için?"

Teknolojiden sonra "hangi mimariyi kullanalım" sorusu kapımıza gelecek. Yemek yapmak ile kıyaslamak gerekirse, teknoloji yumurta, domates gibi ana maddelerdir, mimari ise "yemek tarifidir". Hangi tarif daha iyidir? JSP içine JDBC'mi koyalim, yoksa JDBC kodunu Java nesneleri icine mi koyalım? Sonra JSP->nesne->JDBC->Veri tabanı gibi olsun.. Ne kadar çok soru!

Bu soruları her proje başında, teknik liderler kendilerine sorarlar. Yanlız değilsiniz, merak etmeyin. Soruları sıralayalım:


* Hangi teknoloji?
* Genel Mimari nasil olsun?
* Geliştirme ortamını nasıl kuralım, programcıların gündelik geliştirme işlemi nasıl kurulsun
* Sonuç ortamı hangi işletim sistemi üzerinde çalışsın?
* Sonuç ortamını biz mi yönetelim, yoksa Site Barındırma şirketine mi verelim

Bu yazımızda bu sorulara cevap vermeye calisacağız. Alt kattan baslayarak, yukarıya dogru çıkalım:

Ara Kat (Alt)

Alt arakat dahilinde veri tabanına bağlanmamız lâzım. Bu kat içinde, normal Java nesneleri ile JDO ya da Hibernate kütüphaneleri kullanılarak veriye bağlanılabilir. Daha önceki bir yazımızda JDO metodunu işledik. JDO nesneleri, aynen bildiğiniz Java nesnelerine benziyor, çok basitler. Sadece veri deposu görevini gören bu nesneler 'akıllı' olmayabilirler. Üzerlerindeki işlemler sadece get/set işlemleri olacak. (Not: get/set Turkceye çevirmek isterdik, fakat Java tarifnamesi buna izin vermiyor. Bazı durumlarda get/set ile başlayan kelimeler kullanmak zorunlu).

İşte JDO ve Hibernate için hazır bir nesne modeli..
public class Musteri
{
protected String no;
protected String isim;
protected Portfoy portfoy;
public String getNo() { return no; }
public setNo(String no) {this.no = no; }
// oteki get/set islemleri buraya...
}

public class Portfoy
{
public List gecmisIslemler;
// get/set islemleri buraya...
}

public class Islem
{
protected Senet satilanSenet;
protected Senet alinanSenet;
// get/set islemleri buraya...
}

public class Senet
{
protected String sirketIsmi;
protected long kacLotAlindi;
//get/set islemleri buraya...
}
Nesneler tanımlandıktan sonra, veri tabanı tablolarına bağlantıyı kuralım. Bunu, bir XML ayartanım dosyası kullanarak yapacağız. (Sadece müşteri icin tanımladık, tüm tanımlama değil)

JDO için;

<?xml version="1.0"?>
<!DOCTYPE jdo SYSTEM "jdo.dtd">
<jdo>
<package name="sk">
<class name="Musteri">
<field name="no"/>
<field name="isim"/>
</class>
</package>
</jdo>


Hibernate için;

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
<hibernate-mapping package="sk">
<class name="Account" table="Tablo">
<id name="bizimId" column="bizimId" type="int">
<generator class="increment"/>
</id>
<property name="no" column="no" />
<property name="isim" column="isim" />
</class>
</hibernate-mapping>


Bundan sonra JDO ya da arayüzlerini çağırarak, nesneleri istediğiniz veri tabanına yazabilirsiniz. JDO için makePersistent, Hibernate için save işlemleri, tek başına bütün nesneyi alıp veri tabanına yazacak güçtedir.

Ara Kat (Üst)

Ara kat dahiline, JDO nesnelerini kullanan tabaka da girer. Bu tabakayı proje büyüklüğüne göre es geçebilirsiniz, o zaman JSP/Servlet/Struts Action'lar direk Java/JDO ile bağlantı kurar. Yok eğer büyük bir proje ise, bir arayüz daha çekmenin mahsuru yok.

Arakat dahilinde kullanılması uygun teknoloji EJB Session Bean olacak. Entity Bean kullanmaya gerek yok, Entity Bean ve JDO birbirinin rakibi teknolojilerdir, ve JDO'nun daha rahat olduğunu sanırım ispatladık. EJB Entity Bean katiyen kullanmayın. Session Bean metodu, sayfalar ile veri tabanı arasında bir arayüz oluşturma bakımından önemlidir. Dış dünyaya göstereceğiniz arayüz budur yani.. Dış dünya derken, JSP yerine Swing bile kullanabilirsiniz.

Tavsiyem, üst arayüzleri geniş tutmanızdır. Yani bir işlem, tek çağrıda çok iş yapsın, veri geriye getirebileceği kadar veri geri getirsin. Örnek aşağıda:
public class HesapBean
{
public Collection musteriPortfoyGecmisIslemleriGetir(String musteriNo) {
...
}
}
MusteriPortfoyGecmisIslemleriGetir işlemi içinde, JDO bağlantısı yapıp, get/set ile istediğiniz nesneyi kullanabilirsiniz.

Sayfalar

Her modern internet yazılımı, sunucu sayfa metodu denen bir metod kullanıyor bu günlerde. Mesela PHP, JSP bunlardan sayilabilir. JSP, yani Java Sunucu Sayfaları, kodu sunucuda işletip, tarayıcı programınıza görsel bilgileri gönderir. Kıyas etmek gerekirse, Javascript kodu tarayıcı içinde işletilir, sunucu ile alakası yoktur yani.

İlk projeniz icin salt JSP işinizi gorur. JSP kullanarak <jsp:include>, <% %> icinde Java kullanarak güçlü programlar yazılabilir. Eğer daha kuvvetli bir sayfa dili kullanmak isterseniz, J2EE, istediniz sayfa kütüphanesini (dilini), JSP altında kullanmaniza izin veriyor. Mesela JSTL ve Struts bu sayfa dillerinden bazılarıdır. Apache projesi altındaki bu diller cok kullanışlıdır.

Alt katlardan bahsettik: Buna göre, JSP/Servlet/Action icinde, EJB baglantısı kurup musteriPortfoyGecmisIslemleriGetir işlemini çagırmanız lazım olacak. Geri gelen Collection (liste) içinde ihtiyacınız olan veriyi bulabilirsiniz, JSP ya da Struts komutları kullanarak veriyi görsel bir biçimde ekrana nakledin.

Özetle...


* Veri tabanı: Oracle, MySql, ya da Pür Java JDO'ya Hazır Veri Tabanı - ObjectDB
* Nesne/Veri Bağlaşımı: Hibernate, TJDO (Serbest Yazılım), Kodo (Ticari)
* Alt Ara Kat: WebLogic JBoss ya da herhangi bir J2EE uyumlu sunucu programı
* Üst Ara Kat: Tomcat WebLogic , ya da herhangi bir J2EE uyumlu sunucu programı
* Görsel Katman: Tomcat, WebLogic veya Tomcat üzerinde JSTL, Struts veya JSP

Oracle Tablolarından CSV Dosyası Üretmek

Oracle'daki bir tablonun verilerini metin bazlı bir dosyaya almak istiyorsanız, bunun için aşağıdaki gibi bir betik yardımcı olabilir.

SET TERMOUT OFF
SET HEADING OFF
SET SHOWMODE OFF
SET LINESIZE 2000
SET FEEDBACK OFF
SET VERIFY OFF
SET ECHO OFF
SET TIMING OFF
SET NEWPAGE 1
SET WRAP ON
SET LONG 32767
SET ARRAYSIZE 1
SET HEADING OFF

spool c:/temp/cikti.cvs

select KOLON1||','||KOLON2||','||... from TABLO;

exit;

 
$ sqlplus kulanici/sifre@SID @dump.sql

.. olarak isletildikten sonra, bir CSV dosyasi c:/temp/cikti.csv olarak üretilecektir.

Projeleri Kurtaran Nedir?

Bilgi işlem dünyasında karşımıza çıkan iki katmanlı projelerden, günümüzdeki çok katmanlı mimarilere kadar bir programcının ve teknik liderin kendine sorması gereken en önemli soru şudur.

"Hangi çeşit bilgi, projemi kurtarır ve zamanında sürüm yapmamı sağlar?"

Bu sorulara her projenin ya da şahsın değişik cevaplar verdiğini görebilirsiniz. Bazen de başarılı olan programcılar bile işi nasıl becerdiklerini kelimelere koyamamakta, içgüdüsel bir halde doğru alanlara odaklanıp projeyi başarıya kavuşturan bu arkadaşlara ne zaman bu soruyu sorsak, şöyle bir cevap vermektedirler: "Bilmem, oluyor işte".

Zamanla, yakından gözlem yaparak ve karşılaştırmalı olarak kendimizin de içinde bulunduğu projelerden esinlenerek şu cevabı verebiliyoruz.


Bilgi işlem için odaklanması gereken en önemli konu, kullanılan teknolojinin açıklarını, faydalarını hemen anlayarak, sürekli karşımıza çıkan genel bilgi işlem problemlerine bu teknolojinin verdiği cevapları masaya yatırabilen/kavrayabilen zihniyettir.

Bu cevap bariz gelse de, niye cevabın programlama dilleri, proje idare teknikleri, nesnesel tasarım, oklar/balonlar çizmemizi sağlayan CASE araçlari olMAdığını açıklamamız gerekiyor. Çünkü bazılarının cevabı bu paragrafta yeralan seçenekler olmuştu, ve bu cevaplar verildiğinde mantıklı olan açıklamalar beraberinde kabul görmüşlerdi.

Niye Programlama Dilleri Cevap Değil

1996 tarihinde milyon dolarlık bir proje içinde, milyar dolarlık bir müşterimize CORBA bazlı bir çözüm vermek üzere kolları sıvamıştık. Takımda her türlü alanda güçlü olan arkadaşlar vardı; Dili (C++) iyi bilen, proje idarisinde tecrübeli, işkolu (thread) programcılığını iyi bilen, veri tabanlarında muazzam bilgili sahsiyetler proje takımımızın üyeleri idi. Bir arkadaşımız, KKİ sisteminde herkesin aynı şekilde yaptığı bir kodlama hatasını 3 satırlık Perl koduyla düzeltmiş, ve geri kayıtlayarak (check in), hepimizi bir haftalık düzeltmeden kurtarmıştı. İnanılmaz bu performans karşısında daha projenin başı olduğu için, işi 5-6 ayda bitireceğimizden hepimiz gayet emin olmuştuk.

Fakat yukarıdaki kurala dönersek, en önemli olan faktör C++ ya da Perl dilindeki ustalık değildi. Çünkü projemiz vaktinde bitmedi. Cevap niye dil değildi?

Bir proglama dili boşlukta yaşamaz. Bilgi işlem işi, aslında bir tümleştirme (integration) eylemidir, bu yüzden C++ dilinin 'birşeyler yapabilmesi için', mutlaka XYZ kütüphanesinden 'birtakım kodları' işletmesi gerekir. Bu kütüphane kodları (kod adacıkları), başlıbaşına apayrı bir dünyadırlar. Neredeyse her kütüphane, aslında kendi başına bir dil olarak adledilebilir.

Karşılaştırırsak, "Java'ya Giriş" gibi kitapların öğretikleri dilin, soyut olarak düşünürsek, tekrarlama, koşullar ve metin bazlı giriş çıkış evreni öngeren bir dil olduğunu görürüz. Tabii ki bu temelin mutlaka kavranması gerekir, ama bilgi işlem dünyası için bu kadarı yetmemektedir. Akademik bir ortamda sadece algoritma odaklı bir çevrede iseniz, sırf dil bilgisi yetebilir. Çünkü akademik çevrede amacınız daha işlevsel (functional) olacaktır. Bilgi işlem dünyası tümleştirir, bilgi işlem, çetrefilli algoritmaların dünyası değildir.

İşte bu sebepten ötürü, dil bilgisini önem sırasında en tepeye koymuyoruz.

Niye Nesnesel Tasarım Yöntemleri Değil

80'li yıllarda başlayan, 90'lara damga vuran nesnesel idare teknikleri, bizim projemizde en gözde konu idi. O zamanın danışmanlık dünyası, nesnesel tasarımı tamâmen kabullenmişti. Her teknik liderin kolunun altında bir Gready Booch kitabı, ya da yeni çıkan Design Patterns (Tasarım Kalıpları) kitapları ile etrafta dolaşmaktaydılar. Bir programcının bir ötekine söyleyebileceği en ağır hakaret şu idi: "Ama bu tasarımın, nesnesel değil!". :)

Bu tip teknikleri de oldukça iyi bilen takım arkadaşlarımız vardı. Şahsen yeni okuldan mezun olmuş şahsımız ve birkaç arkadaş ile beraber, modelleyemiyeceğimiz bir problemin daha doğmamış olduğunu adlediyorduk. Bunda da haklıydık. Fakat bilgi işlemde modelleme, yukarıdaki kurala tekrar dönersek, en önemli faktör değildi. Neden?

Nesnesel modelleyi şöyle düşünebilirsiniz. Önümüzde gereksinimleri listelenmiş olan bir bilgi işlem problemi olduğunu düsünelim. Bu problem için, a, b ve c işlemlerinin yapılması gerekiyor. Dikkat edin, işlemler diyoruz. Yani komut dizisi. Bu komutların, bir nesnesel programdan ya da LISP gibi işlevsel bir programdan gelip gelmediği hiç önemli değildir. Sonuçta herşey dönüp dolaşıp ASSEMBLER koduna, yani makina koduna dönecektir, değil mi? Bu yüzden, nesnesel tasarım, bir komut dizisini vesaire şekilde parçalara bölen, taksim eden bir yöntemdir, e tabii vesaire_2 şeklinde kodu parçalara bölen yöntemler de vardır. Nesnesel tasarım sihirli bir şekilde programımıza işlev katan bir yöntem değildir. Nesnesel tasarım, kodun okunabilirliğini, bakım evresinde rahat anlaşılmasını sağlayan bir düzenleme yöntemidir. İyi yazılmış nesnesel kodun amacı, gereksiz şekilde tekrar eden kodu merkezileştirerek, tek bir yere toplamaktır. Böylece yazılmış olan kod miktarı azalır. Bu iyi bir şeydir. Fakat zil takarak oynamamıza sebebiyet verecek bir vahşet bir numara değildir.

Cevap Niye CASE Araçları Değil

CASE araçları, balonlar ve oklar çizmemizi sağlayan, kodumuzu kuşbakışı bakmamıza yardım eden araçlardır. Eğer sunum (presentation) amacı, ya da kodumuzdan geri-mühendislik yaparak şekiller üretecek şekilde kullanılırsa, yararlı araçlardır.

Fakat eğer tasarım/geliştirme sürecinin tam ortasına konulup, kod yazmadan önce şeklini çizmeniz lazım gibi bir kural konulacak olursa, projeniz tehlikeye girecektir. Çünkü, CASE araçları da nihayi sonuçta bir projenin belkemiği olamaz. CASE araçları hâla şekilden kod üretip, sonra aynı kod metinyazar ile değiştirildikten sonra koddan geriye düzgün şekilde yeni şekiller yaratabilecek bir halde değildir. Olsa bile, kod bu sefer iki formatta tutulmuş olacak ve karışıklık yaratacaktır. Eğer kodlar/figürler ikilisini tamtamına uyum halinde otomatik olarak tutamıyorsaniz, figürler geliştirme sürecinin merkezi olamaz.

Otomatik gitme/gelme mümkün olsa bile, kodun tanımlama gücü (expression power) figürlerden her zaman daha yüksektir. "Bir resim, bin kelimeye bedeldir" sözü yazılım dünyasında bazen kullanılıyor, fakat bu söz yazılım dünyası için geçersiz bir sözdür. Yazılımda geçerli olan şekli şöyle olmalıydı: "Üzerinde anlaşma sağlanmış olan bir kelime (kod), bin resime bedeldir".

Cevap Niye Proje İdare Teknikleri Değil?

Bu sorunun cevabında, nesnesel modelleme için verdiğimiz cevaba bir paralellik göreceğiz.

Proje idare teknikleri, yapılacak işi her nasıl taksim, işbölümü, görevlendirme ile dağıtıyor olsa da, sonuçta iş dönüp dolaşıp tek bir şeye bâğlanır. Takım, doğru alanlara odaklanabilen bir teknik lidere, ve yetenekli takım elemanlarından oluşuyor mu? Eğer oluşmuyorsa, takım XYZ projesini diyelim ki 9 ayda bitirecektir. Eğer biliyorlar ise, 6 ayda bitireceklerdir. Bilinmezlik seçeneğinde verilen 9 ayı, her nasıl böler, katlar, görevlendirir, tekrar görevlendirseniz de, sonuçta toplam 9 ay olacaktır. Proje idare yöntemleri hiç yoktan yetenek yaratamazlar.

Bu yüzden en iyi idare yöntemi, ortada en az gözükendir. Yani programcının ayaklarına en az dolaşan proje idare yöntemi en uygunudur. Sitemizde öğretmek için Extreme Programcılığın seçilmesinin sebebi budur: XP'nin merkezi bireydir, yöntemin kendisi değil.

Fakat XP bile, yeteneksiz bir gurup programcıya gaipten teknoloji bilgisi veremez. Bu yüzden proje idare yöntemleri en önemli faktör değillerdir.

En Önemlisi: Teknoloji Kullanımı

Evet, olmazlardan olura geldik. Niye en önemli konu, kullanılan teknolojinin açıklarını, faydalarını hemen anlayarak, sürekli karşımıza çıkan genel bilgi işlem problemlerine bu teknolojinin verdiği cevapları masaya yatırabilmektir?

Çünkü, yapılması gereken iş miktarının ne olduğuna karar veren yegane faktör, teknolojinin nasıl kullanıldığıdır. Bir örnek ile açıklayalım.

Başta bahsettiğimiz CORBA projesinde karşımıza, o zamana göre çetrefilli şu problem çıktı. Kullanıcı bilgisayarlar ile servis bilgisayarı arasında bazı sonuç listelerini (resultset) göndermemiz gerekiyor.

1) Müşterinin kayıtlı olduğu planlar bir değil, birden fazla. Eğer plan sayıları mesela 100 tane ise, temel bir veri yapısı (int, boolean, String gibi) tabii ki kullanamayız, bize bir liste veri yapısı lâzım.

2) Kullanıcı bilgisayar ile servis bilgisayarı bir ağ üzerinden haberleşiyorlar. Aynı bilgisayar üzerinde değiller, bu sebeple dil salt seviyesinde düşünülen bir "return liste" gibi bir çözüm bizi kurtarmıyor.

Bu problem katmanlı bilgi işlem dünyasından çok klasik bir problemdir. Teknolojiden, idareden, modelden bağımsız düşünün: Gayet soyut olarak bakalım. Bu problem hangi çok-katmanlı bilgi işlem projesinde olursak olalım, kesinlikle karşımıza çıkacak.

Bu klasik problemin klasik bir problem olduğunu bilmek öncelikle tecrübe gerektir. Bundan sonra, atılacak adım şudur.

Projeniz için seçilmiş olan teknoloji çorbasına bakarsınız. Hangi teknoloji bu probleme çözüm olacak? Bizim örneğimiz için cevap CORBA.

CORBA'nın bu soruna verdiği cevap nedir? String[] gibi bir tip çeşidi buluyoruz. Fakat bir ağ ortamındayız, iki bilgisayar birbirinden uzakta. Ya 1000 sonuç göndermemiz gerekiyorsa? Bir seferde 1000 tane kayıt ağ üzerinden gönderemeyiz ya?

Kullanıcı tarafında görsel programı şöyle değiştirelim. Listeyi parça parça göstersin, ve "Sonraki" diye bir düğme kullanıcıya sonraki parçayı göstersin.

Peki CORBA otomatik olarak 1000 elemanı parçalara bölüp öyle geriye gönderebiliyor mu?

Cevap hayır.

İşte kullandığımız teknolojinin zayıf tarafını bulduk. Şimdi bu zayıflığın üstesinden nasıl geleceğiz? Bulacağımız cevap yeni baştan bir uygulama servisi yazmayacak şekilde, kısa, etkili, kötünün iyisi bir cevap olmalı.

Potansiyel Çözüm 1: Kullanıcı tarafı, CORBA bağlantısı üzerinden bir servis nesnesine bağlantısını koparmasın, o servis nesnesi de son işletilen veri tabanı sorgusundan gelen sonuç listesine olan bağlantısını koparmasın. Kullanıcı tarafı yeni parça istediğinde, yeni parça mevcut veri tabanı bağlantısı üzerinden verilsin.

Olmaz. Çünkü, bir çok-kullanıcılı mimariyi ölçeklemek istiyorsak, veri taban bağlantısını tek bir kullanıcıya bağlı tutamayız. Bâğlantılar veri taban dünyasında en pahalı kaynaktırlar, çok tutumlu kullanılmalari gerekir. Eğer kullanıcı-bağlantı birebir bağlanırsa, mimarimiz iki katmanlı demektir, üç katmanlı değil! CORBA, RMI, Servlet'leri boşu boşuna kullanmış olurduk. Veri tabanlarının bu özelliğini bilmek, kuralımızın tekrar ilginç bir şekilde ispatladığı üzere, kullandığımız bir teknolojinin zayıf tarafını bilmekten geçer. Veri tabanlarının eşzamanlı şekilde servis edebildiği kullanıcı sayısı, veri tabanının açılmasına izin verdiği "bağlantılar (database connection)" ile orantılıdır.

Birinci çözümü attık.

Potansiyel Çözüm 2: Servis nesnesi, liste istendiğinde listenin bir parçasını geriye göndersin (güzel). Bu işlem yapıldıktan sonra veri taban bağlantısını koparsın (güzel). Şimdi ilginc tarafa geldik: Listenin ikinci parçasını nasıl bulacağız?

Şöyle yapalım: Oracle'ın ROWID denen özelliğini kullanabiliriz. ROWID, her satıra verilen özebir (unique) kimlik numarasıdır. İlk parçayı geri gönderdikten sonra, bu listenin ilk parçasının en son elemanının ROWID'sini bir tarafa yazalım. İkinci parça istendiğinde servis nesnesi aynı sorguyu tekrar işletsin, ama bu sefer sadece son ROWID'den büyük olan satırları geri göndersin. Bu da ikinci parça olur.

Bu çözüm iyiye benziyor. Fazladan yazılan, yani CORBA'nın zayıflığı yüzünden yazmamız gereken kod miktarı çok değil. Kötünün iyisi bir çözüm bulduk galiba. Hem veri tabanı bağlantısına denize düşen yılana sarılır gibisinden sarılmadık, hem de bir sürü yeni kod yazarak projeye fazla yük getirmedik. Her taraftan omuzumuza düşmüş olan gereklilikleri gözettik, ve bir çözüm bulduk.

Ve işin özünü böylece açıklamış olduk. Dikkat ederseniz, potansiyel çözümlerden bahsederken, ne XYZ modeli dedik, ne CASE aracından şöyle balonlar çizelim dedik, ne de "sayın arkadaşlar, Extreme Programcılık bağlamında yönteminde birileri ABC özelliğini karta yazsın, bir tahmin yapsın ve kodlamaya başlasın" dedik. Bunların hiçbiri bize çözüm için yardımcı olmayacaktı.

Doğru çözümü bulduktan sonra yazılması gereken kodu, istediğimiz şekilde keser biçer taksim edebilir ve bir temiz nesne modeli yaratabiliriz.

Doğru çözümü bulduktan sonra harcanması gereken zamanı Extreme Programcılık ile istediğimiz gibi taksim eder, takip edebilirdik.

Doğru çözümü bulduktan sonra bir CASE aracı ile (ya da Visio ile) cözümümüzü anlatan şekiller çizebilir, müşterimizi, takım arkadaşlarımızı bilgilendirebilirdik.

Ve en son olarak, doğru çözümü bulduktan sonra istediğimiz dilde (o dilin izin verdiği şekilde) kodlayıp işi bitirebiliriz.

Yani: İkinci derece önemde olan eylemleri, birinci derece önemli olan bittikten sonra istediğimiz gibi yapabiliriz. Fakat ikinci derece önemde olan eylemler, projeyi kurtarmaz. Önce yapılması, odaklanılması ve katiyetle öğrenilmesi gereken birinci derece önemli olan eylemdir. Yani teknolojiyi doğru şekilde kullanabilmek, anlayabilmek, gerekiyorsa etrafından dolaşabilmek.

Yanlış anlaşılmaması için tekrar belirtmeyi uygun görüyoruz. Tabii ki nesnesel modelleme, mevcut diğer kod düzenleme yöntemleri arasında en iyisidir. XP'nin esnek bir yöntem olduğu muhakkaktır, programcıya rahat nefes aldırır, ve proje içinde özgürlük sağlar. Evet Java rahat bir dildir, ve revaçta olan nesnesel dillerin arasında en temiz sözdizim yapısına sahip olandır. Fakat bunlar bilgi işlem dünyasında tabiri yerinde ise, "buzdağının görünen kısmıdır". Bu yazımızın amacı, düşüncesel, bakış açısı olarak içinde bulunmamız gereken zihin durumunu yansıtmaktır. İkinci derece önemli yöntemleri gereksiz olarak etiketlemek değil.

Doğru Çözümün Bir Tarifi Daha

Doğru çözümden bahsederken, bu sonuç çözümün belli karakteristiklerinden bahsetmeyi uygun görüyoruz. Bu karakteristiklerin en önemlisi, çözümün basit olmasıdır. Mühendislikte de, temel bilimde de basitliğin önemi büyüktür. Bilim dünyasında basitliğe vurgu, Occam'ın Usturası (Occam's Razor) adlı özdeyiş ile yapılır. Occam, şöyle demiştir: "Hiçbir şeyi gereksizce çoğaltmayalım", bunun Latinceden, İngilizceye ve günümüze taşınması sonucu özdeyiş şu hali almıştır: "İki hipotez arasında, daha basit olan hipotezi seçiniz". Bilim dünyası da Occam'ın zamanından beri böyle yapmıştır.

ABD'de programcılar arasında da yaygın bir deyiş vardır: Çözüm basit ve aptal olsun (keep it simple, stupid - KISS)

Yâni çözümü hazırlarken, bunu elimizdeki teknolojilerin açıklarına, güçlü taraflarına göre kurar, ve çözümün her zaman basit olmasına dikkat ederiz. Bu basitlik, her seviyede geçerli bir basitlik olabilir: Kodlamada basitlik, idarede (administration) basitlik, kod miktarının az olması, az sayıda dış arayüz kullanılıyor olunması, vs..vs..

Bağlaşım: Nesneler, Veri Tabanları

İş hayatına danışman/programcı olarak başladığımızdan beri önümüze şöyle bir problem çıkmıştır. Nesnesel bir dil ile yazdığımız programlarımızı veri tabanına nasıl bağlayacağız?

"İnsanlar bunu niye problem haline getirdi?" diyebilirsiniz. Bunun birkaç sebebi var, ama çoğunun ana teması aynıdır: Programların bakım evresinde daha rahat değiştirebilmek... Zaten tasarım yaparken, ana amaçlarımızdan biri bu olmalıdır: Okunabilen, rahat değistirilebilen kod yazmak. Özet olarak: Yukarıda bahsettiğim arkadaşlarımızın (ve şimdi de benim) ana ilkesi şu idi. Veri tabani mimarisini, Java program tasarımından mümkün olduğunca uzak tutmak/bağlantısını hafifletmek. Biraz daha açıklayalım.


* Java, nesne tabanlı olduğu için, Java programcıları her şeyi nesne olarak görürler. Mesela Javacılar için, insan.setIsim(yeniIsim) demek, UPDATE INSAN SET ISIM='' demekten daha rahattır. Eğer SQL kodunu Java içine koyarsanız, programın ne yaptığı rahat anlaşılmaz.
* Java kodunuz, veri taban cizelgesine direk bağlantı yapıyorsa, çizelge değişince Java programı değistirmeniz gerekecektir.

Şimdi iki türlü kod parçası vereceğiz. Hangisinin daha rahat veri tabanına erişim sağladığı belli olacaktır.

//
// JDBC kullanıyoruz
//
class VeriKontrol
{
public void guncellestir(Isci isci, Fabrika fabrika, String
yeniIsim, String yeniSoyad, String yeniFabrika)
{
Connection c = d.getConnection();
PreparedStatement stmt = c.prepareStatement("UPDATE ISCI set
ISIM=?, SOYAD=?, FABRIKA_NO=?");
stmt.setString(1, yeniIsim);
stmt.setString(2, yeniSoyad);
stmt.setString(3, yeniFabrika);
}

public void FabrikaCalisanIscileriIsle(String fabrikaNo)
{
Connection c = d.getConnection();
PreparedStatement stmt = c.prepareStatement("SELECT * FROM
ISCI WHERE FABRIKA_NO=?");
stmt.setString(1, id);
rs = stmt.executeQuery();
while (rs.next())
{
String id = rs.getString("ISCI_NO");
// .. birseyler yap isci ile
// ama dikkat edin, elimizde hala Isci nesnesi yok
// nesneyi NO kullanarak yaratmamiz lazim.
}
}

}

Eğer nesne/veri baglaşımı dışarıda yapılmış ise, program şöyle olabilirdi.

class VeriKontrol
{
public void guncellestir(Isci isci, Fabrika yeniFabrika)
{
// bu iki satiri nesne icinde daha once isletim, sonra
// gerektikce oradan kullanabilirsiniz. Hep yazmaya gerek yok.
PersistenceManagerFactory pmf = JDOFactory.getPersistenceManagerFactory ();
PersistenceManager pm = pmf.getPersistenceManager ();

// kritik yer burasi
fabrika.isciEkle(isci);
pm.guncellestir(isci);
}

public void FabrikaCalisanIscileriIsle(String fabrikaNo)
{
// yukaridakinin aynisi
PersistenceManagerFactory pmf = JDOFactory.getPersistenceManagerFactory ();
PersistenceManager pm = pmf.getPersistenceManager ();

// veriyi al
Collection results = null;

// sorgu yarat
q = pm.newQuery (e, "fabrikaNo == " + fabrikaNo);
sonuclar = (Collection)q.execute ();

Iterator sonucTarayici = results.iterator ();

while (sonucTarayici.hasNext ())
{
Isci isci = (Isci)sonucTarayici.next ();
// isci ile birseyler yap.
// isci.getFabrika() diyerek fabrika objesine erismek mumkun
// mesela..
..
}
}
}

Aradaki fark herhalde belli oluyordur. 2'inci kod parçasının üstün tarafı şurada, sadece nesneler ile haşır neşir oluyoruz ve setDeger, getDeger gibi ifadeler kullanabiliyoruz. Birinci şekilde, bir takım karmakarışık SQL kodunu Java içine koymak gerekiyor, iki dil ile uğrasmak gerekiyor.

Nesne/Tablo Eşleme Programları İçin Aranan Özellikler

Evet, bağlaşım dogru yapılınca Java kodumuz nasıl olacak gördük. Şimdi, bize yardımcı olacak yazılımı tanıyalim. Biraz da tarihçe.

Bağlaşım yazılımlarının tarihi, nesnesel programcılık kadar eskidir. Mesela bazı standardlar 90'li yılların başında ortaya çıkmıştır: POS (Persistent Object Specification/Kalıcı Nesne Tarifnamesi) OMG adlı gurup tarafından ortaya atılmıştı. Oldukça ses getiren yaklaşımlardan biri olsa da, ne yazik ki ürün desteği olmayınca kağıt üzerinde kaldı, ve tarifname'ye göre geliştirmeye calışanlar hüsrana uğradı. Şahsen tanıdığımız bir arkadaş bu tecrübeden geçti, ve geçtiğine pişman.

Fakat sektör pes etmedi, çünkü nesne içine SQL koymak nesneci programcıların hiç icine bir türlü sinmedi. Fakat bağlaşım hazir olmayınca, herkes kendi mutfağında bir şeyler pişirmek zorunda kaldı. Şahsen, bir projede her nesne için güncelleştir(), sil() ve oku() gibi işlemler yazarak, nesne/tablo bağlantısını bir nevi 'yapay' bir şekilde kurmaya calıştık. (Yukarıdaki yöntemler içinde SQL yazıyorduk).

Ayrıca, nesnesel programcıların veri tabanı kolonlarına (column) Java nesne özellikleri (attributes) olarak erişme isteği arkasında bir baska itici kuvvet daha var. Internet Sunucu programları için yazılan sayfalar, mesela JSP, JHTML, Struts gibi diller akabinde sayfa içinde bilgiyi, 'nesne' olarak sayfaya bağlamak gerekiyor. Yani, üzerinde setXX, getXX olan türden nesnelerin olması bu tür programlar için şart. Bu da önümüze kanun gibi çıkınca, veri tabanı ve Java aradasındaki bağlantıyı kurmanın gerekliliği iyice ortaya çıkıyor, ve bağlaşım dünyasında hareketlenme de bu yüzden hızlanıyor.

Son çözümlerden önümüzde olanlar:


* JDO (Java Data Objects) yani Java Veri Nesneleri
* EJB 2.0 Kap Kontrollu Kalıcılık/Container Managed Persistence

Yukarıda verilen ikinci örnek, JDO kullanarak yazılmıştır.

İlerideki yazılarımızda, oldukça yayılmaya başlayan JDO teknolojisini yakından tanıyacağız, ve bağlaşım yazılımı seçerken nelere dikkat etmemiz gerekiyor yakından inceleyeceğiz.

MQSeries Nasıl Kurulur

Bu yazının amacı Solaris 2.7 MQSeries 5.2 kurmayı öğretmek. Ayrıca JMS kullanarak bir örnek kuyruğa mesaj koymayı göstereceğiz.

MQ Kuralım


* İlk once Unix üzerinde mqm adlı bir kullanıcı yaratın.
* mqm adlı bir gurup yaratın.
* MQ program dosyasını IBM sitesinden indirin.
* pkgadd dosya_ismi kullanarak programı kurabilirsiniz. İşiniz bittikten sonra MQ /opt/mqm altında olacak.
* Şimdi JMS kütüphanesini kurmamız lazım. IBM sitesinden ma88_sol.tar adlı kayıdı bulup indirin. Bunu da pkgadd kullanarak kurun. İşiniz bitince program /opt/mqm/java altında olacak.
* /opt/mqm/java altındaki bütün JAR dosyalarını CLASSPATH'inize ekleyin.
* MQ kullanacak bütün Unix kullanıcılarının mqm gurubu altında olmasına dikkat edin.

Kuyruk Yaratmak

Kuyruk yaratmak için aşağıdakini uygulayın

crtmqm -q venus.queue.manager

Kuyruk gözleyicisini başlatmak için

strmqm

Solaris başlayınca otomatik olarak kuyruk gözleyicisi başlasın istiyorsanız, aşağıdaki satırları /etc/inetd.conf dosyasına ekleyin.

MQSeries stream tcp nowait mqm /opt/mqm/bin/amqcrsta amqcrsta -m venus.queue.manager

Bu satırları da /etc/services dosyasına ekleyin.

MQSeries       1414/tcp      # MQSeries channel listener

Aşağıdakiler için, MQ komut satırına inmeniz lazım.

$ runmqsc

0783889, 5765-B75 (C) Copyright IBM Corp. 1994, 2000. ALL RIGHTS RESERVED.
Starting MQSeries Commands.

- kuyruk yaratan komut budur
define qlocal (orange.queue)

-- kanala yaratan budur
DEF CHL('JAVA.CHANNEL ')CHLTYPE(SVRCONN)TRPTYPE(TCP)MCAUSER('')+
DESCR('Java icin ornek kanal')

-- komut satirindan cikmak icin
end

Tetikleyici gözlem programı için:

runmqtrm

Kanal baslangıç programı için:

runmqchi

Komut işlem programı için

strmqcsv

Örnek Java/JMS Nesnesi

Aşağıdaki kod sayesinde, yukarıda yarattığımız kuyruğa bağlanıp bir mesaj koymamiz mümkün.

import com.ibm.mq.jms.MQConnectionFactory;
import com.ibm.mq.jms.MQQueueConnectionFactory;
import java.io.PrintStream;
import javax.jms.*;
import com.ibm.mq.jms.JMSC;

public class Test
{

public static void main(String args[])
{
Test test = new Test();
test.addDataItem();
}

public Test()
{
kuyrukIdareciIsmi = "venus.queue.manager";
makinaIsmi = "localhost";
kanal = "JAVA.CHANNEL";
port = 1414;
kuyrukIsmi = "ORANGE.QUEUE";
transportType = JMSC.MQJMS_TP_CLIENT_MQ_TCPIP;
}

public void addDataItem()
{
try
{
MQQueueConnectionFactory baglantiFabrikasi = new MQQueueConnectionFactory();
System.out.println("kuyrukIdareciIsmi :" + kuyrukIdareciIsmi);
System.out.println("Makina ismi :" + makinaIsmi);
System.out.println("Kanal :" + kanal);
System.out.println("port :" + port);

baglantiFabrikasi.setQueueManager(kuyrukIdareciIsmi);
baglantiFabrikasi.setTransportType(nakilTuru);
baglantiFabrikasi.setHostName(makinaIsmi);
baglantiFabrikasi.setPort(port);
baglantiFabrikasi.setChannel(kanal);

QueueConnection kuyrukbaglantisi =
baglantiFabrikasi.createQueueConnection();

kuyrukbaglantisi.start();

QueueSession queuesession =
kuyrukbaglantisi.createQueueSession(false, getTransportType());

javax.jms.Queue kanal = queuesession.createQueue(getRemoteQueueName());

QueueSender queuesender = queuesession.createSender(queue);

ObjectMessage mesaj = queuesession.createObjectMessage();

mesaj.setObject(new String("---------------"));

queuesender.send(mesaj);

}
catch(JMSException jmsexception)
{
jmsexception.printStackTrace();
}
catch(Exception exception)
{
exception.printStackTrace();
}
}

public String getQueueManagerName()
{
return kuyrukIdareciIsmi;
}

public void setQueueManagerName(String s)
{
kuyrukIdareciIsmi = s;
}

public String getHostName()
{
return makinaIsmi;
}

public void setHostName(String s)
{
makinaIsmi = s;
}

public int getPort()
{
return port;
}

public void setPort(int i)
{
port = i;
}

public String getChannel()
{
return kanal;
}

public void setChannel(String s)
{
kanal = s;
}

public int getTransportType()
{
return nakilTuru;
}

public void setTransportType(int i)
{
nakilTuru = i;
}

public String getRemoteQueueName()
{
return kuyrukIsmi;
}

public void setRemoteQueueName(String s)
{
kuyrukIsmi = s;
}

private String kuyrukIdareciIsmi;
private String makinaIsmi;
private String kanal;
private int port;
private int nakilTuru;
private String kuyrukIsmi;
}

MQ Series

Bilgisayarlarda mikroişlemci temeline kadar inerseniz, aslında herşeyin basit bir temel üzerine oturtulduğunu göreceksiniz. Programlar ana bellek içinde durur, ve program kodlarını işleten merkez işlemci, kodları teker teker bellekten alıp işleme koyar. Ve bu kodlar işlerken onların sonucunu BEKLER.

Bilgisayarları tasarlayan mühendisler için, bu işlet/bekle döngüsü rahat anlaşılır bir kavramdır. İşleri kolaylaştırır, bu yüzden genelde kullanılan kavram budur. Ve en temelden desteklendiği icin, bu bütün program dillerine yansımış, mesela bir Java işlemi de aynen makina dilinin yaptığı gibi (assembler) işlem sonuçlarını bekler olmuştur.

Fakat bazen, alternatif bir yaklaşımı kullanmak yararlı olabilir. Bu değişik yaklaşım şudur: "İşlemi çağır ama sonucu beklemeden yoluna devam et, sonucu bir şekilde geri alirsın".

Bu tip kavramlar, programlama dili seviyesinde kullanılabilir, fakat genelde bu iş, dış paket programlar yardımı ile yapılıyor günümüzde. MQ Series bu programlardan biridir.

İleti (Mesaj) kuyruğu denen programlar, isimleri üzere, cağırılan işlemleri depolarlar. Mesajları yüksek bir hızda, sistemler arasında değiş/tokuş yapmak için yazılmışlardır, ve iyi taraflarindan biri, mesajı KAYBETMEME garantisi verirler (sabit diske yazarak).

Örneğe gelelim.

Birbirinden alakasız iki sistem düşünelim: A ve B sistemi tamamen ayrı makinalar üzerinde çalışıyor olsunlar. Hatta 'işletim sistemleri' bile farklı olsun. Bu iki sistem arasındaki bağlantı TCP/IP bağlantısı, yani Internet usûlü bir bağlantı olabilir fakat bildiğimiz gibi TCP/IP, mesaj iletiminde 'garanti' vermez. Paket düşmesi denen olay sık olmasa bile, olması mümkün vakâlardandır.

Olayı renklendirmek için, B sistemine bir servis programı koyalım. Bu 'sunucu' program, çarpma servisi yapsın bizim için. 3 ve 5 sayısını verince geri cevap 15 verecek yani. Mucize program!

Arayüz şöyle olabilir.
public class Islem
{
public int carp(int sayi1, int sayi2)
{
return sayi1 * sayi2;
}
}

Oldu. Fakat, A sisteminden B sistemine nasıl bağlantı yapacağız? Java dünyasında RMI kullanabiliriz. RMI ile, B sistemindeki arayüzü, A sistemine uzaktan göstermek mümkün oluyor. yani nesne->carp(3, 5) islemini RMI A'dan alip B'ye, cevabı ağ sistemi ile geriye getirebiliriz.

Peki, ya, tam bu işlemi yaparken, B sistemi çökerse?

"Amma da zorlastırdın işi kardeşim, arada sırada çökerse çöksün!"

Bu cevabın geçerli olduğu iş şartları olabilir. Fakat bazı şartlarda B sistemi çökünce, geriye yanlış cevap vermek kabul edilemez olabilir. Bunun üstesinden gelmek icin RMI dünyasında, "olmazsa tekrar dene" gibisinden 'istisna kodu' yazabilirsiniz, fakat bu A sistemindeki kodu iyice karıştıracaktır. Peki çözüm nedir?

Mesaj kuyruk programları bu günler için yazılmıştır. Önce bir kuyruk tanımlarsınız. Kuyruk_1 ismini verelim: Bu kuyruğa mesaj koymak ve mesaj okumak mümkündür. Kuyruk_1'in kendisi, cismen A sisteminde, B sisteminde, ya da en iyisi, bir C sisteminde yaşıyor olabilir. Nerede yaşarsa yaşasın, sabit mesajları diske yazdığı için onları kaybetmeyecektir. Ayrıca MQ Series gibi programlar ile, kuyrukta biriken mesajlar, mesela C sistemi hayata döndüğünde, ona tekrar iletilmeye başlanır.


Böylece, mesaj kaybı sorununu çözmüş olduk.

Yanlız bu yeni sistemde biraz değişik düşünmek zorundayız. Artık, nesne->carp() islemi 'anında' geri dönecek, ve programınızın geri kalan kısmı işleme koyulacaktır, 'çarpım sonucu elinize geçmeden'. Çarpım sonucunu belki başka bir kuyruktan geri almanız mümkün olabilir. Ama, bu tip kararlar program mimarinizi etkileyecektir. Kuyruk bazlı ve olmayan programlar arasındaki mimari farkı o yüzden büyük olabilir; Önemli olan, hangi mimarinin ne zaman lazım olduğunu görebilmek. Eğer,


* İki değisik sistem, birbiri ile mesaj alışverişi yapıp, mesajları kaybetmek istemiyorsa
* Bu iki sistem birbirinden degişik zamanlarda ve kişiler tarafından kontrol ediliyor, o yüzden indirilip/kaldırılması gerekiyorsa
* Bilgi alışverisinin anlık olması gerekmiyorsa

Mesaj kuyruk bazlı programlar ihtiyacınızı karşılıyabilir.