Saturday, November 1, 2003

XML Nasıl İşlenir

Bugünkü yazımızda, XML işlemcileri hakkında yazacağız. Önümüzde ana iki seçenek var.

* DOM
* SAX

DOM, SAX'tan daha yaşlıdır. XML dökümanlarını işlemek için ilk önce DOM kullanıyordu herkes. Fakat DOM'un bazı dezavantajları var. Mesela, XML dökümanı DOM ile işlenirse, DOM programı bütün XML kayıdı tamamen hafızaya alınır. Büyük XML kayıtları için bu işlem yavaş kalabilir.

Eğer bütün XML dosyasını nasıl olsa işlemek istiyorsanız, o zaman DOM işinize yarayabilir. Fakat XML dosyasından, sadece "birkaç" özel bölümlerden okuma yapmak istiyorsanız, SAX kullanmak daha hızlı olacaktır.

SAX, "vaka" modeli ile çalışır. Vaka modeli, yani Java dilinde 'event' modeli, normâl alışık olduğumuz, "sıralı işleyen" program modelinden biraz farklıdır. Sıralı işleyen programda, komutlar sıra ile işlenir. Programcı bu sırayı kod içinde belirtmiştir, önce şu olacak, sonra bu.

Vaka modeli içinde, şöyle demek mümkündür. "Ne zaman X olayı olursa, Y komutunu işlet".

Bu düşünce şeklinin, programlama stilinize etkileri büyüktür. Artık, sıralı dizi gibi işleyen komutlar yerine, artık vukuat komutları yazar hale geliyorsunuz.

SAX program modeli vaka üzerine kurulduğu için, cok hızlı bir şekilde XML işlemi mümkün olur. Çünkü artık işlemci, bütün dosyayı hafızada tutması gerekmez; böylece sadece istenen yere gelince, bir "vaka" başlatır. Sizin vaka komutlarınızda hemen o anda işleme konur. O komut içinde ne yapacağınız size kalmış. Veri tabanına yazabilirsiniz, e-posta gönderebilirsiniz, başka bir XML dosyasi içine kopyalayabilirisiniz.

SAX için yazılmış örnek bir program. Bu program title başlığında girilmiş bilgileri XML dökümanından buluyor.
    class VakaKomutu extends DefaultHandler{

protected String tag = null;
protected String content = null;

public VakaKomutu ()
{
super();
}

public String getContent()
{
return content;
}

public String processFile(String file) throws Exception
{
VakaKomutu komut = null;
try {
//System.out.println("processing .." + file);
XMLReader xr = XMLReaderFactory.createXMLReader( "org.apache.xerces.parsers.SAXParser");

komut = new VakaKomutu();
xr.setContentHandler(komut);
xr.setErrorHandler(komut);

FileReader r = new FileReader(file);
xr.parse(new InputSource(r));
} catch (Exception e) {e.printStackTrace(); throw e;}
//System.out.println("returning content... " + content);
if (komut == null )
return "title";
else
return komut.getContent();
}

public void startElement (String uri, String name,
String qName, Attributes atts)
{
if ("".equals (uri)){
//System.out.println("Start element: " + qName);
if ("title".equals(tag) == false) {
tag = qName;
//System.out.println("=====================");
//System.out.println("tag is " + tag);
//System.out.println("content is " + content);
}
}
else {
//System.out.println("Start element: {" + uri + "}" + name);
}
}

public void characters (char ch[], int start, int length)
{
if ("title".equals(tag) && content == null) {
content = new String(ch, start, length);
//System.out.println(content);
}
}

}

JDOM yardımı ile daha kolay XML işlemek

JDOM, Java için yaratılmış bir araç olarak, XML işlenmesini çok kolaylaştırıyor. JDOM tasarımı Java dilinin özelliklerinden faydalanabilecek gibi yapılmış. Ama acaba öteki standart arayüzlerin yerini alabilecek kadar etkili mi? Bu yazıda beraber göreceğiz.

