Skip to content

5. IMPOTS uygulaması

5.1. Giriş

Burada, aynı yazarın Java ders notlarında birçok kez kullanılan IMPOTS uygulamasını ele alıyoruz. Sorunu hatırlayalım. Bir vergi mükellefinin vergisini hesaplamaya yarayan bir uygulama yazmak söz konusudur. Sadece maaşını beyan edecek bir vergi mükellefinin basitleştirilmiş durumunu ele alıyoruz:

  • evli değilse, çalışanın pay sayısı nbParts=nbEnfants/2 +1 olarak hesaplanır, evliyse nbEnfants/2+2 olarak hesaplanır; burada nbEnfants, çocuk sayısıdır.
  • En az üç çocuğu varsa, yarım pay daha ekler
  • vergilendirilebilir geliri R=0,72*S olarak hesaplanır; burada S, yıllık maaşıdır
  • aile katsayısı QF = R / nbParts olarak hesaplanır
  • Vergisi I hesaplanır. Aşağıdaki tabloyu ele alalım:
12620,0
0
0
13190
0,05
631
15640
0,1
1290,5
24.740
0,15
2072,5
31810
0,2
3309,5
39.970
0,25
4900
48360
0,3
6898,5
55790
0,35
9316,5
92970
0,4
12106
127.860
0,45
16754,5
151250
0,50
23147,5
172040
0,55
30710
195000
0,60
39312
0
0,65
49062

Her satırda 3 alan vardır. I vergisini hesaplamak için, QF<=alan1 olan ilk satır aranır. Örneğin, QF=23000 ise şu satır bulunur:

    24740        0.15        2072.5

bulunur. I vergisi bu durumda 0,15*R - 2072,5*nbParts'e eşittir. Eğer QF, QF<=alan1 ilişkisi hiçbir zaman geçerli olmayacak şekilde belirlenmişse, o zaman son satırdaki katsayılar kullanılır. Burada:

    0                0.65        49062

bu da I=0,65*R - 49062*nbParts vergi tutarını verir.

Farklı vergi dilimlerini tanımlayan veriler bir ODBC-MySQL veritabanında bulunur. MySQL, Windows ve Linux dahil olmak üzere çeşitli platformlarda kullanılabilen, kamu malı olan bir SGBD'tir. Bu SGBD dosyası ile, içinde "impots" adlı tek bir tablo bulunan "dbimpots" adlı bir veritabanı oluşturulmuştur. Veritabanına erişim, burada "admimpots/mdpimpots" olan bir kullanıcı adı/şifre ile kontrol edilmektedir. Aşağıdaki ekran görüntüsü, MySQL dosyası ile "dbimpots" veritabanının nasıl kullanılacağını göstermektedir:


C:\Program Files\EasyPHP\mysql\bin>mysql -u admimpots -p
Enter password: *********
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 18 to server version: 3.23.49-max-nt

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> use dbimpots;
Database changed

mysql> show tables;
+--------------------+
| Tables_in_dbimpots |
+--------------------+
| impots             |
+--------------------+
1 row in set (0.00 sec)

mysql> describe impots;
+---------+--------+------+-----+---------+-------+
| Field   | Type   | Null | Key | Default | Extra |
+---------+--------+------+-----+---------+-------+
| limites | double | YES  |     | NULL    |       |
| coeffR  | double | YES  |     | NULL    |       |
| coeffN  | double | YES  |     | NULL    |       |
+---------+--------+------+-----+---------+-------+
3 rows in set (0.02 sec)

mysql> select * from impots;
+---------+--------+---------+
| limites | coeffR | coeffN  |
+---------+--------+---------+
|   12620 |      0 |       0 |
|   13190 |   0.05 |     631 |
|   15640 |    0.1 |  1290.5 |
|   24740 |   0.15 |  2072.5 |
|   31810 |    0.2 |  3309.5 |
|   39970 |   0.25 |    4900 |
|   48360 |    0.3 |    6898 |
|   55790 |   0.35 |  9316.5 |
|   92970 |    0.4 |   12106 |
|  127860 |   0.45 |   16754 |
|  151250 |    0.5 | 23147.5 |
|  172040 |   0.55 |   30710 |
|  195000 |    0.6 |   39312 |
|       0 |   0.65 |   49062 |
+---------+--------+---------+
14 rows in set (0.00 sec)

mysql>quit

dbimpots veritabanı, aşağıdaki şekilde ODBC veri kaynağına dönüştürülür:

  • 32 bitlik ODBC veri kaynağı yöneticisi başlatılır

Image

  • [Add] düğmesi kullanılarak yeni bir veri kaynağı olan ODBC eklenir

Image

  • MySQL sürücüsünü seçip [Terminer]'i yapıyoruz

54321

Image

  • MySQL sürücüsü bazı bilgiler ister:
1
ODBC veri kaynağına verilecek DSN adı - herhangi bir isim olabilir
2
SGBD ve MySQL'in çalıştığı makine - burada localhost. Veritabanının uzak bir veritabanı olabileceğini belirtmek ilginçtir. ODBC veri kaynağını kullanan yerel uygulamalar bunun farkına varmaz. Bu durum özellikle Java uygulamamız için geçerlidir.
3
kullanılacak MySQL veritabanı. MySQL, ilişkilerle birbirine bağlanmış tablo kümeleri olan ilişkisel veritabanlarını yöneten bir SGBD'tir. Burada, yönetilen veritabanının adı verilir.
4
Bu veritabanına erişim hakkı olan bir kullanıcının adı
5
şifresi

Vergiyi hesaplamak için iki sınıf tanımlanmıştır: impots ve impotsJDBC. impots sınıfının bir örneği, parametre olarak diziler halinde geçirilen vergi dilimlerinin verileriyle oluşturulur:

// vergi sınıfı oluşturuluyor

public class impots{

  // vergi hesaplaması için gerekli veriler
   // harici bir kaynaktan geliyor

  protected double[] limites=null;
  protected double[] coeffR=null;
  protected double[] coeffN=null;

   // boş oluşturucu
  protected impots(){}

   // oluşturucu
  public impots(double[] LIMITES, double[] COEFFR, double[] COEFFN) throws Exception{
     // 3 tablonun da aynı boyutta olup olmadığı kontrol edilir
    boolean OK=LIMITES.length==COEFFR.length && LIMITES.length==COEFFN.length;
    if (! OK) throw new Exception ("Les 3 tableaux fournis n'ont pas la même taille("+
      LIMITES.length+","+COEFFR.length+","+COEFFN.length+")");
    // tamam
    this.limites=LIMITES;
    this.coeffR=COEFFR;
    this.coeffN=COEFFN;
  }//üretici

   // vergi hesaplaması
  public long calculer(boolean marié, int nbEnfants, int salaire){
     // pay sayısının hesaplanması
    double nbParts;
    if (marié) nbParts=(double)nbEnfants/2+2;
    else nbParts=(double)nbEnfants/2+1;
    if (nbEnfants>=3) nbParts+=0.5;
         // vergilendirilebilir gelir ve aile katsayısı hesaplaması
        double revenu=0.72*salaire;
        double QF=revenu/nbParts;
         // vergi hesaplaması
        limites[limites.length-1]=QF+1;
        int i=0;
        while(QF>limites[i]) i++;
        // sonuçun döndürülmesi
        return (long)(revenu*coeffR[i]-nbParts*coeffN[i]);
    }//hesapla
}//sınıf

