Skip to content

7. QuiEst Uygulaması

Burada, eğitim amaçlı olması nedeniyle basit tutulması gereken önceki uygulamalardan biraz daha gelişmiş bir Struts uygulamasını anlatıyoruz.

7.1. users sınıfı

Bir Unix makinesindeki kullanıcı bilgilerini depolayan bir Java sınıfımız var. Bu bilgiler üç ayrı dosyada kaydedilir:

  • /etc/passwd: kullanıcı listesi
  • /etc/group: grup listesi
  • /etc/aliases: e-posta takma adları listesi

Bu üç dosyanın içeriği şu şekildedir:

- /etc/passwd

Bu dosyadaki satırlar şu biçimdedir:


    login:pwd:uid:gid:id:dir:shell

ile

login
kullanıcının kullanıcı adı
pwd
şifrelenmiş şifresi
uid
kullanıcı numarası
gid
grup numarası
id
kimliği
dir
bağlantı dizini
shell
komut yorumlayıcısı

Böylece bir kullanıcının satırı şu şekilde olabilir:

dupond:xg675SDFEkl09:110:57:Guillaume Dupond:/home/iup2-auto/dupond:/bin/bash

Yukarıdaki kullanıcı 110 numaralıdır ve 57 numaralı gruba aittir. 57 numaralı grubun tanımı /etc/group dosyasında bulunur.

- /etc/group

Bu dosyanın satırları şu biçimdedir:


    nomGroupe:pwd:gid:membre1,membre2,....

ile

nomGroupe
grup adı
pwd
şifrelenmiş şifresi - genellikle bu alan boştur
gid
grup numarası
membrei
kullanıcı giriş bilgileri - bu alan boş olabilir

Böylece, yukarıdaki 57 numaralı grubun satırı şu şekilde olabilir:

iup2-auto::57:

Bu, 57 numaralı grubun adının iup2-auto olduğunu gösterir.

- /etc/aliases

Bu dosyadaki satırlar şu biçimdedir:

alias:[tab]login

ile

alias
takma ad
[tab]
bir veya daha fazla sekme
login
takma adın ait olduğu kullanıcının oturum açma adı

Böylece şu satır


    guillaume.dupond:    dupond

satırı, guillaume.dupond takma adının dupond kullanıcı adına ait olduğu anlamına gelir. Takma adların e-posta adreslerinde kullanıldığını hatırlayalım. Dolayısıyla, önceki örnekte Unix makinesinin adı shiva.istia.univ-angers.fr ise, guillaume.dupond@shiva.istia.univ-angers.fr adresine gönderilen bir e-posta, bu makinedeki dupond kullanıcı adının posta kutusuna teslim edilecektir.

Burada users sınıfının arayüzünün tamamıyla değil, sadece oluşturucusu ve birkaç yöntemiyle ilgileneceğiz:

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

public class users{


   // özellikler
  private Hashtable usersByLogin=new Hashtable();       // giriş --> kullanıcı adı, şifre, ..., dizin
    private ArrayList erreurs=new ArrayList();             // hata mesajları listesi

....

   // oluşturucu
  public users(String usersFileName, String groupsFileName, String aliasesFileName) throws Exception {
        // usersFileName : şu biçimdeki satırları içeren kullanıcı dosyasının adı
         // login:pwd:uid:gid:id:dir:shell
         // groupsFileName : şu biçimdeki satırları içeren gruplar dosyasının adı
         // ad:şifre:numara:üye1,üye2,..
         // aliasesFileName : şu biçimdeki satırları içeren takma ad dosyasının adı
         // takma ad:[tab]login
         // usersByLogin sözlüğünü oluşturur
....
    }// oluşturucu

     // kullanıcı listesi
  public Hashtable getUsersByLogin(){
    return usersByLogin;
  }

   // hatalar
  public ArrayList getErreurs(){
    return erreurs;
  }
usersByLogin
Anahtarları passwd dosyasındaki kullanıcı adları olan bir sözlük (Hashtable). Anahtara atanan değer, kullanıcı adıyla ilişkili passwd dosyası satırındaki 7 alanı içeren bir dize dizisidir (String [7]). Satırda 7'den az alan varsa, bazı alanlar boş olabilir.
erreurs
hata mesajları listesi - hata yoksa boştur

7.2. Web uygulaması

Aşağıdaki web uygulamasını (form sayfası) oluşturmayı planlıyoruz:

no
ad
tür HTML
rol
1
cmbLogins
<select ...>...</select>
hakkında bilgi talep edilebilecek tüm giriş bilgilerinin listesini gösterir
2
btnChercher
<input type="submit" ...>
arama işlemini başlatmak için

Kullanıcı [Chercher] (2) düğmesine bastığında, (1) numaralı giriş bilgisi users türündeki bir U nesnesinden istenir. Giriş bilgisi mevcutsa, şu yanıt alınır (bilgi sayfası):

Yukarıda tarayıcının URL'inde görüldüğü gibi, form parametreleri bir GET aracılığıyla sunucuya gönderilir. Dolayısıyla, bu parametreleri içeren URL'i doğrudan tarayıcıya verebiliriz. Burada, var olmayan bir kullanıcı adı girmek için bunu yapıyoruz. Aşağıdaki yanıtı alıyoruz (hata sayfası):

7.3. Uygulamanın mimarisi

Bu mimaride aşağıdaki bileşenleri buluyoruz:

