Skip to content

6. XML ve JAVA

Bu bölümde, Java ile XML belgelerinin kullanımını tanıtacağız. Bunu, bir önceki bölümde incelenen vergi uygulaması bağlamında yapacağız.

6.1. XML dosyaları ve XSL stil sayfaları

Vergi hesaplamaları simülasyonlarının sonucunu temsil edebilecek aşağıdaki XML simulations.xml dosyasını ele alalım:

<?xml version="1.0" encoding="ISO-8859-1"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

Bunu IE 6 ile görüntülediğimizde şu sonuç elde edilir:

Image

IE6, (dosyanın .xml uzantısı sayesinde) bir XML dosyasıyla karşı karşıya olduğunu algılar ve dosyayı kendine özgü bir şekilde sayfa düzenine yerleştirir. Netscape ile boş bir sayfa görüntülenir. Ancak kaynak koduna (Görünüm/Kaynak) bakıldığında, orijinal XML dosyası olduğu görülür:

Image

Netscape neden hiçbir şey göstermiyor? Çünkü XML dosyasını, görüntüleyebileceği HTML dosyasına nasıl dönüştüreceğini belirten bir stil sayfasına ihtiyacı var. Aslında IE 6'nın varsayılan bir stil sayfası varken, XML dosyasında böyle bir stil sayfası bulunmuyor; bu durum burada da geçerliydi.

XSL (eXtended StyleSheet Dili) adlı bir dil mevcuttur; bu dil, bir XML dosyasını herhangi bir metin dosyasına dönüştürmek için gerçekleştirilmesi gereken dönüşümleri tanımlamaya olanak tanır. XSL, çok sayıda komutun kullanımına izin verir ve programlama dillerine oldukça benzer. Burada ayrıntılı olarak ele almayacağız, çünkü bu onlarca sayfa gerektirecektir. Sadece iki XSL stil sayfası örneğini açıklayacağız. İlki, XML dosyasını simulations.xml koduna dönüştürecek olan stil sayfasıdır. Bu dosyayı, tarayıcıların onu HTML belgesine dönüştürmek için kullanabilecekleri stil sayfasını belirtecek şekilde değiştiriyoruz; tarayıcılar bu belgeyi görüntüleyebileceklerdir:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

XML komutu

<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>

komutu, simulations.xsl dosyasını text/xsl türünde bir stil sayfası (xml-stylesheet) olarak tanımlar; c.a.d ise XSL kodunu içeren bir metin dosyasıdır. Bu stil sayfası, tarayıcılar tarafından XML metnini HTML belgesine dönüştürmek için kullanılacaktır. XML simulations.xml dosyasını yüklediğimizde Netscape 7 ile elde edilen sonuç şöyledir:

Image

Belgenin kaynak koduna baktığımızda (Görünüm/Kaynak), görüntülenen HTML belgesi yerine, orijinal XML belgesini görüyoruz:

Image

Netscape, yukarıdaki XML belgesini görüntülenebilir HTML belgesine dönüştürmek için simulations.xsl stil sayfasını kullanmıştır. Şimdi bu stil sayfasının içeriğine bir göz atalım:

<?xml version="1.0" encoding="ISO-8859-1"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="html" indent="yes"/>

    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr/>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
                          <xsl:apply-templates select="/simulations/simulation"/>
                        </table>
         </center>
       </body>
     </html>
        </xsl:template>


    <xsl:template match="simulation">
          <tr>
            <td><xsl:value-of select="@marie"/></td>
            <td><xsl:value-of select="@enfants"/></td>
            <td><xsl:value-of select="@salaire"/></td>
            <td><xsl:value-of select="@impot"/></td>
        </tr>
       </xsl:template>
  </xsl:stylesheet>
  • XSL stil sayfası, bir XML dosyasıdır ve bu nedenle onun kurallarına uyar. Diğer şeylerin yanı sıra "düzgün biçimlendirilmiş" olmalıdır, yani açılan tüm etiketler kapatılmalıdır.
  • Dosya, her XSL stil sayfasında korunabilecek iki komutla başlar:
<?xml version="1.0" encoding="ISO-8859-1"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

encoding="ISO-8859-1" özniteliği, stil sayfasında aksanlı karakterlerin kullanılmasını sağlar.

  • <xsl:output method="html" indent="yes"/> etiketi, XSL yorumlayıcısına "girintili" HTML çıktısı üretilmesini istediğimizi belirtir.
  • <xsl:template match="eleman"> etiketi, <xsl:template ...> ile </xsl:template> arasında yer alan talimatların uygulanacağı XML belgesindeki elemanı tanımlamak için kullanılır.
    <xsl:template match="/">
................
        </xsl:template>

Yukarıdaki örnekte "/" öğesi, belgenin kökünü belirtir. Bu, XML belgesinin başlangıcına ulaşılır ulaşılmaz, iki etiket arasında yer alan XSL komutlarının yürütüleceği anlamına gelir.

  • XSL etiketleri dışında kalan her şey, olduğu gibi çıktı akışına eklenir. XSL etiketleri ise yürütülür. Bunlardan bazıları, çıktı akışına eklenen bir sonuç üretir. Aşağıdaki örneği inceleyelim:
    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr/>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
                          <xsl:apply-templates select="/simulations/simulation"/>
                        </table>
          </center>
        </body>
      </html>
        </xsl:template>

Analiz edilen XML belgesinin içeriği şöyledir:

<?xml version="1.0" encoding="ISO-8859-1"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

Analiz edilen XML belgesinin başından itibaren (match="/"), XSL yorumlayıcısı şu metni çıktı olarak üretecektir

<html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>

İlk metinde <hr/> olduğu, <hr> olmadığı dikkatinizi çekecektir. İlk metinde <hr> yazılmamıştı; bu, geçerli bir HTML etiketi olsa da, geçersiz bir XML etiketidir. Oysa burada "düzgün biçimlendirilmiş" olması gereken bir metinle karşı karşıyayız; yani her etiket kapatılmalıdır. Bu nedenle <hr/> yazılır ve <xsl:output text="html ..."> yazdığımız için XSL yorumlayıcısı <hr/> metnini <hr> şeklinde dönüştürecektir. Bu metnin ardından, XSL komutu tarafından üretilen metin gelecektir:

      <xsl:apply-templates select="/simulations/simulation"/>

Bu metnin ne olduğunu daha sonra göreceğiz. Son olarak yorumlayıcı şu metni ekleyecektir:

                    </table>
          </center>
        </body>
      </html>

<xsl:apply-templates select="/simulations/simulation"/> komutu, /simulations/simulation öğesinin "şablonunun" (template) çalıştırılmasını ister. Bu komut, XSL yorumlayıcısı, analiz edilen XML metninde bir <simulations>..</simulations> etiketi içinde bir <simulation>..</simulations> veya <simulation/> etiketi ile her karşılaştığında çalıştırılacaktır. <simulation> etiketiyle karşılaştığında, yorumlayıcı aşağıdaki şablondaki talimatları uygulayacaktır:

    <xsl:template match="simulation">
          <tr>
            <td><xsl:value-of select="@marie"/></td>
            <td><xsl:value-of select="@enfants"/></td>
            <td><xsl:value-of select="@salaire"/></td>
            <td><xsl:value-of select="@impot"/></td>
        </tr>
       </xsl:template>