impotsJDBC sınıfı, önceki impots sınıfından türetilmiştir. impotsJDBC sınıfının bir örneği, bir veritabanında depolanan vergi dilimlerine ait verilerle oluşturulur. Bu veritabanına erişmek için gerekli bilgiler, oluşturucuya parametre olarak aktarılır:

// ithal edilen paketler
import java.sql.*;
import java.util.*;

public class impotsJDBC extends impots{
  // oluşturmaya olanak tanıyan bir oluşturucu eklenmesi
   // sınır tabloları, coeffr ve coeffn'i oluşturmaya yarayan bir oluşturucunun eklenmesi
   // bir veritabanındaki vergiler
  public impotsJDBC(String dsnIMPOTS, String userIMPOTS, String mdpIMPOTS)
      throws SQLException,ClassNotFoundException{

    // dsnIMPOTS: veritabanının adı DSN
     // userIMPOTS, mdpIMPOTS : veritabanına erişim kullanıcı adı/şifresi

     // veri tabloları
    ArrayList aLimites=new ArrayList();
    ArrayList aCoeffR=new ArrayList();
    ArrayList aCoeffN=new ArrayList();

    // veritabanına bağlantı
    Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
    Connection connect=DriverManager.getConnection("jdbc:odbc:"+dsnIMPOTS,userIMPOTS,mdpIMPOTS);
    // Statement nesnesinin oluşturulması
    Statement S=connect.createStatement();
    // SELECT sorgusu
    String select="select limites, coeffr, coeffn from impots";
    // sorgunun yürütülmesi
    ResultSet RS=S.executeQuery(select);
    while(RS.next()){
      // geçerli satırın işlenmesi
      aLimites.add(RS.getString("limites"));
      aCoeffR.add(RS.getString("coeffr"));
      aCoeffN.add(RS.getString("coeffn"));
    }// sonraki satır
     // kaynakların kapatılması
    RS.close();
    S.close();
    connect.close();
     // verilerin sınırlı tablolara aktarılması
    int n=aLimites.size();
    limites=new double[n];
    coeffR=new double[n];
    coeffN=new double[n];
    for(int i=0;i<n;i++){
      limites[i]=Double.parseDouble((String)aLimites.get(i));
      coeffR[i]=Double.parseDouble((String)aCoeffR.get(i));
      coeffN[i]=Double.parseDouble((String)aCoeffN.get(i));
    }//for
  }//oluşturucu
}//sınıf

impotsJDBC sınıfının bir örneği oluşturulduktan sonra, vergiyi hesaplamak için calculer yöntemini tekrar tekrar çağırabiliriz:

  public long calculer(boolean marié, int nbEnfants, int salaire){

Gerekli üç verinin elde edilmesi çeşitli şekillerde gerçekleştirilebilir. impotsJDBC sınıfının avantajı, kullanıcının yalnızca bu verilerin elde edilmesiyle ilgilenmesi gerekmesidir. Üç bilgi (medeni durum, çocuk sayısı, yıllık maaş) elde edildikten sonra, impotsJDBC sınıfının calculer yöntemini çağırarak ödenecek vergiyi hesaplayabiliriz.

5.2. Sürüm 1

Vergi hesaplaması için gerekli üç parametreyi elde etmek amacıyla kullanıcıya bir HTML arayüzü sunan bir web uygulaması bağlamını ele alalım:

  • medeni durum (evli mi, değil mi)
  • çocuk sayısı
  • yıllık maaş

Image

Formun görüntülenmesi, aşağıdaki JSP sayfası tarafından gerçekleştirilir:

<%@ page import="java.util.*" %>

<%
     // ana servlet tarafından geçirilen öznitelikleri alır
  String chkoui=(String)request.getAttribute("chkoui");
  String chknon=(String)request.getAttribute("chknon");
  String txtEnfants=(String)request.getAttribute("txtEnfants");
  String txtSalaire=(String)request.getAttribute("txtSalaire");
  String txtImpots=(String)request.getAttribute("txtImpots");
  ArrayList erreurs=(ArrayList)request.getAttribute("erreurs");
%>

<html>

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

  <body background="/impots/images/standard.jpg">
      <center>
        Calcul d'impôts
        <hr>
      <form name="frmImpots" action="/impots/main" method="POST">
          <table>
            <tr>
              <td>Etes-vous marié(e)</td>
            <td>
                    <input type="radio" name="optMarie" value="oui" <%= chkoui %>>oui
              <input type="radio" name="optMarie" value="non" <%= chknon %>>non
            </td>
          </tr>
          <tr>
              <td>Nombre d'enfants</td>
            <td><input type="text" size="5" name="txtEnfants" value="<%= txtEnfants %>"></td>
          </tr>
          <tr>
              <td>Salaire annuel</td>
            <td><input type="text" size="10" name="txtSalaire" value="<%= txtSalaire %>"></td>
          </tr>
          <tr>
              <td><font color="green">Impôt</font></td>
            <td><input type="text" size="10" name="txtImpots" value="<%= txtImpots %>" readonly></td>
          </tr>
          <tr></tr>
          <tr>
              <td><input type="submit" value="Calculer"></td>
            <td><input type="button" value="Effacer" onclick="effacer()"></td>
          </tr>
        </table>
      </form>
    </center>
    <%
         // hata var mı
      if(erreurs!=null){
           // hataların görüntülenmesi
        out.println("<hr>");
        out.println("<font color=\"red\">");
        out.println("Les erreurs suivantes se sont produites<br>");
        out.println("<ul>");
        for(int i=0;i<erreurs.size();i++){
            out.println("<li>"+(String)erreurs.get(i));
        }
        out.println("</ul>");
        out.println("</font>");
      } 
   %>
 </body>
</html>

JSP sayfası, uygulamanın ana servleti tarafından kendisine aktarılan bilgileri görüntülemekle yetinir:

     // ana servlet tarafından geçirilen öznitelikler alınır
  String chkoui=(String)request.getAttribute("chkoui");
  String chknon=(String)request.getAttribute("chknon");
  String txtEnfants=(String)request.getAttribute("txtEnfants");
  String txtSalaire=(String)request.getAttribute("txtSalaire");
  String txtImpots=(String)request.getAttribute("txtImpots");
  ArrayList erreurs=(ArrayList)request.getAttribute("erreurs");
chkoui, chknon
"Evet" ve "Hayır" radyo düğmelerinin öznitelikleri, ilgili radyo düğmesini etkinleştirmek veya devre dışı bırakmak için "checked" veya "" değerlerini alabilir
txtEnfants
Vergi mükellefinin çocuk sayısı
txtSalaire
yıllık maaşı
txtImpots
ödenecek vergi tutarı
erreurs
hatalar varsa hataların listesi (hatalar!=null ise)

Müşteriye gönderilen sayfa, "Effacer" düğmesiyle ilişkili effacer işlevini içeren bir javascript betiği içerir; bu işlevin amacı, formu başlangıç durumuna geri döndürmektir: düğme işaretlenmemiş, giriş alanları boş. Bu sonucun, "reset" türündeki bir HTML düğmesi ile elde edilemeyeceğine dikkat edilmelidir. Zira bu tür bir düğme kullanıldığında, tarayıcı formu aldığı durumuna geri döndürür. Oysa uygulamamızda tarayıcı, boş olmayabilecek formlar almaktadır.

Uygulamanın ana servleti main.java adını taşır ve kodu şu şekildedir:

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

public class main extends HttpServlet{

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

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

        // başlatma işlemi başarılı oldu mu?
        if(msgErreur!=null){
             // kontrol, hata sayfasına devredilir
            request.setAttribute("msgErreur",msgErreur);
            getServletContext().getRequestDispatcher(urlErreur).forward(request,response);
        }

         // isteğin öznitelikleri
        String chkoui=null;
        String chknon=null;
        String txtImpots=null;

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

         // beklenen tüm parametreler mevcut mu
        if(optMarie==null || txtEnfants==null || txtSalaire==null){
             // parametreler eksik
            request.setAttribute("chkoui","");
            request.setAttribute("chknon","checked");
            request.setAttribute("txtEnfants","");
            request.setAttribute("txtSalaire","");
            request.setAttribute("txtImpots","");
             // vergi bilgilerinin görüntüleneceği URL'ye yönlendiriliyor
            getServletContext().getRequestDispatcher(urlAffichageImpots).forward(request,response);
        }

         // tüm parametreler mevcut - kontrol ediliyor
        ArrayList erreurs=new ArrayList();
         // medeni durum
        if( ! optMarie.equals("oui") && ! optMarie.equals("non")){
             // hata
            erreurs.add("Etat marital incorrect");
            optMarie="non";
        }
         // ç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");
        }

         // hatalar varsa, bunları sorgunun özniteliği olarak aktarırız
        if(erreurs.size()!=0){
            request.setAttribute("erreurs",erreurs);
            txtImpots="";
        }else{
             // ödenecek vergi hesaplanabilir
            try{
                int nbEnfants=Integer.parseInt(txtEnfants);
                int salaire=Integer.parseInt(txtSalaire);
                txtImpots=""+impots.calculer(optMarie.equals("oui"),nbEnfants,salaire);
            }catch(Exception ex){}
        }

         // isteğin diğer öznitelikleri
        if(optMarie.equals("oui")){
            request.setAttribute("chkoui","checked");
            request.setAttribute("chknon","");
        }else{
            request.setAttribute("chknon","checked");
            request.setAttribute("chkoui","");
        }
        request.setAttribute("txtEnfants",txtEnfants);
        request.setAttribute("txtSalaire",txtSalaire);
        request.setAttribute("txtImpots",txtImpots);

         // vergi görüntüleme URL'sine kontrol devredilir
        getServletContext().getRequestDispatcher(urlAffichageImpots).forward(request,response);
    }

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

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

         // parametreler uygun mu?
        if(urlAffichageImpots==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();
        }
    }
}
  • Servlet'in init yöntemi iki şey yapar:
    • başlatma parametrelerini alır. Bu parametreler, çeşitli vergi dilimlerinin verilerini (DSNimpots, admimpots, mdpimpots) ve uygulamayla ilişkili sayfaların URL değerlerini içeren ODBC veritabanına bağlanmasını sağlar: form için urlAffichageImpots, hata sayfası için urlErreur.
    • impotsJDBC sınıfının bir örneğini oluşturur