  • görünümler:
    • logins.jsp, oturum açma bilgilerinin listesini görüntülemek için kullanılır (görünüm 1)
    • infos.jsp, bir oturum açma bilgisini görüntülemek için kullanılır (görünüm 2)
    • erreurs.jsp, hata listesini görüntülemek için kullanılır (görünüm 3)
  • eylemler tarafından kullanılan ActionForm türü formlar:
    • formLogins, logins.jsp formundaki verileri toplamak için kullanılır
  • Eylemler:
    • SetupLoginAction, formulaire.jsp'in içeriğini hazırlar ve ardından bu görünümü görüntüler
    • InfosLoginAction, logins.jsp'in içeriğini sunucuya gönderildikten sonra işleyen
    • ForwardAction: [Retour vers le formulaire] bağlantısını işleyen, infos.jsp ve erreurs.jsp görünümleri
  • eylemlerin verilerini almak için kullandıkları users iş sınıfı
  • passwd, group ve aliases adlı üç düz dosya tarafından sağlanan model

7.4. Web uygulamasının yapılandırma dosyaları

7.4.1. server.xml dosyası

Uygulamanın bağlamı /strutsquiest2 olarak adlandırılacaktır. Bu nedenle, Tomcat'in server.xml dosyasına aşağıdaki satır eklenecektir:

    <Context path="/strutsquiest2" docBase="..." />

Bu işlem tamamlandıktan sonra, yeni bağlamın dikkate alınması için gerekirse Tomcat'i yeniden başlatırız. URL http://localhost:8080/strutsquiest2 adresini çağırarak bağlamın geçerliliğini kontrol edebiliriz.

7.4.2. web.xml dosyası

Uygulamanın web.xml yapılandırma dosyası aşağıdaki gibi olacaktır:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
    <servlet>
        <servlet-name>strutsquiest2</servlet-name>
        <servlet-class>istia.st.struts.quiest.Quiest2ActionServlet</servlet-class>
        <init-param>
            <param-name>config</param-name>
            <param-value>/WEB-INF/struts-config.xml</param-value>            
        </init-param>
        <init-param>
            <param-name>passwdFileName</param-name>
            <param-value>data/passwd</param-value>            
        </init-param>
        <init-param>
            <param-name>groupFileName</param-name>
            <param-value>data/group</param-value>            
        </init-param>        
    </servlet>

    <servlet-mapping>
        <servlet-name>strutsquiest2</servlet-name>
        <url-pattern>*.do</url-pattern>        
    </servlet-mapping>
</web-app>

Bu web.xml dosyası bir yenilik getiriyor. Struts denetleyicisi artık org.apache.struts.action.ActionServlet değil, burada istia.st.struts.quiest.Quiest2ActionServlet olarak adlandırdığımız türetilmiş bir sınıftır. Bu sayede, passwdFileName (passwd dosyasının konumu) ve groupFileName (group dosyasının konumu) olmak üzere iki başlatma parametresini alabileceğiz. Aliases dosyası bu uygulamada gereksizdir.

7.4.3. struts-config.xml dosyası

struts-config.xml dosyası şu şekilde olacaktır:

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

<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<struts-config>
    <form-beans>
        <form-bean name="formLogins" type="org.apache.struts.action.DynaActionForm">
            <form-property name="cmbLogins" type="java.lang.String" initial=""/>
            <form-property name="tLogins" type="java.lang.String[]"/>            
        </form-bean>            
    </form-beans>

    <action-mappings>

      <action
          path="/init"
            name="formLogins"
            validate="false" 
            scope="session"
          type="istia.st.struts.quiest.SetupLoginsAction"
      >
            <forward name="afficherLogins" path="/vues/logins.jsp"/>
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>            
        </action>

      <action
          path="/infosLogin"
            name="formLogins"
            validate="false" 
            scope="session"
          type="istia.st.struts.quiest.InfosLoginAction"
      >
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
            <forward name="afficherInfos" path="/vues/infos.jsp"/>            
        </action>

      <action
          path="/retourLogins"
            parameter="/vues/logins.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />

    </action-mappings>

        <message-resources 
          parameter="istia.st.struts.quiest.ApplicationResources"
    null="false"
  />    

</struts-config>

Burada üç ana bölüm bulunmaktadır:

  • <form-beans> bölümünde formların tanımlanması
  • <action-mappings> bölümünde eylemlerin tanımlanması
  • <message-ressources> içindeki kaynak dosyasının bildirimi

7.4.4. Uygulamadaki form nesneleri (bean'ler)

    <form-beans>
        <form-bean name="formLogins" type="org.apache.struts.action.DynaActionForm">
            <form-property name="cmbLogins" type="java.lang.String" initial=""/>
            <form-property name="tLogins" type="java.lang.String[]"/>            
        </form-bean>            
    </form-beans>    

Uygulamamızda, formLogins adlı ve DynaActionForm'ten türetilmiş bir form bean'i bulunmaktadır. Bu bean, aşağıdaki durumlarda kullanılacaktır:

  • 1 numaralı görünümün görüntülenmesi için gerekli verileri içermek
  • kullanıcı 1 numaralı görünümdeki formu gönderdiğinde (submit) form değerlerini almak

formLogins bean'inin yapısı, 1 numaralı görünümün formuyla bağlantılıdır. Bunu inceleyelim:

No
ad
tür HTML
rol
1
cmbLogins
<select ...>...</select>
hakkında bilgi talep edilebilecek tüm giriş bilgilerinin listesini gösterir
2
btnChercher
<input type="submit" ...>
arama işlemini başlatmak için

Birkaç durumu ayırt edelim:

  • istemciden sunucuya gönderim sırasında, formLogins nesnesi, yukarıdaki HTML formunun değerlerini içermek üzere kullanılır; bu form, [Envoyer] düğmesi ile gönderilecektir. Bu nedenle, kullanıcının seçtiği giriş bilgisini içeren HTML, cmbLogins ve c.a.d alanlarının değerlerini alacak bir cmbLogins alanı gereklidir.
  • Sunucudan istemciye, formLogins nesnesi, 1 numaralı görünümün başlangıç içeriğini sağlamak için kullanılır. Bu nesnenin tLogins alanı, liste 1'in içeriği olarak kullanılacaktır. cmbLogins alanı ise Liste 1'den seçilecek öğenin belirlenmesini sağlayacaktır.

7.4.5. Uygulamadaki eylemler

Eylemler, Action türü veya türevleri olan nesneler tarafından gerçekleştirilir. Eylemlerin yapılandırması <action-mappings> etiketleri içinde yapılır:

    <action-mappings>

      <action
          path="/init"
            name="formLogins"
            validate="false" 
            scope="session"
          type="istia.st.struts.quiest.SetupLoginsAction"
      >
            <forward name="afficherLogins" path="/vues/logins.jsp"/>
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>            
        </action>

      <action
          path="/infosLogin"
            name="formLogins"
            validate="false" 
            scope="session"
          type="istia.st.struts.quiest.InfosLoginAction"
      >
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
            <forward name="afficherInfos" path="/vues/infos.jsp"/>            
        </action>

      <action
          path="/retourLogins"
            parameter="/vues/logins.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />

    </action-mappings>

/init komutu

      <action
          path="/init"
            name="formLogins"
            validate="false" 
            scope="session"
          type="istia.st.struts.quiest.SetupLoginsAction"
      >
            <forward name="afficherLogins" path="/vues/logins.jsp"/>
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>            
        </action>

/init eyleminin işleyişini açıklayalım:

  • /init eylemi normalde, kullanıcının URL http://localhost:8080/strutsquiest2/init.do'yi talep ettiği ilk istek-yanıt döngüsünde tek bir kez gerçekleşir
  • formsLogins nesnesi oluşturulur veya geri dönüştürülür. Bu nesne, scope özniteliğinin gerektirdiği şekilde oturumda geri kazanılır (geri dönüşüm) veya yerleştirilir (oluşturma).
  • reset yöntemi çağrılır. Bu yöntemin, ActionForm sınıfında ve türevlerinde varsayılan olarak hiçbir işlem yapmadığını hatırlayalım. Bu yöntem, istemcinin isteğindeki verilerin ActionForm nesnesine kopyalanmasından hemen önce çağrılır ve bu kopyalama işleminden önce nesneyi temizlemek için kullanılır. Burada istemcinin isteği nedir? /init eylemi, istenen URL http://localhost:8080/strutsquiest2/init.do olduğunda tetiklenir. Bu URL nesnesi, bir GET veya bir POST nesnesi tarafından çağrılabilir. Bu isteğe, formLogins'teki alan adlarını taşıyan parametreler eklenmesi yeterlidir; böylece bu alanlar aşağıdaki örnekte gösterildiği gibi başlatılır:

Image

  • istek, cmbLogins (afterpak) parametresini içerir. Dolayısıyla Struts denetleyicisi, bu parametrenin değerini formLogins'teki cmbLogins alanına kopyalamıştır. Ardından SetupLoginsAction eylemi gerçekleştirildi ve logins.jsp görünümünün görüntülenmesiyle sonuçlandı. Bu görünümde, bazı alanları değerlerini formLogins'ten alan bir form bulunmaktadır. Böylece, cmbLogins olarak adlandırılan HTML select alanı, formLogins'teki cmbLogins (=afterpak) alanından değerini almıştır. Bu nedenle, oturum açma bilgileri listesi afterpak oturum açma bilgisi üzerinde konumlandırılmış olarak görünür.
  • Aşağıdaki şekilde tLogins parametresini de ileterek deneme yapabilirsiniz:
http://localhost:8080/strutsquiest2/init.do?cmbLogins=afterpak&tLogins=login1&tLogins=login2

Bu, formLogins alanını {"login1","login2"} dizisiyle başlatır. Ancak, ilerleyen bölümlerde göreceğimiz üzere, SetupLoginsAction eylemi tLogins alanına bir değer atar ve bu şekilde oluşturulan diziyi yeni bir diziyle değiştirir. Dolayısıyla, logins.jsp görünümünde görünen bu yeni dizidir.

  • Yukarıdaki tartışma, biraz karmaşık olsa da, /init eyleminin istemciden gelen parametreler olmadan tetikleneceği varsayımında bulunulamayacağını gösterme açısından önemlidir. Bu nedenle, formLogins'i temizlemek için reset yöntemini kullanmak faydalı olabilir. Bu durumda, DynaActionForm sınıfını türetmemiz gerekir. Burada bunu yapmadık.
    • formLogins sınıfının reset yöntemi çağrıldığında, denetleyici istemciden gelen istek verilerini formLogins sınıfındaki aynı adlı alanlara kopyalar. Normalde /init eylemi, istemciden parametre alınmadan çağrılır; ancak daha önce, istemcinin /init eylemini rastgele parametrelerle çağırmasını engelleyen hiçbir şeyin olmadığını göstermiştik. Bu aşamanın sonunda, cmbLogins ve tLogins alanlarının bir değere sahip olması gayet olasıdır. cmbLogins alanının bu değeri koruyacağını, ancak tLogins alanının korumayacağını görmüştük.
    • Denetleyici daha sonra eylemin validate özniteliğine bakar. Burada bu özniteliğin değeri "false"dır. formLogins'in validate yöntemi çağrılmayacaktır. Dolayısıyla bunu yazmayacağız.
    • SetupLoginsAction nesnesi oluşturulur veya daha önce mevcutsa geri dönüştürülür ve execute yöntemi çalıştırılır. Bu nesnenin tek işlevi, formLogins nesnesinin tLogins alanına bir değer atamaktır. Bu değer, kullanıcı adı dizisidir; bu dizi, users iş sınıfından talep edilecektir. Bu işlem başarısız olabilir. Bu nedenle /init eyleminin ardından iki görünüm gelebilir:
      • users sınıfı oturum açma bilgilerini içeren diziyi sağlayamadıysa erreurs.jsp görünümü
      • aksi takdirde logins.jsp görünümü
    • denetleyici bu iki görünümden birini görüntüleyecektir
    • /init eyleminin istek-yanıt döngüsü tamamlanmıştır.

/infosLogin eylemi

      <action
          path="/infosLogin"
            name="formLogins"
            validate="false" 
            scope="session"
          type="istia.st.struts.quiest.InfosLoginAction"
      >
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
            <forward name="afficherInfos" path="/vues/infos.jsp"/>            
        </action>

/ infosLogin eyleminin işleyişini açıklayalım:

  • /infosLogin eylemi, kullanıcı logins.jsp görünümündeki [Chercher] düğmesine tıkladığında normal olarak gerçekleşir. Bunun üzerine, görünümdeki HTML <form> etiketiyle belirlenen bir istek sunucuya gönderilir:
<html:form name="formLogins" method="get" action="/infosLogin" type="org.apache.struts.action.DynaActionForm">
  • Görüldüğü gibi, istek sunucuya GET yöntemi ile gönderilir. Dolayısıyla kullanıcı bunu elle girebilir:

Image

  • formsLogins nesnesi oluşturulur veya geri dönüştürülür. Bu nesne, scope özniteliğinin gerektirdiği şekilde oturumda geri alınır (geri dönüşüm) veya yerleştirilir (oluşturma).
  • Reset yöntemi, istemciden gelen istek verilerinin ActionForm nesnesine kopyalanmasından hemen önce çağrılır. Bu yöntem normalde http://localhost:8080/strutsquiest2/infosLogin.do?cmbLogins=xx biçimindedir; burada xx, oturum açma adları listesinden seçilen bir oturum açma adıdır. Ancak kullanıcı, önceki URL nesnesini rastgele parametreler geçirerek kullanmışsa, bu değer herhangi bir şey olabilir. Aşağıdaki sayfa dizisini ele alalım:

Image

  • /infosLogin eylemi, cmbLogins=xx&tLogins=login1&tLogins=login2 parametre dizisiyle çağrılmıştır. Dolayısıyla, formLogins'in cmbLogins ve tLogins alanları sırasıyla "xx" ve {"login1","login2"} değerlerini alacaktır. /infosLogin eylemi, "xx" kullanıcı adıyla ilişkili bilgileri users iş sınıfından isteyecektir. users sınıfı, bu kullanıcı adının mevcut olmadığını yanıtlayacaktır. Bu nedenle yukarıda gösterilen görünüm ortaya çıkmaktadır. Şimdi, yukarıdaki [Retour au formulaire] bağlantısını kullanalım:

Image

  • [Retour au formulaire] bağlantısı tarafından tetiklenen eylem /retourLogins'tir. Bu eylem, herhangi bir ara işlem yapmadan sadece logins.jsp görünümünü görüntüler. Hatırlanacağı üzere, tLogins alanı, logins.jsp görünümündeki oturum açma bilgisi listesini doldurmak için kullanılır. Kullanıcı bu değeri {"login1","login2"} olarak değiştirdiği için, artık listede bu iki oturum açma bilgisi görünmektedir. Yine, bir uygulamanın işleyişinde, kullanıcı veya bir program tarafından belirlenen keyfi parametrelerin mutlaka dikkate alınması gerektiğinin altını çizmek zorundayız. Burada ortaya çıkan sorunun çözümü, [Retour au formulaire] bağlantısının /init eylemine yönlendirilmesi olacaktır. Böylelikle doğru oturum açma bilgisi listesini elde edeceğimizden emin oluruz.
  • Şimdi, /infosLogin eylemine yönelik şu türden normal bir isteğe geri dönelim:

http://localhost:8080/strutsquiest2/infosLogin.do?cmbLogins=afterpak

  • Struts denetleyicisi, ActionForm nesnesinin cmbLogins alanına bir değer atayacaktır. tLogins alanına ise bir değer atanmayacaktır (gönderilen istekte karşılık gelen bir alan bulunmamaktadır). Bu çalışma şekli bizim için uygundur. Dolayısıyla, formLogins için özel bir reset yöntemi yazmamız gerekmeyecek.
    • formLogins'in sıfırlama yöntemi çağrıldığında, denetleyici istemcinin isteğindeki verileri formLogins'teki aynı adlı alanlara kopyalar. cmbLogins alanı, kullanıcı tarafından seçilen kullanıcı adı (afterpak) değerini alacaktır.
    • Kontrolör daha sonra eylemin validate özniteliğine bakar. Burada bu özniteliğin değeri "false"dır. formLogins'in validate yöntemi çağrılmayacaktır.
    • InfosLoginAction nesnesi oluşturulur veya önceden mevcutsa yeniden kullanılır ve execute yöntemi çalıştırılır. Bu nesnenin görevi, cmbLogins kullanıcı adıyla ilişkili bilgileri almaktır. Bu bilgiler, users iş sınıfından talep edilecektir. Bu işlem başarısız olabilir (örneğin, kullanıcı adı mevcut değilse). Bu nedenle /infosLogin eyleminin ardından iki görünüm gelebilir:
      • users sınıfı istenen bilgileri sağlayamadıysa erreurs.jsp görünümü
      • aksi takdirde infos.jsp görünümü
    • denetleyici bu iki görünümden birini görüntüleyecektir
    • /infosLogin eyleminin istek-yanıt döngüsü tamamlanır.

/retourLogins eylemi

      <action
          path="/retourLogins"
            parameter="/vues/logins.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />
  • /retourLogins eylemi, erreurs.jsp ve infos.jsp görünümlerindeki [Retour au formulaire] bağlantısının etkinleştirilmesiyle tetiklenir.
  • Burada eyleme bağlı bir form bulunmamaktadır. Dolayısıyla, doğrudan ForwardAction nesnesinin execute yönteminin yürütülmesine geçilir; bu yöntem, /vues/logins.jsp görünümüne işaret eden bir ActionForward nesnesi döndürür.

7.4.6. Uygulamanın mesaj dosyası

struts-config.xml dosyasının üçüncü bölümü, mesaj dosyasıdır:

        <message-resources 
      parameter="istia.st.struts.quiest.ApplicationResources"
    null="false"
  />        

ApplicationResources.properties dosyası, WEB-INF/classes/istia/st/struts/quiest dizininde yer almaktadır. İçeriği şöyledir:

errors.header=<ul>
errors.footer=</ul>
parametreManquant=<li>Le paramètre [{0}] n'a pas été initialisé</li>
usersException=<li>Erreur d'initialisation de l'application : {0}</li>
loginInconnu=<li>Le login [{0}] n'existe pas</li>

7.5. Görünüm kodları

Aşağıda sunulan görünüm kodlarını anlamayan okuyucuların, form yönetimi dersini tekrar gözden geçirmeleri önerilir.

7.5.1. logins.jsp görünümü

Bu görünümün iki durumda görüntülendiğini hatırlayalım:

  • 1. istek-yanıt döngüsü sırasında /init eylemi çağrıldığında
  • sonraki döngülerde /retourLogins eylemi çağrıldığında

logins.jsp görünümünün kodu şöyledir:

<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>

<html>
    <head>
      <title>Quiest - formulaire</title>
  </head>
  <body background="<html:rewrite page="/images/standard.jpg"/>">
      <center>
        <h2>Application QuiEst</h2>
      <hr>
      <html:form name="formLogins" method="get" action="/infosLogin" type="org.apache.struts.action.DynaActionForm">
          <table>
            <tr>
              <td>Login cherché</td>
            <td>
                <html:select name="formLogins" property="cmbLogins">
                      <html:options name="formLogins" property="tLogins"/>
                  </html:select>
            </td>
            <td>
                <html:submit value="Chercher"/>
            </td>
          </tr>
        </table>
      </html:form>
    </center>
  </body>
</html>

7.5.2. infos.jsp görünümü

Bu görünüm, /infosLogin eylem çağrısının başarıyla gerçekleştirilmesi durumunda görüntülenir. Kodu şöyledir:

<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>

<html>
    <head>
      <title><bean:write name="infosLoginBean" scope="request" property="titre"/></title>
  </head>
  <body background="<html:rewrite page="/images/standard.jpg"/>">
      <h2><bean:write name="infosLoginBean" scope="request" property="titre"/></h2>
    <hr>
    <table border="1">
            <tr>
                <th>login</th><th>pwd</th><th>uid</th><th>gid</th><th>id</th><th>dir</th><th>shell</th>
            </tr>
            <tr>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[0]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[1]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[2]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[3]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[4]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[5]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[6]"/></td>            
            </tr>                                                                                                                                                                                                                
    </table>                                      
    <br>
    <html:link page="/retourLogins.do">
            Retour au formulaire
        </html:link>        
  </body>
</html>

Bu görünüm, /infosLogin eylemi tarafından sorguya yerleştirilen infosLoginBean adlı bir nesneyi kullanır. Bu nesnenin iki alanı vardır:

String titre;                        // görünümde görüntülenecek başlık
String[] infosLogin;        // görünümde görüntülenecek bilgi tablosu

InfosLoginAction sınıfının kodunu ele alırken bu sınıfı ayrıntılı olarak inceleyeceğiz.

7.5.3. erreurs.jsp görünümü

Bu görünüm, /init veya /infosLogin eylemleri bir hata ile sonuçlandığında görüntülenir. Kod şöyledir:

<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>

<html>
    <head>
      <title>Application QuiEst - erreurs</title>
  </head>
  <body background="<html:rewrite page="/images/standard.jpg"/>">
        <h2 align="center">Application QuiEst - Erreurs</h2>
        <hr>
      <h2>Les erreurs suivantes se sont produites</h2>
        <html:errors/>
    <html:link page="/retourLogins.do">
            Retour au formulaire
        </html:link>    
  </body>
</html>

7.6. Java sınıfları

web.xml dosyası bir Java sınıfına atıfta bulunur:

<web-app>
    <servlet>
      <servlet-name>strutsquiest2</servlet-name>
    <servlet-class>istia.st.struts.quiest.Quiest2ActionServlet</servlet-class>
....
  </servlet>

...
</web-app>

struts-config.xml yapılandırma dosyası ise iki Java sınıfına atıfta bulunmaktadır:

      <action
          path="/infosLogin"
            name="formLogins"
            validate="false" 
            scope="session"
          type="istia.st.struts.quiest.InfosLoginAction"
      >
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
            <forward name="afficherInfos" path="/vues/infos.jsp"/>            
        </action>

      <action
          path="/init"
            name="formLogins"
            validate="false" 
            scope="session"
          type="istia.st.struts.quiest.SetupLoginsAction"
      >
            <forward name="afficherLogins" path="/vues/logins.jsp"/>
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>            
        </action>

7.6.1. Quiest2ActionServlet sınıfı

Quiest2ActionServlet sınıfı, Struts denetleyicisi sınıfı olan ActionServlet sınıfından türetilmiştir. ActionServlet sınıfını, init yöntemini özelleştirmek için türetiriz. Nitekim, servletin ilk yüklenmesi sırasında yalnızca bir kez yürütülen bu yöntem, users türünde bir iş nesnesi oluşturmamızı sağlayacaktır. Bu nesnenin aslında yalnızca bir kez oluşturulması gerekir ve init yöntemi bu oluşturma işlemini gerçekleştirmek için uygun bir yerdir. users nesnesinin oluşturulabilmesi için passwd ve group adlı iki dosyaya ihtiyaç vardır. Bu iki dosyanın konumu, uygulamanın web.xml dosyasında servlet’e parametre olarak aktarılır:

    <servlet>
      <servlet-name>strutsquiest2</servlet-name>
    <servlet-class>istia.st.struts.quiest.Quiest2ActionServlet</servlet-class>
    <init-param>
        <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <init-param>
        <param-name>passwdFileName</param-name>
      <param-value>data/passwd</param-value>
    </init-param>
    <init-param>
        <param-name>groupFileName</param-name>
      <param-value>data/group</param-value>
    </init-param>        
  </servlet>

Servlet kodu şöyledir:

package istia.st.struts.quiest;

import java.util.*;
import javax.servlet.*;
import org.apache.struts.action.*;
import istia.st.users.*;

public class Quiest2ActionServlet
  extends ActionServlet {

   // servlet öznitelikleri
  private users u = null;
  private ActionErrors erreurs = new ActionErrors();
  private String[] tLogins;

   //başlatma
  public void init() throws ServletException {

    // üst sınıfı başlatmayı unutmayın
    super.init();

     // yerel değişkenler
    final String[] initParams = {"passwdFileName", "groupFileName"};
    Properties params = new Properties();

    // servletin başlatma parametrelerini alıyoruz
    ServletConfig config = getServletConfig();
    String servletPath = config.getServletContext().getRealPath("/");
    for (int i = 0; i < initParams.length; i++) {
      String valeur = config.getInitParameter(initParams[i]);
      if (valeur == null) {
        erreurs.add(ActionErrors.GLOBAL_ERROR, new ActionError("parametreManquant", initParams[i]));
        valeur = "";
      }
       // parametreyi saklıyoruz
      params.setProperty(initParams[i], valeur);
    } //for
     // başlatma hatası varsa geri dön
    if (erreurs.size() != 0) {
      return;
    }
     // users nesnesi oluşturulur
    try {
      u = new users(servletPath + "/" + params.getProperty("passwdFileName"),
                    servletPath + "/" + params.getProperty("groupFileName"), null);
    }
    catch (Exception ex) {
      erreurs.add(ActionErrors.GLOBAL_ERROR, new ActionError("usersException", ex.getMessage()));
      return;
    } //catch
     // giriş bilgilerinin listesi alınır
    tLogins = new String[u.getUsersByLogin().size()];
    Enumeration eLogins = u.getUsersByLogin().keys();
    for (int i = 0; i < tLogins.length; i++) {
      tLogins[i] = (String) eLogins.nextElement();
    }
     // giriş bilgilerini sıralıyoruz
    Arrays.sort(tLogins);
  } //başlatma

   // servletin özel bilgilerine erişim yöntemi
  public Object[] getInfos() {
    return new Object[] {erreurs, u, tLogins};
  }
}

Özetle, init yönteminin işleyişi şu şekildedir:

  • öncelikle, üst sınıfın (ActionServlet) init yöntemine çağrı yapılır, böylece bu sınıf doğru şekilde başlatılır
  • ardından başlatma parametreleri okunur. Eksik parametre varsa, özel ActionErrors hatalar özniteliği doldurulur.
  • Başlatma parametreleri mevcutsa, bir users nesnesi oluşturulur. Bu oluşturma işlemi bir istisna oluşturabilir. Bu durumda, ActionErrors hatalar özniteliği doldurulur.
  • Oluşturma işlemi başarılı olursa, oluşturulan nesneden tüm oturum açma bilgilerinin listesi alınır ve bu liste bir diziye sıralanarak özel String[] tLogins özniteliğine yerleştirilir.
  • Oluşturulan users nesnesi, özel users u özniteliğinde saklanır.
  • getInfos adlı genel yöntem, üç özel özniteliği (u, hatalar, tLogins) bir nesne dizisi içinde elde etmeyi sağlar.

7.6.2. SetupLoginsAction sınıfı

Bu eylemin amacı, DynaActionForm formLogins nesnesini başlatmaktır. Oturuma yerleştirilen bu nesnenin daha sonra yeniden başlatılmasına gerek kalmayacaktır. Dolayısıyla SetupLoginsAction eylemi yalnızca bir kez gerçekleştirilir. Kod şöyledir:

package istia.st.struts.quiest;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;

public class SetupLoginsAction
  extends Action {

  public ActionForward execute(ActionMapping mapping, ActionForm form,
                               HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException {

     // görüntülenecek formun hazırlanması
     // kontrol servletinden bilgiler alınır
     // bilgiler=(ActionErrors hatalar, kullanıcılar u, String[] tLogins)
    Object[] infos = ( (Quiest2ActionServlet)this.getServlet()).getInfos();

     // başlatma hataları oldu mu?
    ActionErrors erreurs = (ActionErrors) infos[0];
    if (!erreurs.isEmpty()) {
      this.saveErrors(request, erreurs);
      return mapping.findForward("afficherErreurs");
    }

     // giriş bilgilerini forma giriyoruz
    DynaActionForm formLogins=(DynaActionForm) form;
    formLogins.set("tLogins",infos[2]);
    return mapping.findForward("afficherLogins");
  }
}

Tüm Struts eylemlerinde olduğu gibi, kod execute yönteminde bulunur. Bu yöntem:

  • Struts denetleyicisinden, denetleyicinin init yöntemiyle depoladığı bilgileri alır. Bunu sağlayan, Action sınıfındaki getServlet() yöntemidir.
  • Bunlar arasında ActionErrors denetleyici hataları özniteliği de bulunmaktadır. Bu hata listesi boş değilse, sorguya eklenir ve erreurs.jsp görünümünün görüntülenmesi istenir.
  • Hata listesi boşsa, denetleyici tarafından başlangıçta oluşturulan oturum açma listesi, formLogins bean'inin tLogins alanına atanır. Ardından, oturum açma listesini gösterecek olan logins.jsp görünümünün görüntülenmesi istenir.

7.6.3. InfosLoginBean ve InfosLoginAction sınıfları

InfosLoginAction eyleminin amacı, kullanıcı tarafından seçilen oturum açma bilgileriyle ilgili verileri almak ve bunları kullanıcıya sunmaktır. Veriler, InfosLoginBean türünde bir nesne içinde toplanacaktır:

package istia.st.struts.quiest;

public class InfosLoginBean implements java.io.Serializable{

   // bilgi sayfası için gerekli bilgileri içeren bean
  private String titre;
  private String[] infosLogin;

  // oluşturucu
  public InfosLoginBean(String titre, String[] infosLogin){
    this.titre=titre;
    this.infosLogin=infosLogin;
  }

   // alıcılar
  public String getTitre(){
    return this.titre;
  }
  public String[] getInfosLogin(){
    return this.infosLogin;
  }
  public String getInfosLogin(int i){
    return this.infosLogin[i];
  }
}

Önceki sınıf, c.a.d adlı bir bean'dir. Bu, özel bir T unAttribut özniteliğinin otomatik olarak iki özel yönteme atandığı bir Java sınıfıdır:

  • void setUnAttribut(T değer){unAttribut=değer;}
  • T getUnAttribut(){ return unAttribut;}

get ve set yöntemlerinin özel sözdizimine dikkat edilmelidir. Öznitelik T[] unAttribut şeklinde bir dizi ise, dizinin elemanları için get ve set yöntemleri oluşturulabilir:

  • void setUnAttribut(T değer, int i){unAttribut[i]=değer;}
  • T getUnAttribut(int i){ return unAttribut[i];}

Daha iyi anlamak için, InfosLoginAction eyleminin ardından gönderilmesi gereken infos.jsp görünümünün kodunu tekrar inceleyelim:

<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>

<html>
    <head>
      <title><bean:write name="infosLoginBean" scope="request" property="titre"/></title>
  </head>
  <body background="<html:rewrite page="/images/standard.jpg"/>">
      <h2><bean:write name="infosLoginBean" scope="request" property="titre"/></h2>
    <hr>
    <table border="1">
            <tr>
                <th>login</th><th>pwd</th><th>uid</th><th>gid</th><th>id</th><th>dir</th><th>shell</th>
            </tr>
            <tr>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[0]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[1]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[2]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[3]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[4]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[5]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[6]"/></td>            
            </tr>                                                                                                                                                                                                                
    </table>                                      
    <br>
    <html:link page="/retourLogins.do">
            Retour au formulaire
        </html:link>        
  </body>
</html>

Şu etiketi ele alalım:

<bean:write name="infosLoginBean" scope="request" property="titre"/>

Sorguya (scope) eklenen infosLoginBean (name) nesnesinin “title” (property) alanının değerini yazmasını ister. Yazılacak değer, request.getAttribute("infosLoginBean") komutuyla elde edilecektir.getTitre(). Dolayısıyla, InfosLoginBean sınıfında getTitre yöntemi bulunmalıdır. Durum böyledir. Etiket

<bean:write name="infosLoginBean" scope="request" property="infosLogin[0]"/>

etiketi, sorguya eklenen infosLoginBean nesnesinin infosLogin[0] öğesinin değerini yazmayı talep eder. Yazılacak değer, request.getAttribute("infosLoginBean").getInfosLogin(0) ile elde edilecektir. Bu nedenle, InfosLoginBean sınıfında getInfosLogin(int i) yöntemi bulunmalıdır. Bu durum geçerlidir.

InfosLoginAction sınıfının amacı, kullanıcı tarafından seçilen bir kullanıcı adı temelinde önceki InfosLoginBean nesnesini oluşturmaktır. Kod şöyledir:

package istia.st.struts.quiest;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;
import istia.st.users.*;

public class InfosLoginAction
  extends Action {

  public ActionForward execute(ActionMapping mapping, ActionForm form,
                               HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException {

     // bir oturum açma bilgisiyle ilgili bilgileri görüntülemelidir

     // kontrol servletinden bilgileri alır
     // bilgiler=(ActionErrors hatalar, kullanıcılar u, LoginBean[] tLogins)
    Object[] infos = ( (Quiest2ActionServlet)this.getServlet()).getInfos();

     // başlatma hataları oldu mu?
    ActionErrors erreurs = (ActionErrors) infos[0];
    if (!erreurs.isEmpty()) {
      this.saveErrors(request, erreurs);
      return mapping.findForward("afficherErreurs");
    }

     // öncelikle bu oturum açma bilgilerini al
    String login = (String) ( (DynaActionForm) form).get("cmbLogins");

    // bir şey var mı?
    if (login == null) {
      // Bu normal değil – giriş bilgilerini içeren formu geri gönderiyoruz
            DynaActionForm formLogins=(DynaActionForm) form;
            formLogins.set("tLogins",infos[2]);
            return mapping.findForward("afficherLogins");
    }

     // bir giriş bilgisi var - aranıyor
    String[] infosLogin = (String[]) ( (users) infos[1]).getUsersByLogin().get(login);

     // bulundumu?
    if (infosLogin == null) {
      // giriş bilgisi bulunamadı - hata sayfası görüntüleniyor
      ActionErrors erreurs2=new ActionErrors();
      erreurs2.add(ActionErrors.GLOBAL_ERROR, new ActionError("loginInconnu", login));
      this.saveErrors(request, erreurs2);
      return mapping.findForward("afficherErreurs");
    }

     // giriş bilgisi bulundu - bulunan bilgileri isteğe ekleniyor
    String titre="Application QuiEst - login["+login+"]";
    InfosLoginBean infosLoginBean= new InfosLoginBean(titre,infosLogin);
    request.setAttribute("infosLoginBean",infosLoginBean);
    return mapping.findForward("afficherInfos");
  }

}

execute yönteminin işleyişi şu şekildedir:

  • Struts denetleyicisi tarafından başlatma sırasında toplanan bilgiler alınır. Denetleyici herhangi bir hata kaydetmişse, işlem burada durur ve bu hataların görüntülenmesi istenir.
  • Bir kullanıcı adı olup olmadığı kontrol edilir. Kullanıcı, kullanıcı adı seçim formunu kullanmışsa, kullanıcı adı mevcuttur. Ancak kullanıcı, herhangi bir parametre girmeden eylemin URL'sini doğrudan tarayıcısına girebilir. Kullanıcı adı yoksa, kullanıcı adları listesi yeniden görüntülenir.
  • Kullanıcı adı varsa, users iş sınıfıyla ilişkili bilgiler istenir. Bu sınıf aranan kullanıcı adını bulamazsa, hata sayfası görüntülenir. Aksi takdirde, infos.jsp görünümünün ihtiyaç duyduğu bilgileri içermesi için bir InfosLoginBean nesnesi oluşturulur. Bu nesne isteğe eklenir ve infos.jsp sayfası görüntülenir.

7.7. Dağıtım

Uygulamanın dizin yapısı şu şekildedir:

 

7.8. Sonuç

Bir iş sınıfı kullanan gerçekçi bir uygulamada Struts'u kullandık. Ayrıca, bir istemci tarafından gönderilen isteğe özellikle dikkat edilmesi ve isteğin niteliği hakkında hiçbir varsayımda bulunulmaması gerektiğini gösterdik. Bir istek herhangi bir şekilde olabilir ve her uygulama öncelikle bu isteğin geçerliliğini doğrulamalıdır.