Aşağıdaki XML satırlarını ele alalım:

<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>

<simulation ..> satırı, XSL <xsl:apply-templates select="/simulations/simulation>" komutunun şablonuna karşılık gelir. Dolayısıyla XSL yorumlayıcısı, bu şablona karşılık gelen komutları ona uygulamaya çalışacaktır. <xsl:template match="simulation"> şablonunu bulacak ve bunu çalıştıracaktır. Hatırlanacağı üzere, XSL komutu olmayan kısımlar XSL yorumlayıcısı tarafından olduğu gibi alınır ve XSL komutları ise yürütülmelerinin sonucu ile değiştirilir. Böylece XSL <xsl:value-of select="@champ"/> komutu, analiz edilen düğümün (burada bir <simulation> düğümü) "champ" özniteliğinin değeriyle değiştirilir. Önceki XML satırının analizi, aşağıdaki çıktıyı verecektir:

XSL
çıktı
<tr><td>
<tr><td>
<xsl:value-of select="@marie"/>
evet
</td><td>
</td><td>
<xsl:value-of select="@enfants"/>
2
</td><td>
</td><td>
<xsl:value-of select="@salaire"/>
200000
</td><td>
</td><td>
<xsl:value-of select="@impot"/>
22504
</td></tr>
</td></tr>

Toplamda, XML satırı

<simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>

satırı, HTML satırına dönüştürülecektir:

<tr><td>oui</td><td>2</td><td>200000</td><td>22504</td></tr>

Tüm bu açıklamalar biraz basit olsa da, okuyucuya şu anda aşağıdaki XML metninin:

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

aşağıdaki XSL simulations.xsl stil sayfası ile birlikte:

<?xml version="1.0" encoding="ISO-8859-1"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="html" indent="yes"/>

    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr/>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
                          <xsl:apply-templates select="/simulations/simulation"/>
                        </table>
         </center>
       </body>
     </html>
        </xsl:template>


    <xsl:template match="simulation">
          <tr>
            <td><xsl:value-of select="@marie"/></td>
            <td><xsl:value-of select="@enfants"/></td>
            <td><xsl:value-of select="@salaire"/></td>
            <td><xsl:value-of select="@impot"/></td>
        </tr>
       </xsl:template>
  </xsl:stylesheet>

aşağıdaki HTML metnini üretir:

<html>
<head>
<title>Simulations de calculs d'impôts</title>
</head>
<body>
<center>
<h3>Simulations de calculs d'impots</h3>
<hr>
<table border="1">
<th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
<tr>
<td>oui</td><td>2</td><td>200000</td><td>22504</td>
</tr>
<tr>
<td>non</td><td>2</td><td>200000</td><td>33388</td>
</tr>
</table>
</center>
</body>
</html>

XML ve simulations.xml dosyaları, simulations.xsl stil sayfasıyla birlikte yeni bir tarayıcıda (burada Netscape 7) açıldığında şu şekilde görüntülenir:

Image

6.2. Vergi uygulaması: sürüm 6

6.2.1. Vergi uygulamasına ait XML dosyaları ve XSL stil sayfaları

Vergi web uygulamasına geri dönelim ve müşterilere verilen yanıtın HTML formatında değil, XML formatında olmasını sağlayacak şekilde uygulamayı değiştirelim. Bu XML yanıtına, tarayıcıların onu görüntüleyebilmesi için bir XSL stil sayfası eşlik edecektir. Bir önceki paragrafta şunları sunmuştuk:

  • simulations.xml dosyası; bu, vergi hesaplama simülasyonları içeren bir XML yanıtının prototipidir
  • simulations.xsl dosyası; bu, XML yanıtına eşlik edecek olan XSL stil sayfası olacaktır

Ayrıca, hatalı yanıt durumunu da göz önünde bulundurmamız gerekiyor. Bu durumda XML yanıtının prototipi, aşağıdaki erreurs.xml dosyası olacaktır:

<?xml version="1.0" encoding="windows-1252"?>
<?xml-stylesheet type="text/xsl" href="erreurs.xsl"?>
<erreurs>
    <erreur>erreur 1</erreur>
  <erreur>erreur 2</erreur>
</erreurs>

Bu XML belgesini bir tarayıcıda görüntülemek için kullanılan erreurs.xsl stil sayfası aşağıdaki gibi olacaktır:

<?xml version="1.0" encoding="windows-1252"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="html" indent="yes"/>

    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
          </center>            
          <hr/>
          Les erreurs suivantes se sont produites :
          <ul>
                  <xsl:apply-templates select="/erreurs/erreur"/>
              </ul>            
        </body>
      </html>
        </xsl:template>

    <xsl:template match="erreur">
          <li><xsl:value-of select="."/></li>
        </xsl:template>
</xsl:stylesheet>    

Bu stil sayfası, henüz karşılaşılmamış bir XSL komutunu tanıtmaktadır: <xsl:value-of select="."/>. Bu komut, analiz edilen düğümün değerini çıktı olarak verir; burada bu, bir <erreur>texte</erreur> düğümüdür. Bu düğümün değeri, açılış ve kapanış etiketleri arasında yer alan metindir; bu örnekte texte.

erreurs.xml kodu, erreurs.xsl stil sayfası tarafından aşağıdaki HTML belgesine dönüştürülür:

<html>
<head>
<title>Simulations de calculs d'impots</title>
</head>
<body>
<center>
<h3>Simulations de calculs d'impots</h3>
</center>
<hr>
          Les erreurs suivantes se sont produites :
<ul>
<li>erreur 1</li>
<li>erreur 2</li>
</ul>
</body>
</html>

erreurs.xml dosyası ve stil sayfası, bir tarayıcıda şu şekilde görüntülenir:

Image

6.2.2. xmlsimulations servleti

index.html adlı bir dosya oluşturup bunu impots uygulamasının dizinine yerleştiriyoruz. Görüntülenen sayfa şu şekildedir:

Image

Bu HTML belgesi statik bir belgedir. Kodu şu şekildedir:

<html>
    <head>
      <title>impots</title>
    <script language="JavaScript" type="text/javascript">
        function effacer(){
           // formu sıfırla
        with(document.frmImpots){
            optMarie[0].checked=false;
          optMarie[1].checked=true;
          txtEnfants.value="";
          txtSalaire.value="";
          txtImpots.value="";
        }//with
      }//sil

      function calculer(){
           // parametreleri sunucuya göndermeden önce kontrol et
        with(document.frmImpots){
          //çocuk sayısı
          champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
          if(champs==null){
            // şablon doğrulanmadı
            alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
            nbEnfants.focus();
            return;
          }//eğer
          //maaş
          champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
          if(champs==null){
            // şablon doğrulanmadı
            alert("Le salaire n'a pas été donné ou est incorrect");
            salaire.focus();
            return;
          }//eğer
          // tamam - gönderiliyor
          submit();
        }//ile
      }//hesapla        
        </script>
  </head>

  <body background="/impots/images/standard.jpg">
      <center>
        Calcul d'impôts
        <hr>
      <form name="frmImpots" action="/impots/xmlsimulations" method="POST">
          <table>
            <tr>
              <td>Etes-vous marié(e)</td>
            <td>
                <input type="radio" name="optMarie" value="oui">oui
              <input type="radio" name="optMarie" value="non" checked>non
            </td>
          </tr>
          <tr>
              <td>Nombre d'enfants</td>
            <td><input type="text" size="3" name="txtEnfants" value=""></td>
          </tr>
          <tr>
              <td>Salaire annuel</td>
            <td><input type="text" size="10" name="txtSalaire" value=""></td>
          </tr>
          <tr></tr>
          <tr>
              <td><input type="button" value="Calculer" onclick="calculer()"></td>
            <td><input type="button" value="Effacer" onclick="effacer()"></td>
          </tr>
        </table>
      </form>
    </center>
 </body>
</html>

Form verilerinin URL /impots/xmlsimulations adresine gönderildiğine dikkat edilmelidir. Bu uygulama, impots uygulamasının web.xml dosyasında aşağıdaki şekilde yapılandırılmış bir Java servletidir:

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
...........
    <servlet>
      <servlet-name>xmlsimulations</servlet-name>
    <servlet-class>xmlsimulations</servlet-class>
    <init-param>
          <param-name>xslSimulations</param-name>
        <param-value>simulations.xsl</param-value>
    </init-param>
    <init-param>
          <param-name>xslErreurs</param-name>
        <param-value>erreurs.xsl</param-value>
    </init-param>

    <init-param>
          <param-name>DSNimpots</param-name>
        <param-value>mysql-dbimpots</param-value>
    </init-param>
    <init-param>
          <param-name>admimpots</param-name>
        <param-value>admimpots</param-value>
    </init-param>
    <init-param>
          <param-name>mdpimpots</param-name>
        <param-value>mdpimpots</param-value>
    </init-param>    
  </servlet>
........
  <servlet-mapping>
      <servlet-name>xmlsimulations</servlet-name>
    <url-pattern>/xmlsimulations</url-pattern>
  </servlet-mapping>
</web-app>
  • Servlet'in adı xmlsimulations'tır ve xmlsimulations.class sınıfını temel alır.
  • Bu servletin parametreleri arasında, vergi veritabanına erişim için gerekli olan DSNimpots, admimpots ve mdpimpots parametreleri bulunmaktadır. Ayrıca, iki parametre daha kabul eder:
    • xslSimulations, simülasyonları içeren XML yanıtına eşlik etmesi gereken stil dosyasının adıdır
    • xslErreurs, olası hataları içeren XML yanıtına eşlik etmesi gereken stil dosyasının adıdır
  • Bu servletin xmlsimulations takma adı vardır; bu sayede URL ve http://localhost:8080/impots/xmlsimulations aracılığıyla erişilebilir.

xmlsimulations servletinin iskeleti, daha önce incelediğimiz simulations servletininkine benzerdir. Temel fark, HTML yerine XML üretmesi gerektiğidir. Bu durum, önceki uygulamalarda kullanılan JSP dosyalarının kaldırılmasına yol açacaktır. Bu dosyaların temel işlevi, oluşturulan HTML kodunun, servletin Java kodunun içinde kaybolmasını önleyerek okunabilirliğini artırmaktı. Artık bu işlevin bir gereği kalmamıştır. Servlet, iki tür XML kodu oluşturmalıdır:

  • simülasyonlar için olan
  • hatalar için olan

Daha önce, bu iki durumda sağlanması gereken iki tür XML yanıtını ve bunlara eşlik etmesi gereken stil sayfalarını tanıtmış ve incelemiştik. Servlet kodu şöyledir:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.regex.*;
import java.util.*;

public class xmlsimulations extends HttpServlet{

    // örnek değişkenleri
    String msgErreur=null;
    String xslSimulations=null;
    String xslErreurs=null;
    String DSNimpots=null;
    String admimpots=null;
    String mdpimpots=null;
    impotsJDBC impots=null;

    //-------- GET
    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException{

        // müşteriye yazma akışını alıyoruz
        PrintWriter out=response.getWriter();

        // yanıt türünü belirtme
        response.setContentType("text/xml");

         // hata listesi
        ArrayList erreurs=new ArrayList();

         // başlatma işlemi başarılı oldu mu?
        if(msgErreur!=null){
             // işlem tamamlandı - hataları içeren yanıt sunucuya gönderiliyor
            erreurs.add(msgErreur);
            sendErreurs(out,xslErreurs,erreurs);
             // işlem tamamlandı
            return;
        }

         // oturumun önceki simülasyonları alınıyor
        HttpSession session=request.getSession();
        ArrayList simulations=(ArrayList)session.getAttribute("simulations");
        if(simulations==null) simulations=new ArrayList();

        // geçerli isteğin parametreleri alınıyor
        String optMarie=request.getParameter("optMarie");              // medeni durum
        String txtEnfants=request.getParameter("txtEnfants");       // çocuk sayısı
        String txtSalaire=request.getParameter("txtSalaire");       // yıllık maaş

         // beklenen tüm parametreler mevcut mu
        if(optMarie==null || txtEnfants==null || txtSalaire==null){
             // parametreler eksik
             // yanıt hatalı olarak gönderiliyor
            erreurs.add("Demande incomplète. Il manque des paramètres");
            sendErreurs(out,xslErreurs,erreurs);
             // işlem tamamlandı
            return;
        }

         // tüm parametreler mevcut - kontrol ediliyor
         // medeni durum
        if( ! optMarie.equals("oui") && ! optMarie.equals("non")){
             // hata
            erreurs.add("Etat marital incorrect");
        }
         // çocuk sayısı
        txtEnfants=txtEnfants.trim();
        if(! Pattern.matches("^\\d+$",txtEnfants)){
            // hata
            erreurs.add("Nombre d'enfants incorrect");
        }
         // maaş
        txtSalaire=txtSalaire.trim();
        if(! Pattern.matches("^\\d+$",txtSalaire)){
            // hata
            erreurs.add("Salaire incorrect");
        }

        if(erreurs.size()!=0){
            // hatalar varsa bildirilir
            sendErreurs(out,xslErreurs,erreurs);
        }else{
             // hata yok
            try{
                 // ödenecek vergi hesaplanabilir
                int nbEnfants=Integer.parseInt(txtEnfants);
                int salaire=Integer.parseInt(txtSalaire);
                String txtImpots=""+impots.calculer(optMarie.equals("oui"),nbEnfants,salaire);
                 // mevcut sonucu önceki simülasyonlara eklenir
                String[] simulation={optMarie.equals("oui") ? "oui" : "non",txtEnfants, txtSalaire, txtImpots};
                simulations.add(simulation);
                 // simülasyonlarla birlikte yanıt gönderilir
                sendSimulations(out,xslSimulations,simulations);
            }catch(Exception ex){}
        }//if-else
         // simülasyon listesini oturuma geri aktarılır
        session.setAttribute("simulations",simulations);
    }//GET