Her iki durumda da olası hatalar işlenir ve hata mesajı msgErreur değişkenine kaydedilir.

  • doGET yöntemi
    • öncelikle servletin doğru şekilde başlatılıp başlatılmadığını kontrol eder. Aksi takdirde hata sayfası görüntülenir
    • vergi formundan beklenen parametreleri alır: optMarie, txtEnfants, txtSalaire. Bunlardan biri eksikse (==null), boş bir vergi formu gönderilir. optMarie parametresinin geçerliliğinin kontrol edilmesinin gereksiz olduğu düşünülebilir. Bu parametre bir radyo düğmesinin değeridir ve burada yalnızca "oui" veya "non" değerlerinden birini alabilir. Ancak bu, bir programın istediği parametreleri göndererek servleti doğrudan sorgulamasını engelleyen hiçbir şeyin olmadığını göz ardı etmek anlamına gelir. Bir tarayıcıya gerçekten bağlı olduğumuzdan asla emin olamayız. Bu noktayı göz ardı etmek, uygulamada güvenlik açıklarına yol açabilir ve aslında ticari uygulamalarda bile bu tür açıkların sıklıkla rastlandığı görülür.
    • Alınan üç parametrenin her birinin geçerliliği kontrol edilir. Bulunan her hata, bir hata listesine (ArrayList hatalar) eklenir. Hata yoksa vergi tutarı hesaplanır, aksi takdirde hesaplanmaz.
    • Sayfanın görüntülenmesi için gerekli bilgiler istek özniteliklerine eklenir ve ardından vergi formu görüntülenir

JSP hata sayfası şöyledir:

<%
     // jspService
   // bir hata oluştu
  String msgErreur= (String)request.getAttribute("msgErreur");
  if(msgErreur==null) msgErreur="Erreur non identifiée";
%>
<!-- sayfanın başlangıcı HTML -->
<html>
  <head>
      <title>impots</title>
  </head>
  <body>
      <h3>calcul d'impots</h3>
      <hr>
    Application indisponible(<%= msgErreur %>)
  </body>
</html>

Web uygulaması "impots" olarak adlandırılmıştır ve Tomcat'taki server.xml dosyasında şu şekilde yapılandırılmıştır:

                <Context path="/impots" docBase="e:/data/serge/servlets/impots" />

Uygulama klasörü aşağıdaki klasörleri ve dosyaları içerir:

Image

Image

Image

Image

"impots" uygulamasının yapılandırma dosyası olan web.xml dosyası şöyledir:

<?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>main</servlet-name>
    <servlet-class>main</servlet-class>
    <init-param>
          <param-name>urlAffichageImpots</param-name>
        <param-value>/impots.jsp</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>    
    <init-param>
          <param-name>urlErreur</param-name>
        <param-value>/erreur.jsp</param-value>
    </init-param>    
  </servlet>
  <servlet-mapping>
      <servlet-name>main</servlet-name>
    <url-pattern>/main</url-pattern>
  </servlet-mapping>
</web-app>

Ana servlet’in adı main’tir ve /main takma adı vardır. Dolayısıyla, URL ve http://localhost:8080/impots/main üzerinden erişilebilir.

İşte birkaç uygulama örneği:

Doğru şekilde başlatılabilmesi için servletin mysql-dbimpots veritabanına erişimi olmalıdır. Örneğin, MySQL sunucusu çalışmıyorsa ve dolayısıyla mysql-dbimpots veritabanına erişilemiyorsa, elde edilen sayfa şöyledir:

Image

Yanlış giriş yapıldığında görüntülenen sayfa şöyledir:

Image

Girişler doğruysa, vergi hesaplanır:

Image

5.3. Sürüm 2