Programcı olarak herhalde 80-20 kuralını duymuşsunuzdur (Pareto'nun kuralı diye de bilinir). 80-20 kuralı derki "Metod (tasarım) düzenleri önümüze çıkabilecek şartların 100'ünden 80'inine çözüm getirebilir. Geri kalan 20 durum için metodun dışına çıkıp, o şarta özel çözüm bulmak gerekir". Bu sözün programcılar için önemli dersi şudur: Herhangi bir teknolojiyi ele aldığımızda, bu teknolojinin önümüzdeki problemlerin yüzde seksenini muazzam rahatlıkta çözebilmemize yardım etmesi gerekir.

Ne yazık ki yazılım ürünleri ve standartları bazen bu şartlara göre gelişmiyor. Özellikle Java/XML teknolojilerine baktığımızda 80/20 kuralının birçok kere kırıldığı yerleri görüyoruz. Java dünyasında XML arayüzleri ve ürünleri enflasyonu vardır; Kimi ürün büyük bir şirket tarafından desteklenmiştir, kimisi tek bir XML işlemi etrafına özel yazılmıştır, kimisi de bütün çözüme gayet çetrefilli çözümler getirmeye uğraşmıştır. Fakat 10 koddan 8'ini çözecek, sürekli üst üste yaptığımız işlerde yardımcı olacak kütüphane nerede? Hani o XML ağaç yapısını basitçe işleyebileceğimiz, değiştirebileceğimiz Java diline uygun bir yazılımdan bahsediyoruz.

JDOM işte tam bu sorunlara çözüm olarak yazılmıştır.

Java ve XML

Birçok yönden Java, XML işlemek için en uygun dil haline geldi. Apache Vakfı ve IBM DeveloperWorks tarafından baslatılan büyük atılımlar sayesinde şu anda Java için XML işleyebilen, değiştirebilen esnek kod bazları mevcut.

Biraz da tarih. Java 2 standardı XML ünlü olmaya başlamadan önce piyasaya çıktığı için, Java 2 dahilinde XML yönünden birçok eksik var. Bu yüzden Sun, hem XML eklentileri yapabilmek hem de Sun dışında yaratılmış olan teknolojilere 'evet' diyebilmek için JSR sürecini kullanıyor. Bu sayede eklenen JAXP standardı akılda en çok kalan yenilerden biri. JAXP dahilinde 3 paket eklendi.


* org.w3c.dom: W3C standard arayüzlerinin Java'da gerçekleştirilmiş hali.
* org.xml.sax: olaya dayalı (event driven) XML işleyici basit arayüz
* java.xml.parsers

Bu paketlerin eklenmesi iyi bir şey olsa da, sonuçta yapılan 'zaten revaçta olan' standardlara evet demektir. Hala eksik olan Java'nın dil yapısına uygun esnek bir XML işleyici kütüphane idi.

Hoşgeldin JDOM. Ünlü iki Java yazarı olan Brett McLaughlin ve Jason Hunter tarafından yazılan JDOM, 2000 yılında serbest kod lisansı ile Apaçe projesine dahil edildi. Dünyanın her tarafındaki Java kullanıcılarında aldığı tavsiyeler/hata onarımları ile büyüyerek günümüze geldi. Amacı özetle, Java'ya uygun, basit ve esnek XML işleyici kütüphanesini oluşturmaktır.

JDOM Dosyalarını Yaratmak ve Kullanmak

JDOM alıştığımız Java kodlama tekniklerini kullanıyor. Lazım olan her yerde Java new kelimesi kullanmak mümkün; çetrefilli Factory (Fabrika) nesnelerine gitmemize gerek yok. Mesela, sıfırdan bir XML dosyası yaratmak için ne yapmamız gerektiğine bakalım.

<?xml version="1.0" encoding="UTF-8"?>
<araba no="123fhg5869705iop90">
<!--Araba tarifi-->
<marka>Toyota</make>
<model>Celica</model>
<sene>1997</year>
<rank>yesil</renk>
<plaka il="34">HC-176</plaka>
</araba>


Hemen önceden bir kök (üst) elemanı yaratalım.

Element arabaEleman = new Element("araba");
Document benimBelge = new Document(arabaEleman);


Bu basamak yeni bir org.jdom yaratmış oldu, ve bu elemanı org.jdom.Document benimBelge'nin üst düğümü haline getirdi. Her XML dosyasına mutlaka bir tane kök gerektiği için kurucu işleme Element nesnesini bildirgeç verildiğini görmüş olduk.

Şimdi no özelliğini ekleyeceğiz.

arabaEleman.addAttribute(new Attribute("no", "123fhg5869705iop90"));


Başka yeni elemanlar eklemek zaten basit. Marka elemanını ekleyelim:

Element marka = new Element("marka");
marka.addContent("Toyota");
arabaElemani.addContent(marka);


Not geçelim: Element nesnesinin işlemi addContent geriye Element döndürdüğü için, aslında aynı satırı şöyle de yazabilirdik:

arabaEleman.addContent(new Element("marka").addContent("Toyota"));


..dersek aynı işi görmüş oluyoruz. Bazıları 'ilk satır daha okunaklı' diyebilir, fakat aynı anda birden fazla Element yaratıyorsanız ikinci satır daha okunaklı olabilir. Devam edelim.

arabaEleman.addContent(new Element("model").addContent("Celica"));
arabaEleman.addContent(new Element("sene").addContent("1997"));
arabaEleman.addContent(new Element("renk").addContent("yesil"));
arabaEleman.addContent(new Element("plaka").addContent("HC-176").addAttribute("il", "34"));


Farkettiyseniz plaka elemanı dahilinde hem elemanın kendisini hem de 'il' dediğimiz bir özellikde ekledik. Bunun mümkün olmasının sebebi addContent işleminin gene aynı Element nesnesini geri döndürmesidir. Böylece zincirleme ekleme yapmak kolaylaşıyor. Bazi kütüphaneler (!) Element yerine void döndürüyorlar.. Cık cık cık.

Yorum ekleyelim:

arabaEleman.addContent(new Comment("Araba tarifi"));


Mavcut XML dosyasını işlemekte benzeri bir halde oluyor. Mesela sene elemanına bir göstergeç almak için Element nesnesi üzerindeki getChild işlemini kullanmamız lazım.

Element seneElemani = arabaEleman.getChild("sene");


Bu getChild çağrısı, ilk çocuk elemanı geri getirir. Eğer sene elemanı yok ise, o zaman null (sıfır) geriye gelir. Ayrıca dikkatinize sunmak istedim: Geri gelen değeri üst-dönüşümden (upcast) geçirmemiz gerekmedi (DOM'da öyle olacaktı). Element nesnesinin çocukları aynı şekilde Element. Ne kadar kolay değil mi? Devam edelim, sene elemanını belgeden çıkarmayı görelim.

boolean cikarildi = arabaEleman.removeChild("sene");


Son satır sadece sene elemanını çıkarır, belgenin gerisi aynı şekilde kalır.

Şimdiye kadar gördüğümüz XML dosyalarının yaratılması ve işlenmesi. Artık biten belgeyi sabit diske yazmanın zamanı geldi.

try {
XMLOutputter yazici = new XMLOutputter(" ", true);
yazici.output(benimBelge, System.out);
} catch (java.io.IOException istisna) {
istisna.printStackTrace();
}


XMLOutputter nesnesinin bazı formatlama seçenekleri var. Üstte gösterdiğimiz, her çocuk düğümün, bir üstündeki düğümden iki karakter sonra geleceği. Ayrıca, her elemandan sonra yeni bir satıra geçmek istiyoruz. XMLOutputter ya Writer nesnesine, ya da OutputStream nesnesine yazım yapabilir. Yani normal dosyaya yazmak için aşağıdakini yapmamız yeterli.

FileWriter yazan = new FileWriter("/bir/dizin/benimDosya.xml");
yazici.output(benimDosya, yazan);
yazan.close();


Öteki XML Arayüzleri ile Etkileşim

JDOM'un ilginç özelliklerinden biri de, öteki XML kütüphaneleri ile olan uyumu. JDOM ile Stream ve Reader nesneleri ile konuşabilmenin yanısıra, SAX EventStream ya da DOM Document nesneleri ile konuşabilmeniz mümkün. Böylelikle JDOM'u çok-arayüzlü ortamlarda kullanmanız mümkün oluyor. Ayrıca, daha sonraki örneklerde göreceğimiz üzere JDOM'un iç yapılarına başka kütüphanelerin erişmesi de mümkün.

JDOM'u kullanabileceğimiz başka bir yer, zaten mevcut olan XML dosyalarını işlemek. Bunu org.jdom.input paketini kullanarak yapacağız.

try {
SAXBuilder yapici = new SAXBuilder();
Document baskaBelge =
yapici.build(new File("/bir/dizin/ornek.xml"));
} catch(JDOMException i) {
i.printStackTrace();
} catch(NullPointerException i) {
i.printStackTrace();
}


İşte SAX'tan gelen bu belgeyi önceki yöntemleri kullanarak işleyebilirsiniz.

Ayrıca, diğer bir JDOM kullanımı gene Apaçe vakfından Xalan ile olabiliyor. Mesela ekteki dosyalara bakarak, bir İnternet araba satış mağazası için İnternet sayfaları yaratacağımızı düşünelim. Bu sayfalar araba detay bilgisi verecek. Bunu için JDOM Document nesnesi ile birlikte XSL kullanacağız ve servlet'in OutputStream'ine HTML basacağız.

Bize lazım olan araba.xsl adlı bir dosya.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/araba">
<html>
<head>
<baslik><xsl:value-of select="marka"/> <xsl:value-of select="marka"/>
</head>
<body>
<h1><xsl:value-of select="marka"/></h1><br />
<h2><xsl:value-of select="model"/></h2><br />
<table border="0">
<tr><td>no:</td><td><xsl:value-of select="@no"/></td></tr>
<tr><td>Sene:</td><td><xsl:value-of select="sene"/></td></tr>
<tr><td>Renk:</td><td><xsl:value-of select="renk"/></td></tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>



Şimdi org.jdom.Document nesnesini DOM Document nesnesine çevirelim, ve Xalan'a gönderelim. HTML'e nasıl çeviri yapılacağını anlatan XSL dosyası, ve farz edilen bir uygulama suncusundan gelen servlet OutputStream nesnesini de ekleyerek tabii ki.

TransformerFactory tFabrikasi = TransformerFactory.newInstance();

// XML ve XSLT belgeleri icin giris kaynaklarini yarat.
org.jdom.output.DOMOutputter yazici = new org.jdom.output.DOMOutputter();
org.w3c.dom.Document domBelgesi = yazici.output(benimBelge);
javax.xml.transform.Source xmlKaynagi =
new javax.xml.transform.dom.DOMSource(domBelgesi);
StreamSource xsltKaynagi =
new StreamSource(new FileInputStream("/bir/dizin/araba.xsl"));

// Cikis sonucunu HTTPResponse OutputStream kullanarak yarat.
StreamResult xmlSonucu = new StreamResult(response.getOutputStream());

// XSLT ceviricisi al
Transformer cevirici = tFabrikasi.newTransformer(xsltKaynagi);

// Ceviriyi yap
cevirici.transform(xmlKaynagi, xmlSonucu);


Bu örnekte çıkışı Java servlet'ten HTTPResponse OutputStream nesnelerine verdik. Fakat, bu çıkış geyet rahat bir şekilde normal dosya da olabilirdi. (XMLOutputter örneğinde gördüğümüz gibi). Kullandığımız DOMOutputter ile Xalan için XML kaynağı yarattık. Aynı kaynağı Document nesnesini XMLOutputter kullanarak String yapmak, ve onu StreamSource'a çevirerekte yapabilirdik. Esnekliğe bakın. JDOM çıkışı, String, SAX Event, Stream, hatta DOM Document dosyası bile olabilir. Böylece JDOM'un başka türden giriş bekleyen yazılımlar ile çalışabilmesi sağlanmıştır.

Birkaç satırda JDOM ile yapabileceğimiz birçok şey var. Bu yazıda gördüklerimiz XML'i yoktan yaratmak, olanı işleyebilmek, hatta XML üzerinden HTML sayfasına çevirmek oldu.

Sonuç olarak başlangıçtaki sorumuza gelelim. JDOM, şu anki mevcut arayüzlerden iyi mi? Eğer rüyalarızı bile Java dilinde gören birisi iseniz, cevap bizce 'Evet'.

Kaynaklar


* Bu yazının örnek kodları. JDOM, Xerces, ve Xalan'ı CLASSPATH'e eklemeyi unutmayın.
* JDOM projesi
* JDOM, Xerces, Xalan ve öteki XML ürünleri için Apaçe sitesini tavsiye ederiz.

Bu yazı ilk önce IBM developerWorks sitesinde yayınlanmıştır.

DOM ve XPATH kullarak XML işlemek

DOM belge işleme yöntemi, W3C kurumu tarafından kabul edilmiş dil, sonuç ortamından bağımsız bir belge işleme standartıdır. DOM, bir XML belgesini temsil edebilecek arayüzler tanımlar, ve bu arayüzler içindeki işlemler XML dosyalarındaki elemanlara erişim ve değiştirme gibi hizmetler sağlar. DOM oldukça yaygın destek görüyor ve birçok programlama dili üzerinde kodlanmış halde: Java, Perl, C, C++, Tcl ve Phyton.

Bu yazıda gösterecegimiz üzere DOM, akım temelli XML işlem yöntemleri yeterli olmadığı durumlarda (mesela SAX gibi) çok yararlı olacak. Akım temelli yöntemlerden kastımız, XML dosyasındaki elemanları teker teker işleyen, bütün belgeyi 'görmeyen' türden yöntemler. DOM bütün XML dosyasını hafızada ağaç veri yapısı kullanarak tutabilir.

Ne yazik ki, DOM tarifnâmesinin dilden-bağımsız bölümleri, ve XML belgesini soyutlayarak temsil etmek için öngördüğü 'herşey bir ağaç yapısında ki düğüm noktasıdır" gibi kavramları yanlış yapmayı daha rahat hale getiriyor, ve kullanan kodun yapısındaki aksaklıkları teşvik ediyor. Geçmişte DOM kullanan projelerimizin kodlarına baktığımızda bunu berrak olarak gördük. Bu genel problemleri çözümleri ile burada aktaracağız.

Belge Nesne Modeli (DOM)

DOM'a göre XML dosyasının her yeri bir düğüm noktasıdır, ve bu noktalar içindeki bilgiler 'çeşit' bilgisi ve 'değer' bilgisi olarak bilinir. Mesela ekteki XML parçasına bakalım.

<paragraf duzenle="sola">Bu<it>sola yatık</it>bölüm.</paragraf>


Yukaridaki XML dosya parçası, aşağıdaki görüntü ile temsil edilebilir.


Belge, Eleman, Metin ve Deger olarak tanımlanan parçalar DOM düğüm noktasıdır.

Fakat bu zarif tanım, bazı sorunları beraberinde getiriyor. Mesela ekteki XML parçasını düşünün. Deger . Sanırsınız ki, 'Deger' diye gösterilen metin, Java String nesnesi ile temsil edilecek, ve erişmek için, getDeger() gibi bir işlem yeterli olacak. Gerçekte, yukarıdaki metin bir Düğüm (Node) noktasıdır, ve başka bir dügüm noktası olan 'etiket-ismi' noktasının altında bulunur. (Çocuk düğüm olarak). Bunun yapılmasının DOM tasarımcıları tarafından mutlaka bazı sebebleri vardı: etiket-ismi altında bazen başka belge elemanları çocuk değerler olarak bulunabilirdi, bu gibi durumlarda metin değerini String olarak almak anlamsız olacaktı. Fakat, her şeyin dügüm noktası olarak görülmesi, sadece metin lazım olan (%80 ihtimalle) şartlar için programcılık hatalarını kolaylaştırmış oldu.

Tasarım Sorunları

DOM'ın dilden-bağımsız özelliğinin başka bir kötü yan etkisi de şudur: Her dilin kendine özgü benzer-oluşları mevcuttur, yani çok kez kullanılmış ve işlediği bilinen kullanım kalıpları. Ne yazık ki, DOM ortak bir dil için yazıldığı için, her dilin kendine hâs ve güçlü olan özellikleri DOM altında kullanılamıyor. Mesela, Element diye bilinen Eleman nesnesi bildiğimiz Java 'new' kelimesi ile yaratılamıyor, programcılar dış nesnelere (Factory diye bilinir yâni Fabrika) Element nesnesini yarattırmak zorunda kalıyorlar. Düğüm listeleri (çocuk düğümler örneğinde gördügümüz gibi) NodeList nesnesi içinde bulunuyor, fakat bütün Java programlarımızda java.util.List ve java.util.Iterator nesnelerini kullanmaya alışığız. Böyle ufak değişiklikler, toplana toplana karşımıza acaip kodlama yöntemleri ve kod fazlalığı olarak çıkıyor, ve programcıları 'DOM-nevi kodlamayı öğrenmeye; mecbur bırakıyor; hâlbuki Java-nevi kodlabilsek işimiz daha rahat olacak.

DOM tasarımına göre, herşey bir Dügümdür (Node). Bu yüzden neredeyse her XML değeri, meselâ Belge (Document), Eleman (Element) ve Değer (Attr) Düğüm denen Node arayüzünü uzatıyor. Tasarım olarak gayet muntazam olan bu yöntem sayesinde, her DOM gerçekleştirmesi kendi nesnelerini kendileri kodlayarak, ara nesnelerden geçmeden arayüzleri kullanıcıya açık bırakabiliyor.

'Herşey bir düğümdür' tasarımının problemi elimizde olan Düğüm çesitlerinin fazlalığı, ve bu dügüm noktalarına erişirken birörnek olmayan yöntemler. Mesela insertData yöntemi CharacterData düğümlerinin değerini vermek için kullanılıyor, fakat Attr düğümleri için setValue yöntemi kullanılıyor! Değişik düğümler için değişik arayüzler kullanınca nesnesel tasarımda önemli olan birbiçimlik kalmıyor, herşeyi Düğüm yaparak kazandığımız muntazamlığı kaybediyoruz. Çünkü programcıların öğrenim eğrisi yükseliyor.

JDOM

JDOM kütüphanesi, bahsettiğimiz problemlere çözüm olarak yazıldı. DOM'un dilden-bağımsız problemlerini çözerek DOM'a daha bir Java-nevi erişim şekli kazandırmaya çalıştı. Ve çok tekrar edilen kullanım yöntemleri için kestirme metodlar ve işlemler kazandırmaya uğraştı. JDOM'a göre her düğüm değişik bir nesnedir (Belge, Eleman ve Değer gibi), bu sayede programcılar 'new' kelimesini kullanıp Düğüm yaratabiliyorlar, ikidebir dönüşüm (cast) yapmalarına gerek kalmıyor. JDOM altında Java List ve Iterator kullanılıyor.

Genel kodlama hâtaları

Kod fazlalığı

Ufak şeyleri yapmak için fazla kod gerekiyor. Mesela, bir Attr düğümünün değerini kontrol etmek için 16 satır kod yazıldığını gördük. Bu tip şeyleri yapmak için 3 satırlık kod yetmeli. Tabii DOM'un soyutluk seviyesinin çok altta olması, yanlış bilinen kodlama teknikleri de bu problemi arttırdı.

DOM'u Dolaşmak

Baktığımız proje kodu içinde en çok yapılan işlem, DOM'u dolaşmak, ya da aramak idi. Ekte örnek bir kod görüyoruz. Bu kodda, 'baslik' adli bir dügüm noktasını belgeninin 'ayar' bölümünde arıyoruz.

Bu kod da yaptığımız, kök eleman ayarDugumu'nden başlıyarak, bu kökün ilk çocuğunu almak, ve bütün çocuklara teker teker bakmak. Gördüğümüz gibi, bayağı çetrefilli ve karmaşık bir kullanım, hata yapmaya çok açık.

Örnek olarak, ikinci satır getFirstChild (ilk çocuğu al) işlemi ile 'ayar' dügümünü getiriyor. Bu satırdaki problemlere bakalım. Aslında ilk çocuk dügümü aradığımız dügüm olmayabilir. Hiç kontrol etmeden ilk çocuğu alırsak, etiketin ismina aldırmamış oluyorum ve belgenin yanlış yerine sapmış olabiliyoruz. Çok başımıza gelen bir hata, mesela kök düğümünden sonra (enter) tuşu ile boş karakter ya da satırbaşı karakteri konulmuş ise olması; Bu durumda kökün altındaki ilk çocuk TEXT_NODE dügümü olur, aradığımız dügüm değil. Bunun deneyini siz de yapabilirsiniz; ornek.xml adlı dosyayı açip, örnek ve ayar etiket değerleri arasına bir satırbaşı koyun. Kodu işletince hata verip durduğunu göreceksiniz. Doğru işlem için bütün çocukları teker teker bakıp 'metin' olmayan türden olanını bulmam lazım.

Listing 1 altındaki kodun bir diğer problemi daha var. Eğer dosya yapısı beklediğimiz gibi değilse, mesela kök düğümün hiç çocuğu yok ise, ayarDugumu nesnesi null (boş) değeri taşıyacaktır, bu yüzden 3. satır hata mesajı verir. Bu yüzden kodun düzdün işlemesi için, hem dosyayı doğru gezmek için, hem her çocuk değerine teker teker bakmalıyım, hem de her işlemi çağırdığımda dönüş değerini kontrol etmeliyim. Değişik türden giriş verisini doğru işleyebilecek kod, detaya dikkat ve azami kod miktarı gerektirir.

Ve sonuçta, Listing 1'de gösterilen kod yerine tek bir getElementsByTagName adlı işlem yeterli olabilirdi, eğer programcı böyle bir arayüzün mevcut olduğunu bilse idi.

Etiket Altındaki Değeri Almak

Baktığımız projelerde, DOM dolaşımından sonra en çok ihtiyaç duyulan şey bir etiket altındaki değeri alıp getirmek olduğunu farkettik. Mesela elimizde Deger gibi bir dosya olsun. 'herhangibiretiket' etiketine geldikten sonra (dolaşım yaparak) altındaki metni nasıl alırız?

DOM düzgün olsaydı, şunu yapmak yetebilirdi:
etiket.getData();

Tahmin edebileceğiniz üzere, yukarıdaki işlem istediğimizi yapmıyor. Esas metin, etiket düğümü altında 'çocuk dügüm' olarak durmakta. Yani işleyen kod şöyle olmalı:
etiket.getFirstChild().getData();

İkinci örnekte bile şunu görüyoruz: Aradığımız değer ilk çocuk düğümde olmayabilir. Birkaç alt düğümden sonra olabilir. Arada boşluk karakteri olabilir. Doğru çözüm için bütün çocuklara bakıp Node.TEXT_NODE tipinde olanları aramamız ve birleştirerek sonuç değerini oluşturmamız gerekiyor.

JDOM kütüphanesi bu sorunu tek bir işlem ile çözmüş: getText. DOM Seviye 3 kütüphanesi de bu soruna getTextContent işlemi ile cevap verecek. Alınacak ders: Üst düzey arayüzleri alt düzey arayüzlere tercih edin.

getElementsByTagName

DOM Seviye 2 kütüphanesi, verilen bir etiket isminden, o etiket altındaki bütün çocuk düğümleri geri getirebilen bir işlem sunmuş. Mesela,
NodeList isimler = birEleman.getElementsByTagName("isim");
birEleman altındaki 'isim' adlı düğümleri NodeList nesnesi (listesi) içinde getirecek. Bu yöntem daha önce tartıştığımız yöntemlerden muhakkak daha rahat.

Fakat bu yönteminde problemleri var.

Problem söyle: getElementsByTagName işlemi bütün dosyayı özyineli bir şekilde dolaşıyor, ve uyan bütün düğümleri getiriyor. Farzedin ki, bir XML belgesi içinde müşteri, şirket ve ürün bilgisi taşıyorsunuz. Bütün bu bilgi çeşitleri içinde 'isim' adlı bir etiket taşiyabilir. İsim kelimesi sık kullanılan bir kelimedir. Fakat bu yüzden ürün ismi ararken, müşteri ve şirket isimleri geri alabilirsiniz. İsim aramsını bir alt-ağaç altında işletebilirsiniz, fakat XML'in esnek yapısı dolayısı ile bulduğunuz alt-ağacın yapısının tahmin ettiğiniz yapı olduğunu bilebilmek gayet güç.

DOM'u Düzgün Kullanabilmenin Yolları

DOM'un tasarım eksikleri olduğuna göre, programlarımızı yanlışsız yazabilmek için bazı prensipleri aklımızda tutmamız yararlı olur.

Prensipler


* DOM'u belge içinde gezinti (traversal) için kullanma
* Mümkün olduğunca belge gezintisi veya direk dügüm erişimi için XPath kullanın.
* DOM'un üzerinde kurulmuş ve DOM'u kolaylaştıran daha yüksek seviyeli bir kütüphane kullanın.

DOM gezintisi, hem çok lazım olan, hem de problem yarabilen bir şey. O zaman, belgeyi DOM kullanmadan nasıl gezeriz?

XPath

XPath XML belgelerinin belli bölümlerini uyumlama ile bulabilen, eldeki adresi ile direk erişebilen, ve tüm dosyayı arayabilen bir dildir. W3C kurumunun tavsiye ettiği bir metoddur, ve çoğu programlama dili altında ve XML paketleri içinde mevcuttur. Büyük bir ihtimalle XML paketiniz XPath'i ya zaten ya da eklemeli bir şekilde destekliyor.

XPath, 'yol simgelemi' denilen bir yöntem kullanır; Dosya sistemleri ya da Internet adresleri de aynı simgelemi kullanıyor. Mesela XPath dilinde /x/y/z yolu, x kök düğüm altındaki y düğümünün altındaki z düğümünü arar. Bu yol kalıbına uyan bütün düğümler bulunup getirilecektir.

Daha çetrefilli uyumlarda mümkündür. Mesela /x/y/* gibi bir komut x adlı herhangi bir yerdeki dügümün altındaki bütün y düğümleri geri getirecektir. /x/y[@name='a'] gibi bir kullanım, a değerini taşıyan ve x dügümü altında olan bütün y dügümlerini geri getirir. Dikkat edilmesi gereken, XPath boşluk karakteri gibi olayları bizim için doğru şekilde tetkikleyip, geriye istediğimiz değerleri getirmesi. XPath'in A'dan Z'ye bütün konularını işlemek için yerimiz müsait değil, Kaynaklar bölümünde daha fazla XPath bilgisi alabileceğiniz yerler var. Tavsiyemiz, XPath öğrenmek için biraz vakit ayırırsanız, mükâfat olarak daha basit ve düzgün XML işlemleri elinize geçecek.

Kaynaklar


* DOM Tarifnâmesi
* Bu yazının örnek kodları. CLASSPATH'inize Xerces and Xalan eklemeyi unutmayın.
* Apache projesinden Xerces and Xalan DOM ve XPath paketlerini indirebilirsiniz.
* JDOM projesi
* XPath belgeleri

Bu yazı ilk önce IBM developerWorks sitesinde yayınlanmıştır.

Tomcat ve Güvenlik

Aşağı yukarı her bilgi işlem uygulaması "sistem idarecisi" diye adlandırılabilecek kullanıcılar için özel bölgeler (sayfalar) tanımlar. Mesela, kullanici_ekle.jsp gibi bir sayfaya her kullanıcının girebilmesi güvenlik açısından tehlikeli olur. Bu sayfaları kullanıcı/şifre isteyerek korumamız gerekmektedir.

Tomcat üzerinde bunu yapmanın en basit yolu Realm (alanlar) fikrini kullanmaktır. Hatta eğer, kullacıya göre değişken türden sayfalara ihtiyacınız yok ise, isim/şifre ile sadece sayfaya "erişimi" kontrol etmek için Tomcat üzerinde kod bile yazmanıza gerek yok.

Kullanıcı Tanımlaması

TOMCAT_DIZINI/conf/tomcat_users.xml dosyasını metinyazar (notepad, emacs, vs) ile açın. Bu dosyada şu gibi veriler görebilirsiniz.

<tomcat-users>
<user name="tomcat" password="tomcat" roles="tomcat" />
<user name="role1" password="tomcat" roles="role1" />
<user name="both" password="tomcat" roles="tomcat,role1" />
</tomcat-users>


Bu isimler ve şifreler Tomcat ile beraber paketten çıkıyor. İsterseniz silebilirsiniz bile.. (Ama örnek oluşturması açısından tutsanız iyi olur). Şimdi, ali adında bir kullanıcıya bakıcı rolü vermek istersek,

<user name="ali" password="sifre123" roles="bakici" />

.. gibi bir satır yeterli olacaktır. Kullanıcı tanımlaması bu kadar!

Sayfa/Servlet Korumak

Şimdi, bir servlet'e erişim kontrolü getirelim.

site_dizininiz/WEB-INF/web.xml dosyasını açın, ve KorunacakBirServletIsmi adlı Servlet'inize sadece bakici rolünde olanların erişebilmesi için, şu ekleri yapın.

<security-constraint>
<display-name>skBakicisi</display-name>
<web-resource-collection>
<web-resource-name>skAdmin</web-resource-name>
<url-pattern>/servlet/KorunacakBirServletIsmi</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>bakici</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>
</security-constraint>

<login-config>
<auth-method>BASIC</auth-method>
<realm-name>sk</realm-name>
</login-config>

<security-role>
<role-name>bakici</role-name>
</security-role>




Bu kadar! Artık tarayıcınız ile http://localhost:8080/servlet/KorunacakBirServletIsmi sayfasını ziyaret ettiğinizde, isim ve şifreniz sorulacaktır. Bunun bir resmi aşağıda görülebilir.