     //-------- POST
    public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException{
        doGet(request,response);
    }//POST

     //-------- INIT
    public void init(){
         // başlatma parametreleri alınır
        ServletConfig config=getServletConfig();
        xslSimulations=config.getInitParameter("xslSimulations");
        xslErreurs=config.getInitParameter("xslErreurs");
        DSNimpots=config.getInitParameter("DSNimpots");
        admimpots=config.getInitParameter("admimpots");
        mdpimpots=config.getInitParameter("mdpimpots");

         // parametreler uygun mu?
        if(xslSimulations==null || DSNimpots==null || admimpots==null || mdpimpots==null){
            msgErreur="Configuration incorrecte";
            return;
        }

         // impotsJDBC örneği oluşturuluyor
        try{
            impots=new impotsJDBC(DSNimpots,admimpots,mdpimpots);
        }catch(Exception ex){
            msgErreur=ex.getMessage();
        }
    }//başlatma

     //-------- sendErreurs
    private void sendErreurs(PrintWriter out,String xslErreurs,ArrayList erreurs){
        String réponse="<?xml version=\"1.0\" encoding=\"windows-1252\"?>"
                    + "<?xml-stylesheet type=\"text/xsl\" href=\""+xslErreurs+"\"?>\n"
              +"<erreurs>\n";
        for(int i=0;i<erreurs.size();i++){
            réponse+="<erreur>"+(String)erreurs.get(i)+"</erreur>\n";
        }//for
        réponse+="</erreurs>\n";
         // yanıt gönderiliyor
        out.println(réponse);
    }

     //-------- sendSimulations
    private void sendSimulations(PrintWriter out, String xslSimulations, ArrayList simulations){
        String réponse="<?xml version=\"1.0\" encoding=\"windows-1252\"?>"
                    + "<?xml-stylesheet type=\"text/xsl\" href=\""+xslSimulations+"\"?>\n"
              + "<simulations>\n";
        String[] simulation=null;
        for(int i=0;i<simulations.size();i++){
            // simülasyon no. i
            simulation=(String[])simulations.get(i);
            réponse+="<simulation "
                     +"marie=\""+(String)simulation[0]+"\" "
                     +"enfants=\""+(String)simulation[1]+"\" "
                     +"salaire=\""+(String)simulation[2]+"\" "
                 +"impot=\""+(String)simulation[3]+"\" />\n";
        }//için
        réponse+="</simulations>\n";
         // yanıt gönderiliyor
        out.println(réponse);
    }
}

Bu kodun, daha önce bildiğimiz versiyona kıyasla getirdiği başlıca yenilikleri ayrıntılı olarak inceleyelim:

  • init prosedürü, web.xml yapılandırma dosyasından yeni parametreler alır: yanıtla birlikte gönderilmesi gereken iki stil sayfasının (XSL) adları, xslSimulations ve xslErreurs değişkenlerine yerleştirilir. Bu iki stil sayfası, daha önce incelediğimiz simulations.xsl ve erreurs.xsl dosyalarıdır. Bunlar, impots uygulamasının dizinine yerleştirilir:
dos>dir E:\data\serge\Servlets\impots\*.xsl
27/08/2002  08:15                1 030 simulations.xsl
27/08/2002  09:23                  795 erreurs.xsl
  • GET prosedürü, öncelikle başlatma sırasında bir hata olup olmadığını kontrol eder. Eğer varsa, bu duruma uygun XML yanıtını oluşturan sendErreurs prosedürünü çağırır ve ardından sonlanır. Bu XML yanıtına, kullanılacak stil sayfasını belirten talimat eklenir.
  • Hata olmaması durumunda, GET prosedürü istemcinin isteğinin parametrelerini analiz eder. Herhangi bir hata bulursa, yine sendErreurs prosedürünü kullanarak bunu bildirir. Aksi takdirde, yeni simülasyonu hesaplar, bunu mevcut oturumda saklanan eski simülasyonlara ekler ve sendSimulations prosedürü aracılığıyla XML yanıtını göndererek işlemi sonlandırır. Bu son prosedür, sendErreurs prosedürüne benzer şekilde çalışır.
  • Servlet'in yanıtını text/xml türü olarak bildirdiği dikkat çekmektedir:
         // yanıtın türü belirtilir
        response.setContentType("text/xml");

İşte bazı yürütme örnekleri. Başlangıç formu şu şekilde doldurulur:

Image

MySQL veritabanı başlatılmadığından, servletin init prosedüründe impots nesnesinin oluşturulması mümkün olmamıştır. Servletin yanıtı şu şekildedir:

Image

Tarayıcı tarafından alınan kod (Görünüm/Kaynak) şöyledir:

Image

Şimdi, MySQL veritabanını başlattıktan sonra iki simülasyonu tekrar yaparsak, şu sonucu elde ederiz:

Image

Tarayıcı bu sefer şu kodu aldı:

Image

JSP dosyalarının kaldırılması nedeniyle yeni uygulamamızın eskisinden daha basit olduğu dikkatinizi çekecektir. Bu sayfaların üstlendiği işlerin bir kısmı, XSL stil sayfalarına aktarılmıştır. Yeni görev dağılımımızın avantajı, servletin yanıtlarının XML formatı bir kez belirlendikten sonra, stil sayfalarının geliştirilmesinin servletin geliştirilmesinden bağımsız hale gelmesidir.

6.3. Java'da bir XML belgesinin analizi

Vergi uygulamamızın 7 ve 8 sürümleri, önceki xmlsimulations servletinin programlanmış istemcileri olacaktır. Bu istemciler, XML kodunu alacak ve ilgilendikleri bilgileri çıkarmak için bu kodu analiz etmek zorunda kalacaklardır. Burada farklı sürümlerimize bir ara verip, Java'da bir XML belgesinin nasıl analiz edilebileceğini öğreneceğiz. Bunu, JBuilder 7 ile birlikte gelen ve MySaxParser adlı bir örnek üzerinden yapacağız. Programın adı şu şekildedir:

dos>java MySaxParser
Usage: java MySaxParser [URI]