Önceki örnekte, formdaki txtEnfants ve txtSalaire parametrelerinin geçerliliği sunucu tarafından kontrol edilmektedir. Burada, form sayfasına eklenmiş bir JavaScript komut dosyası aracılığıyla bu doğrulamayı yapmayı öneriyoruz. Böylece parametrelerin doğrulanması tarayıcı tarafından gerçekleştirilir. Sunucuya ise yalnızca parametreler geçerliyse başvurulur. Bu sayede "bant genişliği" tasarrufu sağlanır. Görüntüleme sayfası JSP şu şekilde olur:

<%@ page import="java.util.*" %>
............

<html>

    <head>
      <title>impots</title>
    <script language="JavaScript" type="text/javascript">
        function effacer(){
.......
      }//sil

      function calculer(){
           // parametreleri sunucuya göndermeden önce kontrol ediliyor
        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;
          }//if
          //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
          // sorun yok - form sunucuya gönderiliyor
          submit();
        }//with
      }//hesapla        
        </script>
  </head>

  <body background="/impots/images/standard.jpg">
........
              <td><input type="button" value="Calculer" onclick="calculer()"></td>
          <td><input type="button" value="Effacer" onclick="effacer()"></td>
.....
 </body>
</html>

Aşağıdaki değişikliklere dikkat edilmelidir:

  • Calculer düğmesi artık submit türü değil, button türündedir ve calculer adlı bir işleve bağlıdır. txtEnfants ve txTsalaire alanlarının analizini bu işlev gerçekleştirecektir. Bu alanlar doğruysa, formdaki değerler sunucuya gönderilir (submit); aksi takdirde bir hata mesajı görüntülenir.

Hata durumunda görüntülenecek bir örnek şöyledir:

Image

5.4. Sürüm 3

Uygulamayı, oturum kavramını eklemek için biraz değiştiriyoruz. Artık uygulamayı bir vergi hesaplama simülasyonu uygulaması olarak ele alıyoruz. Böylece bir kullanıcı, farklı vergi mükellefi "konfigürasyonlarını" simüle edebilir ve her biri için ödenecek verginin ne olacağını görebilir. Aşağıdaki WEb sayfası, elde edilebilecek sonuçlara bir örnek sunmaktadır:

Image

Ana servlet değiştirilmiştir ve artık simulations olarak adlandırılmaktadır. Bu servlet, impots uygulaması içinde şu şekilde yapılandırılmıştır:

<?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>
      <servlet-name>simulations</servlet-name>
    <servlet-class>simulations</servlet-class>
    <init-param>
          <param-name>urlSimulationImpots</param-name>
        <param-value>/simulationsImpots.jsp</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>    
    <init-param>
          <param-name>urlErreur</param-name>
        <param-value>/erreur.jsp</param-value>
    </init-param>    
  </servlet>
......
  <servlet-mapping>
      <servlet-name>simulations</servlet-name>
    <url-pattern>/simulations</url-pattern>
  </servlet-mapping>
</web-app>

Ana servlet "simulations" adını taşır ve simulations.class sınıf dosyasına dayanır. Bu servletin /simulations takma adı vardır; bu sayede URL ve http://localhost:8080/impots/simulations üzerinden erişilebilir. Veritabanına erişim açısından, daha önce incelenen ana servlet ile aynı başlatma parametrelerine sahiptir. Yeni bir parametre ortaya çıkmaktadır: urlSimulationsImpots; bu, simülasyon sayfası JSP’in (yukarıda kısaca tanıtılan) URL parametresidir.

simulations.java servleti, main.java servletine benzemektedir. Aralarındaki temel farklar şunlardır:

  • ana servlet, optmarie parametrelerinden txtImpots değerini hesaplar, txtEnfants ve txtSalaire parametrelerinden bir txtImpots değeri hesaplar ve bu değeri JSP görüntüleme sayfasına aktarır
  • simülasyonlar servleti de aynı şekilde txtImpots değerini hesaplar, parametreleri (optMarie, txtEnfants, txtsalaire, txtImpots) "simülasyonlar" adlı bir listeye kaydeder. Görüntüleme sayfası JSP'e parametre olarak aktarılan bu listedir. Bu listenin kullanıcı tarafından gerçekleştirilen tüm simülasyonları içermesi için, mevcut oturumun bir özniteliği olarak kaydedilir.

simulations servleti aşağıdaki gibidir (sadece önceki uygulamadakinden farklı olan kod satırları korunmuştur):

import java.io.*;
.......

public class simulations extends HttpServlet{

    // örnek değişkenleri
    String msgErreur=null;
    String urlSimulationImpots=null;
    String urlErreur=null;
...........

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

...........

         // oturumun önceki simülasyonları alınır
        HttpSession session=request.getSession();
        ArrayList simulations=(ArrayList)session.getAttribute("simulations");
        if(simulations==null) simulations=new ArrayList();
        // simülasyonları geçerli sorguya eklenir
        request.setAttribute("simulations",simulations);

         // sorgunun diğer öznitelikleri
...........

         // beklenen tüm parametreler mevcut mu
        if(optMarie==null || txtEnfants==null || txtSalaire==null){
........
             // vergi hesaplamaları simülasyonlarını görüntülemek için URL'ye kontrol devredilir
            getServletContext().getRequestDispatcher(urlSimulationImpots).forward(request,response);
        }

         // tüm parametreler mevcut mu? - kontrol ediliyor
...........

         // Hata varsa, bunları isteğin özniteliklerine aktarıyoruz
        if(erreurs.size()!=0){
            request.setAttribute("erreurs",erreurs);
        }else{
            try{
                 // ödenecek vergi hesaplanabilir
                int nbEnfants=Integer.parseInt(txtEnfants);
                int salaire=Integer.parseInt(txtSalaire);
                txtImpots=""+impots.calculer(optMarie.equals("oui"),nbEnfants,salaire);
                 // mevcut sonucu önceki simülasyonlara ekliyoruz
                String[] simulation={optMarie.equals("oui") ? "oui" : "non",txtEnfants, txtSalaire, txtImpots};
                simulations.add(simulation);
                 // simülasyonların yeni değeri oturuma aktarılır
                session.setAttribute("simulations",simulations);
            }catch(Exception ex){}
        }

         // sorgunun diğer öznitelikleri
..........

         // simülasyonların görüntüleneceği URL'ye kontrol devredilir
        getServletContext().getRequestDispatcher(urlSimulationImpots).forward(request,response);
    }

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

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

         // parametreler uygun mu?
.........................
    }
}

simulationsImpots.jsp görüntüleme sayfası, JSP sayfası olarak değiştirildi (önceki uygulamanın JSP görüntüleme sayfasından farklı olan kodlar korunmuştur).

<%@ page import="java.util.*" %>

<%
     // ana servlet tarafından aktarılan öznitelikler alınır
...........
  ArrayList simulations=(ArrayList)request.getAttribute("simulations");  
%>

<html>

    <head>
      <title>impots</title>
    <script language="JavaScript" type="text/javascript">
........
        </script>
  </head>

  <body background="/impots/images/standard.jpg">
      <center>
        Calcul d'impôts
        <hr>
      <form name="frmImpots" action="/impots/simulations" method="POST">
.......................
           </form> 
