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:
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:
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

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

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

- 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:
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ş

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");
"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 | |
Vergi mükellefinin çocuk sayısı | |
yıllık maaşı | |
ödenecek vergi tutarı | |
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:
Uygulama klasörü aşağıdaki klasörleri ve dosyaları içerir:




"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:

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

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

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:

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:

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

- yanlış bir maaş tespit edildiğinde

- 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ı)



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

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¶m2=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:
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:

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:

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:
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