MySaxParser uygulaması tek bir parametre kabul eder: analiz edilecek XML belgesinin URI (Tekdüzen Kaynak Tanımlayıcı) değeri. Örneğimizde, bu URI, MySaxParser uygulamasının dizinine yerleştirilmiş bir XML dosyasının adı olacaktır. İki çalışma örneğini ele alalım. İlk örnekte, analiz edilen XML dosyası, erreurs.xml dosyasıdır:

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="erreurs.xsl"?>
<erreurs>
    <erreur>erreur 1</erreur>
  <erreur>erreur 2</erreur>
</erreurs>

Analiz şu sonuçları vermektedir:

dos> java MySaxParser erreurs.xml
Début du document
Début élément <erreurs>
Début élément <erreur>
[erreur 1]
Fin élément <erreur>
Début élément <erreur>
[erreur 2]
Fin élément <erreur>
Fin élément <erreurs>
Fin du document

MySaxParser uygulamasının ne işe yaradığını henüz belirtmemiştik, ancak burada, analiz edilen XML belgesinin yapısını görüntülediğini görüyoruz. İkinci örnekte XML ve simulations.xml dosyaları analiz ediliyor:

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

Analiz şu sonuçları vermektedir:

dos>java MySaxParser simulations.xml
Début du document
Début élément <simulations>
Début élément <simulation>
marie = oui
enfants = 2
salaire = 200000
impot = 22504
Fin élément <simulation>
Début élément <simulation>
marie = non
enfants = 2
salaire = 200000
impot = 33388
Fin élément <simulation>
Fin élément <simulations>
Fin du document

MySaxParser sınıfı, web sunucusunun gönderebileceği hataları veya simülasyonları tespit edebildiğinden, vergi uygulamamızda ihtiyacımız olan her şeyi içeriyor. Koduna bir göz atalım:

import java.io.IOException;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;
import java.util.regex.*;

// sınıf
public class MySaxParser extends DefaultHandler {

     // ağacın bir elemanının değeri XML
    private StringBuffer valeur=new StringBuffer();
    // bir öğenin değerini göz ardı etmek istendiğinde kullanılan düzenli ifade
     // önündeki veya arkasındaki "boşlukları" göz ardı etmek istendiğinde
    private static Pattern ptnValeur=null;
    private static Matcher résultats=null;

    // -------- ana
    public static void main(String[] argv) {
        // parametre sayısının doğrulanması
        if (argv.length != 1) {
            System.out.println("Usage: java MySaxParser [URI]");
            System.exit(0);
        }
         // analiz edilecek XML dosyasından URI alınır
        String uri = argv[0];
        try {
            // XML ayrıştırıcısı (parser) oluşturuluyor
            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
             // parser'a, yöntemleri uygulayacak nesneyi belirtir
             // startDocument, endDocument, startElement, endElement karakterlerini içeren nesneyi aktarmalıyız
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // bir öğenin değer şablonu başlatılır
            ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
             // ayıklayıcıya analiz edilecek XML belgesini belirtilir
            parser.parse(uri);
        }
        catch(Exception ex) {
             // hata
            System.err.println("Erreur : " + ex);
             // izleme
            ex.printStackTrace();
        }
    }//ana işlev


     // -------- startDocument
    public void startDocument() throws SAXException {
         // ayrıştırıcı belgenin başına geldiğinde çağrılan yordam
        System.out.println("Début du document");
    }//startDocument

     // -------- endDocument
    public void endDocument() throws SAXException {
         // parser belgenin sonuna ulaştığında çağrılan prosedür

        System.out.println("Fin du document");
    }//endDocument

     // -------- startElement
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
         // Ayrıştırıcı, bir etiket başlangıcına ulaştığında çağrılan prosedür
         // uri: Analiz edilen belgenin URI'i mi?
         // localName: analiz edilen öğenin adı
         // qName: aynı, ancak varsa bir ad alanı ile "nitelendirilmiş" hali
         // attributes: öğenin özniteliklerinin listesi

         // izleme
        System.out.println("Début élément <"+localName+">");
         // Öğenin öznitelikleri var mı?
        for (int i = 0; i < attributes.getLength(); i++) {
            System.out.println(attributes.getLocalName(i) + " = " + attributes.getValue(i));
        }//for
    }//startElement

    // -------- karakterler
    public void characters(char[] ch, int start, int length) throws SAXException {
        // parser, metinle karşılaştığında tekrar tekrar çağırdığı yordam
         // <etiket>metin</etiket> etiketleri arasında
         // metin, start karakterinden başlayarak length karakter uzunluğunda ch içinde yer alır

         // metin, değer tamponuna eklenir
        valeur.append(ch, start, length);
    }//karakter

        // -------- endElement
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
         // parser, bir etiket sonu ile karşılaştığında bu prosedürü çağırır
         // URI: Analiz edilen belgenin URI'i mi?
         // localName: analiz edilen öğenin adı
         // qName: aynı, ancak varsa bir ad alanı ile "nitelendirilmiş"

         // öğenin değeri görüntülenir
        String strValeur=valeur.toString();
        if (ptnValeur==null) System.out.println("null");
        résultats=ptnValeur.matcher(strValeur);
        if (résultats.find() && ! résultats.group(1).equals("")){
            System.out.println("["+résultats.group(1)+"]");
        }//if
         // öğenin değeri boş bırakılır
        valeur.setLength(0);

         // ardından
        System.out.println("Fin élément <"+localName+">");
    }//endElement

}//sınıf

Öncelikle, XML belgelerinin analizinde sıkça karşımıza çıkan bir kısaltmayı tanımlayalım: SAX, yani Simple API for XML. Bu, XML belgeleriyle çalışmayı kolaylaştıran bir Java sınıfları kümesidir. API'in iki sürümü vardır: SAX1 ve SAX2. Yukarıdaki uygulama, API ve SAX2'i kullanmaktadır.

Uygulama bir dizi paketi içe aktarıyor:

import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;

İlk ikisi JDK 1.4 sürümüyle birlikte gelir, üçüncüsü ise gelmez. xerces.jar paketi, Apache web sunucusunun web sitesinde mevcuttur. Bu paket, JBuilder 7 sürümüyle birlikte gelir, ancak aynı zamanda Tomcat 4.x sürümüyle de uyumludur:

Image

Dolayısıyla, önceki uygulamayı JBuilder 7 dışında derlemek istiyorsak ve JDK 1.4 ile Tomcat 4.x'e sahipsek, şu komutu yazabiliriz:

dos>javac -classpath ".;E:\Program Files\Apache Tomcat 4.0\common\lib\xerces.jar" MySaxParser.java

Çalıştırma sırasında da aynı şekilde yapacağız:

dos>java -classpath ".;E:\Program Files\Apache Tomcat 4.0\common\lib\xerces.jar" MySaxParser simulations.xml