</center>
    <hr>
    <%
         // Hata var mı?
      if(erreurs!=null){
..................
      }else if(simulations.size()!=0){
          // simülasyon sonuçları
        out.println("<h3>Résultats des simulations<h3>");
        out.println("<table \"border=\"1\">");
        out.println("<tr><td>Marié</td><td>Enfants</td><td>Salaire annuel (F)</td><td>Impôts à payer (F)</td></tr>");
        for(int i=0;i<simulations.size();i++){
            String[] simulation=(String[])simulations.get(i);
            out.println("<tr><td>"+simulation[0]+"</td><td>"+simulation[1]+"</td><td>"+simulation[2]+"</td><td>"+simulation[3]+"</td></tr>");
        }
        out.println("</table>");
      }
   %>
 </body>
</html>

5.5. Sürüm 4

Şimdi, önceki /impots/simulations uygulamasının bir web istemcisi olacak bağımsız bir uygulama oluşturacağız. Bu uygulamanın grafik arayüzü aşağıdaki gibi olacaktır:

no
tür
ad
rol
1
JTextField
txtUrlServiceImpots
URL vergi hesaplama simülasyon hizmeti
2
JRadioButton
rdOui
evli ise işaretlenir
3
JRadioButton
rdNon
evli değilseniz işaretleyin
4
JSpinner
spinEnfants
Vergi mükellefinin çocuk sayısı (minimum=0, maksimum=20, artış=1)
5
JTextField
txtSalaire
Vergi mükellefinin yıllık maaşı (F cinsinden)
6
JList, JScrollPane içinde
lstSimulations
simülasyon listesi

Vergiler menüsü aşağıdaki seçeneklerden oluşur:

seçenek
ana
ikincil
ikincil
ad
rol
Vergiler
   
 
Hesapla
mnuCalculer
hesaplama için gerekli tüm veriler mevcut ve doğruysa ödenecek vergiyi hesaplar
 
Sil
mnuEffacer
formu başlangıç durumuna geri getirir
 
Çık
mnuQuitter
uygulamayı sonlandırır

Çalışma kuralları

  • 1 veya 5 numaralı alanlardan biri boşsa, Calculer menü seçeneği devre dışı kalır
  • 1 numaralı alanda sözdizimi açısından hatalı bir URL algılanırsa

Image

  • yanlış bir maaş tespit edildiğinde

Image

  • sunucuya bağlantı hatası bildirildiğinde (aşağıdaki 1 numaralı örnekte bağlantı noktası hatalı; 2 numaralı örnekte istenen URL mevcut değil; 3 numaralı örnekte MySQL veritabanı başlatılmamıştı)

Image

Image

Image

  • Her şey doğruysa, simülasyonlar görüntülenir

Image

Programlanmış bir web istemcisi yazarken, sunucunun bir istemciden gelebilecek çeşitli isteklerin yanıtı olarak tam olarak ne gönderdiğini bilmek gerekir. Sunucu, yararlı bilgiler ile yalnızca sayfa düzeni için bulunan diğer bilgileri içeren bir dizi satır HTML gönderir HTML. Java’daki düzenli ifadeler, sunucu tarafından gönderilen satır akışında yararlı bilgileri bulmamıza yardımcı olabilir. Bunun için, sunucunun farklı yanıtlarının tam formatını bilmemiz gerekir. Burada, bir URL isteğine sunucunun verdiği yanıtı ekranda görüntülememizi sağlayan, daha önce ele aldığımız web istemcisini kullanacağız. İstenen URL, http://localhost:8080/impots/simulations adlı vergi hesaplama simülasyon hizmetimize ait olacak ve bu hizmete http://localhost:8080/impots/simulations?param1=vam1&param2=val2&... biçiminde parametreler iletebileceğiz.

URL'i talep edelim; ancak impots türünde bir nesne oluşturmaya olanak tanıyan MySQL tabanı başlatılmamışken:


Dos>java clientweb http://localhost:8080/impots/simulations GET

HTTP/1.1 200 OK
Content-Type: text/html;charset=ISO-8859-1
Date: Fri, 16 Aug 2002 16:31:04 GMT
Connection: close
Server: Apache Tomcat/4.0.3 (HTTP/1.1 Connector)
Set-Cookie: JSESSIONID=9DEC8B27966A1FBE3D4968A7B9DF3331;Path=/impots


<!-- dÚbut sayfası HTML -->
<html>
  <head>
        <title>impots</title>
  </head>
  <body>
        <h3>calcul d'impôts</h3>
        <hr>
    Application indisponible([TCX][MyODBC]Can't connect to MySQL server on 'localhost' (10061))
  </body>
</html>

Hatayı almak için, web istemcisi web sunucusunun yanıtında "Uygulama kullanılamıyor" metnini içeren satırı aramalıdır. Şimdi MySQL veritabanını başlatıp, beklediği değerleri ileterek aynı URL'i isteyelim (sunucu, bu değerleri GET veya POST biçiminde ayırt etmeden kabul eder – bkz. sunucunun Java kodu):


Dos>java clientweb "http://localhost:8080/impots/simulations?ptMarie=oui&txtEnfants=2&txtSalaire=200000" GET

HTTP/1.1 200 OK
Content-Type: text/html;charset=ISO-8859-1
Date: Fri, 16 Aug 2002 16:42:36 GMT
Connection: close
Server: Apache Tomcat/4.0.3 (HTTP/1.1 Connector)
Set-Cookie: JSESSIONID=C2A707600E98A37A343611D80DD5C8A2;Path=/impots


<html>

        <head>
        <title>impots</title>
    <script language="JavaScript" type="text/javascript">
        function effacer(){
...................................................
      }//sil

      function calculer(){
...................................................
      }//hesapla
                </script>
  </head>

  <body background="/impots/images/standard.jpg">
        <center>
        Calcul d'imp¶ts
        <hr>
      <form name="frmImpots" action="/impots/simulations" method="POST">
...................................................
      </form>
    </center>
    <hr>
    <h3>Résultats des simulations<h3>
<table "border="1">
<tr><td>Marié</td><td>Enfants</td><td>Salaire annuel (F)</td><td>Impôts à payer (F)</td></tr>
<tr><td>oui</td><td>2</td><td>200000</td><td>22504</td></tr>
</table>

 </body>
</html>

Burada, sunucu tarafından gönderilen HTML belgesinin tamamı yer almaktadır. Bu belgedeki tek tabloda çeşitli simülasyonların sonuçları bulunmaktadır. Belgedeki ilgili bilgileri almamızı sağlayan düzenli ifade şu şekilde olabilir:

"<tr>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*</tr>"

burada parantez içindeki dört ifade, alınacak dört bilgiyi temsil etmektedir.

Artık, kullanıcı önceki grafik arayüzünden vergi hesaplamasını talep ettiğinde ne yapılması gerektiğine dair kılavuz ilkelerimiz var:

  • Arayüzdeki tüm verilerin geçerli olup olmadığını kontrol etmek ve gerekirse hataları bildirmek.
  • 1. alanda belirtilen URL'e bağlanmak. Bunun için, daha önce sunulmuş ve incelenmiş olan genel web istemcisi modelini takip edeceğiz
  • sunucu yanıt akışında, düzenli ifadeler kullanarak ya:
    • varsa hata mesajını bulmak
    • hata yoksa simülasyon sonuçlarını bulmak

calculer menüsüyle ilgili kod şöyledir:

  void mnuCalculer_actionPerformed(ActionEvent e) {
    // vergi hesaplama
         // doğrulama URL hizmet
        URL urlImpots=null;
        try{
            urlImpots=new URL(txtURLServiceImpots.getText().trim());
            String query=urlImpots.getQuery();
            if(query!=null) throw new Exception();
        }catch (Exception ex){
             // hata mesajı
            JOptionPane.showMessageDialog(this,"URL incorrecte. Recommencez","Erreur",JOptionPane.ERROR_MESSAGE);
             // hatalı alana odaklanma
            txtURLServiceImpots.requestFocus();
             // arayüze geri dönüş
            return;
        }
     // maaş doğrulama
    int salaire=0;
    try{
      salaire=Integer.parseInt(txtSalaire.getText().trim());
      if(salaire<0) throw new Exception();
    }catch (Exception ex){
       // hata mesajı
            JOptionPane.showMessageDialog(this,"Salaire incorrect. Recommencez","Erreur",JOptionPane.ERROR_MESSAGE);
       // hatalı alana odaklanma
      txtSalaire.requestFocus();
       // arayüze geri dön
      return;
    }
     // çocuk sayısı
    Integer nbEnfants=(Integer)spinEnfants.getValue();

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


    public void calculerImpots(URL urlImpots,boolean marié, int nbEnfants, int salaire)
          throws Exception{
         // vergi hesaplaması
         // urlImpots: Vergi dairesinin URL kodu
         // 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
        String path=urlImpots.getPath();
        if(path.equals("")) path="/";
        String query="?"+"optMarie="+(marié ? "oui":"non")+"&txtEnfants="+nbEnfants+"&txtSalaire="+salaire;
        String host=urlImpots.getHost();
        int port=urlImpots.getPort();
        if(port==-1) port=urlImpots.getDefaultPort();

         // yerel veriler
        Socket  client=null;                        // istemci
        BufferedReader IN=null;                    // müşterinin okuma akışı
        PrintWriter OUT=null;                        // istemcinin yazma akışı
        String réponse=null;                        // sunucu yanıtı
         // başlıklarda aranan şablon HTTP
        Pattern modèleCookie=Pattern.compile("^Set-Cookie: JSESSIONID=(.*?);");
        // doğru yanıt şablonu
        Pattern réponseOK=Pattern.compile("^.*? 200 OK");
         // şablonla karşılaştırma sonucu
        Matcher résultat=null;

        try{
             // sunucuya bağlanılıyor
            client=new Socket(host,port);

            // istemcinin giriş-çıkış akışları oluşturuluyor TCP
            IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
            OUT=new PrintWriter(client.getOutputStream(),true);

            // URL isteniyor - başlıkların gönderilmesi HTTP
            OUT.println("GET " + path + query + " HTTP/1.1");
            OUT.println("Host: " + host + ":" + port);
            if(! JSESSIONID.equals("")){
                OUT.println("Cookie: JSESSIONID="+JSESSIONID);
            }
            OUT.println("Connection: close");
            OUT.println("");

             // yanıtın ilk satırı okunur
            réponse=IN.readLine();
             // HTTP satırı, doğru yanıt şablonuyla karşılaştırılır
            résultat=réponseOK.matcher(réponse);
            if(! résultat.find()){
                // URL ile ilgili bir sorun var
                throw new Exception("Le serveur a répondu : URL ["+ txtURLServiceImpots.getText().trim() + "] inconnue");
            }//if(sonuç)

             // olası bir çerez olup olmadığını kontrol ederek yanıtı başlıkların sonuna kadar okunur
            while((réponse=IN.readLine())!=null){
                 // satır boş mu?
                if(réponse.equals("")) break;
                // HTTP satırı boş değil
                 // oturum çerezi yoksa aranır
                if (JSESSIONID.equals("")){
                    // HTTP satırını çerez şablonuyla karşılaştırırız
                    résultat=modèleCookie.matcher(réponse);
                    if(résultat.find()){
                        // jeton çerezi bulundu
                        JSESSIONID=résultat.group(1);
                    }//if(sonuç)
                }//if(JSESSIONID)
            }//while

             // başlıklar bitti HTTP - HTML koduna geçiyoruz
             // 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));
            }

            // işlem tamamlandı
            client.close();
        }catch (Exception ex){
            throw new Exception(ex.getMessage());
        }
    }//calculerImpots

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

        // simülasyon tablosundaki bir satırın modeli
        Pattern ptnSimulation=Pattern.compile("<tr>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*</tr>");
        // hata listesindeki bir satırın şablonu
        Pattern ptnErreur=Pattern.compile("(Application indisponible.*?)\\s*$");
         // modelle karşılaştırma sonucu
        Matcher résultat=null;
         // simülasyonlar
        ArrayList listeSimulations=new ArrayList();

         // sonuna kadar tüm satırlar okunur
        String ligne=null;
        boolean simulationRéussie=false;
        while((ligne=IN.readLine())!=null){
             // izleme
             // simülasyonlar bölümü henüz karşımıza çıkmamışsa, satırı hata şablonuyla karşılaştırılır
            if(! simulationRéussie){
                résultat=ptnErreur.matcher(ligne);
                if(résultat.find()){
                    // hata mesajı
                    JOptionPane.showMessageDialog(this,résultat.group(1),"Erreur",JOptionPane.ERROR_MESSAGE);
                    // işlem tamamlandı
                    return listeSimulations;
                }//eğer
            }//eğer
             // satırı simülasyon şablonuyla karşılaştırılıyor
            résultat=ptnSimulation.matcher(ligne);
            if(résultat.find()){
                // tablodan bir satır bulundu
                listeSimulations.add(résultat.group(1)+":"+résultat.group(2)+":"+résultat.group(3)+
                                                ":"+résultat.group(4));
                 // simülasyon başarılı oldu
                simulationRéussie=true;
            }//if
        }//while
        // son
        return listeSimulations;
    }

Bu kodu biraz açıklayalım:

  • mnuCalculer_actionPerformed prosedürü, arayüzdeki verilerin geçerli olup olmadığını kontrol eder. Geçerli değilse bir hata mesajı verilir ve prosedür sonlandırılır. Geçerliyse, calculerImpots prosedürü yürütülür.
  • calculerImpots prosedürü, talep etmesi gereken URL'i oluşturarak başlar
         // urlImpots'ten vergi sunucusuna bağlanmak için gerekli bilgiler alınır
        String path=urlImpots.getPath();
        if(path.equals("")) path="/";
        String query="?"+"optMarie="+(marié ? "oui":"non")+"&txtEnfants="+nbEnfants+"&txtSalaire="+salaire;
        String host=urlImpots.getHost();
        int port=urlImpots.getPort();
        if(port==-1) port=urlImpots.getDefaultPort();