MySaxParser sınıfı, DefaultHandler sınıfından türetilmiştir. Buna daha sonra tekrar döneceğiz. Şimdi main prosedürünün kodunu inceleyelim:

         // analiz edilecek XML dosyasından URI alınır
        String uri = argv[0];
        try {
            // XML analizcisi (parser) oluşturulur
            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
             // parser'a, yöntemleri uygulayacak nesneyi belirtir
             // startDocument, endDocument, startElement, endElement karakterlerini içeren nesneyi aktarmalıyız
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // bir öğenin değer şablonu başlatılır
            ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
             // ayıklayıcıya analiz edilecek XML belgesini belirtilir
            parser.parse(uri);
        }
        catch(Exception ex) {
             // hata
            System.err.println("Erreur : " + ex);
             // izleme
            ex.printStackTrace();
        }

Bir XML belgesini analiz etmek için, uygulamamızın "parser" adı verilen bir XML kod analizcisine ihtiyacı vardır.

            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");

Kullanılan XML ayrıştırıcısı, xerces.jar paketi tarafından sağlanan ayrıştırıcıdır. Alınan nesne, XMLReader türündedir. XMLReader bir arayüzdür ve burada bu arayüzün iki yöntemini kullanıyoruz:

void setContentHandler(ContentHandler handler)
, ayrıştırıcıya, ContentHandler türündeki nesneyi belirtir; bu nesne, XML belgesinin ayrıştırılması sırasında ayrıştırıcının oluşturacağı olayları işleyecektir
void parse(InputSource input)
parametre olarak geçirilen XML belgesinin analizini başlatır

Ayrıştırıcı, XML belgesini ayrıştırırken şu tür olaylar üretecektir: "belgenin başlangıcına rastladım, bir etiket başlangıcına rastladım, bir etiket özniteliğine rastladım, bir etiket içeriğine rastladım, bir etiket sonuna rastladım, belgenin sonuna rastladım, ...". Bu olayları kendisine verilen ContentHandler nesnesine iletir. ContentHandler, XML ayrıştırıcısının üretebileceği tüm olayları işlemek için uygulanması gereken yöntemleri tanımlayan bir arayüzdür. DefaultHandler, bu yöntemlerin varsayılan uygulamasını yapan bir sınıftır. DefaultHandler'te uygulanan yöntemler hiçbir işlem yapmaz, ancak mevcuttur. Aşağıdaki komut aracılığıyla ayrıştırıcıya, üreteceği olayları hangi nesnenin işleyeceğini belirtmemiz gerektiğinde

void setContentHandler(ContentHandler handler)

komutu aracılığıyla oluşturacağı olayları hangi nesnenin işleyeceğini belirtmemiz gerektiğinde, DefaultHandler türünde bir nesneyi parametre olarak geçirmek pratiktir. Eğer burada durursak, ayrıştırıcının hiçbir olayı işlenmez, ancak programımız sözdizimsel olarak doğru olur. Uygulamada ise, ayrıştırıcıya DefaultHandler sınıfından türetilmiş bir nesne parametre olarak verilir; bu nesnede, yalnızca ilgilendiğimiz olayları işleyen yöntemler yeniden tanımlanmıştır. Burada yapılan da budur:

             // ayıklayıcıya, yöntemleri uygulayacak nesneyi belirtilir
             // startDocument, endDocument, startElement, endElement karakterlerini içeren nesneyi belirtin
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // parser'a analiz edilecek XML belgesini belirtir
            parser.parse(uri);

Parsere, bizim sınıfımız olan ve yukarıda şu bildirimle tanımlanan mySaxParser sınıfının bir örneğini aktarıyoruz

public class MySaxParser extends DefaultHandler {

ile tanımlanmış olan sınıfımızın bir örneğini parser'a aktarıyoruz ve URI'i parametre olarak aktardığımız belgenin analizini başlatıyoruz. Bu noktadan itibaren, XML belgesinin analizi başlar. Ayrıştırıcı olaylar üretir ve her bir olay için bu olayları işlemekle görevli nesnenin (burada MySaxParser nesnesi) belirli bir yöntemini çağırır. Bu nesne beş özel olayı işler; diğerleri ise göz ardı edilir:

ayrıştırıcı tarafından gönderilen olay
işleme yöntemi
début du document    
void startDocument()
fin du document
void endDocument()
début d'un élément :<balise>
public void startElement(String uri, String localName, String qName, Attributes attributes)
uri : ?
localName: analiz edilen öğenin adı. Karşılaşılan öğe <simulations> ise, localName="simulations" olur.
qName: Analiz edilen öğenin ad alanı ile nitelendirilmiş adı. Bir XML belgesi, örneğin XX gibi bir ad alanı tanımlayabilir. Bu durumda, önceki etiketin nitelikli adı XX:simulations olur.
attributes: etiketin öznitelik listesi
valeur d'un élément :
<balise>valeur</balise>
public void characters(char[] ch, int start, int length)
ch: karakter dizisi
start: ch dizisinde kullanılacak ilk karakterin indeksi
length: ch dizisinden alınacak karakter sayısı
characters yöntemi tekrar tekrar çağrılabilir. Bir elemanın değerini oluşturmak için bir tampon kullanılır; bu tampon:
  • elemanın başlangıcında boşaltılır
  • characters yönteminin her yeni çağrısında doldurulur
  • öğenin sonunda sonlandırılır
fin d'un élément : </balise> ou <balise .../>
void endElement(String uri, String localName, String qName)
parametreler, startElement yöntemindeki parametrelerle aynıdır.

startElement yöntemi, Attributes türündeki attributes parametresi sayesinde öğenin özniteliklerini almayı sağlar:

  • Özelliklerin sayısı attributes.getLength() içinde mevcuttur
  • i özniteliğinin adı attributes.getLocalName(i)'de mevcuttur
  • i özniteliğinin değeri attributes.getValue(i) içinde mevcuttur
  • localName adlı özniteliğin değeri, attributes.getValue(localName) içinde bulunur

Bu açıklama yapıldıktan sonra, önceki program ve yürütme örnekleri kendiliğinden anlaşılır hale gelir. Öğelerin değerlerini almak için bir düzenli ifade kullanılmıştır; böylece XML gibi bir metin:

<erreur>
    erreur 1
</erreur>

<erreur> etiketine atanan değer, öncesinde ve/veya sonrasında bulunabilecek boşluklar ve satır sonlarından arındırılmış "hata 1" metni olacaktır.

6.4. Vergi uygulaması: sürüm 7

Artık, XML çıktısı veren vergi hizmetimiz için programlanmış istemciler yazmak üzere tüm unsurlara sahibiz. İstemciyi oluşturmak için uygulamamızın 4. sürümünü kullanıyoruz ve sunucu tarafında ise 6. sürümü koruyoruz. Bu istemci-sunucu uygulamasında:

  • vergi hesaplama simülasyon hizmeti, xmlsimulations servlet'i tarafından gerçekleştirilir. Dolayısıyla, sunucunun yanıtı, sürüm 6'da gördüğümüz gibi XML biçimindedir.
  • İstemci artık bir tarayıcı değil, bağımsız bir Java istemcisidir. Grafik arayüzü, sürüm 4'tekiyle aynıdır.

İşte birkaç çalışma örneği. Öncelikle bir hata durumu: istemci, xmlsimulations servlet’ine sorgu gönderir, ancak SGBD ve MySQL başlatılmadığı için servlet düzgün bir şekilde başlatılamamıştır:

Image

MySQL'i başlatıp birkaç simülasyon yapıyoruz:

Image

Bu yeni sürümün istemcisi, sunucunun yanıtını işleme biçiminden başka, sürüm 4'ün istemcisinden hiçbir fark göstermiyor. Başka hiçbir şey değişmiyor. Sürüm 4'te istemci, HTML kodunu alıyordu ve bu koddan düzenli ifadeler kullanarak ilgilendiği bilgileri çıkarıyordu. Burada istemci, XML kodunu alır ve bu koddan, bir XML ayrıştırıcısı yardımıyla ilgilendiği bilgileri çıkarır.

Değişikliklerin esas olarak burada yapıldığı için, istemcimizin 4. sürümündeki Hesapla menüsüyle ilgili prosedürün ana hatlarını hatırlayalım:

  void mnuCalculer_actionPerformed(ActionEvent e) {
....
    try{
             // vergi hesaplanır
            calculerImpots(urlImpots,rdOui.isSelected(),nbEnfants.intValue(),salaire);
    }catch (Exception ex){
             // hata görüntülenir
            JOptionPane.showMessageDialog(this,"L'erreur suivante s'est produite : " + ex.getMessage(),"Erreur",JOptionPane.ERROR_MESSAGE);
        }
....
    }//mnuCalculer_actionPerformed

    public void calculerImpots(URL urlImpots,boolean marié, int nbEnfants, int salaire)
          throws Exception{
         // vergi hesaplaması
         // urlImpots: Vergi dairesinden gelen URL
         // evli: evliyse true, değilse false
         // nbEnfants: çocuk sayısı
         // maaş: yıllık maaş

         // urlImpots'ten vergi dairesi sunucusuna bağlanmak için gerekli bilgiler alınır
....
        try{
             // sunucuya bağlanılır
....

             // müşterinin giriş-çıkış akışları oluşturulur TCP
....

             // URL talep ediliyor - başlıkların gönderilmesi HTTP
....

             // yanıtın ilk satırı okunur
....
             // Yanıt, başlıkların sonuna kadar okunur ve olası bir çerez aranır
            while((réponse=IN.readLine())!=null){
....            }//while

            // HTTP başlıkları bittiğinde - HTML koduna geçilir
             // simülasyonları almak için
            ArrayList listeSimulations=getSimulations(IN,OUT,simulations);
            simulations.clear();
            for (int i=0;i<listeSimulations.size();i++){
                simulations.addElement(listeSimulations.get(i));
            }

            // bitti
....
    }//calculerImpots

    private ArrayList getSimulations(BufferedReader IN, PrintWriter OUT, DefaultListModel simulations) throws Exception{
....
    }

Bu kodun tamamı yeni sürümde de geçerlidir. Yalnızca sunucudan gelen HTML yanıtının işlenmesi (yukarıda çerçeveli kısım) ve görüntülenmesi, sunucudan gelen XML yanıtının işlenmesi ve görüntülenmesi ile değiştirilmelidir:

             // başlıklar için işlem tamamlandı HTTP - XML koduna geçiyoruz
             // simülasyonları veya hataları almak için
            ImpotsSaxParser parseur=new ImpotsSaxParser(IN);
            ArrayList listeErreurs=parseur.getErreurs();
            ArrayList listeSimulations=parseur.getSimulations();
             // sunucu bağlantısını kapatma
            client.close();

             // görüntüleme listesini temizleme
            simulations.clear();
             // hatalar
            if(listeErreurs.size()!=0){
                 // tüm hataları birleştirme
                String msgErreur="Le serveur a signalé les erreurs suivantes :\n";
                for(int i=0;i<listeErreurs.size();i++){
                    msgErreur+=" - "+(String)listeErreurs.get(i);
                }
                 // hataların görüntülenmesi
                throw new Exception(msgErreur);
            }//if

            // simülasyonlar
            for (int i=0;i<listeSimulations.size();i++){
                simulations.addElement(listeSimulations.get(i));
            }
            return;

Yukarıdaki kod parçası ne işe yarar?

  • Bir XML ayrıştırıcısı oluşturur ve ona, sunucu tarafından gönderilen XML kodunu içeren IN akışını iletir. Bu akışta HTTP başlıkları da bulunuyordu, ancak bunlar daha önce okunup işlenmişti. Dolayısıyla yanıtın geriye kalan kısmı sadece XML kısmıdır. Ayrıştırıcı iki karakter dizisi listesi üretir: hata varsa hata listesi, yoksa simülasyon listesi. Bu iki liste birbirini dışlar.
  • Hata listesi boş değilse, listede yer alan mesajlar tek bir hata mesajı olarak birleştirilir ve bu mesaj parametre olarak kullanılarak bir istisna atılır. Bu istisna, calculerImpots'i çağıran mnuCalculer_actionPeformed prosedüründe görüntülenir.
  • Simülasyon listesi boş değilse, bu liste grafik arayüzün jList bileşeninde görüntülenir.

Şimdi, sunucunun XML yanıtını ayrıştıran ayrıştırıcıyı inceleyelim; bu ayrıştırıcı, daha önce Java'da bir XML belgesinin nasıl analiz edileceğine dair yaptığımız çalışmadan doğrudan türetilmiştir:

import java.io.IOException;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;
import java.util.regex.*;
import java.io.*;
import java.util.*;
import javax.swing.*;

// sınıf
public class ImpotsSaxParser extends DefaultHandler {

    // ağacın bir elemanının değeri XML
    private StringBuffer valeur=new StringBuffer();
    // bir öğenin değerini göz ardı etmek istediğimizde kullanılacak düzenli ifade
     // önündeki veya arkasındaki "boşlukları" göz ardı etmek istendiğinde
    private Pattern ptnValeur=null;
    private Matcher résultats=null;
     // XML öğe listeleri
    private ArrayList listeSimulations=new ArrayList();
    private ArrayList listeErreurs=new ArrayList();
     // XML öğeleri
    private ArrayList éléments=new ArrayList();
    String élément="";

     // -------- üretici
    public ImpotsSaxParser(BufferedReader IN) throws Exception{
        // XML analizörünün (parser) oluşturulması
        XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
         // parser'a yöntemleri uygulayacak nesneyi belirtir
         // startDocument, endDocument, startElement, endElement karakterlerini içeren nesneyi aktarmalıyız
        parser.setContentHandler(this);
        // bir öğenin değer şablonu başlatılır
        ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
         // başlangıçta geçerli bir XML öğesi yok
        éléments.add("");
         // belge analiz ediliyor
        parser.parse(new InputSource(IN));
    }//oluşturucu

     // -------- startElement
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
         // parser, bir etiket başlangıcıyla karşılaştığında çağırdığı prosedür
         // URI: Analiz edilen belgenin URI adresi mi?
         // localName: analiz edilen öğenin adı
         // qName: aynı, ancak varsa bir ad alanı ile "nitelendirilmiş" hali
         // attributes: öğenin özniteliklerinin listesi

         // öğenin adı belirtilir
        élément=localName.toLowerCase();
        éléments.add(élément);
         // Öğenin öznitelikleri var ?
        if(élément.equals("simulation") && attributes.getLength()==4){
            // bu bir simülasyondur - öznitelikler alınır
            String simulation=attributes.getValue("marie")+","+
                                         attributes.getValue("enfants")+","+
                                         attributes.getValue("salaire")+","+
                                         attributes.getValue("impot");
             // simülasyonu simülasyonlar listesine eklenir
            listeSimulations.add(simulation);
        }//if
    }//startElement

    // -------- karakterler
    public void characters(char[] ch, int start, int length) throws SAXException {
        // parser, metinle karşılaştığında tekrar tekrar çağırdığı yordam
         // iki <etiket>metin</etiket> etiketi arasında
         // metin, start karakterinden başlayarak length karakter uzunluğunda ch içinde yer alır

         // metin, hata öğesi ise s değerindeki tampona eklenir
        if (élément.equals("erreur"))
           valeur.append(ch, start, length);
    }//karakter

        // -------- endElement
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
         // parser, bir etiket sonu ile karşılaştığında çağırdığı prosedür
         // uri: Analiz edilen belgenin URI'i mi?
         // localName: analiz edilen öğenin adı
         // qName: aynı, ancak varsa bir ad alanı ile "nitelendirilmiş"

         // hata durumu
        if(élément.equals("erreur")){
             // hata öğesinin değeri alınır
            String strValeur=valeur.toString();
             // gereksiz "boşluklar" temizlenir ve
             // boş değilse hata listesine kaydedilir
            résultats=ptnValeur.matcher(strValeur);
            if (résultats.find() && ! résultats.group(1).equals("")){
                listeErreurs.add(résultats.group(1));
            }//if
        }
         // elemanın değerini boş bırakılır
        valeur.setLength(0);
         // öğenin adı sıfırlanır
        éléments.remove(éléments.size()-1);
        élément=(String)éléments.get(éléments.size()-1);
    }//endElement