........................
  • ardından uygun HTTP başlıklarını göndererek bu URL'e bağlanır:
             // sunucuya bağlanılır
            client=new Socket(host,port);

            // müşterinin giriş-çıkış akışları oluşturulur TCP
            IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
            OUT=new PrintWriter(client.getOutputStream(),true);

            // URL talep ediliyor - başlıkların gönderilmesi HTTP
            OUT.println("GET " + path + query + " HTTP/1.1");
            OUT.println("Host: " + host + ":" + port);
            if(! JSESSIONID.equals("")){
                OUT.println("Cookie: JSESSIONID="+JSESSIONID);
            }
            OUT.println("Connection: close");
            OUT.println("");
  • İstek gönderildikten sonra, web istemcimiz yanıtı bekler. Öncelikle sunucunun yanıtındaki HTTP başlıklarını alır. Bu başlıkları inceleyerek oturum jetonunu bulur. Zira, sunucunun yapılan çeşitli simülasyonların kaydını tutabilmesi için bu jetonu sunucuya geri göndermesi gerekecektir. Burada şunu belirtmek gerekir ki, kullanıcının gerçekleştirdiği çeşitli simülasyonları istemcinin kendisi hatırlaması daha basit olurdu. Bununla birlikte, oturum yönetimine dair yeni bir örnek sunmak amacıyla jetonu geri gönderme fikrini koruyoruz. Yanıtın ilk satırı ayrı olarak işlenir. Zaten bu satır, istenen URL'in gerçekten mevcut olduğunu belirtmek için HTTP/version 200 OK biçiminde olmalıdır. Dolayısıyla, bu biçimde değilse, kullanıcının hatalı bir URL talep ettiği sonucuna varılır ve kullanıcıya bu durum bildirilir.
         // başlıklarda aranan şablon HTTP
        Pattern modèleCookie=Pattern.compile("^Set-Cookie: JSESSIONID=(.*?);");
        // doğru yanıtın şablonu
        Pattern réponseOK=Pattern.compile("^.*? 200 OK");
..........

             // yanıtın ilk satırı okunur
            réponse=IN.readLine();
             // HTTP satırı, doğru cevabın şablonuyla karşılaştırılır
            résultat=réponseOK.matcher(réponse);
            if(! résultat.find()){
                // URL ile ilgili bir sorun var
                throw new Exception("Le serveur a répondu : URL ["+ txtURLServiceImpots.getText().trim() + "] inconnue");
            }//if(sonuç)

             // olası bir çerez olup olmadığını kontrol ederek yanıtı başlıkların sonuna kadar okunur
            while((réponse=IN.readLine())!=null){
                 // satır boş mu?
                if(réponse.equals("")) break;
                // HTTP satırı boş değil
                 // oturum çerezi yoksa aranır
                if (JSESSIONID.equals("")){
                    // HTTP satırını çerez şablonuyla karşılaştırın
                    résultat=modèleCookie.matcher(réponse);
                    if(résultat.find()){
                        // jeton çerezi bulundu
                        JSESSIONID=résultat.group(1);
                    }//if(sonuç)
                }//if(JSESSIONID)
            }//while
  • HTTP başlıkları işlendikten sonra, yanıtın HTML kısmına geçilir
             // başlıklar için işlem tamamlandı HTTP - HTML koduna geçiyoruz
             // 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));
            }
  • getSimulations prosedürü, varsa simülasyonların listesini döndürür; sunucu bir hata mesajı gönderirse bu liste boş olacaktır. Bu durumda, hata mesajı bir mesaj kutusunda görüntülenir. Liste boş değilse, grafik arayüzün açılır listesinde görüntülenir.
  • getSimulations prosedürü, HTML yanıtının her satırını, hata mesajını (Uygulama kullanılamıyor...) temsil eden düzenli ifadeyle ve bir simülasyonu temsil eden düzenli ifadeyle karşılaştırır. Hata mesajı bulunursa, bu mesaj görüntülenir ve prosedür sonlandırılır. Bir simülasyon sonucu bulunursa, bu simülasyon simülasyonlar listesine eklenir. Prosedürün sonunda bu liste sonuç olarak döndürülür.
    private ArrayList getSimulations(BufferedReader IN, PrintWriter OUT, DefaultListModel simulations) throws Exception{

        // simülasyon tablosundaki bir satırın şablonu
        Pattern ptnSimulation=Pattern.compile("<tr>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*</tr>");
        // hata listesindeki bir satırın şablonu
        Pattern ptnErreur=Pattern.compile("(Application indisponible.*?)\\s*$");

5.6. Sürüm 5

Burada, önceki bağımsız grafik uygulamasını bir Java applet'ine dönüştürüyoruz. Grafik arayüzü biraz farklıdır. Bağımsız uygulamada kullanıcı, vergi hesaplama simülasyon hizmetinin URL'sini kendisi girerdi ve ardından uygulama bu URL'e bağlanırdı. Burada istemci uygulaması bir tarayıcıdır ve kullanıcı, applet'i içeren HTML belgesinin URL adresini talep edecektir. Şimdi şunu hatırlamak gerekir: Bir Java applet'i, yalnızca indirildiği sunucuyla ağ bağlantısı kurabilir. Dolayısıyla, simülasyon hizmetinin URL'i, applet'i içeren HTML belgesiyle aynı sunucuda olacaktır. Örneğimizde, txtUrlServiceImpots alanına applet’in bir başlatma parametresi yerleştirilecek ve bu alan kullanıcı tarafından düzenlenemeyecektir. Böylece şu istemci elde edilecektir:

Image

Applet'i içeren HTML belgesi, simulations.htm olarak adlandırılır ve şu şekildedir:

<html>
    <head>
      <title>Simulations de calculs d'impôts</title>
  </head>
  <body background="/impots/images/standard.jpg">
      <center>
          <h3>Simulations de calculs d'impôts</h3>
        <hr>
        <applet code="appletImpots.class" width="400" height="360">
            <param name="urlServiceImpots" value="simulations">
            </applet>
    </center>
  </body>
</html>  

Applet'in, vergi hesaplama simülasyon hizmetinin URL'i olan urlServiceImpots adlı bir parametresi vardır. Bu URL, HTML ve simulations.htm belgelerinin URL değerine göre belirlenir ve ölçülür. Dolayısıyla, bir tarayıcı bu belgeyi http://localhost:8080/impots/simulations.htm adresindeki URL kodu ile alırsa, simülasyon hizmetinin URL değeri http://localhost:8080/impots/simulations. olacaktır. Eğer bu URL değeri http://stahe olsaydı:8080/impots/simulations.htm olsaydı, simülasyon hizmetindeki URL, http://stahe:8080/impots/simulations olurdu.

appletImpots.java applet'i, bir grafik uygulamasını applet'e dönüştürme kurallarına uyarak önceki bağımsız grafik uygulamasının kodunu tamamen devralır.

public class appletImpots extends JApplet {
  // pencerenin bileşenleri
  JPanel contentPane;
  JMenuBar jMenuBar1 = new JMenuBar();
  JMenu jMenu1 = new JMenu();
  JMenuItem mnuCalculer = new JMenuItem();
.............


   //Çerçeveyi oluşturmak
  public void init() {
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
     // diğer başlatma işlemleri
    moreInit();
  }

   // form başlatma
  private void moreInit(){
         // parametreyi alıyoruz urlServiceImpots
        String urlServiceImpots=getParameter("urlServiceImpots");
        if(urlServiceImpots==null){
            // parametre eksik
            JOptionPane.showMessageDialog(this,"Le paramètre urlServiceImpots de l'applet n'a pas été défini","Erreur",JOptionPane.ERROR_MESSAGE);
             // son
            return;
        }
         // URL değerini ilgili alana girin
        String codeBase=""+getCodeBase();
        if(codeBase.endsWith("/"))
           txtURLServiceImpots.setText(codeBase+urlServiceImpots);
      else txtURLServiceImpots.setText(codeBase+"/"+urlServiceImpots);
    // Hesapla menüsü devre dışı
    mnuCalculer.setEnabled(false);
     // Çocuk sayısı seçici - 0 ile 20 arasında çocuk
    spinEnfants=new JSpinner(new SpinnerNumberModel(0,0,20,1));
    spinEnfants.setBounds(new Rectangle(130,140,50,27));
    contentPane.add(spinEnfants);
  }//moreInit

   //Bileşeni başlat
  private void jbInit() throws Exception  {
    contentPane = (JPanel) this.getContentPane();
    contentPane.setLayout(null);
...............
  }

Bir tarayıcı bir applet yüklediğinde, öncelikle applet'in init prosedürünü çalıştırır. Bu prosedürde, applet'in urlServiceImpots parametresinin değerini aldık, ardından simülasyon hizmetinin URL tam adını hesapladık ve bu değeri, sanki kullanıcı tarafından girilmiş gibi txtURLServiceImpots alanına yerleştirdik. Bu işlem tamamlandıktan sonra, iki uygulama arasında artık hiçbir fark kalmamıştır. Özellikle, Calculer menüsüne ait kod tamamen aynıdır. İşte bir çalışma örneği:

Image

5.7. Sonuç

Vergi hesaplama amaçlı istemci-sunucu uygulamamızın farklı sürümlerini gösterdik:

  • sürüm 1: hizmet, bir dizi servlet ve JSP sayfaları tarafından sağlanır; istemci ise bir tarayıcıdır. Tek bir simülasyon yapar ve öncekileri hatırlamaz.
  • sürüm 2: tarayıcı tarafından yüklenen HTML belgesine javascript komut dosyaları ekleyerek tarayıcı tarafında bazı yetenekler eklenir. Bu, form parametrelerinin geçerliliğini denetler.
  • sürüm 3: Bir oturum yönetilerek, hizmetin bir istemci tarafından gerçekleştirilen farklı simülasyonları hatırlamasına olanak sağlanır. HTML arayüzü, bunları görüntülemek üzere buna göre değiştirilir.
  • sürüm 4: İstemci artık bağımsız bir grafik uygulamasıdır. Bu sayede, programlanmış web istemcileri geliştirme aşamasına geri dönebiliriz.
  • sürüm 5: istemci bir Java applet'ine dönüşür. Böylece, hem sunucu tarafında hem de istemci tarafında tamamen Java ile programlanmış bir istemci-sunucu uygulamasına sahip oluruz.

Bu noktada birkaç hususa değinebiliriz:

  • Sürüm 1 ila 3, JavaScript komut dosyalarını çalıştırabilme özelliği dışında başka bir özelliği olmayan tarayıcılara izin verir. Bir kullanıcının bu komut dosyalarının yürütülmesini her zaman engelleme imkânı olduğu unutulmamalıdır. Bu durumda uygulama, sürüm 1'de yalnızca kısmen çalışır (Sil seçeneği çalışmaz) ve sürüm 2 ile 3'te hiç çalışmaz (Sil ve Hesapla seçenekleri çalışmaz). JavaScript komut dosyalarını kullanmayan bir hizmet sürümü tasarlamak ilginç olabilir.
  • Sürüm 4 için istemci bilgisayarın bir Java 2 sanal makinesine sahip olması gerekir.
  • Sürüm 5 için, istemci bilgisayarın Java 2 sanal makinesine sahip bir tarayıcıya sahip olması gerekir.

Bir web hizmeti yazarken, hangi tür istemcileri hedeflediğimizi kendimize sormalıyız. Mümkün olduğunca çok sayıda istemciyi hedeflemek istiyorsak, tarayıcılara yalnızca HTML gönderen bir uygulama yazmalıyız (javascript veya applet içermeyen). Bir intranet ortamında çalışıyorsanız ve bu ortamdaki bilgisayarların yapılandırmasını kontrol edebiliyorsanız, istemci açısından daha katı şartlar koyabilirsiniz; bu durumda önceki sürüm 5 kabul edilebilir olabilir.

Sürüm 4 ve 5, sunucu tarafından gönderilen HTML akışında ihtiyaç duydukları bilgileri bulan web istemcileridir. Çoğu zaman bu akış üzerinde kontrolümüz yoktur. Bu durum, ağda mevcut olan ve başka biri tarafından yönetilen bir web hizmeti için bir istemci yazdığımızda geçerlidir. Bir örnek verelim. Vergi hesaplama simülasyon hizmetimizin X şirketi tarafından yazıldığını varsayalım. Şu anda hizmet, simülasyonları bir HTML tablosunda gönderiyor ve istemcimiz bu durumu kullanarak verileri alıyor. Böylece, sunucunun yanıtındaki her satırı şu düzenli ifadeyle karşılaştırıyor:

         // simülasyon tablosundaki bir satırın şablonu
        Pattern ptnSimulation=Pattern.compile("<tr>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*</tr>");

Şimdi, uygulama geliştiricisinin yanıtın görsel görünümünü değiştirerek simülasyonları bir tablo yerine şu biçimdeki bir listeye yerleştirdiğini varsayalım:

Résultat des simulations
<ul>
    <li>oui,2,200000,22504
  <li>non,2,200000,33388
</ul>  

Bu durumda, web istemcimizin yeniden yazılması gerekecektir. İşte bu, kendi kontrolümüz altında olmayan uygulamaların web istemcileri üzerinde sürekli olarak duran bir tehdittir. XML bu soruna bir çözüm sunabilir:

  • HTML yerine, simülasyon hizmeti XML üretecektir. Örneğimizde bu şöyle olabilir:
<simulations>
    <entetes marie="marié" enfants="enfants" salaire="salaire" impot="impôt"/>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504" />
  <simulation marie="non" enfants="2" salaire="200000" impot="33388" />
</simulations>
  • Bu yanıta, tarayıcılara bu yanıtın görsel olarak nasıl görüntüleneceğini belirten bir stil sayfası eklenebilir. XML
  • Programlanmış web istemcileri bu stil sayfasını yok sayar ve bilgileri doğrudan yanıtın XML akışından alır

Hizmetin tasarımcısı, sağladığı sonuçların görsel sunumunu değiştirmek isterse, XML dosyasını değil, stil sayfasını değiştirecektir. Stil sayfası sayesinde tarayıcılar yeni görsel biçimi gösterecek ve programlanmış web istemcilerinin değiştirilmesine gerek kalmayacaktır. Böylece, simülasyon hizmetimizin yeni sürümleri şu şekilde yazılabilir:

  • sürüm 6: hizmet, tarayıcılara yönelik bir stil sayfası ile birlikte XML yanıtını sağlar
  • sürüm 7: istemci, sunucunun XML yanıtını kullanan bağımsız bir grafik uygulamasıdır
  • sürüm 8: istemci, sunucudan gelen XML yanıtını kullanan bir Java uygulamasıdır