     // --------- getErreurs
    public ArrayList getErreurs(){
        return listeErreurs;
    }

     // --------- getSimulations
    public ArrayList getSimulations(){
        return listeSimulations;
    }

}//sınıf
  • Üretici, analiz edilmesi gereken XML IN akışını alır ve bu analizi derhal gerçekleştirir. Analiz tamamlandıktan sonra, nesne oluşturulur ve hata listesi (ArrayList), hata listesi (listeErreurs) ile simülasyon listesi (listeSimulations) oluşturulur. Nesneyi oluşturan prosedürün geriye kalan tek işi, getErreurs ve getSimulations yöntemlerini kullanarak bu iki listeyi almaktır.
  • Burada, XML ayrıştırıcısı tarafından üretilen yalnızca üç olay bizi ilgilendiriyor:
    • XML öğesinin başlangıcı; bu olay, startElement prosedürü tarafından işlenecektir. Bu yordam, <simulation marie=".." enfants=".." salaire=".." impot=".."> ve <erreur>...</erreur> etiketlerini işleyecektir.
    • XML öğesinin değeri; bu olay, characters yordamı tarafından işlenecektir.
    • XML öğesinin sonu; bu olay, endElement prosedürü tarafından işlenecektir.
  • startElement yordamında, <simulation marie=".." enfants=".." salaire=".." impot=".."> öğesiyle karşılaşıldığında, dört öznitelik attributes.getValue("öznitelik adı") ile alınır. Her durumda, öğenin adı "élément" adlı bir değişkene kaydedilir ve öğe listesine (ArrayList) eklenir: élé1, élé2, ..., élén. Bu liste, son elemanı analiz edilen XML elemanı olan bir yığın olarak yönetilir. "Eleman sonu" olayı meydana geldiğinde, listenin son elemanı kaldırılır ve yeni geçerli eleman güncellenir. Bu işlem, endElement prosedüründe gerçekleştirilir.
  • characters prosedürü, önceki bir örnekte incelenen prosedürle aynıdır. Sadece geçerli öğenin gerçekten <erreur> öğesi olup olmadığını kontrol etmeye özen gösteriyoruz; bu önlem burada normalde gereksizdir. Bu tür bir önlem, startElement prosedüründe de, söz konusu öğenin <simulation> öğesi olup olmadığını doğrulamak için alınmıştı.

6.5. Sonuç

XML yanıtı sayesinde, impots uygulaması hem tasarımcısı hem de istemci uygulamalarının tasarımcıları için daha kolay yönetilebilir hale gelmiştir.

  • Artık sunucu uygulamasının tasarımı iki tür kişiye emanet edilebilir: servletin Java geliştiricisi ve tarayıcılarda sunucu yanıtının görünümünü yönetecek grafik tasarımcı. Grafik tasarımcının, buna eşlik edecek stil sayfalarını oluşturmak için sunucunun XML yanıtının yapısını bilmesi yeterlidir. Bu stil sayfalarının, Java servletinden ayrı ve bağımsız XSL dosyaları olarak düzenlendiğini hatırlatalım. Dolayısıyla grafik tasarımcı, Java geliştiricisinden bağımsız olarak çalışabilir.
  • Müşteri uygulamalarının tasarımcıları da sadece sunucunun XML yanıtının yapısını bilmek zorundadır. Grafik tasarımcının stil sayfalarında yapabileceği değişiklikler, her zaman aynı kalan bu XML yanıtı üzerinde hiçbir etki yaratmaz. Bu çok büyük bir avantajdır.
  • Geliştirici, her şeyi bozmadan Java servletini nasıl geliştirebilir? Öncelikle, XML yanıtı değişmediği sürece, servletini istediği gibi düzenleyebilir. Ayrıca, istemcilerin beklediği <hata> ve <simülasyon> öğelerini koruduğu sürece XML yanıtını da geliştirebilir. Böylece bu yanıta yeni etiketler ekleyebilir. Grafik tasarımcısı bunları stil sayfalarında dikkate alacak ve tarayıcılar yanıtın yeni sürümlerini alabilecektir. Programlanmış istemciler ise eski modelle çalışmaya devam edecek; yeni etiketler basitçe göz ardı edilecektir. Bunun mümkün olabilmesi için, sunucunun yanıtının XML analizinde aranan etiketlerin doğru bir şekilde tanımlanmış olması gerekir. Vergi uygulamasındaki XML istemcimizde de bu şekilde yapılmıştır; burada prosedürlerde, özellikle <erreur> ve <simulation> etiketlerinin işleneceği belirtilmiştir. Dolayısıyla diğer etiketler göz ardı edilir.