Skip to content

2. Java Server Faces

Şimdi Java Server Faces çerçevesini tanıtacağız. Kullanılacak sürüm 2 olacaktır, ancak örnekler çoğunlukla sürüm 1'in özelliklerini göstermektedir. Sürüm 2'den, takip edecek örnek uygulama için gerekli olan özellikleri ele alacağız.

2.1. JSF'in bir web uygulamasındaki yeri

Öncelikle, JSF'in bir web uygulaması geliştirme sürecindeki yerini belirleyelim. Çoğu zaman, bu uygulama aşağıdaki gibi çok katmanlı bir mimari üzerine inşa edilir:

  • [web] katmanı, web uygulamasının kullanıcısıyla temas halinde olan katmandır. Kullanıcı, bir tarayıcı aracılığıyla görüntülenen web sayfaları vasıtasıyla web uygulamasıyla etkileşime girer. JSF bu katmanda yer alır ve yalnızca bu katmanda bulunur;
  • [métier] katmanı, maaş veya fatura hesaplaması gibi uygulama yönetim kurallarını uygular. Bu katman, [web] katmanı aracılığıyla kullanıcıdan ve [DAO] katmanı aracılığıyla veritabanı yönetim sisteminden gelen verileri kullanır,
  • [DAO] katmanı (Veri Erişim Nesneleri), [jpa] katmanı (Java Persistence API) ve JDBC sürücüsü, veritabanındaki verilere erişimi yönetir. [jpa] katmanı, ORM (Nesne-İlişkisel Eşleştirici) işlevi görür. Bu katman, [DAO] katmanı tarafından işlenen nesneler ile ilişkisel bir veritabanındaki verilerin satırları ve sütunları arasında bir köprü görevi görür;
  • katmanların entegrasyonu bir Spring konteyneri veya EJB3 (Enterprise Java Bean) aracılığıyla gerçekleştirilebilir.

Aşağıda JSF'i açıklamak için verilen örneklerde, yalnızca tek bir katman, yani [web] katmanı kullanılacaktır:

JSF'in temellerini öğrendikten sonra, çok katmanlı Java EE uygulamaları geliştireceğiz.

2.2. JSF'in MVC geliştirme modeli

JSF, MVC olarak bilinen mimari modelini (Model – Görünüm – Denetleyici) şu şekilde uygular:

Bu mimari, MVC Tasarım Desenini (Model, Görünüm, Denetleyici) uygular. Bir istemciden gelen isteğin işlenmesi aşağıdaki dört adımda gerçekleşir:

  1. istek – tarayıcı istemcisi, [Faces Servlet] denetleyicisine bir istek gönderir. Bu denetleyici, tüm istemci isteklerini alır. Burası, uygulamanın giriş kapısıdır. Bu, MVC'teki C'dir,
  2. işleme - C denetleyicisi bu talebi işler. Bunu yapmak için, [2a] adlı uygulamaya özgü olay işleyicilerinden yardım alır. Bu işleyiciler, [2b] iş katmanının yardımına ihtiyaç duyabilir. Müşterinin isteği işlendikten sonra, bu işlem çeşitli yanıtlara yol açabilir. Klasik bir örnek şudur:
    • istek düzgün bir şekilde işlenemediğinde bir hata sayfası;
    • aksi takdirde bir onay sayfası,
  3. navigasyon - denetleyici, müşteriye gönderilecek yanıtı (= görünüm) seçer. Müşteriye gönderilecek yanıtın seçilmesi birkaç adımdan oluşur:
    • yanıtı oluşturacak Facelet'i seçmek. Buna V görünümü denir; MVC'teki V. Bu seçim genellikle kullanıcının talep ettiği eylemin yürütülme sonucuna bağlıdır;
    • bu Facelet’e yanıtı oluşturması için ihtiyaç duyduğu verileri sağlamak. Nitekim bu yanıt, çoğu zaman denetleyici tarafından hesaplanan bilgileri içerir. Bu bilgiler, görünümün M modeli olarak adlandırılan kısmı oluşturur; MVC’teki M harfi budur,

Dolayısıyla 3. adım, bir V görünümü seçmek ve bunun için gerekli olan M modelini oluşturmaktan ibarettir.

  1. yanıt - C denetleyicisi, seçilen Facelet’ten kendini görüntülemesini ister. Facelet, C denetleyicisi tarafından hazırlanan M modelini kullanarak, müşteriye göndermesi gereken yanıtın dinamik kısımlarını başlatır. Yanıtın tam biçimi çeşitli şekillerde olabilir: bir HTML akışı, PDF, Excel dosyası vb. olabilir.

Bir JSF projesinde:

  • C denetleyicisi, [javax.faces.webapp.FacesServlet] servletidir. Bu servlet, [javaee.jar] kütüphanesinde bulunur;
  • V görünümleri, Facelets teknolojisini kullanan sayfalarla uygulanır,
  • M modelleri ve olay işleyicileri, genellikle "backing beans" veya daha basitçe "beans" olarak adlandırılan Java sınıfları tarafından uygulanır.

Şimdi, MVC web mimarisi ile katmanlı mimari arasındaki bağlantıyı açıklayalım. Bunlar, bazen birbiriyle karıştırılan iki farklı kavramdır. Tek katmanlı bir JSF web uygulamasını ele alalım:

[web] katmanını JSF ile uygularsak, bir web mimarisine sahip oluruz, ancak bu çok katmanlı bir mimari değildir. Burada, [web] katmanı her şeyi üstlenecektir: sunum, iş mantığı, verilere erişim. JSF ile ise bu işi bean'ler yapacaktır.

Şimdi, çok katmanlı bir web mimarisini ele alalım:

[web] katmanı, herhangi bir çerçeve kullanmadan ve MVC modelini takip etmeden uygulanabilir. Bu durumda gerçekten çok katmanlı bir mimariye sahibiz, ancak web katmanı MVC modelini uygulamamaktadır.

MVC'te, M modelinin V görünümüne ait olduğunu, yani c.a.d'te V görünümü tarafından görüntülenen veri kümesini ifade ettiğini belirtmiştik. MVC'teki M modeline ilişkin sıkça verilen başka bir tanım şöyledir:

Birçok yazar, [web] katmanının sağındaki kısmın MVC'in M modelini oluşturduğunu düşünür. Belirsizlikleri önlemek için:

  • [web] katmanının sağındaki her şeyi ifade ederken "alan modelinden",
  • bir görünüm V tarafından görüntülenen verileri ifade ederken "görünüm modeli"

Bundan sonra, "M modeli" terimi yalnızca bir V görünümünün modelini ifade edecektir.

2.3. Örnek mv-jsf2-01: JSF projesinin bileşenleri

İlk örnekler, JSF 2 ile uygulanan tek web katmanıyla sınırlı olacaktır:

Temel bilgiler edinildikten sonra, çok katmanlı mimarilere sahip daha karmaşık örnekleri inceleyeceğiz.

2.3.1. Projenin Oluşturulması

İlk projemiz olan JSF2'i NetBeans 7 ile oluşturuyoruz.

  
  • [1]'te yeni bir proje oluşturun,
  • [2]'te, [Maven] kategorisini ve [Web Application] proje türünü seçin,
  • [3]'te, yeni projenin klasörünün üst klasörünü belirleyin,
  • [4]'te projeye bir ad verin,
  • [5] adımında bir sunucu seçin. NetBeans 7'de Apache Tomcat ve Glassfish sunucuları arasında seçim yapabilirsiniz. İkisi arasındaki fark, Glassfish'in EJB'leri (Enterprise Java Bean) desteklemesi, Tomcat'in ise desteklememesidir. JSF örneklerimizde EJB kullanılmayacaktır. Dolayısıyla burada herhangi bir sunucu seçilebilir,
  • [6]'te Java 6 Web sürümünü seçeriz,
  • [7]'te ise oluşturulan projeyi seçiyoruz.

Projenin bileşenlerini inceleyelim ve her birinin rolünü açıklayalım.

  • [1]: projenin farklı dalları:
    • [Web Pages]: web sayfalarını (.xhtml, .jsp, .html), kaynakları (görseller, çeşitli belgeler), web katmanının yapılandırmasını ve JSF çerçeve yapısının yapılandırmasını içerecektir;
    • [Source packages]: projenin Java sınıfları;
    • [Dependencies]: proje için gerekli olan ve Maven çerçeve tarafından yönetilen .jar arşivlerini içerecektir;
    • [Java Dependencies]: proje için gerekli olan ve Maven çerçeve tarafından yönetilmeyen .jar dosyaları;
    • [Project Files]: Maven ve NetBeans yapılandırma dosyası,
  • [2] içinde: [Web Pages] dalı,

Bu dalda şu [index.jsp] sayfası bulunmaktadır:


<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/HTML4/loose.dtd">

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        <h1>Hello World!</h1>
    </body>
</html>

Bu, 'Hello World' karakter dizisini büyük harflerle görüntüleyen bir web sayfasıdır.

[META-INF/context.xml] dosyası şöyledir:


<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/mv-jsf2-01"/>
  1. satır, uygulama bağlamının (veya adının) /mv-jsf2-01 olduğunu gösterir. Bu, projenin web sayfalarının http://machine:port/mv-jsf2-01/page biçimindeki bir URL aracılığıyla çağrılacağı anlamına gelir. Bağlam, varsayılan olarak projenin adıdır. Bu dosyayı değiştirmemiz gerekmeyecektir.
  • 'yi [3] olarak değiştirirsek, [Source Packages] dalı,

Bu dal, projenin Java sınıflarının kaynak kodlarını içerir. Burada herhangi bir sınıf bulunmamaktadır. NetBeans, silinebilen varsayılan bir paket oluşturmuştur: [4].

  • [5]'te, [Dependencies] dalında,

Bu dal, proje için gerekli olan ve Maven tarafından yönetilen tüm kütüphaneleri gösterir. Burada listelenen tüm kütüphaneler Maven tarafından otomatik olarak indirilecektir. Bu nedenle bir Maven projesinin İnternet erişimine ihtiyacı vardır. İndirilen kütüphaneler yerel olarak depolanacaktır. Başka bir proje, yerel olarak zaten mevcut olan bir kütüphaneye ihtiyaç duyarsa, bu kütüphane indirilmeyecektir. Göreceğiz ki, bu kütüphane listesi ve bunların bulunduğu depolar, Maven projesinin yapılandırma dosyasında tanımlanmıştır.

  • [6]'te, proje için gerekli olan ve Maven tarafından yönetilmeyen kütüphaneler,
  • [7]'te, Maven projesinin yapılandırma dosyaları:
    • [nb-configuration.xml], NetBeans yapılandırma dosyasıdır. Bu dosya ile ilgilenmeyeceğiz.
    • [pom.xml]: Maven yapılandırma dosyası. POM, Project Object Model anlamına gelir. Bazen bu dosyada doğrudan değişiklik yapmamız gerekebilir.

Oluşturulan [pom.xml] dosyası şöyledir:


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>istia.st</groupId>
  <artifactId>mv-jsf2-01</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>mv-jsf2-01</name>

  <properties>
    <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-web-api</artifactId>
      <version>6.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
          <compilerArguments>
            <endorseddirs>${endorsed.dir}</endorseddirs>
          </compilerArguments>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.1.1</version>
        <configuration>
          <failOnMissingWebXml>false</failOnMissingWebXml>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>2.1</version>
        <executions>
          <execution>
            <phase>validate</phase>
            <goals>
              <goal>copy</goal>
            </goals>
            <configuration>
              <outputDirectory>${endorsed.dir}</outputDirectory>
              <silent>true</silent>
              <artifactItems>
                <artifactItem>
                  <groupId>javax</groupId>
                  <artifactId>javaee-endorsed-api</artifactId>
                  <version>6.0</version>
                  <type>jar</type>
                </artifactItem>
              </artifactItems>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

</project>
  • 5-8. satırlar, Maven projesi tarafından oluşturulacak Java nesnesini (artefakt) tanımlar. Bu bilgiler, proje oluşturulurken kullanılan sihirbazdan alınmıştır:

Bir Maven nesnesi dört özellik ile tanımlanır:

  • [groupId]: Paket adına benzeyen bir bilgidir. Böylece, Spring çerçevesinin kütüphaneleri groupId=org.springframework değerine sahipken, JSF çerçevesinin kütüphaneleri groupId=javax.faces değerine sahiptir,
  • [artifactId]: Maven nesnesinin adı. [org.springframework] grubunda ise şu artifactId'ler bulunur: spring-context, spring-core, spring-beans, ... [javax.faces] grubunda ise artifactId ve jsf-api, bulunur
  • [version]: Maven artefaktının sürüm numarası. Dolayısıyla org.springframework.spring-core artefaktının sürümleri şunlardır: 2.5.4, 2.5.5, 2.5.6, 2.5.6.SECO1, ...
  • [packaging]: Artefaktın aldığı biçim; çoğunlukla war veya jar şeklindedir.

Dolayısıyla Maven projemiz, [istia.st] grubu (satır 5) içinde, [mv-jsf2-01] adında (satır 6) ve [1.0-SNAPSHOT] sürümüne sahip (satır 7) bir [war] (satır 8) oluşturacaktır (7. satır) adında bir sürüm olacaktır. Bu dört bilgi, bir Maven artefaktını benzersiz bir şekilde tanımlamalıdır.

17-24. satırlar, Maven projesinin bağımlılıklarını, yani proje için gerekli kütüphanelerin listesini gösterir. Her kütüphane dört bilgi ile tanımlanır (groupId, artifactId, sürüm, paketleme). Burada olduğu gibi packaging bilgisi eksik olduğunda, packaging jar dosyası kullanılır. Buraya, kütüphanenin projenin hangi aşamalarında gerekli olduğunu belirleyen scope bilgisi eklenir. Varsayılan değer olan compile, kütüphanenin derleme ve çalıştırma aşamalarında gerekli olduğunu belirtir. provided değeri ise kütüphanenin derleme aşamasında gerekli olduğunu, ancak çalıştırma aşamasında gerekli olmadığını ifade eder. Burada, çalıştırma aşamasında kütüphane Tomcat 7 sunucusu tarafından sağlanacaktır.

2.3.2. Projenin çalıştırılması

Projeyi çalıştırıyoruz:

[1] dosyasında Maven projesi çalıştırılır. Tomcat sunucusu, henüz başlatılmamışsa bu aşamada başlatılır. Ayrıca bir tarayıcı açılır ve proje bağlamındaki URL dosyası, [2] adresinden istenir. Herhangi bir belge talep edilmediğinden, varsa index.html, index.jsp, index.xhtml sayfaları kullanılır. Bu durumda, [index.jsp] sayfası kullanılacaktır.

2.3.3. Bir Maven projesinin dosya yapısı

  • [1]: projenin dosya yapısı [Files] sekmesinde bulunur,
  • [2]: Java kaynakları [src / main / java] klasöründedir,
  • [3]: web sayfaları [src / main / webapp] klasöründedir,
  • [4]: [target] klasörü, projenin derlenmesi (build) sırasında oluşturulmuştur,
  • [5]: Burada, projenin derlenmesi sonucunda [mv-jsf2-01-1.0-SNAPSHOT.war] adlı bir arşiv oluşturulmuştur. Tomcat sunucusu tarafından çalıştırılan bu arşivdir.

2.3.4. JSF için bir projeyi yapılandırma

Mevcut projemiz bir JSF projesi değildir. Projede JSF çerçeve kütüphaneleri eksiktir. Mevcut projeyi bir JSF projesi haline getirmek için aşağıdaki adımları izleyin:

  • [1]'e dönüştürürken, proje özelliklerine erişin,
  • [2]'te, [Frameworks] kategorisini seçin,
  • [3]'te bir çerçeve eklenir,
  • [4]'te Java Server Faces seçildiğinde,
  • [5]'te NetBeans bize çerçevenin 2.1 sürümünü önerir. Bunu kabul edersek,
  • [6]'te proje yeni bağımlılıklarla zenginleşir.

[pom.xml] dosyası, bu yeni yapılandırmayı yansıtacak şekilde güncellendi:


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>istia.st</groupId>
  <artifactId>mv-jsf2-01</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>mv-jsf2-01</name>

  ...
  <dependencies>
    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-api</artifactId>
      <version>2.1.1-b04</version>
    </dependency>
    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-impl</artifactId>
      <version>2.1.1-b04</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.1.2</version>
    </dependency>
    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
    </dependency>
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-web-api</artifactId>
      <version>6.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
    ...
  </build>
  <repositories>
    <repository>
      <URL>http://download.java.net/maven/2/</URL>
      <id>jsf20</id>
      <layout>default</layout>
      <name>Repository for library Library[jsf20]</name>
    </repository>
    <repository>
      <URL>http://repo1.maven.org/maven2/</URL>
      <id>jstl11</id>
      <layout>default</layout>
      <name>Repository for library Library[jstl11]</name>
    </repository>
  </repositories>
</project>

14-33. satırlarda yeni bağımlılıklar eklenmiştir. Maven bunları otomatik olarak indirir. Bunları "depo" olarak adlandırılan yerlerden alır. Merkezi depo (Central Repository) otomatik olarak kullanılır. <repository> etiketiyle başka depolar da eklenebilir. Burada iki depo eklenmiştir:

  • 46-51. satırlar: JSF 2 kütüphanesi için bir depo,
  • 52-57. satırlar: JSTL 1.1 kütüphanesi için bir depo.

Projeye ayrıca yeni bir web sayfası eklenmiştir:

[index.HTML] sayfası şu şekildedir:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
  <h:head>
    <title>Facelet Title</title>
  </h:head>
  <h:body>
    Hello from Facelets
  </h:body>
</html>

Burada bir XML dosyası bulunmaktadır (1. satır). Bu dosyada HTML'in etiketleri yer almaktadır, ancak XML formatındadır. Buna XHTML denir. JSF 2 ile web sayfaları oluşturmak için kullanılan teknolojiye Facelets denir. Bu nedenle, XHTML sayfasına bazen Facelet sayfası da denir.

3-4. satırlar, <html> etiketini XML ad alanlarıyla (xmlns=XML Name Space) tanımlar.

  • 3. satır, ana ad alanını http://www.w3.org/1999/xhtml olarak tanımlar;
  • 4. satır ise HTML etiketlerinin http://java.sun.com/jsf/html ad alanını tanımlar. Bu etiketlerin önüne, xmlns:h ile belirtildiği gibi h: öneki eklenecektir. Bu etiketler 5., 7., 8. ve 10. satırlarda bulunur.

Bir ad alanı bildirimi ile karşılaştığında, web sunucusu uygulamanın [META-INF] ve Classpath klasörlerini tarayarak .tld uzantılı dosyaları (TagLib Definition) arayacaktır. Burada, bu dosyaları [jsf-impl.jar] ve [1,2] arşivlerinde bulacaktır:

Şimdi [3] dosyasını ve [HTML_basic.tld] dosyasını inceleyelim:

<?xml version="1.0" encoding="UTF-8"?>

<taglib xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1">

<!-- ============== Etiket Kütüphanesi Açıklama Öğeleri ============= -->

    <description>
        This tag library contains JavaServer Faces component tags for all
        UIComponent + HTML RenderKit Renderer combinations defined in the
        JavaServer Faces Specification.
    </description>
    <tlib-version>
        2.1
    </tlib-version>
    <short-name>
        h
    </short-name>
    <uri>
        http://java.sun.com/jsf/html
    </uri>

<!-- ============== Etiket Kütüphanesi Doğrulayıcısı ============= -->
...
  • 19. satırda, etiket kütüphanesinin URI'si,
  • 16. satırda ise kısa adı yer almaktadır.

Farklı <h:xx> etiketlerinin tanımları bu dosyada bulunur. Bu etiketler, yine [jsf-impl.jar] artefaktında bulunan Java sınıfları tarafından yönetilir.

JSF projemize geri dönelim. Projeye yeni bir dal eklendi:

[Other Sources] [1] dalı, projenin Classpath'inde bulunması gereken ve Java kodu olmayan dosyaları içerir. JSF'teki mesaj dosyaları buna örnektir. Projeye JSF çerçevesinin eklenmemesi durumunda bu dalın bulunmadığını gördük. Bunu oluşturmak için, [Files] [2] sekmesinde [src / main / resources] [3] klasörünü oluşturmanız yeterlidir.

Son olarak, [Web Pages] dalında yeni bir klasör ortaya çıktı:

[WEB-INF] klasörü oluşturuldu ve içinde [web.xml] dosyası yer alıyor. Bu dosya web uygulamasını yapılandırıyor:


<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <URL-pattern>/faces/*</URL-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>faces/index.xhtml</welcome-file>
    </welcome-file-list>
</web-app>
  • 7-10. satırlar, istemcilerin isteklerini işleyebilen bir Java sınıfı olan c.a.d adlı bir servlet tanımlar. JSF uygulaması şu şekilde çalışır:

Bu mimari, MVC Tasarım Desenini (Model, Görünüm, Denetleyici) uygular. Yukarıda daha önce yazılanları hatırlatmak isteriz. Bir istemciden gelen isteğin işlenmesi aşağıdaki dört adımda gerçekleşir:

1 - istek - tarayıcı istemcisi, [Faces Servlet] denetleyicisine bir istek gönderir. Bu denetleyici, tüm istemci isteklerini alır. Burası uygulamanın giriş kapısıdır. MVC'teki C'dir,

2 - işleme - C denetleyicisi bu talebi işler. Bunu yapmak için, [2a] adlı yazılmış uygulamaya özgü olay işleyicilerinden yardım alır. Bu işleyiciler, [2b] iş katmanının yardımına ihtiyaç duyabilir. Müşterinin isteği işlendikten sonra, bu işlem çeşitli yanıtlara yol açabilir. Klasik bir örnek şudur:

  • istek düzgün bir şekilde işlenemediğinde bir hata sayfası;
  • aksi takdirde bir onay sayfası,

3 - yönlendirme - denetleyici, müşteriye gönderilecek yanıtı (= görünüm) seçer. Müşteriye gönderilecek yanıtın seçilmesi birkaç adımdan oluşur:

  • yanıtı oluşturacak Facelet'i seçmek. Buna V görünümü denir; MVC'teki V harfi budur. Bu seçim genellikle kullanıcının talep ettiği eylemin yürütülme sonucuna bağlıdır;
  • bu Facelet’e yanıtı oluşturması için ihtiyaç duyduğu verileri sağlamak. Nitekim bu yanıt, çoğu zaman denetleyici tarafından hesaplanan bilgileri içerir. Bu bilgiler, görünümün M modeli olarak adlandırılan kısmı oluşturur; MVC’teki M harfi budur,

Dolayısıyla 3. adım, bir V görünümü seçmek ve bunun için gerekli olan M modelini oluşturmaktan ibarettir.

4 - yanıt - C denetleyicisi, seçilen Facelet’ten kendini görüntülemesini ister. Facelet, C denetleyicisi tarafından hazırlanan M modelini kullanarak, müşteriye göndermesi gereken yanıtın dinamik kısımlarını başlatır. Yanıtın tam biçimi çeşitli şekillerde olabilir: bir HTML akışı, PDF, Excel dosyası vb. olabilir.

Bir JSF projesinde:

  • C denetleyicisi, [javax.faces.webapp.FacesServlet] servletidir,
  • V görünümleri, Facelets teknolojisini kullanan sayfalarla uygulanır,
  • M modelleri ve olay işleyicileri, genellikle "backing beans" veya daha basitçe Beans olarak adlandırılan Java sınıfları ile uygulanır.

[web.xml] dosyasının içeriğine tekrar bakalım:


<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <URL-pattern>/faces/*</URL-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>faces/index.xhtml</welcome-file>
    </welcome-file-list>
</web-app>
  • 12-15. satırlar: <servlet-mapping> etiketi, bir servleti istemci tarayıcısı tarafından istenen bir URL ile ilişkilendirmek için kullanılır. Burada, [/faces/*] biçimindeki URL dosyalarının, [Faces Servlet] adlı servlet tarafından işlenmesi gerektiği belirtilmiştir. Bu servlet, 7-10. satırlarda tanımlanmıştır. Dosyada başka bir <servlet-mapping> etiketi bulunmadığından, bu durum [Faces Servlet] servletinin yalnızca [/faces/*] biçimindeki URL isteklerini işleyeceği anlamına gelir. Uygulama bağlamının adının [/mv-jsf2-01] olduğunu görmüştük. Dolayısıyla, [Faces Servlet] servleti tarafından işlenen istemcilerin URL dosyaları, [http://machine:port/mv-jsf2-01/faces/*] biçiminde olacaktır. .html ve .jsp sayfaları varsayılan olarak belirli bir servlet tarafından değil, servlet kapsayıcısı tarafından işlenir. Nitekim servlet kapsayıcısı bu sayfaları nasıl yöneteceğini bilir,
  • 7-10. satırlar: [Faces Servlet] servletini tanımlar. Kabul edilen tüm URL istekleri bu servlete yönlendirildiğinden, bu servlet MVC modelinin C denetleyicisidir,
  • 10. satır: servletin, web sunucusu başlatılır başlatılmaz belleğe yüklenmesi gerektiğini belirtir. Varsayılan olarak, bir servlet kendisine gelen ilk istek alındığında yüklenir,
  • 3-6. satırlar: [Faces Servlet] servlet'i için bir parametre tanımlar. javax.faces.PROJECT_STAGE parametresi, yürütülen projenin hangi aşamada olduğunu belirler. Development aşamasında, [Faces Servlet] servleti hata ayıklamaya yardımcı olan hata mesajlarını görüntüler. Production aşamasında ise bu mesajlar artık görüntülenmez,
  • 17-19. satırlar: bir oturumun süresi (dakika cinsinden). Bir istemci, uygulama ile bir dizi istek/yanıt döngüsü aracılığıyla iletişim kurar. Her döngü, kendisine özgü bir TCP-IP bağlantısı kullanır ve bu bağlantı her yeni döngüde yenilenir. Ayrıca, bir C istemcisi D1 ve D2 olmak üzere iki istek gönderirse, S sunucusu bu iki isteğin aynı C istemcisine ait olduğunu bilemez. S sunucusu, istemcinin bilgilerini tutmaz. Bunun nedeni, kullanılan HTTP protokolüdür (HyperText Taşıma Protokolü): İstemci, her seferinde yeni bir TCP-IP bağlantısı kullanarak, istemci isteği / sunucu yanıtı döngülerinin bir dizisiyle sunucuyla iletişim kurar. Buna durum bilgisi içermeyen protokol denir. Diğer protokollerde, örneğin FTP (Dosya Aktarım Protokolü) gibi, istemci C, sunucu S ile iletişim süresince aynı bağlantıyı kullanır. Dolayısıyla bir bağlantı, belirli bir istemciye bağlıdır. Sunucu S, kiminle iletişim kurduğunu her zaman bilir. Bir isteğin belirli bir istemciye ait olduğunu tanıyabilmek için, web sunucusu oturum tekniğini kullanabilir:
    • bir istemcinin ilk isteği sırasında, sunucu S ona beklenen yanıtın yanı sıra, o istemciye özgü rastgele bir karakter dizisi olan bir jeton gönderir;
    • sonraki her istekte, müşteri C aldığı jetonu sunucu S’ye geri gönderir; böylece sunucu S onu tanıyabilir.

Artık uygulama, sunucudan belirli bir müşteriyle ilişkili bilgileri saklamasını isteyebilir. Buna müşteri oturumu denir. 18. satır, bir oturumun ömrünün 30 dakika olduğunu belirtir. Bu, bir müşteri C'nin 30 dakika boyunca yeni bir istek göndermemesi durumunda oturumunun sonlandırılacağı ve içerdiği bilgilerin kaybolacağı anlamına gelir. Bir sonraki isteğinde ise, sanki yeni bir müşteriymiş gibi işlem yapılır ve yeni bir oturum başlatılır;

  • 21-23. satırlar: Kullanıcı, belirli bir sayfa belirtmeden bağlamı talep ettiğinde görüntülenecek sayfaların listesi; örneğin burada [http://machine:port/mv-jsf2-01]. Bu durumda, web sunucusu (servlet değil), uygulamanın bir <welcome-file-list> etiketini tanımlayıp tanımlamadığını kontrol eder. Tanımlanmışsa, listede bulunan ilk sayfayı görüntüler. Eğer yoksa, ikinci sayfayı görüntüler ve mevcut bir sayfa bulunana kadar bu işlem devam eder. Burada, istemci URL [http://machine:port/mv-jsf2-01] adresini talep ettiğinde, kendisine URL [http://machine:port/mv-jsf2-01/index.xhtml] adresi sunulur.

2.3.5. Projeyi çalıştırma

Yeni proje çalıştırıldığında, tarayıcıda elde edilen sonuç şöyledir:

  • [1]'te, belge belirtilmeden bağlam istendi,
  • [2]'te ise, daha önce açıklandığı gibi, [index.xhtml] numaralı ana sayfa (welcome-file) sunulur.

Alınan kaynak koduna ([3]) bir göz atmak ilginç olabilir:

1
2
3
4
5
6
7
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
    <title>Facelet Title</title></head><body>
    Hello from Facelets
  </body>
</html>

HTML dosyası alındı. index.xhtml dosyasındaki tüm <h:xx> etiketleri, karşılık gelen HTML etiketlerine çevrildi.

2.3.6. Yerel Maven deposu

Maven'in proje için gerekli bağımlılıkları indirip yerel olarak depoladığını belirtmiştik. Bu yerel depoyu şu şekilde inceleyebiliriz:

  • [1]'te, [Window / Other / Maven Repository Browser] seçeneğini seçtiğimizde,
  • [2]'te, [Maven Repositories] sekmesi açılır,
  • [3]'te iki dal bulunur; biri yerel depo, diğeri merkezi depo içindir. Merkezi depo devasa boyuttadır. İçeriğini görüntülemek için [4] dizinini güncellemek gerekir. Bu güncelleme işlemi onlarca dakika sürer.
  • [5]'te, yerel deponun kütüphaneleri,
  • [6]'e; burada, projemizin [groupId]'ine karşılık gelen [istia.st] adlı bir dal bulunur,
  • [7]'te yerel deponun özelliklerine erişilir;
  • [8]'te ise yerel deponun yolu bulunur. Bunu bilmek yararlıdır çünkü bazen (nadiren de olsa) Maven projenin en son sürümünü kullanmaz. Değişiklikler yapıp bunların dikkate alınmadığını fark edebiliriz. Bu durumda, [groupId]'e karşılık gelen yerel depo dalını manuel olarak silebiliriz. Bu, Maven'ı projenin en son sürümünden dalı yeniden oluşturmaya zorlar.

2.3.7. Maven ile bir artefakt aramak

Şimdi Maven ile bir artefakt aramayı öğrenelim. [pom.xml] dosyasının mevcut bağımlılık listesinden başlayalım:


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>istia.st</groupId>
  <artifactId>mv-jsf2-01</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>mv-jsf2-01</name>

  ...
  <dependencies>
    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-api</artifactId>
      <version>2.1.1-b04</version>
    </dependency>
    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-impl</artifactId>
      <version>2.1.1-b04</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.1.2</version>
    </dependency>
    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
    </dependency>
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-web-api</artifactId>
      <version>6.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
    ...
  </build>
  <repositories>
    <repository>
      <url>http://download.java.net/maven/2/</url>
      <id>jsf20</id>
      <layout>default</layout>
      <name>Repository for library Library[jsf20]</name>
    </repository>
    <repository>
      <url>http://repo1.maven.org/maven2/</url>
      <id>jstl11</id>
      <layout>default</layout>
      <name>Repository for library Library[jstl11]</name>
    </repository>
  </repositories>
</project>

13-40. satırlar bağımlılıkları, 45-58. satırlar ise her zaman kullanılan merkezi deponun yanı sıra bu bağımlılıkların bulunabileceği depoları tanımlar. Bağımlılıkları, kütüphanelerin en güncel sürümlerini kullanacak şekilde değiştireceğiz.

Öncelikle, mevcut bağımlılıkları [1] siliyoruz. Ardından [pom.xml] dosyası şu şekilde değiştirilir:


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
...
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
...
    <repositories>
        <repository>
            <url>http://download.java.net/maven/2/</url>
            <id>jsf20</id>
            <layout>default</layout>
            <name>Repository for library Library[jsf20]</name>
        </repository>
        <repository>
            <url>http://repo1.maven.org/maven2/</url>
            <id>jstl11</id>
            <layout>default</layout>
            <name>Repository for library Library[jstl11]</name>
        </repository>
    </repositories>
</project>

5-12. satırlarda, kaldırılan bağımlılıklar artık [pom.xml] dosyasında görünmüyor. Şimdi bunları Maven depolarında arayalım.

  • [1]'te projeye bir bağımlılık eklenir,
  • [2]'te, aranan artefaktla ilgili bilgileri (groupId, artifactId, sürüm, paketleme (Tür) ve kapsam) belirtmemiz gerekiyor. Öncelikle [groupId] ve [3]'i belirtiriz,
  • [4]'te, olası artefaktların listesini görüntülemek için [espace] yazıyoruz. Burada [jsf-api] ve [jsf-impl] görünüyor. [jsf-api]'i seçiyoruz,
  • aynı şekilde [5]'e geçip en yeni sürümü seçiyoruz. Paketleme türü jar'tir.

Tüm artefaktlar için aynı şekilde ilerliyoruz:

[6] dosyasında, eklenen bağımlılıklar projede görünür. [pom.xml] dosyası bu değişiklikleri yansıtmaktadır:


<dependencies>
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-api</artifactId>
            <version>2.1.7</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-impl</artifactId>
            <version>2.1.7</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

Şimdi, istediğimiz artefaktın [groupId]'sini bilmediğimizi varsayalım. Örneğin, Hibernate'i ORM (Nesne-İlişkisel Eşleştirici) olarak kullanmak istiyoruz ve tek bildiğimiz bu. Bu durumda şu adrese gidebiliriz:

[1] bölümüne anahtar kelimeler girebiliriz. hibernate yazıp aramayı başlatalım.

  • [2]'te, [groupId], org.hibernate ve [artifactId], hibernate-core'i seçelim,
  • [3]'ten 4.1.2-Final sürümünü seçelim,
  • [4]'e geldiğimizde, [pom.xml] dosyasına yapıştıracağımız Maven kodunu elde ediyoruz. Bunu yapıyoruz.

<dependencies>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>4.1.2.Final</version>
    </dependency>
    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-api</artifactId>
      <version>2.1.7</version>
      <type>jar</type>
    </dependency>
    ...
  </dependencies>

[pom.xml] dosyasını kaydediyoruz. Maven daha sonra yeni bağımlılıkları indirmeye başlıyor. Proje şu şekilde gelişiyor:

  • [5]'e, [hibernate-core-4.1.2-Final] bağımlılığı. Bulunduğu depoda, bu [artifactId] dosyası da bir [pom.xml] dosyası ile tanımlanmıştır. Bu dosya okundu ve Maven, [artifactId]'in bağımlılıkları olduğunu tespit etti. Bunları da indiriyor. İndirilen her [artifactId] dosyası için de aynısını yapacak. Sonuç olarak, [6] dosyasında doğrudan talep etmediğimiz bağımlılıklar bulunur. Bunlar, ana [artifactId] dosyasındakinden farklı bir simgeyle gösterilir.

Bu belgede, Maven’ı esas olarak bu özelliği için kullanıyoruz. Bu sayede, kullanmak istediğimiz bir kütüphanenin tüm bağımlılıklarını bilmemize gerek kalmaz. Bunları yönetme işini Maven’a bırakırız. Ayrıca, bir [pom.xml] dosyasını geliştiriciler arasında paylaşarak, her geliştiricinin aynı kütüphaneleri kullandığından emin oluruz.

Aşağıdaki örneklerde, kullanılan [pom.xml] dosyasını vereceğiz. Okuyucunun, belgeyle aynı koşullara sahip olmak için bu dosyayı kullanması yeterlidir. Ayrıca, Maven projeleri başlıca Java ortamları (Eclipse, NetBeans, IntelliJ, JDeveloper) tarafından desteklenmektedir. Dolayısıyla okuyucu, örnekleri test etmek için kendi tercih ettiği ortamı kullanabilir.

2.4. örneği mv-jsf2-02: olay yöneticisi – uluslararasılaştırma - sayfalar arası gezinme

2.4.1. Uygulama

Uygulama şu şekildedir:

  • [1], uygulamanın ana sayfası,
  • [2]'te, uygulamanın sayfalarının dilini değiştirmek için iki bağlantı,
  • [3]'e, başka bir sayfaya yönlendiren bir bağlantı,
  • [3]'e tıklandığında, [4] sayfası görüntülenir,
  • [5] bağlantısı ana sayfaya geri dönmeyi sağlar.
  • [1] ana sayfasında, [2] bağlantıları dil değiştirmeye,
  • [3] sayfasında ise ana sayfa İngilizce olarak görüntülenir.

2.4.2. NetBeans projesi

2.3.1. paragrafında açıklandığı gibi yeni bir web projesi oluşturulacaktır. Buna mv-jsf2-02 adı verilecektir:

  • [1] olarak, oluşturulan proje,
  • [2]'te, [istia.st.mvjsf202] paketini ve [index.jsp] dosyasını sildik,
  • [3] dosyasında, aşağıdaki [pom.xml] dosyası aracılığıyla Maven bağımlılıkları eklenmiştir:

<dependencies>
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-api</artifactId>
            <version>2.1.7</version>
        </dependency>
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-impl</artifactId>
            <version>2.1.7</version>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

Eklenen bağımlılıklar, JSF çerçevesine aittir. Yukarıdaki satırları, eski bağımlılıkların yerine [pom.xml] dosyasına kopyalamanız yeterlidir.

  • [4, 5]'te: [Files] sekmesinde [src / main / resources] adlı bir klasör oluşturulur,
  • [6]'te, [Projects] sekmesinde, bu işlem [Other Sources] dalını oluşturdu.

Artık JSF adlı bir projemiz var. Burada farklı türde dosyalar oluşturacağız:

  • XHTML formatında web sayfaları,
  • Java sınıfları,
  • mesaj dosyaları,
  • JSF proje yapılandırma dosyası.

Her bir dosya türünü nasıl oluşturacağımıza bir göz atalım:

  • dosyasını [1] dosyasına dönüştürürken, JSF
  • [2]'te, [index.xhtml] sayfasını [Facelets] formatında oluşturuyoruz; [3]'te,
  • [4]'te iki dosya oluşturuldu: [index.xhtml] ve [WEB-INF / web.xml].

[web.xml] dosyası , JSF uygulamasını yapılandırır. Şu şekildedir:


<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <URL-pattern>/faces/*</URL-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>faces/index.xhtml</welcome-file>
    </welcome-file-list>
</web-app>

Bu dosyayı 2.3.4. paragrafında daha önce ele almıştık. Başlıca özelliklerini hatırlatalım:

  • faces/* türündeki tüm URL dosyaları, [javax.faces.webapp.FacesServlet] servleti tarafından işlenir;
  • [index.xhtml] sayfası, uygulamanın ana sayfasıdır.

Oluşturulan [index.xhtml] dosyası şöyledir:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
  <h:head>
    <title>Facelet Title</title>
  </h:head>
  <h:body>
    Hello from Facelets
  </h:body>
</html>

Bu dosyayla 2.3.4. paragrafında daha önce karşılaşmıştık.

Şimdi bir Java sınıfı oluşturalım:

  • [1] içinde, [Source Packages] dalında bir Java sınıfı oluşturuyoruz,
  • [2]'te, sınıfa bir ad verip [3] paketine yerleştiriyoruz,
  • [4]'te, oluşturulan sınıf projede görünür.

Oluşturulan sınıfın kodu bir sınıf iskeletidir:


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package istia.st;

/**
 *
 * @author Serge Tahé
 */
public class Form {
  
}

Son olarak, bir mesaj dosyası oluşturalım:

  • [1]'te, [Properties] dosyasını oluşturup,
  • [2]'te dosya adı verilir ve [3]'te klasörü belirlenir,
  • [4]'te, [messages.properties] dosyası oluşturuldu.

Bazen, JSF projesini yapılandırmak için [WEB-INF/faces-config.xml] dosyasının oluşturulması gerekir. Bu dosya, JSF 1 ile zorunluydu. JSF 2 ile ise isteğe bağlıdır. Ancak, JSF sitesi uluslararası hale getirilmişse gereklidir. Bundan sonra durum böyle olacaktır. Bu nedenle şimdi bu yapılandırma dosyasının nasıl oluşturulacağını göstereceğiz.

  • [1]'te, JSF yapılandırma dosyasını oluşturuyoruz,
  • [2]'te ona bir ad veriyoruz ve [3]'te klasörünü belirtiyoruz,
  • [4]'te oluşturulan dosya.

Oluşturulan [faces-config.xml] dosyası şöyledir:


<?xml version='1.0' encoding='UTF-8'?>

<!-- =========== FULL CONFIGURATION FILE ================================== -->

<faces-config version="2.0"
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">


</faces-config>

Kök etiket <faces-config>'dir. Bu etiketin gövdesi boştur. Bunu doldurmamız gerekecektir.

Artık bir JSF projesi oluşturmak için gerekli tüm öğelere sahibiz. Aşağıdaki örneklerde, tam bir JSF projesini sunacağız ve ardından öğeleri tek tek ayrıntılı olarak ele alacağız. Şimdi, kavramları açıklamak için bir proje sunuyoruz:

  • form olay yöneticisi,
  • JSF sitesindeki sayfaların uluslararasılaştırılması,
  • sayfalar arası gezinme.

[mv-jsf2-02] projesi aşağıdaki gibidir. Okuyucu, bu projeyi örnekler sitesinde bulabilir (bkz. paragraf 1.2).

  • [1]'e, JSF projesinin yapılandırma dosyaları,
  • [2]'e, projenin JSF sayfaları,
  • [3] dosyasına, tek Java sınıfı,
  • [4] dosyasına mesaj dosyaları.

2.4.3. [index.xhtml] sayfası

[index.xhtml] dosyası, [1] dosyasını kullanarak [2] sayfasını istemci tarayıcısına gönderir:

Bu sayfayı oluşturan kod şöyledir:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
  <f:view locale="#{changeLocale.locale}">
    <head>
      ...
    </head>
    <body>
      ....
    </body>
  </f:view>
</html>
  • 7-9. satırlar: Sayfa tarafından kullanılan ad alanları / etiket kütüphaneleri. h önekine sahip etiketler HTML etiketleriyken, f önekine sahip etiketler JSF'e özgü etiketlerdir,
  • 10. satır: <f:view> etiketi, JSF motorunun işlemesi gereken kodu sınırlamak için kullanılır; bu kodda <f:xx> etiketleri yer alır. locale özniteliği, sayfanın görüntülenme dilini belirlemeye yarar. Burada iki dil kullanacağız: İngilizce ve Fransızca. local özniteliğinin değeri, EL (Expression Language) #{ifade} biçiminde ifade edilir. İfadenin biçimi çeşitli olabilir. Biz bunu çoğunlukla bean['clé'] veya bean.champ biçiminde ifade edeceğiz. Örneklerimizde, bean ya bir Java sınıfı ya da bir mesaj dosyası olacaktır. JSF 1 sürümünde, bu bean'ler [faces-config.xml] dosyasında bildirilmeliydi. JSF 2 sürümünde ise bu, Java sınıfları için artık zorunlu değildir. Artık bir Java sınıfını JSF 2 tarafından tanınan bir bean haline getiren anotasyonlar kullanılabilir. Mesaj dosyası ise [faces-config.xml] yapılandırma dosyasında bildirilmelidir.

2.4.4. [changeLocale] bean'i

"EL #{changeLocale.locale}" ifadesinde:

  • changeLocale bir bean adıdır; burada Java sınıfı ChangeLocale'tir,
  • locale ise ChangeLocale sınıfının bir alanıdır. İfade, [ChangeLocale].getLocale() tarafından değerlendirilir. Genel olarak, #{bean.champ} ifadesi, [Bean] olarak değerlendirilir.getChamp(), burada [Bean], bean ve getChamp isimleri atanan Java sınıfının bir örneğidir, bean'in champ alanına ilişkin getter'dır.

ChangeLocale sınıfı şu şekildedir:


package utils;

import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.enterprise.context.SessionScoped;

@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
  // sayfaların yerel ayarı
  private String locale="fr";
  
  public ChangeLocale() {
  }
  
  ...
  public String getLocale() {
    return locale;
  }
  
}
  • 11. satır: yerel alan,
  • 17. satır: getter yöntemi,
  • 7. satır: ManagedBean anotasyonu, Java sınıfı ChangeLocale'i JSF tarafından tanınan bir bean haline getirir. Bir bean, bir adla tanımlanır. Bu ad, anotasyonun name özniteliği ile belirlenebilir: @ManagedBean(name= "xx "). name özniteliği yoksa, sınıf adı kullanılır ve ilk harfi küçük harfe dönüştürülür. Dolayısıyla, ChangeLocale bean'inin adı changeLocale'tir. ManagedBean anotasyonunun javax.faces.bean.ManagedBean paketine ait olduğu ve javax.annotations.ManagedBean paketine ait olmadığına dikkat edilmelidir.
  • 8. satır: SessionScoped anotasyonu, bean'in kapsamını belirler. Bunların birkaç türü vardır. Genellikle aşağıdaki üçünü kullanacağız:
    • RequestScoped: Bean'in ömrü, tarayıcı isteği / sunucu yanıtı döngüsünün süresi kadardır. Aynı tarayıcıdan veya başka bir tarayıcıdan gelen yeni bir isteği işlemek için bu bean'e tekrar ihtiyaç duyulursa, bean yeniden örneklenecektir,
    • SessionScoped: Bean'in ömrü, belirli bir müşterinin oturum süresiyle aynıdır. Bean, başlangıçta bu müşterinin isteklerinden birinin ihtiyaçları için oluşturulur. Daha sonra bu müşterinin oturumu kapsamında bellekte kalır. Bu tür bir bean genellikle belirli bir müşteriye özgü verileri depolar. Müşterinin oturumu sona erdiğinde yok edilir,
    • ApplicationScoped: Bean'in ömrü, uygulamanın kendisiyle aynıdır. Bu ömre sahip bir bean, çoğunlukla uygulamanın tüm müşterileri tarafından paylaşılır. Genellikle uygulamanın başlangıcında başlatılır.

Bu anotasyonlar iki pakette bulunur: javax.enterprise.context.SessionScoped (JSF 2) ve javax.faces.bean.SessionScoped (JSF 1). Burada, JSF 2 paketini kullanıyoruz. Bu, [WEB-INF / beans.xml] dosyasını oluşturmamızı gerektirir:

  

Bu dosya, [javax.enterprise.context.SessionScoped] paketini içe aktardığımızda NetBeans tarafından otomatik olarak oluşturulur. İçeriği şöyledir:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>

Kök <beans> etiketinin dışında dosya boştur. Bu yeterlidir. Yalnızca dosyanın varlığı gereklidir.

Son olarak, [ChangeLocale] sınıfının [Serializable] arayüzünü uyguladığına dikkat edilmelidir. Bu, web sunucusunun dosyalara serileştirmesi gerekebilecek Session kapsamındaki bean’ler için zorunludur. [ChangeLocale] bean'ine daha sonra tekrar değineceğiz.

2.4.5. Mesaj dosyası

[index.xhtml] dosyasına geri dönelim:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
  <f:view locale="#{changeLocale.locale}">
    <head>
      <title><h:outputText value="#{msg['welcome.titre']}" /></title>
    </head>
    <body>
    ...
    </body>
  </f:view>
</html>
  • 8. satır: <h:outputText> etiketi, #{bean['champ']} biçimindeki EL #{msg['welcome.titre']} ifadesinin değerini görüntüler. bean, ya bir Java sınıfının adı ya da bir mesaj dosyasının adıdır. Burada, bir mesaj dosyasının adıdır. Bu dosya, [faces-config.xml] yapılandırma dosyasında tanımlanmalıdır. msg bean'i şu şekilde tanımlanır:

<?xml version='1.0' encoding='UTF-8'?>

<!-- =========== FULL CONFIGURATION FILE ================================== -->

<faces-config version="2.0"
              xmlns="http://java.sun.com/xml/ns/javaee" 
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">


  <application>
    <resource-bundle>
      <base-name>
        messages
      </base-name>
      <var>msg</var>
    </resource-bundle>
  </application>
</faces-config>
  • 11-18. satırlar: <application> etiketi, JSF uygulamasını yapılandırmak için kullanılır,
  • 12-17. satırlar: <resource-bundle> etiketi, uygulama için kaynakları tanımlamak amacıyla kullanılır; burada bir mesaj dosyası,
  • 13-15. satırlar: <base-name> etiketi, mesaj dosyasının adını tanımlar,
  • 14. satır: Dosyanın adı messages[_CodeLangue][_CodePays].properties olacaktır. <base-name> etiketi, adın yalnızca ilk kısmını tanımlar. Geri kalan kısmı zımni olarak eklenir. Her dil için bir tane olmak üzere birden fazla mesaj dosyası bulunabilir:
  • [1] dosyasında, [faces-config.xml] dosyasında tanımlanan temel mesaj adına karşılık gelen dört mesaj dosyası görülmektedir;
    • messages_fr.properties: Fransızca (kod fr) mesajları içerir;
    • messages_en.properties: İngilizce mesajları (kod en) içerir;
    • messages_es_ES.properties: İspanya'ya ait (kod ES) İspanyolca mesajları (kod es) içerir. Başka İspanyolca türleri de mevcuttur; örneğin Bolivya İspanyolcası (es_BO);
    • messages.properties: Sunucu, üzerinde çalıştığı makinenin diline ait herhangi bir mesaj dosyası bulunmadığında bu dosyayı kullanır. Örneğin, uygulama Almanya'da, varsayılan dilin Almanca (de) olduğu bir makinede çalışıyorsa bu dosya kullanılır. [messages_de.properties] dosyası bulunmadığından, uygulama [messages.properties] dosyasını kullanır,
  • [2]'te: dil kodları uluslararası bir standarda tabidir,
  • [3] dosyasında: ülke kodları için de durum aynıdır.

Mesaj dosyasının adı 14. satırda tanımlanır. Bu dosya, projenin Classpath dosyasında aranacaktır. Eğer bir paket içindeyse, bu paket 14. satırda tanımlanmalıdır; örneğin, ressources.messages, [messages.properties] dosyası Classpath'in [ressources] klasöründe bulunuyorsa. 14. satırdaki adda paket adı bulunmadığından, [messages.properties] dosyası [src / main / resources] klasörünün kök dizinine yerleştirilmelidir:

[1] dosyasında, NetBeans projesinin [Projects] sekmesinde, [messages.properties] dosyası, tanımlanmış farklı mesaj sürümlerinin bir listesi olarak gösterilir. Sürümler, bir ila üç koddan oluşan bir diziyle tanımlanır: [codeLangue_codePays_codeVariante]. [1] dosyasında yalnızca [codeLangue] kodu kullanılmıştır: en İngilizce, fr ise Fransızca için. Her sürüm, dosya sisteminde ayrı bir dosyada yer alır.

Örneğimizde, Fransızca mesaj dosyası [messages_fr.properties] aşağıdaki öğeleri içerecektir:


welcome.titre=Tutoriel JSF (JavaServer Faces)
welcome.langue1=Fran\u00e7ais
welcome.langue2=Anglais
welcome.page1=Page 1
page1.titre=page1
page1.entete=Page 1
page1.welcome=Page d'accueil

[messages_en.properties] dosyası ise şu şekilde olacaktır:


welcome.titre=JSF (JavaServer Faces) Tutorial
welcome.langue1=French
welcome.langue2=English
welcome.page1=Page 1
page1.titre=page1
page1.entete=Page 1
page1.welcome=Welcome page

[messages.properties] dosyası, [messages_en.properties] dosyasıyla aynıdır. Sonuç olarak, istemci tarayıcısı Fransızca ve İngilizce sayfalar arasında seçim yapabilir.

Mesaj dosyasını tanımlayan [faces-config.xml] dosyasına geri dönelim:


...

  <application>
    <resource-bundle>
      <base-name>
        messages
      </base-name>
      <var>msg</var>
    </resource-bundle>
  </application>
</faces-config>
  1. satır, mesaj dosyasındaki bir satırın JSF sayfalarında msg tanımlayıcısıyla referanslanacağını belirtir. Bu tanımlayıcı, incelediğimiz [index.xhtml] dosyasında kullanılmaktadır:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
  <f:view locale="#{changeLocale.locale}">
    <head>
      <title><h:outputText value="#{msg['welcome.titre']}" /></title>
    </head>
    <body>
      ...
    </body>
  </f:view>
</html>

8. satırdaki <h:outputText> etiketi, welcome.titre anahtarına ait mesajın değerini (msg tanımlayıcısının varlığı) görüntüleyecektir. Bu mesaj, o anda etkin olan dilin [messages.properties] dosyasında aranır ve bulunur. Örneğin, Fransızca için:


welcome.titre=Tutoriel JSF (JavaServer Faces)

Bir mesaj, anahtar=değer biçimindedir. #{msg['welcome.titre']} ifadesinin değerlendirilmesinden sonra [index.xhtml] dosyasının 8. satırı şu şekilde olur:


      <title><h:outputText value="Tutoriel JSF (JavaServer Faces)" /></title>

Bu mesaj dosyaları mekanizması, JSF projesinin sayfalarının dilini kolayca değiştirmeye olanak tanır. Buna projenin uluslararasılaştırılması veya daha sık kullanılan kısaltması olan i18n denir; çünkü “uluslararasılaştırma” kelimesi i ile başlar ve n ile biter, ayrıca i ile n arasında 18 harf vardır.

2.4.6. Form

[index.xhtml] dosyasının içeriğini incelemeye devam edelim:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
  <f:view locale="#{changeLocale.locale}">
    <head>
      <title><h:outputText value="#{msg['welcome.titre']}" /></title>
    </head>
    <body>
      <h:form id="formulaire">
        <h:panelGrid columns="2">
          <h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
          <h:commandLink value="#{msg['welcome.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
        </h:panelGrid>
        <h1><h:outputText value="#{msg['welcome.titre']}" /></h1>
        <h:commandLink value="#{msg['welcome.page1']}" action="page1"/>
      </h:form>
    </body>
  </f:view>
</html>
  • 11-18. satırlar: <h:form> etiketi bir formu başlatır. Bir form genellikle şunlardan oluşur:
    • giriş alan etiketleri (metin, radyo düğmeleri, onay kutuları, öğe listeleri, ...);
    • form doğrulama etiketleri (düğmeler, bağlantılar). Kullanıcı, girdilerini işleyecek sunucuya bir düğme veya bağlantı aracılığıyla gönderir,

Herhangi bir JSF etiketi, bir id özniteliği ile tanımlanabilir. Çoğu zaman bu öznitelik kullanılmasa da olur ve burada kullanılan JSF etiketlerinin çoğunda da bu şekilde yapılmıştır. Bununla birlikte, bu öznitelik bazı durumlarda yararlıdır. 17. satırda, form “form” id’siyle tanımlanmıştır. Bu örnekte, formun id’si kullanılmayacaktır ve atlanabilirdi.

  • 18-21. satırlar: <h:panelGrid> etiketi burada iki sütunlu bir tablo tanımlar. Bu etiket, <table> etiketini oluşturur,
  • formda, 19., 20. ve 23. satırlarda işlenmesini tetikleyen üç bağlantı bulunmaktadır. <h:commandLink> etiketinin en az iki özniteliği vardır:
    • value: bağlantının metni;
    • action: ya bir C karakter dizisi ya da çalıştırıldıktan sonra C karakter dizisini döndüren bir yöntemin referansı. Bu C karakter dizisi şunlardan biri olabilir:
      • ya projenin JSF adlı bir sayfasının adı,
      • ya da [faces-config.xml] dosyasının gezinme kurallarında tanımlanmış ve projenin bir JSF sayfasına atfedilmiş bir ad;

Her iki durumda da, action özniteliğiyle tanımlanan eylem yürütüldükten sonra JSF sayfası görüntülenir.

13. satırdaki bağlantı örneğini kullanarak form işleme mekanizmasını inceleyelim:


         <h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>}"/>

İlk olarak, mesaj dosyası kullanılarak #{msg['welcome.langue1']} ifadesi, değeriyle değiştirilir. Değerlendirme işleminden sonra etiket şu hale gelir:


<h:commandLink value="Français" action="#{changeLocale.setFrenchLocale}"/>}"/>

Bu JSF etiketinin HTML çevirisi şu şekilde olacaktır:

<a href="<a href="view-source:http://localhost:8080/mv-jsf2-02/faces/page1.xhtml#">#</a>" onclick="mojarra.jsfcljs(document.getElementById('formulaire'),{'formulaire:j_idt8':'formulaire:j_idt8'},'');return false">Français</a>

Bu da aşağıdaki görsel görünümü verecektir:

HTML <a> etiketindeki onclick özniteliğine dikkat edilmelidir. Kullanıcı [Français] bağlantısına tıkladığında, bir Javascript kodu çalıştırılacaktır. Bu kod, tarayıcının aldığı sayfaya gömülüdür ve tarayıcı tarafından çalıştırılır. Javascript kodu, JSF ve AJAX (Asynchronous Javascript And Xml) teknolojilerinde yaygın olarak kullanılmaktadır. Genellikle web uygulamalarının kullanıcı dostu olmasını ve tepki süresini iyileştirmeyi amaçlar. Çoğu zaman yazılım araçları tarafından otomatik olarak oluşturulur ve bu durumda onu anlamak gerekli değildir. Ancak bazen bir geliştirici, JSF sayfalarına JavaScript kodu eklemek zorunda kalabilir. Bu durumda JavaScript bilgisi gereklidir.

Burada, <h:commandLink> etiketine yönelik olarak oluşturulan JavaScript kodunu anlamanın bir yararı yoktur. Bununla birlikte iki noktaya dikkat çekilebilir:

  • Javascript kodu, <h:form> etiketine verdiğimiz form kimliğini kullanır,
  • JSF, id özniteliği tanımlanmamış tüm etiketler için otomatik tanımlayıcılar oluşturur. Buna bir örnek burada görülebilir: j_idt8. Etiketlere açık bir tanımlayıcı atamak, gerektiğinde oluşturulan JavaScript kodunu daha iyi anlamayı sağlar. Bu durum özellikle, geliştiricinin sayfanın bileşenlerini işleyen JavaScript kodunu kendisi eklemesi gerektiğinde geçerlidir. Bu durumda, bileşenlerinin id tanımlayıcılarını bilmesi gerekir.

Kullanıcı yukarıdaki sayfadaki [Français] bağlantısına tıkladığında ne olacak? JSF uygulamasının mimarisini ele alalım:

[Faces Servlet] denetleyicisi, istemci tarayıcısından aşağıdaki HTTP biçiminde bir istek alacaktır:

1
2
3
4
5
6
POST /mv-jsf2-02/faces/index.xhtml HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-URLencoded
Content-Length: 126

formulaire=formulaire&javax.faces.ViewState=-9139703055324497810%3A8197824608762605653&formulaire%3Aj_idt8=formulaire%3Aj_idt8 
  • 1-2. satırlar: Tarayıcı, URL [http://localhost:8080/mv-jsf2-02/faces/index.xhtml]'i talep eder. Durum her zaman böyledir: Başlangıçta URL ve URLFormulaire ile elde edilen bir JSF formuna girilen veriler, yine aynı URL'e gönderilir. Tarayıcının girilen değerleri göndermek için iki yolu vardır: GET ve POST. GET yöntemi ile girilen değerler, tarayıcı tarafından istenen URL formuna gönderilir. Yukarıdaki örnekte, tarayıcı şu ilk satırı gönderebilirdi:

GET /mv-jsf2-02/faces/index.xhtml?formulaire=formulaire&javax.faces.ViewState=-9139703055324497810%3A8197824608762605653&formulaire%3Aj_idt8=formulaire%3Aj_idt8 HTTP/1.1

Burada kullanılan POST yöntemi ile tarayıcı, 6. satır aracılığıyla girilen değerleri sunucuya gönderir.

  • 3. satır: form değerlerinin kodlama biçimini belirtir,
  • 4. satır: 6. satırın bayt cinsinden boyutunu belirtir,
  • 5. satır: HTTP başlıklarının sonunu ve form değerlerinin 126 baytlık kısmının başlangıcını belirten boş satır,
  • 6. satır: form değerleri element1=değer1&element2=değer2& ... biçimindedir; kodlama biçimi 3. satırda tanımlanmıştır. Bu kodlama biçiminde, bazı karakterler onaltılık değerleriyle değiştirilir. Son öğede durum böyledir:

formulaire=formulaire&javax.faces.ViewState=...&formulaire%3Aj_idt8=formulaire%3Aj_idt8

burada %3A, : karakterini temsil eder. Dolayısıyla sunucuya gönderilen dize, "form:j_idt8=form:j_idt8" şeklindedir. Belki de j_idt8 tanımlayıcısını,


          <h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>

Bu tanımlayıcı, JSF tarafından otomatik olarak oluşturulmuştu. Burada önemli olan nokta, bu tanımlayıcının istemci tarayıcısı tarafından gönderilen değer dizisinde bulunması sayesinde JSF'in, [Français] bağlantısına tıklandığını anlayabilmesidir. Ardından, alınan dizgiyi nasıl işleyeceğine karar vermek için yukarıdaki action özniteliğini kullanacaktır. action="#{changeLocale.setFrenchLocale}" özniteliği, JSF'e, istemcinin isteğinin changeLocale adlı bir nesnenin [setFrenchLocale] yöntemiyle işlenmesi gerektiğini bildirir. Bu bean'in, [ChangeLocale] adlı Java sınıfında anotasyonlarla tanımlandığını hatırlayalım:


@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{

Bir bean'in adı, @ManagedBean anotasyonunun name özniteliği ile tanımlanır. Bu özniteliğin bulunmaması durumunda, sınıf adı, ilk harfi küçük harf olarak yazılmış şekilde bean adı olarak kullanılır.

Tarayıcının isteğine geri dönelim:

ve tıklandığımız [Français] bağlantısını oluşturan <h:commandLink> etiketine:


          <h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>

Denetleyici, tarayıcının isteğini <h:commandLink> etiketinin action özniteliği ile tanımlanan olay işleyicisine iletecektir. Bir <h:commandLink> komutunun action özniteliği tarafından referans verilen M olay işleyicisi, aşağıdaki imzaya sahip olmalıdır:

public String M();
  • hiçbir parametre almaz. Yine de istemcinin isteğine erişebileceğini göreceğiz;
  • String türünde bir C sonucu döndürmelidir. Bu C karakter dizisi şunlardan biri olabilir:
    • ya da projedeki bir JSF sayfasının adı;
    • ya da [faces-config.xml] dosyasının gezinme kurallarında tanımlanmış ve projenin bir JSF sayfasıyla ilişkilendirilmiş bir ad;
    • ya da istemci tarayıcısının sayfa değiştirmemesi gerekiyorsa bir null işaretçisi olabilir.

Yukarıdaki JSF mimarisinde, [Faces Servlet] denetleyicisi, olay yöneticisi tarafından döndürülen C dizesini ve gerekirse [faces-config.xml] yapılandırma dosyasını kullanarak, JSF sayfasının hangisi olduğunu belirleyecek ve [4] istemcisine yanıt olarak hangi sayfayı göndereceğini belirlemek için kullanacaktır.

<head> etiketinde


          <h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>

içinde, [Français] bağlantısına tıklama olayını işleyen yöntem [changeLocale.setFrenchLocale]'tir; burada changeLocale, daha önce incelediğimiz [utils.ChangeLocale] sınıfının bir örneğidir:


package utils;

import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.faces.bean.ManagedBean;

@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
  // sayfaların yerel ayarı
  private String locale="fr";
  
  public ChangeLocale() {
  }
  
  public String setFrenchLocale(){
    locale="fr";
    return null;
  }
  
  public String setEnglishLocale(){
    locale="en";
    return null;
  }

  public String getLocale() {
    return locale;
  }
}

setFrenchLocale yöntemi, olay işleyicilerinin imza biçimine sahiptir. Olay işleyicisinin, istemcinin isteğini işlemesi gerektiğini hatırlayalım. Herhangi bir parametre almadığına göre, bu isteğe nasıl erişebilir? Bunu yapmanın çeşitli yolları vardır:

  • JSF P sayfasının olay yöneticisini içeren B bean’i, genellikle bu sayfanın M modelini de içerir. Bu, B bean'inin, P sayfasında girilen değerlerle başlatılacak alanlar içerdiği anlamına gelir. Bu işlem, B bean'inin olay işleyicisi çağrılmadan önce [Faces Servlet] denetleyicisi tarafından gerçekleştirilir. Dolayısıyla bu olay işleyicisi, ait olduğu B bean'inin alanları aracılığıyla müşterinin forma girdiği değerlere erişebilir ve bunları işleyebilir.
  • [FacesContext] türündeki statik [FacesContext.getCurrentInstance()] yöntemi, JSF türünde bir nesne olan mevcut [FacesContext] isteğinin yürütme bağlamına erişim sağlar. Bu şekilde elde edilen sorgunun yürütme bağlamı, aşağıdaki yöntemle istemci tarayıcısı tarafından sunucuya gönderilen parametrelere erişim sağlar:
Map FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap()

İstemci tarayıcısı tarafından gönderilen parametreler (POST) şu şekildeyse:

formulaire=formulaire&javax.faces.ViewState=...&formulaire%3Aj_id_id21=formulaire%3Aj_id_id21

getRequestParameterMap() yöntemi aşağıdaki sözlüğü döndürür:

anahtar
değer
form
form
javax.faces.ViewState
...
form:j_id_id21
form:j_id_id21

Etiket içinde


          <h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>

içinde locale.setFrenchLocale olay yöneticisinden ne beklenir? Uygulamanın kullanacağı dili belirlemesi istenir. Java terimleriyle buna uygulamanın "yerelleştirilmesi" denir. Bu yerelleştirme, JSF [index.xhtml] sayfasındaki <f:view> etiketinde kullanılır:


  <f:view locale="#{changeLocale.locale}">
    ...
</f:view>

Sayfayı Fransızca'ya geçirmek için locale özniteliğinin değerinin fr olması yeterlidir. İngilizce'ye geçirmek için ise en değerini vermemiz gerekir. locale özniteliğinin değeri, [ChangeLocale].getLocale() ifadesiyle elde edilir. Bu ifade, [ChangeLocale] sınıfındaki locale alanının değerini döndürür. Bundan, sayfaları Fransızca'ya çevirmesi gereken [ChangeLocale].setFrenchLocale() yönteminin kodu çıkarılabilir:


  public String setFrenchLocale(){
    locale="fr";
    return null;
}

Bir olay işleyicinin, [Faces Servlet] tarafından istemci tarayıcısına yanıt olarak gönderilecek JSF sayfasını bulmak için kullanılacak bir C karakter dizisi döndürmesi gerektiğini açıklamıştık. Geri gönderilecek sayfa, işlenmekte olan sayfayla aynıysa, olay işleyicisi null değerini döndürmekle yetinebilir. Burada 3. satırda yapılan da budur: aynı [index.xhtml] sayfasını, ancak farklı bir dilde geri göndermek istiyoruz.

İsteğin işlenme mimarisine geri dönelim:

changeLocale.setFrenchLocale olay yöneticisi çalıştırıldı ve null değerini [Faces Servlet] denetleyicisine döndürdü. Dolayısıyla bu denetleyici, [index.xhtml] sayfasını yeniden görüntüleyecektir. Bu sayfayı tekrar inceleyelim:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
  <f:view locale="#{changeLocale.locale}">
    <head>
      <title><h:outputText value="#{msg['welcome.titre']}" /></title>
    </head>
    <body>
      <h:form id="formulaire">
        <h:panelGrid columns="2">
          <h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
          <h:commandLink value="#{msg['welcome.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
        </h:panelGrid>
        <h1><h:outputText value="#{msg['welcome.titre']}" /></h1>
        <h:commandLink value="#{msg['welcome.page1']}" action="page1"/>
      </h:form>
    </body>
  </f:view>
</html>

{msg['...']} türündeki bir değer her değerlendirildiğinde, [messages.properties] mesaj dosyalarından biri kullanılır. Kullanılan dosya, sayfanın "yerelleştirme"sine karşılık gelen dosyadır (6. satır). changeLocale.setFrenchLocale olay yöneticisi bu konumu fr olarak tanımladığından, [messages_fr.properties] dosyası kullanılacaktır. [Anglais] bağlantısına (14. satır) tıklandığında, konum en olarak değişecektir (bkz. changeLocale.setEnglishLocale yöntemi). Bu durumda [messages_en.properties] dosyası kullanılacak ve sayfa İngilizce olarak görüntülenecektir:

[index.xhtml] sayfası her görüntülendiğinde, <f:view> etiketi çalıştırılır:


  <f:view locale="#{changeLocale.locale}">

ve dolayısıyla [ChangeLocale].getLocale() yöntemi yeniden çalıştırılır. Bean'imize Session kapsamı verdiğimiz için:


@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{

bir istek sırasında yapılan yerelleştirme, sonraki istekler için korunur.

[index.xhtml] sayfasından incelememiz gereken son bir öğe kaldı:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
  <f:view locale="#{changeLocale.locale}">
    <head>
      <title><h:outputText value="#{msg['welcome.titre']}" /></title>
    </head>
    <body>
      <h:form id="formulaire">
        <h:panelGrid columns="2">
          <h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
          <h:commandLink value="#{msg['welcome.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
        </h:panelGrid>
        <h1><h:outputText value="#{msg['welcome.titre']}" /></h1>
        <h:commandLink value="#{msg['welcome.page1']}" action="page1"/>
      </h:form>
    </body>
  </f:view>
</html>

17. satırdaki <h:commandLink> etiketinin action özniteliği bir karakter dizisiyle eşittir. Bu durumda, sayfayı işlemek için hiçbir olay işleyicisi çağrılmaz. Hemen [page1.xhtml] sayfasına geçilir. Bu kullanım senaryosunda uygulamanın nasıl çalıştığını inceleyelim:

Kullanıcı, [Page 1] bağlantısına tıklar. Form, [Faces Servlet] denetleyicisine gönderilir. Denetleyici, aldığı istekte [Page 1] bağlantısının tıklandığını algılar. İlgili etiketi inceler:


        <h:commandLink value="#{msg['welcome.page1']}" action="page1"/>

Bağlantıyla ilişkili herhangi bir olay işleyicisi yoktur. [Faces Servlet] denetleyicisi hemen yukarıdaki [3] adımına geçer ve [page1.xhtml] sayfasını görüntüler:

2.4.7. JSF sayfası [page1.xhtml]

[page1.xhtml] sayfası, istemci tarayıcısına aşağıdaki akışı gönderir:

 

Bu sayfayı oluşturan kod şöyledir:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
  <f:view locale="#{changeLocale.locale}">
    <head>
      <title><h:outputText value="#{msg['page1.titre']}"/></title>
    </head>
    <body>
      <h1><h:outputText value="#{msg['page1.entete']}"/></h1>
      <h:form>
        <h:commandLink value="#{msg['page1.welcome']}" action="index"/>
      </h:form>
    </body>
  </f:view>
</html>

Bu sayfada daha önce açıklanmamış hiçbir şey yoktur. Okuyucu, JSF kodunu istemci tarayıcısına gönderilen sayfayla eşleştirecektir. Ana sayfaya geri dönme bağlantısı:


        <h:commandLink value="#{msg['page1.welcome']}" action="index"/>

[index.xhtml] sayfasını görüntüleyecektir.

2.4.8. Projenin çalıştırılması

Projemiz artık tamamlandı. Projeyi derleyebiliriz (Clean and Build):

  • Projenin derlenmesi, [Files] sekmesinde [target] klasörünü oluşturur. Bu klasörde, projenin [mv-jsf2-02-1.0-SNAPSHOT.war] arşivi bulunur. Sunucuya dağıtılan bu arşivdir;
  • [WEB-INF / classes] ve [2] klasörlerinde, projenin [Source Packages] klasöründeki derlenmiş sınıflar ile [Other Sources] dalında bulunan dosyalar (burada mesaj dosyaları) yer alır,
  • [WEB-INF / lib] ve [3] dizinlerinde ise projenin kütüphaneleri bulunur,
  • [WEB-INF] ve [4] dizinlerinin kökünde ise projenin yapılandırma dosyaları bulunur,
  • [5] arşivinin kök dizininde, projenin [Web Pages] dalında bulunan JSF sayfaları bulunur,
  • proje derlendikten sonra [6] çalıştırılabilir. [7] dosyasındaki çalıştırma yapılandırmasına göre çalıştırılacaktır,
  • Tomcat sunucusu henüz başlatılmamışsa [8] ile başlatılacak,
  • [mv-jsf2-02-1.0-SNAPSHOT.war] arşivi sunucuya yüklenecektir. Buna, projenin uygulama sunucusuna dağıtılması denir,
  • [9]'te, yürütme sırasında bir tarayıcının başlatılması istenir. Tarayıcı, [10], c.a.d uygulama bağlamını isteyecektir. URL ve [http://localhost:8080/mv-jsf2-02]. [web.xml] dosyasındaki kurallara göre (bkz. sayfa 44), istemci tarayıcısına [faces/index.xhtml] dosyası sunulacaktır. URL, [/faces/*] biçiminde olduğundan, [Faces Servlet] denetleyicisi tarafından işlenecektir (bkz. [web.xml], sayfa 44). Bu denetleyici sayfayı işleyecek ve aşağıdaki HTML akışını gönderecektir:
 
  • [Faces Servlet] denetleyicisi ise bu sayfadan kaynaklanacak olayları işleyecektir.

2.4.9. [faces-config.xml] yapılandırma dosyası

Aşağıdaki [faces-config.xml] dosyasını kullandık:


<?xml version='1.0' encoding='UTF-8'?>

<!-- =========== FULL CONFIGURATION FILE ================================== -->

<faces-config version="2.0"
              xmlns="http://java.sun.com/xml/ns/javaee" 
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">


  <application>
    <resource-bundle>
      <base-name>
        messages
      </base-name>
      <var>msg</var>
    </resource-bundle>
  </application>
</faces-config>

Bu, uluslararasılaştırılmış bir JSF 2 uygulaması için gerekli minimum dosyadır. Burada, JSF 1'e kıyasla JSF 2'nin sunduğu yeni özellikleri kullandık:

  • @ManagedBean, @RequestScoped, @SessionScoped, @ApplicationScoped anotasyonlarıyla bean'leri ve kapsamlarını tanımlamak,
  • sayfalar arasında gezinirken, xhtml sonekleri olmadan sayfa adlarını XHTML gezinme anahtarları olarak kullanmak.

Bu seçenekleri kullanmak istemeyebilir ve JSF projesindeki bu öğeleri, [faces-config.xml] ve JSF'te olduğu gibi tanımlamak isteyebilirsiniz. Bu durumda, [faces-config.xml] dosyası şu şekilde olabilir:


<?xml version='1.0' encoding='UTF-8'?>

<!-- =========== FULL CONFIGURATION FILE ================================== -->

<faces-config version="2.0"
              xmlns="http://java.sun.com/xml/ns/javaee" 
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<!-- uygulama -->
  <application>
    <resource-bundle>
      <base-name>
        messages
      </base-name>
      <var>msg</var>
    </resource-bundle>
  </application>
  
  <!-- yönetilen bean'ler -->
  <managed-bean>
    <managed-bean-name>changeLocale</managed-bean-name>
    <managed-bean-class>utils.ChangeLocale</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
  </managed-bean>

   <!-- navigasyon -->
  <navigation-rule>
    <description/>
    <from-view-id>/index.xhtml</from-view-id>
    <navigation-case>
      <from-outcome>p1</from-outcome>
      <to-view-id>/page1.xhtml</to-view-id>
    </navigation-case>
  </navigation-rule>

  <navigation-rule>
    <description/>
    <from-view-id>/page1.xhtml</from-view-id>
    <navigation-case>
      <from-outcome>welcome</from-outcome>
      <to-view-id>/index.xhtml</to-view-id>
    </navigation-case>
  </navigation-rule>


</faces-config>
  • 20-24. satırlar: changeLocale bean'inin tanımlanması:
    • 21. satır: bean adı;
    • 22. satır: bean ile ilişkili sınıfın tam adı;
    • 23. satır: bean'in kapsamı. Olası değerler şunlardır: request, session, application,
  • 27-34. satırlar: bir gezinme kuralının bildirimi:
    • satır 28: kuralı tanımlayabilirsiniz. Burada bu yapılmamıştır;
    • 29. satır: gezinmenin başlayacağı sayfa (başlangıç noktası);
    • satır 30-33: bir gezinme durumu. Bunlardan birden fazla olabilir;
    • satır 31: gezinme anahtarı;
    • satır 32: gezinilecek sayfa.

Gezinti kuralları daha görsel bir şekilde görüntülenebilir. [faces-config.xml] dosyası düzenlendiğinde, [PageFlow] sekmesi kullanılabilir:

 

Önceki [faces-config.xml] dosyasını kullandığımızı varsayalım. Uygulamamız nasıl değişir?

  • [ChangeLocale] sınıfında, @ManagedBean ve @SessionScoped anotasyonları ortadan kalkacaktır, çünkü artık bean [faces-config] içinde tanımlanmıştır,
  • [index.xhtml]'ten [page1.xhtml]'e bir bağlantı yoluyla geçiş şu şekilde olur:

        <h:commandLink value="#{msg['welcome.page1']}" action="p1"/>

"action" özniteliğine, [faces-config]'te tanımlanan p1 gezinme anahtarı atanır;

  • [page1.xhtml]'ten [index.xhtml]'e bir bağlantı yoluyla geçiş şu şekilde olur:

        <h:commandLink value="#{msg['page1.welcome']}" action="welcome"/>

"action" özniteliğine, [faces-config]'te tanımlanan "welcome" navigasyon anahtarı atanır;

  • bir gezinme anahtarı döndürmesi gereken setFrenchLocale ve setEnglishLocale yöntemlerinin değiştirilmesine gerek yoktur, çünkü aynı sayfada kalındığını belirtmek için null değerini döndürüyorlardı.

2.4.10. Sonuç

Yazdığımız NetBeans projesine geri dönelim:

Bu proje aşağıdaki mimariyi içermektedir:

Her bir JSF projesinde şu öğeleri bulacağız:

  • JSF ve [A] sayfaları, [4] denetleyicisi tarafından istemci tarayıcılara gönderilir;
  • [C] mesaj dosyaları, JSF sayfalarının dilini değiştirmeye olanak tanır,
  • istemci tarayıcısında meydana gelen olayları işleyen ve/veya sayfalara şablon olarak hizmet eden [B] Java sınıfları [2a, 2b] ve JSF [3]. Çoğu zaman, [métier] ve [DAO] katmanları ayrı ayrı geliştirilir ve test edilir. [web] katmanı ise hayali bir [métier] katmanı ile test edilir. [métier] ve [DAO] katmanları mevcutsa, çoğunlukla bunların .jar dosyalarıyla çalışılır.
  • Bu çeşitli öğeleri birbirine bağlamak için [D] yapılandırma dosyaları kullanılır. [web.xml] dosyası 44. sayfada açıklanmıştır ve nadiren değiştirilecektir. Aynı durum, her zaman basitleştirilmiş sürümünü kullanacağımız [faces-config] dosyası için de geçerlidir.

2.5. mv-jsf2-03 örneği: veri girişi formu - JSF bileşenleri

Bundan sonra, projenin oluşturulma sürecini artık göstermeyeceğiz. Hazır projeler sunacak ve bunların nasıl çalıştığını açıklayacağız. Okuyucu, tüm örnekleri bu belgenin web sitesinden indirebilir (bkz. paragraf 1.2).

2.5.1. Uygulama

Uygulamanın tek bir görünümü vardır:

Uygulama, bir veri giriş formunda kullanılabilecek başlıca bileşenleri gösterir:

  • [1] sütunu, kullanılan JSF / HTML etiketinin adını gösterir,
  • [2] sütunu, karşılaşılan her bir etiket için bir giriş örneği sunar,
  • [3] sütunu, sayfaya şablon olarak kullanılan bean'in değerlerini gösterir,
  • [2]'te yapılan girişler, [4] düğmesi ile onaylanır. Bu onay işlemi, yalnızca sayfanın şablon bean'ini günceller. Ardından aynı sayfa yeniden gönderilir. Dolayısıyla, onaylamadan sonra [3] sütununda şablon bean'in yeni değerleri görüntülenir; böylece kullanıcı, girdilerinin sayfa şablonu üzerindeki etkisini kontrol edebilir.

2.5.2. NetBeans Projesi

Uygulamanın NetBeans projesi şöyledir:

  • [1]'te, proje yapılandırma dosyaları JSF'te,
  • [2] dosyası, projenin tek sayfası: index.xhtml,
  • [3]'e, sayfanın görünümünü yapılandırmak için bir stil sayfası [styles.css]'e, [index.xhtml]'e
  • [4], projenin Java sınıfları,
  • [5] dosyasına, uygulamanın Fransızca ve İngilizce olmak üzere iki dilde mesaj dosyası.

2.5.3. [pom.xml] dosyası

Sadece bağımlılıkları sunuyoruz:


    <dependencies>
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-api</artifactId>
            <version>2.1.7</version>
        </dependency>
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-impl</artifactId>
            <version>2.1.7</version>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
</dependencies>

Bunlar, JSF projesinin gerekli bağımlılıklarıdır. Aşağıdaki örneklerde, bu dosya yalnızca değiştiğinde gösterilecektir.

2.5.4. [web.xml] dosyası

[web.xml] dosyası, [index.xhtml] sayfasının projenin ana sayfası olması için yapılandırılmıştır:


<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  <context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>client</param-value>
  </context-param>  
  <context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
  </context-param>
  <context-param>
    <param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
    <param-value>true</param-value>
  </context-param> 
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/faces/*</url-pattern>
  </servlet-mapping>
  <session-config>
    <session-timeout>
      30
    </session-timeout>
  </session-config>
  <welcome-file-list>
    <welcome-file>faces/index.xhtml</welcome-file>
  </welcome-file-list>
</web-app>
  • 30. satır: [index.xhtml] sayfası ana sayfadır,
  • 11-14. satırlar: [Faces Servlet] servlet'i için bir ayar. Bu ayar, aşağıdaki gibi bir facelet içindeki yorumların:

        <!-- diller -->

gibi bir facelet içindeki yorumların yok sayılmasını sağlar. Bu parametre olmadan, yorumlar anlaşılması zor sorunlara yol açar,

  • 3-6. satırlar: [Faces Servlet] servleti için bir parametre; bu parametre biraz ileride açıklanacaktır.

2.5.5. [faces-config.xml] dosyası

Uygulamanın [faces-config.xml] dosyası şöyledir:


<?xml version='1.0' encoding='UTF-8'?>

<!-- =========== FULL CONFIGURATION FILE ================================== -->

<faces-config version="2.0"
              xmlns="http://java.sun.com/xml/ns/javaee" 
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">

  <application>
    <resource-bundle>
      <base-name>
        messages
      </base-name>
      <var>msg</var>
    </resource-bundle>
  </application>
</faces-config>
  • 11-16. satırlar: Uygulamanın mesaj dosyasını yapılandırır.

2.5.6. [messages.properties] mesaj dosyası

Mesaj dosyaları (projenin ekran görüntüsündeki [5]'e bakınız) şunlardır:

[messages_fr.properties]


form.langue1=Fran\u00e7ais
form.langue2=Anglais
form.titre=Java Server Faces - les tags
form.headerCol1=Type
form.headerCol2=Champs de saisie
form.headerCol3=Valeurs du modèle de la page
form.loginPrompt=login : 
form.passwdPrompt=mot de passe : 
form.descPrompt=description : 
form.selectOneListBox1Prompt=choix unique : 
form.selectOneListBox2Prompt=choix unique : 
form.selectManyListBoxPrompt=choix multiple : 
form.selectOneMenuPrompt=choix unique : 
form.selectManyMenuPrompt=choix multiple : 
form.selectBooleanCheckboxPrompt=marié(e) : 
form.selectManyCheckboxPrompt=couleurs préférées : 
form.selectOneRadioPrompt=moyen de transport préféré : 
form.submitText=Valider
form.buttonRazText=Raz

Bu mesajlar sayfanın aşağıdaki yerlerinde görüntülenir:

Mesajların İngilizce versiyonu şöyledir:

[messages_en.properties]


form.langue1=French
form.langue2=English
form.titre=Java Server Faces - the tags
form.headerCol1=Input Type
form.headerCol2=Input Fields
form.headerCol3=Page Model Values
form.loginPrompt=login : 
form.passwdPrompt=password : 
form.descPrompt=description : 
form.selectOneListBox1Prompt=unique choice : 
form.selectOneListBox2Prompt=unique choice : 
form.selectManyListBoxPrompt=multiple choice : 
form.selectOneMenuPrompt=unique choice : 
form.selectManyMenuPrompt=multiple choice : 
form.selectBooleanCheckboxPrompt=married : 
form.selectManyCheckboxPrompt=preferred colors : 
form.selectOneRadioPrompt=preferred transport means : 
form.submitText=Submit
form.buttonRazText=Reset

2.5.7. [index.xhtml] sayfasındaki [Form.java] şablonu

Yukarıdaki projede, [Form.java] sınıfı, JSF ve [index.xhtml] sayfaları için şablon veya destek fasulyesi görevi görecektir. Bu şablon kavramını, [index.xhtml] sayfasından alınan bir örnekle açıklayalım:


<!-- satır 1 -->
          <h:outputText value="inputText"  styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.loginPrompt']}"/>
            <h:inputText id="inputText" value="#{form.inputText}"/>
          </h:panelGroup>
          <h:outputText value="#{form.inputText}"/>

[index.xhtml] sayfasının ilk çağrısında, yukarıdaki kod giriş tablosunun 2. satırını oluşturur:

  1. satırda [1] alanı, 3-6. satırlarda [2] alanı, 7. satırda ise [3] alanı görüntülenir.

  2. ve 7. satırlar, [Form.java] sınıfında tanımlanan form bean'ini aşağıdaki şekilde kullanan EL ifadesini kullanır:


package forms;

import javax.enterprise.context.RequestScoped;
import javax.faces.bean.ManagedBean;


@ManagedBean
@RequestScoped
public class Form {
  • 7. satır, isimsiz bir bean tanımlar. Dolayısıyla bu, küçük harfle başlayan sınıf adı olacaktır: form,
  • Bean'in kapsamı istek (request) düzeyindedir. Bu, istemci isteği / sunucu yanıtı döngüsünde, isteğin gerektirdiği anda örneklendiği ve istemciye yanıt verildikten sonra silindiği anlamına gelir.

Aşağıdaki [index.xhtml] sayfasındaki kodda:


<!-- satır 1 -->
          <h:outputText value="inputText"  styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.loginPrompt']}"/>
            <h:inputText id="inputText" value="#{form.inputText}"/>
          </h:panelGroup>
<h:outputText value="#{form.inputText}"/>
  1. ve 7. satırlarda form bean'inin inputText değeri kullanılmaktadır. Bir P sayfasını M modeliyle birbirine bağlayan ilişkileri anlamak için, bir web uygulamasını karakterize eden istemci isteği / sunucu yanıtı döngüsüne geri dönmek gerekir:

P sayfasının tarayıcıya yanıt olarak gönderildiği durum (4. adım) — örneğin sayfanın ilk talebi sırasında — ile, kullanıcının P sayfasında bir olay tetiklemesi üzerine bu olayın [Faces Servlet] denetleyicisi tarafından işlendiği durum (1. adım) arasında ayrım yapmak gerekir.

Bu iki durumu tarayıcının bakış açısından inceleyerek ayırt edebiliriz:

  1. sayfanın ilk talebi sırasında, tarayıcı sayfanın URL'i üzerinde bir GET işlemi gerçekleştirir,
  2. sayfaya girilen değerlerin gönderilmesi sırasında ise tarayıcı, sayfanın URL dosyası üzerinde POST işlemini gerçekleştirir.

Her iki durumda da aynı URL istenir. Tarayıcının GET veya POST isteğinin niteliğine göre, isteğin işlenmesi farklılık gösterecektir.

[cas 1 – demande initiale de la page P]

Tarayıcı, bir GET ile sayfanın URL'sini talep eder. [Faces Servlet] denetleyicisi, doğrudan yanıtın oluşturulduğu [4] aşamasına geçecek ve [index.xhtml] sayfası istemciye gönderilecektir. JSF denetleyicisi, sayfanın her bir etiketinin görüntülenmesini isteyecektir. [index.xhtml] kodunun 5. satırını örnek olarak ele alalım:


            <h:inputText id="inputText" value="#{form.inputText}"/>

JSF <h:inputText value="değer"/> etiketi, HTML <input type="text" value="değer"/> etiketini oluşturur. Bu etiketi işlemekle görevli sınıf, değerlendirilmesi gereken #{form.inputText} ifadesiyle karşılaşır:

  • form bean'i henüz mevcut değilse, forms.Form sınıfının örneklenmesiyle oluşturulur;
  • #{form.inputText} ifadesi, form.getInputText() yöntemine çağrı yapılarak değerlendirilir,
  • <input id="form:inputText" type="text" name="formulaire:inputText" value="metin" /> metni, HTML akışına eklenir; bu akış, form.getInputText() yönteminin "metin" dizesini döndürdüğünü varsayarsak, müşteriye gönderilecektir. Ayrıca, JSF, akışa eklenen HTML bileşenine bir ad (name) verecektir. Bu ad, analiz edilen JSF bileşeninin id tanımlayıcıları ile üst bileşenlerinin (burada <h:form id="formulaire"/> etiketi) id tanımlayıcılarından oluşturulur.

Unutulmamalıdır ki, P sayfasında #{M.champ} ifadesi kullanılırsa (burada M, P sayfasının model bean'idir), bu bean'in getChamp() adlı genel bir yönteme sahip olması gerekir. Bu yöntem tarafından döndürülen tür, String türüne dönüştürülebilmelidir. Olası ve sık kullanılan bir M modeli şöyledir:

1
2
3
4
private T champ;
public T getChamp(){
    return champ;
} 

burada T, gerekirse toString yöntemi kullanılarak String türüne dönüştürülebilen bir türdür.

Yine P sayfasının görüntülenmesi durumunda, şu satırın işlenmesi:


<h:outputText value="#{form.inputText}"/>

benzer şekilde gerçekleştirilecek ve aşağıdaki HTML akışı oluşturulacaktır:

texte

Sunucu içinde, P sayfası, müşteriye gönderilen sayfanın etiket ağacının bir yansıması olan bir bileşen ağacı olarak temsil edilir. Bu ağaca sayfanın görünümü veya durumu adını vereceğiz. Bu durum bellekte saklanır. Uygulamanın [web.xml] dosyasında yapılan yapılandırmaya göre iki şekilde saklanabilir:


<web-app ...>
...
  <context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>client</param-value>
  </context-param>
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
...
</web-app>

7-11. satırlar, [Faces Servlet] denetleyicisini tanımlar. Bu denetleyici, çeşitli <context-param> etiketleriyle yapılandırılabilir; bunlardan 3-6. satırlardaki etiket, bir sayfanın durumunun istemciye (tarayıcıya) kaydedilmesi gerektiğini belirtir. Diğer olası değer olan 5. satırdaki "server" seçeneği, durumun sunucuda kaydedileceğini belirtir. Bu, varsayılan değerdir.

Bir sayfanın durumu istemcide kaydedildiğinde, JSF denetleyicisi, gönderdiği her HTML sayfasına, değeri sayfanın mevcut durumu olan gizli bir alan ekler. Bu gizli alan şu biçimdedir:

<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="H4sIAAAAAAAAANV...Bnoz8dqAAA=" />

Değeri, istemciye gönderilen sayfanın durumunu kodlanmış biçimde temsil eder. Anlaşılması gereken önemli nokta, bu gizli alanın sayfanın formunun bir parçası olduğu ve dolayısıyla formun gönderilmesi sırasında tarayıcı tarafından gönderilen değerlerin bir parçası olacağıdır. Bu gizli alan sayesinde, JSF denetleyicisi, sayfayı müşteriye gönderildiği haliyle geri yükleyebilir.

Bir sayfanın durumu sunucuya kaydedildiğinde, müşteriye gönderilen sayfanın durumu da müşterinin oturumunda kaydedilir. Müşteri tarayıcısı, forma girilen değerleri gönderirken oturum jetonunu da gönderir. JSF denetleyicisi, bu jeton aracılığıyla müşteriye gönderilen sayfanın durumunu bulur ve geri yükler.

JSF sayfasının durumunun kodlanması için yüzlerce bayt gerekebilir. Bu durum, uygulamanın her kullanıcısı için tutulduğundan, kullanıcı sayısı fazla olduğunda bellek sorunları yaşanabilir. Bu nedenle, burada sayfanın durumunu istemcide kaydetmeyi tercih ettik (bkz. [web.xml], 2.5.4. paragraf, sayfa 66).

[cas 2 – traitement de la page P]

Yukarıdaki [1] adımındayız; burada [Faces Servlet] denetleyicisi, daha önce [index.xhtml] sayfasını gönderdiği istemci tarayıcısından bir POST isteği alacaktır. Burada bir sayfa olayının işlenmesi söz konusudur. Olayın [2a]'te işlenebilmesi için bile öncesinde birkaç adım gerçekleşecektir. POST isteğinin JSF denetleyicisi tarafından işlenme döngüsü şu şekildedir:

FEDCBA

Image

  • [A]'te, gizli alan javax.faces.ViewState sayesinde, başlangıçta istemci tarayıcısına gönderilen görünüm yeniden oluşturulur. Burada, sayfanın bileşenleri gönderilen sayfadaki değerlerine geri döner. inputText bileşenimiz "metin" değerine geri döner,
  • [B]'te ise, istemci tarayıcısı tarafından gönderilen değerler, görünüm bileşenlerini güncellemek için kullanılır. Dolayısıyla, HTML adlı giriş alanında (inputText), kullanıcı "jean" yazmışsa, "jean" değeri "metin" değerinin yerine geçer. Artık görünüm, tarayıcıya gönderildiği haliyle değil, kullanıcı tarafından değiştirildiği haliyle sayfayı yansıtmaktadır;
  • [C]'te, gönderilen değerler doğrulanır. Önceki inputText bileşeninin bir yaş giriş alanı olduğunu varsayalım. Girilen değerin bir tamsayı olması gerekir. Tarayıcı tarafından gönderilen değerler her zaman String türündedir. P sayfasına bağlı M modelindeki nihai türleri ise tamamen farklı olabilir. Bu durumda, String türünden T türüne bir dönüştürme işlemi gerçekleşir. Bu dönüştürme işlemi başarısız olabilir. Bu durumda, istek/yanıt döngüsü sona erer ve [B] türünde oluşturulan P sayfası, P sayfasının yazarı tarafından öngörülmüşse hata mesajlarıyla birlikte istemci tarayıcısına geri gönderilir. Kullanıcının, geliştiricinin herhangi bir çaba sarf etmesine gerek kalmadan sayfayı girdiği haliyle gördüğü unutulmamalıdır. JSP gibi başka bir teknolojide ise, geliştirici, kullanıcı tarafından girilen değerlerle P sayfasını kendisi yeniden oluşturmalıdır. Bir bileşenin değeri ayrıca bir doğrulama sürecinden geçebilir. Yine, yaş giriş alanı olan inputText bileşenini örnek olarak alırsak, girilen değer sadece bir tamsayı olmakla kalmayıp, [1,N] aralığı içinde yer alan bir tamsayı olmalıdır. Girilen değer dönüştürme aşamasını geçse bile, doğrulama aşamasını geçemeyebilir. Bu durumda da istek/yanıt döngüsü sona erer ve [B] ile oluşturulan P sayfası istemci tarayıcısına geri gönderilir,
  • [D]'te, P sayfasının tüm bileşenleri dönüştürme ve doğrulama aşamasını geçerse, bu bileşenlerin değerleri P sayfasının M şablonuna atanacaktır. Aşağıdaki etiketle oluşturulan giriş alanının değeri:

        <h:inputText value="#{form.inputText}"/>

"jean" ise, bu değer form.setInputText("jean") kodunun çalıştırılmasıyla sayfanın form şablonuna atanacaktır. Unutulmamalıdır ki, P sayfasının M şablonunda, P sayfasındaki bir giriş alanının değerini depolayan M şablonundaki özel alanların bir set yöntemine sahip olması gerekir;

  • sayfa P'nin M modeli gönderilen değerlerle güncellendikten sonra, sayfa P'deki POST olayını tetikleyen olay işlenebilir. Bu, [E] aşamasıdır. Bu olayın işleyicisi M bean’ine aitse, aynı bean’in alanlarında depolanan P formunun değerlerine erişebilir.
  • [E] adımı, JSF denetleyicisine bir gezinme anahtarı döndürür. Örneklerimizde bu, her zaman görüntülenecek XHTML sayfasının adı olacaktır; .xhtml son eki hariçtir. Bu, [F] adımıdır. Bunu yapmanın başka bir yolu da, [faces-config.xml] dosyasında aranacak bir gezinme anahtarı döndürmektir. Bu durumu daha önce açıklamıştık.

Yukarıdakilerden şunu çıkarabiliriz:

  • bir P sayfası, [M].getC() yöntemlerini kullanarak M şablonundaki C alanlarını görüntüler,
  • bir P sayfasının M modelindeki C alanları, [M].setC(giriş) yöntemleri kullanılarak P sayfasında girilen değerlerle başlatılır. Bu aşamada, başarısızlıkla sonuçlanabilecek dönüştürme ve doğrulama işlemleri gerçekleşebilir. Bu durumda, P sayfasının POST olayını tetikleyen olay işlenmez ve sayfa, müşterinin girdiği haliyle müşteriye geri gönderilir.

[index.xhtml] sayfasının [Form.java] şablonu şu şekilde olacaktır:


package forms;

import javax.enterprise.context.RequestScoped;
import javax.faces.bean.ManagedBean;


@ManagedBean
@RequestScoped
public class Form {
  
  /** Form'un yeni bir örneğini oluşturur */
  public Form() {
  }
  
  // form alanları
  private String inputText="texte";
  private String inputSecret="secret";
  private String inputTextArea="ligne1\nligne2\n";
  private String selectOneListBox1="2";
  private String selectOneListBox2="3";
  private String[] selectManyListBox=new String[]{"1","3"};
  private String selectOneMenu="1";
  private String[] selectManyMenu=new String[]{"1","2"};
  private String inputHidden="initial";
  private boolean selectBooleanCheckbox=true;
  private String[] selectManyCheckbox=new String[]{"1","3"};
  private String selectOneRadio="2";
  
  // olaylar
  public String submit(){
    return null;
  }
  
  // alıcı ve ayarlayıcılar
  ...
}

16-27. satırlardaki alanlar, formun aşağıdaki yerlerinde kullanılır:

2.5.8. [index.xhtml] sayfası

Önceki görünümü oluşturan [index.xhtml] sayfası şöyledir:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">

  <f:view locale="#{changeLocale.locale}">
    <h:head>
      <title>JSF</title>
      <h:outputStylesheet library="css" name="styles.css"/>
    </h:head>
    <h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
      <h:form id="formulaire">
        <!-- diller -->
        <h:panelGrid columns="2">
          <h:commandLink value="#{msg['form.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
          <h:commandLink value="#{msg['form.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
        </h:panelGrid>
        <h1><h:outputText value="#{msg['form.titre']}"/></h1>
        <h:panelGrid columnClasses="col1,col2,col3" columns="3" border="1">
          <!-- başlıklar -->
          <h:outputText value="#{msg['form.headerCol1']}" styleClass="entete"/>
          <h:outputText value="#{msg['form.headerCol2']}" styleClass="entete"/>
          <h:outputText value="#{msg['form.headerCol3']}" styleClass="entete"/>
          <!-- satır 1 -->
          ...
          <!-- satır 2 -->
          ...
          <!-- satır 3 -->
          ...
          <!-- satır 4 -->
          ...
          <!-- satır 5 -->
          ...
          <!-- satır 6 -->
          ...
          <!-- satır 7 -->
          ...
          <!-- satır 8 -->
          ...
          <!-- satır 9 -->
          ...
          <!-- satır 10 -->
          ...
          <!-- satır 11 -->
          ...
          <!-- satır 12 -->
          ...
        </h:panelGrid>
        <p>
          <h:commandButton type="submit" id="submit" value="#{msg['form.submitText']}"/>
        </p>
      </h:form>
    </h:body>
  </f:view>
</html>

Bu sayfanın ana bileşenlerini sırayla inceleyeceğiz. JSF formunun genel yapısına dikkat edelim:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">

  <f:view ...>
    <h:head>
      ...
    </h:head>
    <h:body ...>
      <h:form id="formulaire">
        ...
        <h:commandButton type="submit" id="submit" value="#{msg['form.submitText']}"/>
        ...
      </h:form>
    </h:body>
  </f:view>
</html>

Bir formun bileşenleri <h:form> etiketinin içinde yer almalıdır (12-16. satırlar). Uygulamayı uluslararası hale getirmek istiyorsak <f:view> etiketi (7-18. satırlar) gereklidir. Ayrıca, bir formun gönderilebilmesi için bir yol bulunmalıdır (POST); bu genellikle 14. satırda olduğu gibi bir bağlantı veya düğmedir. Form, birçok olayla da gönderilebilir (bir listede seçimin değişmesi, aktif alanın değişmesi, giriş alanına bir karakterin girilmesi vb.).

2.5.9. Formun stili

Form tablosundaki sütunların daha okunaklı hale getirilmesi için, forma bir stil sayfası eklenmiştir:


  <f:view locale="#{changeLocale.locale}">
    <h:head>
      <title>JSF</title>
      <h:outputStylesheet library="css" name="styles.css"/>
</h:head>
  • 4. satır: Sayfanın stil sayfası, HTML <head> etiketinin içinde şu etiketle tanımlanmıştır:

<h:outputStylesheet library="css" name="styles.css"/>

Stil sayfası, [resources] klasöründe aranacaktır:

Etiket içinde:


<h:outputStylesheet library="css" name="styles.css"/>
  • library, stil sayfasını içeren klasörün adıdır,
  • name, stil sayfasının adıdır.

Bu stil sayfasının bir kullanım örneğine bakalım:


        <h:panelGrid columnClasses="col1,col2,col3" columns="3" border="1">

<h:panelGrid columns="3"/> etiketi, üç sütunlu bir tablo tanımlar. columnClasses özniteliği, bu sütunlara stil verilmesini sağlar. columnClasses özniteliğinin col1, col2, col3 değerleri, tablonun 1, 2 ve 3. sütunlarının stilini belirtir. Bu stiller, sayfanın stil sayfasında aranır:


.info{
   font-family: Arial,Helvetica,sans-serif;
   font-size: 14px;
   font-weight: bold
}

.col1{
   background-color: #ccccff
}

.col2{
   background-color: #ffcccc
}

.col3{
   background-color: #ffcc66
}

.entete{
   font-family: 'Times New Roman',Times,serif;
   font-size: 14px;
   font-weight: bold
}
  • 7-9. satırlar: col1 adlı stil,
  • 11-13. satırlar: col2 adlı stil,
  • 15-17. satırlar: col3 adlı stil,

Bu üç stil, her bir sütunun arka plan rengini belirler.

  • 19-23. satırlar: "entete" stili, tablonun ilk satırındaki metinlerin stilini tanımlamak için kullanılır:

          <!-- başlıklar -->
          <h:outputText value="#{msg['form.headerCol1']}" styleClass="entete"/>
          <h:outputText value="#{msg['form.headerCol2']}" styleClass="entete"/>
          <h:outputText value="#{msg['form.headerCol3']}" styleClass="entete"/>
  • 1-5. satırlar: "info" stili, tablonun ilk sütunundaki metinlerin stilini tanımlamak için kullanılır:

          <!-- satır 1 -->
          <h:outputText value="inputText"  styleClass="info"/>

Stil sayfalarının kullanımı üzerinde fazla durmayacağız, çünkü bu konu tek başına bir kitap dolduracak kadar geniş ve ayrıca stil sayfalarının hazırlanması genellikle uzmanlara bırakılır. Yine de, stil sayfalarının kullanımının vazgeçilmez olduğunu hatırlatmak amacıyla minimalist bir stil sayfası kullanmayı tercih ettik.

Şimdi sayfanın arka plan görüntüsünün nasıl tanımlandığına bakalım:


<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">

Arka plan görüntüsü, <h:body> etiketinin style özniteliği ile belirlenir. Bu öznitelik, stil öğelerinin belirlenmesini sağlar. Arka plan görüntüsü, [resources/images/standard.jpg] klasöründedir:

Bu resim, URL ve [/mv-jsf2-03/resources/images/standard.jpg] aracılığıyla elde edilir. Dolayısıyla şöyle yazabiliriz:


<h:body style="background-image: url('mv-jsf2-03/resources/images/standard.jpg');">

/mv-jsf2-03, uygulamanın bağlamıdır. Bu bağlam, web sunucusu yöneticisi tarafından belirlenir ve dolayısıyla değişebilir. Bu bağlam, EL ${request.contextPath} ifadesiyle elde edilebilir. Bu nedenle, şu style özniteliği tercih edilmelidir:


style="background-image: url('${request.contextPath}/resources/images/standard.jpg');"

çünkü bu, bağlam ne olursa olsun geçerli olacaktır.

2.5.10. Bir formun iki istemci isteği / sunucu yanıtı döngüsü

2.5.7 numaralı paragrafta genel bir durum için açıklananlara geri dönelim ve bunu incelenen forma uygulayalım. Bu form, klasik JSF ortamında test edilecektir:

Burada, olay işleyiciler veya [métier] katmanı olmayacaktır. Dolayısıyla [2x] adımları mevcut olmayacaktır. Form F'nin başlangıçta tarayıcı tarafından talep edildiği durum ile, kullanıcının Form F'de bir olay tetiklemesi üzerine bu olayın [Faces Servlet] denetleyicisi tarafından işlendiği durum arasında ayrım yapılacaktır. Birbirinden farklı iki istemci talebi / sunucu yanıtı döngüsü vardır.

  • Birincisi, sayfanın ilk isteğine karşılık gelir ve tarayıcının F formundaki URL üzerinde gerçekleştirdiği GET işlemiyle tetiklenir;
  • ikincisi ise sayfaya girilen değerlerin gönderilmesine karşılık gelir ve aynı URL üzerinde gerçekleştirilen POST işlemi tarafından tetiklenir.

Tarayıcının GET veya POST isteğinin türüne göre, [Faces Servlet] denetleyicisi tarafından isteğin işlenmesi farklılık gösterir.

[cas 1 – demande initiale du formulaire F]

Tarayıcı, bir GET ile sayfanın URL'sini ister. [Faces Servlet] denetleyicisi, yanıtın oluşturulma aşaması olan [4]'e doğrudan geçecektir. [index.xhtml] formu, [Form.java] şablonu ile başlatılacak ve müşteriye gönderilecektir; müşteri şu görünümü alacaktır:

Image

Bu durumda, istemci ile sunucu arasındaki HTTP iletişimleri şu şekildedir:

Müşteriden gelen HTTP isteği:

1
2
3
4
5
6
7
8
GET /mv-jsf2-03/ HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0,8
Accept-Language: fr,fr-fr;q=0.8,en;q=0.6,en-us;q=0.4,es;q=0.2
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
  1. satırda, tarayıcının GET mesajı görülmektedir.

Sunucudan gelen HTTP yanıtı:

1
2
3
4
5
6
7
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Powered-By: JSF/2.0
Set-Cookie: JSESSIONID=F6E66136BF00EEE026ADAB1BBEBFD587; Path=/mv-jsf2-03/; HTTPOnly
Content-Type: text/html;charset=UTF-8
Content-Length: 7371
Date: Tue, 15 May 2012 09:04:57 GMT

Burada gösterilmemiştir, 7. satırın ardından boş bir satır ve formun HTML kodu gelir. Tarayıcı bu kodu yorumlayıp görüntüler.

[cas 2 – traitement des valeurs saisies dans le formulaire F]

Kullanıcı formu doldurur ve [Valider] düğmesiyle gönderir. Tarayıcı daha sonra bir POST ile formun URL kodunu ister. [Faces Servlet] denetleyicisi bu isteği işler, [index.xhtml] formunun [Form.java] şablonunu günceller ve bu yeni şablonla güncellenmiş [index.xhtml] formunu geri gönderir. Bu döngüyü bir örnek üzerinden inceleyelim:

Image

Yukarıda, kullanıcı girişlerini yaptı ve onayladı. Yanıt olarak aşağıdaki görünümü alıyor:

Image

Bu durumda, HTTP istemci/sunucu arasındaki iletişim şu şekildedir:

İstemciden gelen HTTP isteği:

POST /mv-jsf2-03/faces/index.xhtml HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en;q=0.6,en-us;q=0.4,es;q=0.2
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
Referer: http://localhost:8080/mv-jsf2-03/faces/index.xhtml
Cookie: JSESSIONID=374CC5F1D2ACAC182A5747A443651E36
Content-Type: application/x-www-form-URLencoded
Content-Length: 1543

formulaire=formulaire&formulaire%3AinputText=nouveau+texte&formulaire%3AinputSecret=mdp&formulaire%3AinputTextArea=Tutoriel+JSF%0D%0A&formulaire%3AselectOneListBox1=3&formulaire%3AselectOneListBox2=5&formulaire%3AselectManyListBox=3&formulaire%3AselectManyListBox=4&formulaire%3AselectManyListBox=5&formulaire%3AselectOneMenu=4&formulaire%3AselectManyMenu=5&formulaire%3AinputHidden=initial&formulaire%3AselectManyCheckbox=2&formulaire%3AselectManyCheckbox=3&formulaire%3AselectManyCheckbox=4&formulaire%3AselectOneRadio=4&formulaire%3Asubmit=Valider&javax.faces.ViewState=H4sIAAAAAAAAAJVUT0g...P4BKm1E4F0FAAA  
  1. satırda, tarayıcı tarafından oluşturulan POST yer almaktadır. 14. satırda ise kullanıcı tarafından girilen değerler bulunmaktadır. Örneğin, giriş alanına yazılan metni burada görebiliriz:
formulaire%3AinputText=nouveau+texte
  1. satırda, gizli alan javax.faces.ViewState gönderilmiştir. Bu alan, ilk GET sırasında tarayıcıya gönderildiği şekliyle formun durumunu kodlanmış biçimde temsil eder.

Sunucudan gelen HTTP yanıtı:

1
2
3
4
5
6
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Powered-By: JSF/2.0
Content-Type: text/html;charset=UTF-8
Content-Length: 7299
Date: Tue, 15 May 2012 09:37:17 GMT

Burada gösterilmese de, 6. satırın ardından boş bir satır ve POST'ten türetilen yeni şablonla güncellenen formun HTML kodu gelir.

Şimdi bu formun farklı bileşenlerini inceleyeceğiz.

2.5.11. <h:inputText> etiketi

<h:inputText> etiketi, bir HTML <input type="text" ...> etiketi oluşturur.

Aşağıdaki kodu ele alalım:


          <!-- satır 1 -->
          <h:outputText value="inputText"  styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.loginPrompt']}"/>
            <h:inputText id="inputText" value="#{form.inputText}"/>
          </h:panelGroup>
<h:outputText value="#{form.inputText}"/>

ve bunun şablonu [Form.java]:


  private String inputText="texte";

  public String getInputText() {
    return inputText;
  }
  
  public void setInputText(String inputText) {
    this.inputText = inputText;
}

[index.html] sayfası ilk kez istendiğinde, elde edilen sayfa şöyledir:

  • XHTML kodunun 2. satırı, [1]'i oluşturur;
  • <h:panelGroup> etiketi (3-6. satırlar), sayfanın tam kodunun 20. satırındaki <h:panelGrid> etiketi tarafından oluşturulan tablonun aynı hücresinde birden fazla öğeyi bir araya getirmeye olanak tanır (bkz. 2.5.8. paragraf). [2] metni 4. satır tarafından oluşturulur. [3] giriş alanı ise [5] satırı tarafından oluşturulur. Burada, giriş alanının metnini oluşturmak için [Form.java]’in (Java kodunun 3-5. satırları) getInputText yöntemi kullanılmıştır;
  • XHTML kodunun 7. satırı ise [4]'i oluşturur. [4] metnini oluşturmak için yine [Form.java]'in getInputText yöntemi kullanılmıştır.

XHTML sayfası tarafından oluşturulan HTML akışı şöyledir:


<tr>
<td class="col1"><span class="info">inputText</span></td>
<td class="col2">login : <input id="formulaire:inputText" type="text" name="formulaire:inputText" value="texte" /></td>
<td class="col3">texte</td>
</tr>

HTML etiketindeki <tr> ve <td> etiketleri, form tablosunu oluşturmak için kullanılan <h:panelGrid> etiketi tarafından oluşturulur.

Şimdi, aşağıda [1] giriş alanına bir değer girelim ve [Valider] [2] düğmesiyle formu gönderelim. Yanıt olarak [3, 4] sayfasını elde ederiz:

[1] alanının değeri şu şekilde gönderilir:

formulaire%3AinputText=nouveau+texte

[2]'te, form aşağıdaki düğmeyle onaylanır:


          <h:commandButton id="submit" type="submit" value="#{msg['form.submitText']}"/>

<h:commandButton> etiketinde action özniteliği yoktur. Bu durumda, hiçbir olay işleyicisi çağrılmaz ve hiçbir gezinme kuralı uygulanmaz. İşlemden sonra aynı sayfa geri gönderilir. İşlem döngüsünü tekrar gözden geçirelim:

ABCDEF

Image

  • [A]'te P sayfası, gönderildiği haliyle geri yüklenir. Bu, inputText kimliğine sahip bileşenin başlangıç değeri olan "metin" ile geri yüklendiği anlamına gelir,
  • [B]'te, tarayıcı tarafından gönderilen değerler (kullanıcı tarafından girilenler) P sayfasının bileşenlerine atanır. Burada, ID'si inputText olan bileşen "yeni bir metin" değerini alır,
  • [C]'te dönüştürme ve doğrulama işlemleri gerçekleştirilir. Burada herhangi bir işlem yoktur. M şablonunda, inputText kimliğine sahip bileşene ilişkin alan şöyledir:

private String inputText="texte";

Girilen değerler String türünde olduğundan, yapılacak bir dönüştürme yoktur. Ayrıca, herhangi bir doğrulama kuralı oluşturulmamıştır. Bunları daha sonra oluşturacağız.

  • [D]'te, girilen değerler şablona atanır. [Form.java]'in inputText alanı "yeni bir metin" değerini alır,
  • [E]'te, [Valider] düğmesine hiçbir olay işleyicisi atanmadığı için herhangi bir işlem yapılmaz.
  • [F]'te, [Valider] düğmesinin action özniteliği olmadığı için P sayfası tekrar istemciye gönderilir. Ardından [index.xhtml]'teki aşağıdaki satırlar yürütülür:

          <!-- satır 1 -->
          <h:outputText value="inputText"  styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.loginPrompt']}"/>
            <h:inputText id="inputText" value="#{form.inputText}"/>
          </h:panelGroup>
<h:outputText value="#{form.inputText}"/>
  1. ve 7. satırlar, şablondaki inputText alanının değerini kullanır; bu değer artık "yeni bir metin"dir. Bu nedenle şu görüntü elde edilir:

Image

2.5.12. <h:inputSecret> etiketi

<h:inputSecret> etiketi, bir HTML <input type="password" ...> etiketini oluşturur. Bu, <h:JSF> <h:inputText> etiketindeki giriş alanına benzer; tek fark, kullanıcı tarafından girilen her karakterin görsel olarak * karakteriyle değiştirilmesidir.

Aşağıdaki kodu ele alalım:


          <!-- satır 2 -->
          <h:outputText value="inputSecret"  styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.passwdPrompt']}"/>
            <h:inputSecret id="inputSecret" value="#{form.inputSecret}"/>
          </h:panelGroup>
<h:outputText value="#{form.inputSecret}"/>

ve [Form.java]'teki şablonu:


private String inputSecret="secret";

[index.xhtml] sayfası ilk kez istendiğinde, elde edilen sayfa şöyledir:

  • XHTML kodunun 2. satırı, [1]'i oluşturur
  • [2] metni, 4. satır tarafından oluşturulur. [3] giriş alanı, [5] satırı tarafından oluşturulur. Normalde, giriş alanının metnini oluşturmak için [Form.java]'in getInputSecret yöntemi kullanılması gerekirdi. Bunun "şifre" türü olması durumunda bir istisna vardır. <h:inputSecret> etiketi yalnızca bir girişi okumak için kullanılır, görüntülemek için değil.
  • XHTML kodunun 7. satırı, [4]'i oluşturur. Burada, [Form.java]'in getInputSecret yöntemi, [4] metnini oluşturmak için kullanılmıştır (bkz. Java kodunun 1. satırı).

XHTML sayfası tarafından oluşturulan HTML akışı şöyledir:


<tr>
<td class="col1"><span class="info">inputSecret</span></td>
<td class="col2">mot de passe : <input id="formulaire:inputSecret" type="password" name="formulaire:inputSecret" value="" /></td>
<td class="col3">secret</td>
</tr>
  • 3. satır: JSF <h:inputSecret> etiketiyle oluşturulan HTML <input type="password" .../> etiketi

Şimdi, aşağıda [1] giriş alanına bir değer girelim ve [Valider] [2] düğmesiyle formu gönderelim. Yanıt olarak [3] sayfasını elde ederiz:

[1] alanının değeri şu şekilde gönderilir:

formulaire%3AinputSecret=mdp

[2] tarafından formun onaylanması, [1] girişi ile [Form.java] şablonunun güncellenmesine neden oldu. Bunun üzerine, [Form.java]'in inputSecret alanı mdp değerini almıştır. [index.xhtml] formu herhangi bir gezinme kuralı veya olay işleyicisi tanımlamadığından, şablonu güncellendikten sonra yeniden görüntülenir. Böylece, [index.xhtml] sayfasının ilk çağrıldığında görüntülenen haline geri dönülür; burada sadece şablondaki inputSecret alanının değeri [3] olarak değişmiştir.

2.5.13. <h:inputTextArea> etiketi

<h:inputTextArea> etiketi, HTML <textarea ...>metin</textarea> etiketini oluşturur. Bu, <h:inputText> etiketindeki JSF giriş alanına benzer, ancak burada birden fazla satır metin yazılabilir.

Aşağıdaki kodu ele alalım:


          <!-- satır 3 -->
          <h:outputText value="inputTextArea" styleClass="info"/>          
          <h:panelGroup>
            <h:outputText value="#{msg['form.descPrompt']}"/>
            <h:inputTextarea id="inputTextArea" value="#{form.inputTextArea}" rows="4"/>
          </h:panelGroup>         
<h:outputText value="#{form.inputTextArea}"/>

ve [Form.java]'teki şablonu:


private String inputTextArea="ligne1\nligne2\n";

[index.xhtml] sayfası ilk kez istendiğinde, elde edilen sayfa şöyledir:

  • XHTML kodunun 2. satırı, [1]'i oluşturur;
  • [2] metni 4. satır tarafından oluşturulur. [3] giriş alanı, [5] satırı tarafından oluşturulur. İçeriği, modelin getInputTextArea yönteminin çağrılmasıyla oluşturulmuştur; bu yöntem, yukarıdaki Java kodunun 1. satırında tanımlanan değeri döndürmüştür,
  • XHTML kodunun 7. satırı, [4]'i oluşturur. Burada, [Form.java]'in getInputTextArea yöntemi yeniden kullanılmıştır. "satır1\nsatır2" dizesi \n satır sonları içeriyordu. Bunlar hâlâ orada. Ancak bir HTML akışına eklendiklerinde, tarayıcılar tarafından boşluk olarak görüntüleniyorlar. [3]'i görüntüleyen HTML <textarea> etiketi ise satır sonlarını doğru şekilde yorumluyor.

XHTML sayfası tarafından oluşturulan HTML akışı şöyledir:


<tr>
<td class="col1"><span class="info">inputTextArea</span></td>
<td class="col2">description : <textarea id="formulaire:inputTextArea" name="formulaire:inputTextArea" rows="4">ligne1
ligne2
</textarea></td>
<td class="col3">ligne1
ligne2
</td>
</tr>
  • 3-5. satırlar: HTML etiketi <textarea>...</textarea> etiketi, JSF <h:inputTextArea> etiketi tarafından oluşturulmuştur

Şimdi, aşağıda [1] giriş alanına bir değer girelim ve [Valider] [2] düğmesiyle formu gönderelim. Yanıt olarak [3] sayfasını alıyoruz:

Gönderilen [1] alanının değeri şöyledir:

formulaire%3AinputTextArea=Tutoriel+JSF%0D%0Apartie+1%0D%0A

[2] tarafından formun onaylanması, [Form.java] şablonunun [1] girişi ile güncellenmesine neden oldu. Bunun üzerine, [Form.java]'in textArea alanı "JSF Eğitimi\n1. bölüm" değerini almıştır. [index.xhtml]'in yeniden görüntülenmesi, şablondaki textArea alanının [3] olarak başarıyla güncellendiğini göstermektedir.

2.5.14. <h:selectOneListBox> etiketi

<h:selectOneListBox> etiketi, <select>...</select> biçiminde bir HTML etiketi oluşturur. Görsel olarak, bir açılır liste veya kaydırma çubuğu içeren bir liste oluşturur.

Aşağıdaki kodu ele alalım:


<!-- satır 4 -->
          <h:outputText value="selectOneListBox (size=1)" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectOneListBox1Prompt']}"/>
            <h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
              <f:selectItem itemValue="1" itemLabel="un"/>
              <f:selectItem itemValue="2" itemLabel="deux"/>
              <f:selectItem itemValue="3" itemLabel="trois"/>
            </h:selectOneListbox>
          </h:panelGroup>
          <h:outputText value="#{form.selectOneListBox1}"/>

ve [Form.java] içindeki şablonu:


private String selectOneListBox1="2";

[index.xhtml] sayfası ilk kez istendiğinde, elde edilen sayfa şöyledir:

  • XHTML kodunun 2. satırı, [1]'i oluşturur
  • [2] metni, 4. satır tarafından oluşturulur. [3] açılır listesi, [5-9] satırları tarafından oluşturulur. Listenin yalnızca bir öğe göstermesinin nedeni, size="1" özniteliğinin değeridir. Bu öznitelik eksikse, size özniteliğinin varsayılan değeri 1'dir. Listenin öğeleri, 6-8. satırlardaki <f:selectItem> etiketleri tarafından oluşturulmuştur. Bu etiketlerin sözdizimi şöyledir:

<f:selectItem itemValue="valeur" itemLabel="texte"/>

itemLabel özniteliğinin değeri, listede görüntülenen içeriktir. itemValue özniteliğinin değeri, öğenin değeridir. Açılır listeden öğe seçildiğinde, [Faces Servlet] denetleyicisine gönderilecek olan değer budur.

[3]'te görüntülenen öğe, getSelectOneListBox1() yönteminin çağrılmasıyla belirlenmiştir (5. satır). Elde edilen "2" sonucu (Java kodunun 1. satırı), açılır listenin 7. satırındaki öğenin görüntülenmesine neden olmuştur; bunun nedeni, bu öğenin itemValue özniteliğinin "2" olmasıdır;

  • XHTML kodunun 11. satırı, [4]'i üretir. Burada, [Form.java]'in getSelectOneListBox1 yöntemi yeniden kullanılmıştır.

XHTML sayfası tarafından oluşturulan HTML akışı şöyledir:


<tr>
<td class="col1"><span class="info">selectOneListBox (size=1)</span></td>
<td class="col2">choix unique : <select id="formulaire:selectOneListBox1" name="formulaire:selectOneListBox1" size="1">
    <option value="1">un</option>
    <option value="2" selected="selected">deux</option>
    <option value="3">trois</option>
</select></td>
<td class="col3">2</td>
</tr>
  • 3. ve 7. satırlar: JSF <h:selectOneListBox> etiketi tarafından oluşturulan HTML <select ...>...</select> etiketi,
  • 4-6. satırlar: JSF <f:selectItem> etiketleri tarafından oluşturulan HTML <option ...> ... </option> etiketleri,
  • 5. satır: listeden value="2" öğesinin seçilmesi, selected="selected" özniteliğinin varlığıyla ifade edilir.

Şimdi, aşağıda listeden [1] yeni bir değer seçelim ve [Valider] [2] düğmesiyle formu gönderelim. Yanıt olarak [3] sayfasını elde ediyoruz:

Gönderilen [1] alanının değeri şöyledir:

formulaire%3AselectOneListBox1=3

[2] tarafından formun onaylanması, [Form.java] şablonunun [1] girişi ile güncellenmesine neden oldu. HTML öğesi


    <option value="3">trois</option>

seçildi. Tarayıcı, açılır listeyi oluşturan JSF bileşeninin değeri olarak "3" dizesini gönderdi:


            <h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">

JSF denetleyicisi, açılır listenin şablonunu güncellemek için setSelectOneListBox1("3") yöntemini kullanacaktır. Ayrıca bu güncellemeden sonra, [Form.java] şablonundaki alan


        private String selectOneListBox1;

artık "3" değerini içerir.

[index.xhtml] sayfası işleme tamamlandıktan sonra yeniden görüntülendiğinde, bu değer yukarıdaki [3,4] görüntüsünün ortaya çıkmasına neden olur:

  • bu değer, açılır listede görüntülenmesi gereken öğeyi belirler ([3]),
  • selectOneListBox1 alanının değeri, [4] olarak görüntülenir.

<h:selectOneListBox> etiketinin bir varyantını ele alalım:


<!-- satır 5 -->
          <h:outputText value="selectOneListBox (size=3)" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectOneListBox2Prompt']}"/>
            <h:selectOneListbox id="selectOneListBox2" value="#{form.selectOneListBox2}" size="3">
              <f:selectItem itemValue="1" itemLabel="un"/>
              <f:selectItem itemValue="2" itemLabel="deux"/>
              <f:selectItem itemValue="3" itemLabel="trois"/>
              <f:selectItem itemValue="4" itemLabel="quatre"/>
              <f:selectItem itemValue="5" itemLabel="cinq"/>
            </h:selectOneListbox>
          </h:panelGroup>
          <h:outputText value="#{form.selectOneListBox2}"/>
  1. satırdaki <h:selectOneListBox> etiketinin [Form.java]'teki şablonu şöyledir:

  private String selectOneListBox2="3";

[index.xhtml] sayfası ilk kez istendiğinde, elde edilen sayfa şöyledir:

  • XHTML kodunun 2. satırı [1]'i oluşturur,
  • [2] metni 4. satır tarafından oluşturulur. Kaydırma çubuklu liste [3], [5-11] satırları tarafından oluşturulur. Açılır menü yerine kaydırmalı liste elde etmemizi sağlayan, size="3" özniteliğinin değeridir. Liste öğeleri, 6-8. satırlardaki <f:selectItem> etiketleri tarafından oluşturulmuştur,

[3]'te seçilen öğe, getSelectOneListBox2() yönteminin çağrılmasıyla belirlenmiştir (5. satır). Elde edilen "3" sonucu (Java kodunun 1. satırı), listenin 8. satırındaki öğenin görüntülenmesine neden oldu; bunun nedeni, bu öğenin itemValue özniteliğinin değeri "3" olmasıdır;

  • XHTML kodunun 13. satırı, [4]'i üretir. Burada, [Form.java]'in getSelectOneListBox2 yöntemi yeniden kullanılmıştır.

XHTML sayfası tarafından oluşturulan HTML akışı şöyledir:


<tr>
<td class="col1"><span class="info">selectOneListBox (size=3)</span></td>
<td class="col2">choix unique : <select id="formulaire:selectOneListBox2" name="formulaire:selectOneListBox2" size="3">
    <option value="1">un</option>
    <option value="2">deux</option>
    <option value="3" selected="selected">trois</option>
    <option value="4">quatre</option>
    <option value="5">cinq</option>
</select></td>
<td class="col3">3</td>
</tr>
  • 6. satır: Listeden value="3" öğesinin seçilmesi, selected="selected" özniteliğinin varlığıyla ifade edilir.

Şimdi, aşağıda listeden [1] yeni bir değer seçelim ve [Valider] [2] düğmesiyle formu gönderelim. Yanıt olarak [3] sayfasını alırız:

[1] alanı için gönderilen değer şöyledir:

formulaire%3AselectOneListBox2=5

[2] tarafından formun onaylanması, [Form.java] şablonunun [1] girişi ile güncellenmesine neden oldu. HTML öğesi


    <option value="5">cinq</option>

seçildi. Tarayıcı, açılır listeyi oluşturan JSF bileşeninin değeri olarak "5" dizesini gönderdi:


            <h:selectOneListbox id="selectOneListBox2" value="#{form.selectOneListBox2}" size="3">

JSF denetleyicisi, liste şablonunu güncellemek için setSelectOneListBox2("5") yöntemini kullanacaktır. Bu güncellemeden sonra,


        private String selectOneListBox2;

alanı artık "5" değerini içerir.

[index.xhtml] sayfası işleme tamamlandıktan sonra yeniden görüntülendiğinde, bu değer yukarıdaki [3,4] görüntüsünün ortaya çıkmasına neden olur:

  • bu değer, seçilmesi gereken liste öğesini [3] olarak belirler;
  • selectOneListBox2 alanının değeri, [4] olarak görüntülenir.

2.5.15. <h:selectManyListBox> etiketi

<h:selectmanyListBox> etiketi, kullanıcının bir listeden birden fazla öğe seçmesine olanak tanıyan <select multiple="multiple">...</select> etiketini oluşturur.

Aşağıdaki kodu ele alalım:


<!-- satır 6 -->
          <h:outputText value="selectManyListBox (size=3)"  styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectManyListBoxPrompt']}"/>
            <h:selectManyListbox id="selectManyListBox" value="#{form.selectManyListBox}" size="3">
              <f:selectItem itemValue="1" itemLabel="un"/>
              <f:selectItem itemValue="2" itemLabel="deux"/>
              <f:selectItem itemValue="3" itemLabel="trois"/>
              <f:selectItem itemValue="4" itemLabel="quatre"/>
              <f:selectItem itemValue="5" itemLabel="cinq"/>
            </h:selectManyListbox>
            <p><input type="button" value="#{msg['form.buttonRazText']}" onclick="this.form['formulaire:selectManyListBox'].selectedIndex=-1;" /></p>
          </h:panelGroup>
          <h:outputText value="#{form.selectManyListBoxValue}"/>

ve [Form.java]'teki şablonu:


private String[] selectManyListBox=new String[]{"1","3"};

[index.xhtml] sayfası ilk kez istendiğinde, elde edilen sayfa şöyledir:

  • XHTML kodunun 2. satırı, [1]'i oluşturur
  • [2] metni 4. satır tarafından oluşturulur. [3] listesi, [5-11] satırları tarafından oluşturulur. size="3" özniteliği, listenin herhangi bir anda bu öğelerden üçünü göstermesini sağlar. Listeden seçilen öğeler, Java modelinin getSelectManyListBox() yönteminin çağrılmasıyla (5. satır) belirlenmiştir. Elde edilen {"1","3"} sonucu (Java kodunun 1. satırı), String türünde öğelerden oluşan bir dizidir. Bu öğelerin her biri, listedeki öğelerden birini seçmek için kullanılır. Burada, {"1","3"} dizisinde itemValue özniteliğine sahip olan 6. ve 10. satırlardaki öğeler seçilecektir. [3] bunu göstermektedir.
  • XHTML kodunun 14. satırı, [4]'i üretir. Burada, listenin Java modelindeki getSelectManyListBox yöntemine değil, aşağıdaki getSelectManyListBoxValue yöntemine başvurulur:

private String[] selectManyListBox=new String[]{"1","3"};
  ...
  // alıcı ve ayarlayıcılar
  
  public String getSelectManyListBoxValue(){
    return getValue(selectManyListBox);
  }
  
  private String getValue(String[] chaines){
    String value="[";
    for(String chaine : chaines){
      value+=" "+chaine;
    }
    return value+"]";
  }

Eğer getSelectManyListBox yöntemini çağırmış olsaydık, bir String dizisi elde ederdik. Bu öğeyi HTML akışına dahil etmek için denetleyici, toString yöntemini çağırırdı. Ancak bu yöntem, bir dizi için sadece dizinin "hashcode" değerini döndürür; bizim istediğimiz gibi elemanlarının listesini döndürmez. Bu nedenle, dizinin içeriğini temsil eden bir karakter dizisi elde etmek için yukarıdaki getSelectManyListBoxValue yöntemini kullanırız;

  • XHTML kodunun 12. satırı, [5] düğmesini oluşturur. Bu düğmeye tıklandığında, onclick özniteliğindeki JavaScript kodu çalıştırılır. Bu kod, JSF kodu tarafından oluşturulacak olan HTML sayfasına gömülecektir. Bunu anlamak için, bu sayfanın tam olarak ne olduğunu bilmemiz gerekir.

XHTML sayfası tarafından oluşturulan HTML akışı şöyledir:


<tr>
<td class="col1"><span class="info">selectManyListBox (size=3)</span></td>
<td class="col2">choix multiple : <select id="formulaire:selectManyListBox" name="formulaire:selectManyListBox" multiple="multiple" size="3">
    <option value="1" selected="selected">un</option>
    <option value="2">deux</option>
    <option value="3" selected="selected">trois</option>
    <option value="4">quatre</option>
    <option value="5">cinq</option>
</select>
            <p><input type="button" value="Raz" onclick="this.form['formulaire:selectManyListBox'].selectedIndex=-1;" /></p>
          </td>
<td class="col3">[ 1 3]</td>
</tr>
  • 3. ve 9. satırlar: JSF <h:selectManyListBox> etiketiyle oluşturulan HTML <select multiple="multiple"...>...</select> etiketi. "multiple" özniteliğinin varlığı, bunun çoklu seçim listesi olduğunu gösterir;
  • liste şablonunun {"1","3"} String dizisi olması nedeniyle, 4. satırdaki (value="1") ve 6. satırdaki (value="3") liste öğeleri selected="selected" özniteliğine sahiptir,
  • 10. satır: [Raz] düğmesine tıklandığında, onclick özniteliğindeki JavaScript kodu çalıştırılır. Sayfa, tarayıcıda genellikle DOM (Document Object Model) olarak adlandırılan bir nesne ağacıyla temsil edilir. Ağacın her nesnesine, name özniteliği aracılığıyla JavaScript kodundan erişilebilir. Yukarıdaki HTML kodunun 3. satırındaki listeye form:selectManyListBox adı verilir. Formun kendisi çeşitli şekillerde belirtilebilir. Burada, this.form notasyonu ile belirtilmiştir; burada this, [Raz] düğmesini, this.form ise bu düğmenin bulunduğu formu ifade eder. form:selectManyListBox listesi de aynı formda yer almaktadır. Dolayısıyla, this.form['formulaire:selectManyListBox'] notasyonu, listenin form bileşen ağacındaki konumunu belirtir. Bir listeyi temsil eden nesne, listeden seçilen öğenin numarasını değer olarak alan selectedIndex özniteliğine sahiptir. Bu numara, listenin ilk öğesini belirtmek üzere 0'dan başlar. -1 değeri, listeden hiçbir öğenin seçili olmadığını gösterir. selectedIndex özniteliğine -1 değerini atayan JavaScript kodu, listede herhangi bir öğe varsa bunların tümünün seçimini kaldırır.

Şimdi, aşağıda listeden [1] yeni değerler seçelim (listeden birden fazla öğe seçmek için Ctrl tuşunu basılı tutarak tıklayın) ve [Valider] [2] düğmesiyle formu gönderelim. Yanıt olarak şu sayfayı alıyoruz:

Gönderilen [1] alanının değeri şöyledir:

formulaire%3AselectManyListBox=3&formulaire%3AselectManyListBox=4&formulaire%3AselectManyListBox=5

[2] tarafından formun onaylanması, [Form.java] şablonunun [1] girişi ile güncellenmesine neden oldu. HTML öğeleri


    <option value="3">trois</option>
    <option value="4">quatre</option>
    <option value="5">cinq</option>

seçildi. Tarayıcı, açılır listeyi oluşturan JSF bileşeninin değerleri olarak "3", "4", "5" dizelerini gönderdi:


            <h:selectManyListbox id="selectManyListBox" value="#{form.selectManyListBox}" size="3">

Şablondaki setSelectManyListBox yöntemi, tarayıcı tarafından gönderilen değerlerle bu şablonu güncellemek için kullanılacak:


  private String[] selectManyListBox;
....
  public void setSelectManyListBox(String[] selectManyListBox) {
    this.selectManyListBox = selectManyListBox;
}
  1. satırda, yöntemin parametresinin bir String dizisi olduğu görülüyor. Burada bu dizi {"3","4","5"} olacaktır. Bu güncellemeden sonra,

        private String[] selectManyListBox;

artık {"3","4","5"} dizisini içerir.

[index.xhtml] sayfası işleme tamamlandıktan sonra yeniden görüntülendiğinde, bu değer yukarıdaki [3,4] görüntüsünün ortaya çıkmasına neden olur:

  • bu değer, listeden seçilmesi gereken öğeleri belirler ([3]),
  • selectManyListBox alanının değeri, [4] olarak görüntülenir.

2.5.16. <h:selectOneMenu> etiketi

<h:selectOneMenu> etiketi, <h:selectOneListBox size="1"> etiketiyle aynıdır. Örnekte, yürütülen JSF kodu şöyledir:


<!-- satır 7 -->
          <h:outputText value="selectOneMenu" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectOneMenuPrompt']}"/>
            <h:selectOneMenu id="selectOneMenu" value="#{form.selectOneMenu}">
              <f:selectItem itemValue="1" itemLabel="un"/>
              <f:selectItem itemValue="2" itemLabel="deux"/>
              <f:selectItem itemValue="3" itemLabel="trois"/>
              <f:selectItem itemValue="4" itemLabel="quatre"/>
              <f:selectItem itemValue="5" itemLabel="cinq"/>
            </h:selectOneMenu>
          </h:panelGroup>
          <h:outputText value="#{form.selectOneMenu}"/>

[Form.java] içindeki <h:selectOneMenu> etiketinin şablonu şöyledir:


  private String selectOneMenu="1";

[index.xhtml] sayfasının ilk çağrısında, yukarıdaki kod şu görünümü oluşturur:

Bir yürütme örneği şu şekilde olabilir:

[1] alanı için gönderilen değer şöyledir:

formulaire%3AselectOneMenu=4

2.5.17. <h:selectManyMenu> etiketi

<h:selectManyMenu> etiketi, <h:selectManyListBox size="1"> etiketiyle aynıdır. Örnekte yürütülen JSF kodu şöyledir:


<!-- satır 8 -->
          <h:outputText value="selectManyMenu" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectManyMenuPrompt']}" styleClass="prompt" />
            <h:selectManyMenu id="selectManyMenu" value="#{form.selectManyMenu}" >
              <f:selectItem itemValue="1" itemLabel="un"/>
              <f:selectItem itemValue="2" itemLabel="deux"/>
              <f:selectItem itemValue="3" itemLabel="trois"/>
              <f:selectItem itemValue="4" itemLabel="quatre"/>
              <f:selectItem itemValue="5" itemLabel="cinq"/>
            </h:selectManyMenu>
            <p><input type="button" value="#{msg['form.buttonRazText']}" onclick="this.form['formulaire:selectManyMenu'].selectedIndex=-1;" /></p>
          </h:panelGroup>
          <h:outputText value="#{form.selectManyMenuValue}" styleClass="prompt"/>

[Form.java] içindeki <h:selectManyMenu> etiketinin şablonu şöyledir:


    private String[] selectManyMenu=new String[]{"1","2"};

[index.xhtml] sayfasının ilk isteği üzerine, yukarıdaki kod şu sayfayı oluşturur:

[1] listesi, "bir", ..., "beş" metinlerini içerir ve "bir" ile "iki" öğeleri seçilidir. Oluşturulan HTML kodu şöyledir:


<tr>
<td class="col1"><span class="info">selectManyMenu</span></td>
<td class="col2"><span class="prompt">choix multiple : </span><select id="formulaire:selectManyMenu" name="formulaire:selectManyMenu" multiple="multiple" size="1">
    <option value="1" selected="selected">un</option>
    <option value="2" selected="selected">deux</option>
    <option value="3">trois</option>
    <option value="4">quatre</option>
    <option value="5">cinq</option>
</select>
            
            
            <p><input type="button" value="Raz" onclick="this.form['formulaire:selectManyMenu'].selectedIndex=-1;" /></p>
          </td>
<td class="col3"><span class="prompt">[ 1 2]</span></td>
</tr>

Yukarıda, 4. ve 5. satırlarda "un" ve "deux" öğelerinin seçili olduğu görülmektedir (selected özniteliğinin varlığı).

Menüde seçili öğeleri gösteremediğimiz için bir çalışma örneğinin ekran görüntüsünü sunmak zordur. Okuyucunun bu testi kendisi yapması önerilir (listeden birden fazla öğeyi seçmek için Ctrl tuşunu basılı tutarak tıklayın).

2.5.18. <h:inputHidden> etiketi

<h:inputHidden> etiketinin görsel bir gösterimi yoktur. Bu etiket, yalnızca sayfanın HTML akışına bir <input type="hidden" value="..."/> etiketi eklemek için kullanılır. <h:form> etiketinin içine yerleştirildiklerinde, bu alanların değerleri form gönderildiğinde sunucuya gönderilen değerlerin bir parçası olur. Bunlar form alanları olduğu ve kullanıcı tarafından görülmedikleri için gizli alanlar olarak adlandırılır. Bu alanların amacı, aynı istemcinin farklı istek/yanıt döngüleri arasında bellek tasarrufu sağlamaktır:

  • istemci bir F formu ister. Sunucu bunu istemciye gönderir ve gizli C alanına <h:inputHidden id="C" value="I"/> biçiminde bir I bilgisi yerleştirir,
  • istemci F formunu doldurup sunucuya gönderdiğinde, C alanındaki I değeri sunucuya geri gönderilir. Sunucu böylece sayfada depoladığı I bilgisini geri alabilir. Böylelikle iki istek/yanıt döngüsü arasında bir bellek oluşturulmuş olur,
  • JSF de bu tekniği kullanır. Form F’de depoladığı I bilgisi, formun tüm bileşenlerinin değerleridir. Bunun için şu gizli alanı kullanır:

<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="H4sIAAAAAAAAANV...8PswawAA" />

Gizli alanın adı javax.faces.ViewState'tir ve değeri, müşteriye gönderilen sayfanın tüm bileşenlerinin değerini kodlanmış biçimde temsil eden bir dizedir. Müşteri, formda girişler yaptıktan sonra sayfayı geri gönderdiğinde, javax.faces.ViewState adlı gizli alan, girilen değerlerle birlikte geri gönderilir. Bu sayede JSF denetleyicisi, sayfayı başlangıçta gönderildiği haliyle yeniden oluşturabilir. Bu mekanizma 72. sayfada açıklanmıştır.

Örnekteki JSF kodu şöyledir:


<!-- satır 9 -->
          <h:outputText value="inputHidden"  styleClass="info"/>
          <h:inputHidden id="inputHidden" value="#{form.inputHidden}"/>
          <h:outputText value="#{form.inputHidden}"/>

[Form.java] içindeki <h:inputHidden> etiketinin şablonu şöyledir:


  private String inputHidden="initial";

Bu da, [index.xhtml] sayfasının ilk çağrılması sırasında aşağıdaki görüntüyü verir:

  • 2. satır [1]'i, 4. satır ise [2]'i oluşturur. 3. satır ise herhangi bir görsel öğe oluşturmaz.

Oluşturulan HTML kodu şöyledir:


<tr>
<td class="col1"><span class="info">inputHidden</span></td>
<td class="col2"><input id="formulaire:inputHidden" type="hidden" name="formulaire:inputHidden" value="initial" /></td>
<td class="col3">initial</td>
</tr>

Formun POST aşamasında, 3. satırdaki "form:inputHidden" adlı alanın "başlangıç" değeri, formun diğer değerleriyle birlikte gönderilecektir. Şu alan


  private String inputHidden;

alanı, başlangıçta sahip olduğu bu değerle güncellenecektir. Bu değer, müşteriye gönderilen yeni sayfaya dahil edilecektir. Dolayısıyla her zaman yukarıdaki ekran görüntüsü elde edilir.

Gizli alan için gönderilen değer şöyledir:

formulaire%3AinputHidden=initial

2.5.19. <h:selectBooleanCheckBox> etiketi

<h:selectBooleanCheckBox> etiketi, <input type="checkbox" ...> biçiminde bir HTML etiketi oluşturur.

Aşağıdaki JSF kodunu ele alalım:


<!-- satır 10 -->
  <h:outputText value="selectBooleanCheckbox" styleClass="info"/>
  <h:panelGroup>
    <h:outputText value="#{msg['form.selectBooleanCheckboxPrompt']}" styleClass="prompt" />
    <h:selectBooleanCheckbox id="selectBooleanCheckbox" value="#{form.selectBooleanCheckbox}"/>
  </h:panelGroup>
  <h:outputText value="#{form.selectBooleanCheckbox}"/>

Yukarıdaki 5. satırdaki <h:selectBooleanCheckbox> etiketinin [Form.java] içindeki şablonu şöyledir:


  private boolean selectBooleanCheckbox=true;

[index.xhtml] sayfası ilk kez istendiğinde, elde edilen sayfa şöyledir:

  • XHTML kodunun 2. satırı, [1]'i oluşturur;
  • [2] metni 4. satır tarafından oluşturulur. [3] onay kutusu, [5] satırı tarafından oluşturulur. Burada, onay kutusunu işaretlemek veya işaretini kaldırmak için [Form.java]'in getSelectBooleanCheckbox yöntemi kullanılmıştır. Yöntem, boole değerini true olarak ayarladığından (bkz. Java kodu), onay kutusu işaretlenmiştir;
  • XHTML kodunun 7. satırı, [4]'i oluşturur. [Form.java] kodundaki getSelectBooleanCheckbox yöntemi, [4] metnini oluşturmak için yeniden kullanılmıştır.

Önceki JSF kodu tarafından oluşturulan HTML akışı şöyledir:


<tr>
<td class="col1"><span class="info">selectBooleanCheckbox</span></td>
<td class="col2"><span class="prompt">mari&eacute;(e) : </span>
<input id="formulaire:selectBooleanCheckbox" type="checkbox" name="formulaire:selectBooleanCheckbox" checked="checked" /></td>
<td class="col3">true</td>
</tr>

[4]'te, oluşturulan HTML <input type="checkbox"> etiketini görüyoruz. İlişkili şablondaki true değeri, etikete checked="checked" özniteliğinin eklenmesine neden olmuştur. Bu da kutucuğun işaretli olmasını sağlar.

Şimdi, aşağıda [1] onay kutusunun işaretini kaldıralım, [2] formunu gönderelim ve elde edilen sonucu [3, 4] inceleyelim:

Bu kutucuk işaretlenmediği için, [1] alanı için girilmiş bir değer bulunmamaktadır.

[2] tarafından formun doğrulanması, [1] girişi ile [Form.java] şablonunun güncellenmesine neden oldu. Bunun üzerine, [Form.java]'in selectBooleanCheckbox alanı false değerini aldı. [index.xhtml]'in yeniden görüntülenmesi, şablonun selectBooleanCheckbox alanının [3] ve [4] olarak başarıyla güncellendiğini göstermektedir. Burada dikkat çekici olan nokta, javax.faces.ViewState adlı gizli alan sayesinde JSF'in, başlangıçta işaretli olan onay kutusunun kullanıcı tarafından işaretinin kaldırıldığını tespit edebilmiş olmasıdır. Nitekim, işaretinin kaldırılmış bir onay kutusunun değeri, tarayıcı tarafından gönderilen değerler arasında yer almaz. Gizli javax.faces.ViewState alanında depolanan bileşen ağacı sayesinde, JSF, formda "selectBooleanCheckbox" adlı bir onay kutusu olduğunu ve bu kutunun değerinin istemci tarayıcısı tarafından gönderilen değerler arasında yer almadığını tespit eder. Bundan, gönderilen formda bu kutucuğun işaretli olmadığı sonucuna varabilir; bu da ona, ilgili Java modeline false boole değerini atamasını sağlar:


  private boolean selectBooleanCheckbox;

2.5.20. <h:selectManyCheckBox> etiketi

<h:selectManyCheckBox> etiketi, bir grup onay kutusu oluşturur ve dolayısıyla birden fazla <input type="checkbox" ...> HTML etiketi oluşturur> etiketini oluşturur. Bu etiket, <h:selectManyListBox> etiketinin karşılığıdır; tek fark, seçilecek öğelerin liste şeklinde değil, birbirine bitişik onay kutuları şeklinde sunulmasıdır. <h:selectManyListBox> etiketi için söylenenler burada da geçerlidir.

Aşağıdaki JSF kodunu ele alalım:


          <!-- satır 11 -->
          <h:outputText value="selectManyCheckbox" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectManyCheckboxPrompt']}" styleClass="prompt" />
            <h:selectManyCheckbox id="selectManyCheckbox" value="#{form.selectManyCheckbox}">
              <f:selectItem itemValue="1" itemLabel="rouge"/>
              <f:selectItem itemValue="2" itemLabel="bleu"/>
              <f:selectItem itemValue="3" itemLabel="blanc"/>
              <f:selectItem itemValue="4" itemLabel="noir"/>
            </h:selectManyCheckbox>
          </h:panelGroup>
<h:outputText value="#{form.selectManyCheckboxValue}"/>

Yukarıdaki 5. satırdaki <h:selectManyCheckbox> etiketinin şablonu [Form.java]'te şu şekildedir:


private String[] selectManyCheckbox=new String[]{"1","3"};

[index.xhtml] sayfası ilk kez istendiğinde, elde edilen sayfa şöyledir:

  • XHTML kodunun 2. satırı, [1]'i oluşturur;
  • [2] metni 4. satır tarafından oluşturulur. [3] onay kutuları 5-10. satırlar tarafından oluşturulur. Her biri için:
  • itemLabel özniteliği, onay kutusunun yanında görüntülenen metni tanımlar;
  • itemvalue özniteliği, kutucuk işaretlendiğinde sunucuya gönderilecek değeri tanımlar;

Dört onay kutusunun şablonu aşağıdaki Java alanıdır:


private String[] selectManyCheckbox=new String[]{"1","3"};

Bu tablo şunları tanımlar:

  • sayfa görüntülendiğinde hangi kutucukların işaretli olması gerektiğini. Bu, değerleri olan c.a.d aracılığıyla yapılır. itemValue alanları. Yukarıda, değerleri {"1","3"} tablosunda yer alan kutular işaretlenecektir. Yukarıdaki ekran görüntüsünde görülen budur;
  • sayfa gönderildiğinde, selectManyCheckbox şablonu, kullanıcının işaretlediği kutuların değerlerini içeren tabloyu alır. Bunu birazdan göreceğiz;
  • XHTML kodunun 12. satırı, [4]'i oluşturur. [4]'i oluşturan ise bir sonraki getSelectManyCheckboxValue yöntemidir:

  public String getSelectManyCheckboxValue(){
    return getValue(getSelectManyCheckbox());
  }
  
  private String getValue(String[] chaines){
    String value="[";
    for(String chaine : chaines){
      value+=" "+chaine;
    }
    return value+"]";
}

Önceki JSF kodu tarafından oluşturulan HTML akışı şöyledir:


    <tr>
<td>
<input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:0" value="1" type="checkbox" checked="checked" /><label for="formulaire:selectManyCheckbox:0"> rouge</label></td>
<td>
<input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:1" value="2" type="checkbox" /><label for="formulaire:selectManyCheckbox:1"> bleu</label></td>
<td>
<input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:2" value="3" type="checkbox" checked="checked" /><label for="formulaire:selectManyCheckbox:2"> blanc</label></td>
<td>
<input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:3" value="4" type="checkbox" /><label for="formulaire:selectManyCheckbox:3"> noir</label></td>
    </tr>
</table></td>
<td class="col3">[ 1 3]</td>
</tr>

Dört adet HTML <input type="checkbox" ...> etiketi oluşturulmuştur. 3. ve 7. satırlardaki etiketler, checked="checked" özniteliğine sahiptir; bu da bunların işaretli olarak görünmesini sağlar. Hepsinin aynı name="form:selectManyCheckbox" özniteliğine sahip olduğu dikkat çekmektedir; başka bir deyişle, dört HTML alanı aynı ada sahiptir. Kullanıcı 5. ve 9. satırlardaki kutuları işaretlerse, tarayıcı dört onay kutusunun değerlerini şu biçimde gönderecektir:

formulaire:selectManyCheckbox=2&formulaire:selectManyCheckbox=4

şeklinde gönderir ve dört kutucuğun şablonu


private String[] selectManyCheckbox=new String[]{"1","3"};

şablonu {"2","4"} dizisini alacaktır.

Bunu aşağıda kontrol edelim. [1]'te değişikliği yapıyoruz, [2]'te formu gönderiyoruz. [3]'te elde edilen sonuç:

[1] alanları için gönderilen değerler şunlardır:

formulaire%3AselectManyCheckbox=2&formulaire%3AselectManyCheckbox=4

2.5.21. <h:selectOneRadio> etiketi

<h:selectOneRadio> etiketi, birbirini dışlayan bir grup radyo düğmesi oluşturur.

Aşağıdaki JSF kodunu ele alalım:


<!-- satır 12 -->
          <h:outputText value="selectOneRadio" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectOneRadioPrompt']}" />
            <h:selectOneRadio id="selectOneRadio" value="#{form.selectOneRadio}">
              <f:selectItem itemValue="1" itemLabel="voiture"/>
              <f:selectItem itemValue="2" itemLabel="vélo"/>
              <f:selectItem itemValue="3" itemLabel="scooter"/>
              <f:selectItem itemValue="4" itemLabel="marche"/>
            </h:selectOneRadio>
          </h:panelGroup>
          <h:outputText value="#{form.selectOneRadio}"/>

Yukarıdaki 5. satırdaki <h:selectOneRadio> etiketinin şablonu, [Form.java] içinde şu şekildedir:


  private String selectOneRadio="2";

[index.xhtml] sayfası ilk kez istendiğinde, elde edilen görünüm şöyledir:

  • XHTML kodunun 2. satırı, [1]'i oluşturur,
  • [2] metni 4. satır tarafından oluşturulur. [3] radyo düğmeleri ise 5-10. satırlar tarafından oluşturulur. Her biri için:
  • itemLabel özniteliği, radyo düğmesinin yanında görüntülenen metni tanımlar;
  • itemvalue özniteliği, düğme işaretlendiğinde sunucuya gönderilecek değeri belirler;

Dört radyo düğmesinin şablonu aşağıdaki Java alanıdır:


  private String selectOneRadio="2";

Bu şablon şunları tanımlar:

  • sayfa görüntülendiğinde, işaretlenmesi gereken tek radyo düğmesi. Bu, c.a.d değeri aracılığıyla yapılır. itemValue alanı. Yukarıda, değeri "2" olan radyo düğmesi işaretlenecektir. Bu, yukarıdaki ekran görüntüsünde görüldüğü gibidir;
  • sayfa gönderildiğinde, selectOneRadio şablonu, işaretlenen radyo düğmesinin değerini alır. Bunu birazdan göreceğiz;
  • XHTML kodunun 12. satırı, [4]'i oluşturur.

Önceki JSF kodu tarafından oluşturulan HTML akışı şöyledir:


<tr>
<td class="col1"><span class="info">selectOneRadio</span></td>
<td class="col2">moyen de transport pr&eacute;f&eacute;r&eacute; : <table id="formulaire:selectOneRadio">
    <tr>
<td>
<input type="radio" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:0" value="1" /><label for="formulaire:selectOneRadio:0"> voiture</label></td>
<td>
<input type="radio" checked="checked" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:1" value="2" /><label for="formulaire:selectOneRadio:1"> v&eacute;lo</label></td>
<td>
<input type="radio" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:2" value="3" /><label for="formulaire:selectOneRadio:2"> scooter</label></td>
<td>
<input type="radio" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:3" value="4" /><label for="formulaire:selectOneRadio:3"> marche</label></td>
</tr>

Dört adet HTML <input type="radio" ...> etiketi oluşturulmuştur. 8. satırdaki etiketin checked="checked" özniteliği, ilgili radyo düğmesinin işaretli görünmesini sağlar. Etiketlerin hepsinin aynı name="form:selectOneRadio" özniteliğine sahip olduğu dikkat çekmektedir; başka bir deyişle, dört HTML alanı aynı ada sahiptir. Bu, birbirini dışlayan bir radyo düğmesi grubuna sahip olmanın şartıdır: biri işaretlendiğinde, diğerleri işaretlenmez.

Aşağıda, [1]'te radyo düğmelerinden birini işaretliyoruz, [2]'te formu gönderiyoruz, [3]'te ise elde edilen sonuç:

[1] alanı için gönderilen değer şöyledir:

formulaire%3AselectOneRadio=4

2.6. mv-jsf2-04 örneği: dinamik listeler

2.6.1. Uygulama

Uygulama, öncekiyle aynıdır:

Tek değişiklik, [1] ve [2] alanlarındaki liste öğelerinin oluşturulma biçimindedir. Bu liste öğeleri burada Java kodu ile dinamik olarak oluşturulurken, önceki sürümde JSF sayfasının koduna "sabit" olarak yazılmıştı.

2.6.2. NetBeans projesi

Uygulamanın NetBeans projesi şöyledir:

[mv-jsf2-04] projesi, aşağıdaki farklılıklar dışında [mv-jsf2-03] projesiyle aynıdır:

  • [1]'te, JSF sayfasında, liste öğeleri artık kodda "sabit" olarak yazılmayacak,
  • [2]'te, JSF ve [1] sayfalarının şablonu değiştirilecektir,
  • [3]'te mesajlardan biri değiştirilecektir.

2.6.3. [index.xhtml] sayfası ve şablonu [Form.java]

JSF [index.xhtml] sayfası şu şekilde olacak:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">

  <f:view locale="#{changeLocale.locale}">
    <h:head>
      <title>JSF</title>
      <h:outputStylesheet library="css" name="styles.css"/>
    </h:head>
    <h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
      <h:form id="formulaire">
        <!-- diller -->
        <h:panelGrid columns="2">
          <h:commandLink value="#{msg['form.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
          <h:commandLink value="#{msg['form.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
        </h:panelGrid>
        <h1><h:outputText value="#{msg['form.titre']}"/></h1>
        <h:panelGrid columnClasses="col1,col2,col3" columns="3" border="1">
...
          <!-- satır 4 -->
          <h:outputText value="selectOneListBox (size=1)" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectOneListBox1Prompt']}"/>
            <h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
              <f:selectItems value="#{form.selectOneListbox1Items}"/>
            </h:selectOneListbox>
          </h:panelGroup>
          <h:outputText value="#{form.selectOneListBox1}"/>
          <!-- satır 5 -->
          <h:outputText value="selectOneListBox (size=3)" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectOneListBox2Prompt']}"/>
            <h:selectOneListbox id="selectOneListBox2" value="#{form.selectOneListBox2}" size="3">
              <f:selectItems value="#{form.selectOneListbox2Items}"/>
            </h:selectOneListbox>
          </h:panelGroup>
          <h:outputText value="#{form.selectOneListBox2}"/>
          <!-- satır 6 -->
          <h:outputText value="selectManyListBox (size=3)"  styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectManyListBoxPrompt']}"/>
            <h:selectManyListbox id="selectManyListBox" value="#{form.selectManyListBox}" size="3">
              <f:selectItems value="#{form.selectManyListBoxItems}"/>
            </h:selectManyListbox>
            <p><input type="button" value="#{msg['form.buttonRazText']}" onclick="this.form['formulaire:selectManyListBox'].selectedIndex=-1;" /></p>
          </h:panelGroup>
          <h:outputText value="#{form.selectManyListBoxValue}"/>
          <!-- satır 7 -->
          <h:outputText value="selectOneMenu" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectOneMenuPrompt']}"/>
            <h:selectOneMenu id="selectOneMenu" value="#{form.selectOneMenu}">
              <f:selectItems value="#{form.selectOneMenuItems}"/>
            </h:selectOneMenu>
          </h:panelGroup>
          <h:outputText value="#{form.selectOneMenu}"/>
          <!-- satır 8 -->
          <h:outputText value="selectManyMenu" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectManyMenuPrompt']}" styleClass="prompt" />
            <h:selectManyMenu id="selectManyMenu" value="#{form.selectManyMenu}" >
              <f:selectItems value="#{form.selectManyMenuItems}"/>
            </h:selectManyMenu>
            <p><input type="button" value="#{msg['form.buttonRazText']}" onclick="this.form['formulaire:selectManyMenu'].selectedIndex=-1;" /></p>
          </h:panelGroup>
          <h:outputText value="#{form.selectManyMenuValue}" styleClass="prompt"/>
...
          <!-- satır 11 -->
          <h:outputText value="selectManyCheckbox" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectManyCheckboxPrompt']}" styleClass="prompt" />
            <h:selectManyCheckbox id="selectManyCheckbox" value="#{form.selectManyCheckbox}">
              <f:selectItems value="#{form.selectManyCheckboxItems}"/>
            </h:selectManyCheckbox>
          </h:panelGroup>
          <h:outputText value="#{form.selectManyCheckboxValue}"/>
          <!-- satır 12 -->
          <h:outputText value="selectOneRadio" styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.selectOneRadioPrompt']}" />
            <h:selectOneRadio id="selectOneRadio" value="#{form.selectOneRadio}">
              <f:selectItems value="#{form.selectOneRadioItems}"/>
            </h:selectOneRadio>
          </h:panelGroup>
          <h:outputText value="#{form.selectOneRadio}"/>
        </h:panelGrid>
        <p>
          <h:commandButton type="submit" id="submit" value="#{msg['form.submitText']}"/>
        </p>
      </h:form>
    </h:body>
  </f:view>
</html>

Yapılan değişiklikler 26-28. satırlarda gösterilmiştir. Daha önce şu kod vardı:


<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
              <f:selectItem itemValue="1" itemLabel="un"/>
              <f:selectItem itemValue="2" itemLabel="deux"/>
              <f:selectItem itemValue="3" itemLabel="trois"/>
</h:selectOneListbox>

şimdi şu kod yer almaktadır:


<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
              <f:selectItems value="#{form.selectOneListbox1Items}"/>
</h:selectOneListbox>

2-4. satırlardaki üç <f:selectItem> etiketi, b satırındaki tek bir <f:selectItems> etiketiyle değiştirilmiştir. Bu etiketin value özniteliğinin değeri, javax.faces.model.SelectItem türünde öğelerden oluşan bir koleksiyondur. Yukarıda, value özniteliğinin değeri aşağıdaki [form].getSelectOneListbox1Items yönteminin çağrılmasıyla elde edilecektir:


  public SelectItem[] getSelectOneListbox1Items() {
    return getItems("A",3);
  }

  private SelectItem[] getItems(String label, int qte) {
    SelectItem[] items=new SelectItem[qte];
    for(int i=0;i<qte;i++){
      items[i]=new SelectItem(i,label+i);
    }
    return items;
}
  • 1. satırda, getSelectOneListbox1Items yöntemi, 5. satırdaki özel getItems yöntemi tarafından oluşturulan javax.faces.model.SelectItem türündeki öğelerden oluşan bir dizi döndürür. getSelectOneListbox1Items yönteminin, özel bir selectOneListBox1Items alanının getter yöntemi olmadığına dikkat edilmelidir;
  • javax.faces.model.SelectItem sınıfının çeşitli oluşturucuları vardır.

Image

getItems yönteminin 8. satırını kullanıyoruz; bu, JSF etiketine karşılık gelen SelectItem(Object value, String label) oluşturucusudur


    <f:selectItem itemValue="value" labelValue="label"/>
  • 5-10. satırlar: getItems(String label, int qte) yöntemi, SelectItem türünde qte adet eleman içeren bir dizi oluşturur; burada i'inci eleman, SelectItem(i, label+i) oluşturucu ile elde edilir.

JSF kodu


<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
              <f:selectItems value="#{form.selectOneListbox1Items}"/>
</h:selectOneListbox>

işlevsel olarak aşağıdaki JSF koduyla eşdeğer hale gelir:


<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
              <f:selectItem itemValue="0" itemLabel="A0"/>
              <f:selectItem itemValue="1" itemLabel="A1"/>
              <f:selectItem itemValue="2" itemLabel="A2"/>
</h:selectOneListbox>

Aynı işlem, JSF sayfasındaki diğer tüm listeler için de yapılır. Böylece, [Form.java] şablonunda aşağıdaki yeni yöntemler bulunur:


  public SelectItem[] getSelectOneListbox1Items() {
    return getItems("A",3);
  }
  
  public SelectItem[] getSelectOneListbox2Items() {
    return getItems("B",4);
  }
  
  public SelectItem[] getSelectManyListBoxItems() {
    return getItems("C",5);
  }
  
  public SelectItem[] getSelectOneMenuItems() {
    return getItems("D",3);
  }
  
  public SelectItem[] getSelectManyMenuItems() {
   return getItems("E",4);
   }
  
  public SelectItem[] getSelectManyCheckboxItems() {
   return getItems("F",3);
   }
  
  public SelectItem[] getSelectOneRadioItems() {
   return getItems("G",4);
   }
  
  private SelectItem[] getItems(String label, int qte) {
    SelectItem[] items=new SelectItem[qte];
    for(int i=0;i<qte;i++){
      items[i]=new SelectItem(i,label+i);
    }
    return items;
}

2.6.4. Mesaj dosyası

Yalnızca bir mesaj değiştirilmiştir:

[messages_fr.properties]


form.titre=Java Server Faces - remplissage dynamique des listes

[messages_en.properties]


form.titre=Java Server Faces - dynamic filling of lists of elements

2.6.5. Testler

Kullanıcıların bu yeni sürümü test etmeleri önerilir.

Çoğu zaman, bir formdaki dinamik öğeler iş süreci sonucunda ortaya çıkar veya bir veritabanından alınır:

Tarayıcıdan gelen GET isteği ile JSF [index.xhtml] sayfasının ilk isteğini inceleyelim:

  • JSF sayfası, [1] tarafından talep edilir,
  • [Faces Servlet] denetleyicisi, sayfanın [3] adresinde görüntülenmesini talep eder. Sayfayı işleyen JSF motoru, sayfanın [Form.java] modelini, örneğin getSelectOneListBox1Items yöntemini çağırır. Bu yöntem, bir veritabanında kayıtlı bilgilerden yola çıkarak SelectItem türünde bir dizi döndürebilir. Bunun için [métier] ve [2b] katmanlarını kullanır.

2.7. mv-jsf2-05 örneği: gezinme – oturum – istisna yönetimi

2.7.1. Uygulama

Uygulama, formun artık çok sayfalı bir sihirbaz şeklinde sunulması dışında öncekiyle aynıdır:

  • [1]'te, formun 1. sayfası – [2]'teki 1 numaralı bağlantıdan da erişilebilir
  • [2]'te, 5 bağlantıdan oluşan bir grup.
  • [3]'e, [2]'teki 2 numaralı bağlantıdan ulaşılan formun 2. sayfası
  • [4]'te, [2]'in 3. bağlantısı ile ulaşılan formun 3. sayfası
  • [5]'te, [2]'teki "Bir istisna oluştur" bağlantısıyla ulaşılan sayfa
  • [6]'te, [2]'teki 4 numaralı bağlantıdan ulaşılan sayfa. Bu sayfa, 1 ila 3. sayfalarda yapılan girişleri özetler.

2.7.2. NetBeans projesi

Uygulamanın NetBeans projesi şöyledir:

[mv-jsf2-05] projesi iki yenilik getirir:

  1. [1]'te, JSF ve [index.xhtml] sayfaları, girişlerin dağıtıldığı üç [form1.xhtml, form2.xhtml, form3.xhtml] sayfasına bölünmüştür. [form4.xhtml] sayfası, önceki projenin [index.xhtml] sayfasının bir kopyasıdır. [2] sayfasında, [Form.java] sınıfı değişmeden kalır. Bu sınıf, önceki dört JSF sayfası için şablon görevi görecek,
  2. [3] sayfasında ise [exception.xhtml] sayfası eklenmiştir: bu sayfa, uygulamada bir istisna oluştuğunda kullanılacaktır.

2.7.3. [form.xhtml] sayfaları ve bunların şablonu [Form.java]

2.7.3.1. XHTML sayfalarının kodu

JSF [form1.xhtml] sayfası şöyledir:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
  <f:view locale="#{changeLocale.locale}">
    <h:head>
      <title>JSF</title>
      <h:outputStylesheet library="css" name="styles.css"/>
    </h:head>
    <h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
    <h:form id="formulaire">
        <!-- bağlantılar -->
        <h:panelGrid columns="2">
          <h:commandLink value="#{msg['form.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
          <h:commandLink value="#{msg['form.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
        </h:panelGrid>
        <h1><h:outputText value="#{msg['form1.titre']}"/></h1>
        <h:panelGrid columnClasses="col1,col2" columns="2" border="1">
          <h:outputText value="#{msg['form.headerCol1']}" styleClass="entete"/>
          <h:outputText value="#{msg['form.headerCol2']}" styleClass="entete"/>
          <!-- satır 1 -->
          <h:outputText value="inputText"  styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.loginPrompt']}"/>
            <h:inputText id="inputText" value="#{form.inputText}"/>
          </h:panelGroup>
          <!-- satır 2 -->
          <h:outputText value="inputSecret"  styleClass="info"/>
          <h:panelGroup>
            <h:outputText value="#{msg['form.passwdPrompt']}"/>
            <h:inputSecret id="inputSecret" value="#{form.inputSecret}"/>
          </h:panelGroup>
          <!-- satır 3 -->
          <h:outputText value="inputTextArea" styleClass="info"/>          
          <h:panelGroup>
            <h:outputText value="#{msg['form.descPrompt']}"/>
            <h:inputTextarea id="inputTextArea" value="#{form.inputTextArea}" rows="4"/>
          </h:panelGroup>         
        </h:panelGrid>
        <!-- bağlantılar -->
        <h:panelGrid columns="6">
          <h:commandLink value="1" action="form1"/>
          <h:commandLink value="2" action="#{form.doAction2}"/>
          <h:commandLink value="3" action="form3"/>
          <h:commandLink value="4" action="#{form.doAction4}"/>
          <h:commandLink value="#{msg['form.pagealeatoireLink']}" action="#{form.doAlea}"/>
          <h:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}"/>
        </h:panelGrid>
      </h:form>
      </h:body>
  </f:view>
</html>

ve aşağıdaki görüntüye karşılık gelir:

Aşağıdaki noktalara dikkat edilmelidir:

  • 16. satırda, daha önce üç sütunlu olan tablo artık sadece iki sütunlu hale gelmiştir. Model değerlerini gösteren 3. sütun kaldırılmıştır. Bu değerleri [form4.xhtml] gösterecektir,
  • 40-46. satırlar: Altı bağlantıdan oluşan bir tablo. 44. ve 46. satırlardaki bağlantılar statik gezinme özelliğine sahiptir: action özniteliği sabit olarak kodlanmıştır. Diğer bağlantılar ise dinamik gezinme özelliğine sahiptir: action özniteliği, gezinme anahtarını döndürmekle görevli form bean’indeki bir yönteme işaret eder. [Form.java] dosyasında referans verilen yöntemler şunlardır:

// olaylar
  public String doAction2(){
    return "form2";
  }
  
  public String doAction4(){
    return "form4";
  }
  
  public String doAlea(){
    // 1 ile 3 arasında rastgele bir sayı
    int i=1+(int)(3*Math.random());
    // navigasyon anahtarını oluşturur
    return "form"+i;
  }
  
  public String throwException() throws java.lang.Exception{
    throw new Exception("Exception test");
}

Şimdilik 17. satırdaki throwException yöntemini göz ardı edeceğiz. Buna daha sonra geri döneceğiz. doAction2 ve doAction4 yöntemleri, herhangi bir işlem yapmadan sadece gezinme anahtarını döndürür. Dolayısıyla şu şekilde de yazabilirdik:


<h:commandLink value="1" action="form1"/>
          <h:commandLink value="2" action="form2"/>
          <h:commandLink value="3" action="form3"/>
          <h:commandLink value="4" action="form4"/>
          <h:commandLink value="#{msg['form.pagealeatoireLink']}" action="#{form.doAlea}"/>
          <h:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}"/>

doAlea yöntemi ise, değerini {"form1", "form2", "form3"} kümesinden alan rastgele bir gezinme anahtarı üretir.

[form2.xhtml, form3.xhtml, form3.xhtml] sayfalarının kodu, [form1.xhtml] sayfasının koduna benzerdir.

2.7.3.2. [form*.xhtml] sayfalarının [Form.java] şablonunun ömrü

Aşağıdaki eylem dizisini ele alalım:

  • [1] sayfasında, 1. sayfa doldurulur ve 3. sayfaya geçilir,
  • [2]'te, sayfa 3 doldurulur ve sayfa 1'e geri dönülür,
  • [3]'te, 1. sayfa girildiği haliyle karşımıza çıkar. Ardından 3. sayfaya geri dönülür,
  • [4]'te, sayfa 3 girildiği haliyle karşımıza çıkar.

Gizli alan [javax.faces.ViewState]'in işleyişi bu olguyu açıklamaya yetmez.

[1]'ten [2]'e geçiş sırasında birkaç adım gerçekleşir:

  • [Form.java] şablonu, [form1.jsp]'ten alınan POST ile güncellenir. Özellikle, inputText alanı "başka bir metin" değerini alır,
  • "form3" gezinme anahtarı, [form3.xhtml]'in görüntülenmesini sağlar. [form3.xhtml] içinde yer alan ViewState, yalnızca [form3.xhtml] bileşenlerinin durumunu gösterir; [form1.xhtml] bileşenlerinin durumunu göstermez.

[2]'ten [3]'e geçiş sırasında:

  • [Form.java] modeli, [form3.xhtml]'teki POST ile güncellenir. [Form.java] şablonunun ömrü sona erdiğinde, yepyeni bir [Form.java] nesnesi oluşturulur ve ardından [form3.xhtml]'ten gelen POST ile güncellenir. Bu durumda, şablonun inputText alanı varsayılan değerine geri döner:

  private String inputText="texte";

ve bu değeri korur: zira [form3.xhtml]'ten POST'e yapılan güncellemede, [form1.xhtml] şablonunun bir parçası olan ve [form3.xhtml] şablonuna ait olmayan inputText alanını güncelleyecek hiçbir şey yoktur;

  • "form1" gezinme anahtarı, [form1.xhtml]'in görüntülenmesine neden olur. Sayfa, kendi şablonunu görüntüler. Bizim örneğimizde, inputText şablonuna bağlı login giriş alanı, [1]'te girilen "başka bir metin" değerini değil, texte değerini gösterecektir. inputText alanının [1]'te girilen değeri korumasını sağlamak için, [Form.java] şablonunun ömrü "request" değil, "session" olmalıdır. Bu durumda,
    • [form1.xhtml]'in POST işlemi tamamlandığında, şablon müşterinin oturumuna yerleştirilecektir. inputText alanı "başka bir metin" değerini alacaktır,
    • [form3.xhtml]'in POST'i çalıştırıldığında, şablon bu oturumda aranacak ve [form3.xhtml]'in POST'i tarafından güncellenecektir. inputText alanı bu POST tarafından güncellenmeyecek, ancak POST'in [form1.xhtml]'i tamamlamasının ardından değerini koruyacaktır.

Dolayısıyla, [Form.java] bean'inin bildirimi şu şekildedir:


package forms;

import javax.enterprise.context.SessionScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.model.SelectItem;

@ManagedBean
@SessionScoped
public class Form {
  1. satır, bean'e oturum kapsamı atar.

2.7.4. İstisna Yönetimi

JSF uygulamasının genel mimarisine geri dönelim:

Bir olay işleyicisi veya bir model, iş katmanından gelen bir istisnayı (örneğin, veritabanıyla beklenmedik bir bağlantı kesintisi) yakaladığında ne olur?

  • [2a] olay işleyicileri, [métier] katmanından gelen tüm istisnaları yakalayabilir ve [Faces Servlet] denetleyicisine, istisnaya özgü bir hata sayfasına yönlendiren bir gezinme anahtarı döndürebilir,
  • Modeller için bu çözüm kullanılamaz; çünkü [3,4] çağrıldığında, o anda belirli bir sayfanın (XHTML) görüntüleme aşamasındayız ve artık o sayfanın seçilme aşamasında değiliz. Bir sayfanın görüntüleme aşamasındayken nasıl sayfa değiştirebiliriz? Basit ancak her zaman uygun olmayan bir çözüm, istisnayı yönetmemektir; bu durumda istisna, uygulamayı çalıştıran servlet konteynerine kadar yükselir. Servlet konteyneri, bir istisna servlet konteynerine ulaştığında belirli bir sayfayı görüntüleyecek şekilde yapılandırılabilir. Bu çözüm her zaman kullanılabilir ve şimdi bunu inceleyeceğiz.

2.7.4.1. İstisna yönetimi için web uygulamasının yapılandırılması

Bir web uygulamasının istisna yönetimi için yapılandırılması, [web.xml] dosyasında yapılır:


<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  <context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>client</param-value>
  </context-param>  
  <context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
  </context-param>
  <context-param>
    <param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
    <param-value>true</param-value>
  </context-param> 
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/faces/*</url-pattern>
  </servlet-mapping>
  <session-config>
    <session-timeout>
      30
    </session-timeout>
  </session-config>
  <welcome-file-list>
    <welcome-file>faces/form1.xhtml</welcome-file>
  </welcome-file-list>
  <error-page>
    <error-code>500</error-code>
    <location>/faces/exception.xhtml</location>
  </error-page>
  <error-page>
    <exception-type>java.lang.Exception</exception-type>
    <location>/faces/exception.xhtml</location>
  </error-page>
</web-app>

32-39. satırlarda iki hata sayfasının tanımı bulunur. Gerektiği kadar <error-page> etiketi kullanılabilir. <location> etiketi, hata durumunda görüntülenecek sayfayı belirtir. Sayfayla ilişkili hata türü iki şekilde tanımlanabilir:

  • <exception-type> etiketi ile, bu etiket yönetilen istisnanın Java türünü tanımlar. Böylece, 36-39. satırlardaki <error-page> etiketi, uygulama yürütülürken servlet konteynerinin [java.lang.Exception] türünde veya bunun türevlerinden bir istisna yakaladığı takdirde (37. satır), [/faces/exception.xhtml] sayfasını görüntülemesi gerektiğini belirtir sayfasını görüntülemesi gerektiğini belirtir (satır 38). Burada en genel istisna türü olan [java.lang.Exception]'i kullanarak,
  • <error-code> etiketiyle (satır 33) bir HTTP hata kodu tanımlayarak tüm istisnaları yönetiriz. Örneğin, bir tarayıcı URL [http://machine:port/contexte/P]'i talep ederse ve P sayfası uygulama bağlamında mevcut değilse, uygulama yanıtta müdahale etmez. Bu yanıtı, varsayılan bir hata sayfası göndererek servlet konteyneri oluşturur. Yanıt akışının ilk satırı, istenen P sayfasının mevcut olmadığını belirten 404 hata kodunu içerir. Örneğin, uygulamanın grafik tasarım kurallarına uyan veya sorunu çözmek için bağlantılar sunan bir yanıt oluşturmak istenebilir. Bu durumda, <error-code>404</error-code> etiketiyle birlikte bir <error-page> etiketi kullanılır.

Yukarıda, 500 numaralı hata kodu HTTP, uygulamanın "çökmesi" durumunda geri gönderilen koddur. Bu, bir istisna servlet konteynerine kadar ulaştığında geri gönderilecek olan koddur. Dolayısıyla, 28-35. satırlardaki iki <error-page> etiketi muhtemelen gereksizdir. Bunlar, bir hatayı yönetmenin iki farklı yolunu göstermek amacıyla her ikisi de eklenmiştir.

2.7.4.2. İstisnanın simülasyonu

[Lancer une exception] bağlantısı aracılığıyla yapay olarak bir istisna oluşturulur:

[Lancer une exception] [1] bağlantısına tıklandığında, [2] sayfası görüntülenir.

[formx.xhtml] sayfalarının kodunda, [Lancer une exception] bağlantısı şu şekilde oluşturulur:


<!-- bağlantılar -->
        <h:panelGrid columns="6">
          <h:commandLink value="1" action="form1"/>
...
          <h:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}"/>
        </h:panelGrid>
  1. satırda, bağlantıya tıklandığında [form].throwException yönteminin çalıştırılacağı görülmektedir. Bu yöntem şöyledir:

  public String throwException() throws java.lang.Exception{
    throw new Exception("Exception test");
}

Burada [java.lang.Exception] türünde bir istisna tetiklenir. Bu istisna, servlet konteynerine kadar yükselir ve servlet konteyneri [/faces/exception.xhtml] sayfasını görüntüler.

2.7.4.3. İstisna ile ilgili bilgiler

Bir istisna servlet konteynerine ulaştığında, konteyner istisna hakkındaki bilgileri ilgili hata sayfasına ileterek bu sayfanın görüntülenmesini sağlar. Bu bilgiler, işlenmekte olan isteğin yeni öznitelikleri olarak eklenir. Tarayıcının isteği ve alacağı yanıt, [HttpServletRequest request] ve [HttpServletResponse response] türündeki Java nesneleri içinde kapsüllenir. Bu nesneler, tarayıcı isteğinin işlenmesinin tüm aşamalarında kullanılabilir.

Tarayıcıdan gelen HTTP isteği alındığında, servlet konteyneri bu isteği [HttpServletRequest request] adlı Java nesnesine kapsüller ve yanıtın oluşturulmasını sağlayacak [HttpServletResponse response] nesnesini oluşturur. Bu nesnede, özellikle yanıtın HTTP akışı için kullanılacak TCP-IP kanalı bulunur. request nesnesinin işlenmesinde rol alacak t1, t2, ..., tn katmanlarının tümü bu iki nesneye erişebilir. Her bir katman, ilk istek olan request'in öğelerine erişebilir ve response nesnesini zenginleştirerek yanıtı hazırlayabilir. Örneğin, bir localisation katmanı, response.setLocale(Locale l) yöntemi aracılığıyla yanıtın localisation nesnesini belirleyebilir.

Farklı ti katmanları, request nesnesi aracılığıyla birbirlerine bilgi aktarabilir. Bu nesne, oluşturulduğunda boş olan bir öznitelik sözlüğüne sahiptir ve bu sözlük, ardışık işleme katmanları tarafından zenginleştirilebilir. Bu katmanlar, bir sonraki işleme katmanı için gerekli bilgileri request nesnesinin özniteliklerine ekleyebilir. request nesnesinin özniteliklerini yönetmek için iki yöntem mevcuttur:

  • s dizesi ile tanımlanan o nesnesini özniteliklere eklemeye olanak tanıyan void setAttribute(String s, Object o) yöntemi,
  • Object getAttribute(String s) yöntemi, s dizesiyle tanımlanan o özniteliğini alır.

Bir istisna servlet kapsayıcısına ulaştığında, kapsayıcı işlenmekte olan isteğe aşağıdaki öznitelikleri ekler:

anahtar
değer
javax.servlet.error.status_code
Müşteriye gönderilecek olan HTTP hata kodu
javax.servlet.error.exception
hata mesajıyla birlikte istisnanın Java türü.
javax.servlet.error.request_uri
istisna oluştuğunda istenen URL
javax.servlet.error.servlet_name
istisna oluştuğunda isteği işleyen servlet

Bu istek özniteliklerini, [exception.xhtml] sayfasında görüntülemek için kullanacağız.

2.7.4.4. [exception.xhtml] hata sayfası

İçeriği şöyledir:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
  <f:view locale="#{changeLocale.locale}">
    <h:head>
      <title>JSF</title>
      <h:outputStylesheet library="css" name="styles.css"/>
    </h:head>
    <h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
      <h:form id="formulaire">
        <h3><h:outputText value="#{msg['exception.header']}"/></h3>
        <h:panelGrid columnClasses="col1,col2" columns="2" border="1">
          <h:outputText value="#{msg['exception.httpCode']}"/>
          <h:outputText value="#{requestScope['javax.servlet.error.status_code']}"/>
          <h:outputText value="#{msg['exception.message']}"/>
          <h:outputText value="#{requestScope['javax.servlet.error.exception']}"/>
          <h:outputText value="#{msg['exception.requestUri']}"/>
          <h:outputText value="#{requestScope['javax.servlet.error.request_uri']}"/>
          <h:outputText value="#{msg['exception.servletName']}"/>
          <h:outputText value="#{requestScope['javax.servlet.error.servlet_name']}"/>
        </h:panelGrid>
        <!-- bağlantılar -->
        <h:panelGrid columns="6">
          <h:commandLink value="1" action="form1"/>
          <h:commandLink value="2" action="#{form.doAction2}"/>
          <h:commandLink value="3" action="form3"/>
          <h:commandLink value="4" action="#{form.doAction4}"/>
          <h:commandLink value="#{msg['form.pagealeatoireLink']}" action="#{form.doAlea}"/>
        </h:panelGrid>
      </h:form>
    </h:body>
  </f:view>
</html>

2.7.4.4.1. Hata sayfasındaki ifadeler

Müşteri isteğinin işleme zincirinde, XHTML sayfası normalde zincirin son halkasıdır:

Zincirdeki tüm öğeler, XHTML sayfası da dahil olmak üzere Java sınıflarıdır. Bu sayfa, servlet konteyneri tarafından bir servlet'e dönüştürülür; c.a.d ise normal bir Java sınıfına dönüştürülür. Daha açık bir ifadeyle, XHTML sayfası, aşağıdaki yöntem içinde çalıştırılan Java koduna dönüştürülür:


public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {

JspFactory _jspxFactory = null;
PageContext pageContext = null;
HTTPSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;
... 
...code de la page XHTML

  1. satırdan itibaren, XHTML sayfasının Java kodu bulunur. Bu kod, yukarıdaki 1. satırdaki _jspService yöntemi tarafından başlatılan bir dizi nesneye sahip olacaktır:
  • 1. satır: HttpServletRequest request: işlenmekte olan istek,
  • satır 1: HttpServletResponse response: müşteriye gönderilecek yanıt,
  • satır 7: ServletContext application: web uygulamasının kendisini temsil eden bir nesne. request nesnesi gibi, application nesnesi de özniteliklere sahip olabilir. Bu öznitelikler, tüm istemcilerin tüm istekleri tarafından paylaşılır. Bunlar genellikle salt okunur özniteliklerdir,
  • 6. satır: HTTPSession oturumu: İstemcinin oturumunu temsil eder. request ve application nesneleri gibi, session nesnesi de özniteliklere sahip olabilir. Bu öznitelikler, aynı müşterinin tüm istekleri tarafından paylaşılır,
  • 9. satır: JspWriter out: istemci tarayıcısına yönelik bir yazma akışı. Bu nesne, bir XHTML sayfasının hata ayıklamasında kullanışlıdır. out.println(metin) aracılığıyla yazılan her şey istemci tarayıcısında görüntülenir.

JSF sayfasında #{ifade} yazıldığında, ifade yukarıdaki request, session veya application nesnelerinin bir özniteliğinin anahtarı olabilir. İlgili öznitelik bu üç nesnede sırayla aranır. Böylece #{anahtar} şu şekilde değerlendirilir:

  1. request.getAttribute(anahtar)
  2. session.getAttribute(anahtar)
  3. application.getAttribute(anahtar)

null dışında bir değer elde edilir edilmez, #{anahtar} ifadesinin değerlendirilmesi durdurulur. Özniteliğin aranacağı bağlamı belirterek daha kesin sonuçlar elde etmek mümkündür:

  • #{requestScope['clé']} ile özniteliği request nesnesinde aramak,
  • #{sessionScope['clé']} özniteliği session nesnesinde aramak için,
  • #{applicationScope['clé']} özniteliği application nesnesinde aramak için.

[exception.xhtml] sayfasının 116. sayfasında bu işlem gerçekleştirilmiştir. Kullanılan öznitelikler şunlardır:

anahtar
alan
değer
javax.servlet.error.status_code
istek
bkz. paragraf 2.7.4.3.
javax.servlet.error.exception
aynı
aynı
javax.servlet.error.request_uri
aynı
aynı
javax.servlet.error.servlet_name
aynı
aynı

JSF ve [exception.xhtml] sayfaları için gerekli olan çeşitli mesajlar, mevcut mesaj dosyalarına eklenmiştir:

[messages_fr.properties]


exception.header=L'exception suivante s'est produite
exception.httpCode=Code HTTP de l'erreur
exception.message=Message de l'exception
exception.requestUri=URL demandée lors de l'erreur
exception.servletName=Nom de la servlet demandée lorsque l'erreur s'est produite

[messages_en.properties]


exception.header=The following error occurred
exception.httpCode=HTTP error code
exception.message=Exception message
exception.requestUri=URL requested when error occurred
exception.servletName=Servlet requested when error occurred

2.8. mv-jsf2-06 örneği: girişlerin doğrulanması ve dönüştürülmesi

2.8.1. Uygulama

Uygulama, bir veri girişi formu sunar. Formun doğrulanmasının ardından, girişler hatalı bulunursa olası hata mesajlarıyla birlikte aynı form yanıt olarak geri gönderilir.

2.8.2. NetBeans projesi

Uygulamanın NetBeans projesi şöyledir:

[mv-jsf2-06] projesi yine tek bir sayfaya dayanmaktadır: [index.html] [1] ve şablonu [Form.java] [2]. Proje, [messages.properties]'ten alınan mesajları kullanmaya devam ediyor, ancak yalnızca Fransızca [3]. Dil değiştirme seçeneği sunulmamaktadır.

2.8.3. Uygulamanın ortamı

Burada, özel bir açıklama yapmadan uygulamayı yapılandıran dosyaların içeriğini veriyoruz. Bu dosyalar, aşağıda anlatılanları daha iyi anlamanıza yardımcı olacaktır.

[faces-config.xml]


<?xml version='1.0' encoding='UTF-8'?>

<!-- =========== FULL CONFIGURATION FILE ================================== -->

<faces-config version="2.0"
              xmlns="http://java.sun.com/xml/ns/javaee" 
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">

  <application>
    <resource-bundle>
      <base-name>
        messages
      </base-name>
      <var>msg</var>
    </resource-bundle>
    <message-bundle>messages</message-bundle>
  </application>
</faces-config>
  1. satır yenidir. Bu satır daha sonra açıklanacaktır.

Mesaj dosyası [messages_fr.properties]


form.titre=Jsf - validations et conversions
saisie1.prompt=1-Nombre entier de type int
saisie2.prompt=2-Nombre entier de type int
saisie3.prompt=3-Nombre entier de type int
data.required=Vous devez entrer une donn\u00e9e
integer.required=Vous devez entrer un nombre entier
saisie4.prompt=4-Nombre entier de type int dans l'intervalle [1,10]
saisie4.error=4-Vous devez entrer un nombre entier dans l'intervalle [1,10]
saisie5.prompt=5-Nombre r\u00e9el de type double
double.required=Vous devez entrer un nombre
saisie6.prompt=6-Nombre r\u00e9el>=0  de type double
saisie6.error=6-Vous devez entrer un nombre >=0
saisie7.prompt=7-Bool\u00e9en
saisie7.error=7-Vous devez entrer un bool\u00e9en
saisie8.prompt=8-Date au format jj/mm/aaaa
saisie8.error=8-Vous devez entrer une date valide au format jj/mm/aaaa
date.required=Vous devez entrer une date
saisie9.prompt=9-Cha\u00eene de 4 caract\u00e8res
saisie9.error=9-Vous devez entrer une cha\u00eene de 4 caract\u00e8res exactement
saisie9B.prompt=9B-Heure au format hh:mm
saisie9B.error=La cha\u00eene saisie ne respecte pas le format hh:mm
submit=Valider
cancel=Annuler
saisie.type=Type de la saisie
saisie.champ=Champ de saisie
saisie.erreur=Erreur de saisie
bean.valeur=Valeurs du mod\u00e8le du formulaire
saisie10.prompt=10-Nombre entier de type int <1 ou >7
saisie10.incorrecte=10-Saisie n\u00b0 10 incorrecte
saisie10.incorrecte_detail=10-Vous devez entrer un nombre entier <1 ou >7
saisies11et12.incorrectes=La propri\u00e9t\u00e9 saisie11+saisie12=10 n'est pas v\u00e9rifi\u00e9e
saisies11et12.incorrectes_detail=La propri\u00e9t\u00e9 saisie11+saisie12=10 n'est pas v\u00e9rifi\u00e9e
saisie11.prompt=11-Nombre entier de type int
saisie12.prompt=12-Nombre entier de type int
error.sign="!"
error.sign_detail="!"

[styles.css] stil sayfası şu şekildedir:


.info{
   font-family: Arial,Helvetica,sans-serif;
   font-size: 14px;
   font-weight: bold
}

.col1{
   background-color: #ccccff
}

.col2{
   background-color: #ffcccc
}

.col3{
   background-color: #ffcc66
}

.col4{
   background-color: #ccffcc
}

.error{
   color: #ff0000
}

.saisie{
   background-color: #ffcccc;
   border-color: #000000;
   border-width: 5px;
   color: #cc0033;
   font-family: cursive;
   font-size: 16px
}

.entete{
   font-family: 'Times New Roman',Times,serif;
   font-size: 14px;
   font-weight: bold
}

2.8.4. [index.xhtml] sayfası ve şablonu [Form.java]

[index.xhtml] sayfası şöyledir:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
  <h:head>
    <title>JSF</title>
    <h:outputStylesheet library="css" name="styles.css"/>
  </h:head>
  <h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
    <h2><h:outputText value="#{msg['form.titre']}"/></h2>
    <h:form id="formulaire">
      <h:messages globalOnly="true" />
      <h:panelGrid columns="4" columnClasses="col1,col2,col3,col4" border="1">
        <!-- 1. satır -->
        <h:outputText value="#{msg['saisie.type']}" styleClass="entete"/>
        <h:outputText value="#{msg['saisie.champ']}" styleClass="entete"/>
        <h:outputText value="#{msg['saisie.erreur']}" styleClass="entete"/>
        <h:outputText value="#{msg['bean.valeur']}" styleClass="entete"/>
        <!-- satır 2 -->
        <h:outputText value="#{msg['saisie1.prompt']}"/>
        <h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
        <h:message for="saisie1" styleClass="error"/>
        <h:outputText value="#{form.saisie1}"/>
        <!-- satır 3 -->
        <h:outputText value="#{msg['saisie2.prompt']}" />
        <h:inputText id="saisie2" value="#{form.saisie2}"  styleClass="saisie"/>
        <h:message for="saisie2" showSummary="true" showDetail="false" styleClass="error"/>
        <h:outputText value="#{form.saisie2}"/>
        <!-- satır 4 -->
        <h:outputText value="#{msg['saisie3.prompt']}" />
        <h:inputText id="saisie3" value="#{form.saisie3}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
        <h:message for="saisie3" styleClass="error"/>
        <h:outputText value="#{form.saisie3}"/>
        <!-- satır 5 -->
        <h:outputText value="#{msg['saisie4.prompt']}" />
        <h:inputText id="saisie4" value="#{form.saisie4}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}" validatorMessage="#{msg['saisie4.error']}">
          <f:validateLongRange minimum="1" maximum="10" />
        </h:inputText>
        <h:message for="saisie4" styleClass="error"/>
        <h:outputText value="#{form.saisie4}"/>
        <!-- satır 6 -->
        ...
        <!-- satır 7 -->
        ...
        <!-- satır 8 -->
        ...
        <!-- satır 9 -->
        ...
        <!-- satır 10 -->
        ...
        <!-- satır 11 -->
        ...
        <!-- satır 12 -->
        ...
        <!-- satır 13 -->
        ...
      </h:panelGrid>
      <!-- kontrol düğmeleri -->
      <h:panelGrid columns="2">
        <h:commandButton value="#{msg['submit']}" action="#{form.submit}"/>
        <h:commandButton value="#{msg['cancel']}" immediate="true" action="#{form.cancel}"/>
      </h:panelGrid>
    </h:form>
  </h:body>
</html>

En önemli yenilik, hata mesajlarını görüntülemek için kullanılan etiketlerin eklenmesidir:

  • hata mesajlarını görüntülemek için <h:messages> (14. satır), <h:message> (24., 29., 34. satırlar),
  • girişlere geçerlilik kısıtlamaları getiren <f:validateLongRange> (satır 39), <f:validateDoubleRange>, <f:validateLength>, <f:validateRegex>,
  • bu girişler ile şablonu arasında bir dönüştürücü tanımlayan <f:convertDateTime> gibi.

Bu sayfanın şablonu, aşağıdaki [Form.java] sınıfıdır:


package forms;

import com.corejsf.util.Messages;
import java.util.Date;
import javax.enterprise.context.RequestScoped;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.ValidatorException;

@ManagedBean
@RequestScoped
public class Form {

public Form() {
}
// girdiler
private Integer saisie1 = 0;
private Integer saisie2 = 0;
private Integer saisie3 = 0;
private Integer saisie4 = 0;
private Double saisie5 = 0.0;
private Double saisie6 = 0.0;
private Boolean saisie7 = true;
private Date saisie8 = new Date();
private String saisie9 = "";
private Integer saisie10 = 0;
private Integer saisie11 = 0;
private Integer saisie12 = 0;
private String errorSaisie11 = "";
private String errorSaisie12 = "";

// eylemler
public String submit() {
...
}

public String cancel() {
...
}

// doğrulayıcılar
public void validateSaisie10(FacesContext context, UIComponent component, Object value) {
...
}
// alıcı ve ayarlayıcılar
...
}

Buradaki yenilik, şablondaki alanların artık sadece String türünde değil, çeşitli türlerde olmasıdır.

2.8.5. Formdaki farklı girişler

Şimdi formdaki farklı giriş alanlarını sırayla inceleyeceğiz.

2.8.5.1. Girişler 1 ila 4: tamsayı girişi

[index.xhtml] sayfası, 1 numaralı girişi şu şekilde gösterir:


<!-- 2. satır -->
        <h:outputText value="#{msg['saisie1.prompt']}"/>
        <h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
        <h:message for="saisie1" styleClass="error"/>
        <h:outputText value="#{form.saisie1}"/>

form.saisie1 şablonu, [Form.java]'te şu şekilde tanımlanmıştır:


private Integer saisie1 = 0;

Tarayıcıdaki bir GET örneğinde, [Form.java] şablonuyla ilişkili [index.xhtml] sayfası görsel olarak şu sonucu verir:

  • 2. satır [1]'i üretir,
  • 3. satır [2]'i üretir,
  • 4. satır [3]'i üretir,
  • 5. satır [4] sonucunu verir.

Aşağıdaki girişin yapılıp onaylandığını varsayalım:

Bu durumda, uygulama tarafından geri gönderilen formda şu sonuç elde edilir:

  • [1]'te, hatalı giriş,
  • [2]'te, bunu bildiren hata mesajı,
  • [3]'te ise, modelin Integer saisie1 alanının değerinin değişmediğini görüyoruz.

Neler olduğunu açıklayalım. Bunun için JSF sayfasının işleme döngüsüne geri dönelim:

Bu döngüyü şu bileşen açısından inceleyeceğiz:


<h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>

ve şablonu:


private Integer saisie1 = 0;
  • [A]'te, tarayıcının GET sırasında gönderdiği [index.xhtml] sayfası geri yüklenir. [A]'te sayfa, kullanıcının aldığı haliyle kalır. id="saisie1" bileşeni başlangıç değeri olan "0"a geri döner,
  • [B]'te, sayfanın bileşenleri tarayıcı tarafından gönderilen değerleri alır. [B]'te, sayfa kullanıcının girdiği ve onayladığı şekildedir. id="saisie1" bileşeni, gönderilen "x" değerini alır;
  • [C]'te ise, sayfa açık doğrulayıcılar ve dönüştürücüler içeriyorsa bunlar yürütülür. Bileşene bağlı alanın türü String değilse, örtük dönüştürücüler de çalıştırılır. Burada durum böyledir; form.saisie1 alanı Integer türündedir. JSF, id="saisie1" bileşeninin "x" değerini Integer türüne dönüştürmeye çalışacaktır. Bu, [A-F] işleme döngüsünü durduracak bir hataya neden olacaktır. Bu hata, id="saisie1" bileşeniyle ilişkilendirilecektir. [D2] aracılığıyla, daha sonra doğrudan yanıtın oluşturma aşamasına geçilir. Aynı [index.xhtml] sayfası geri gönderilir;
  • [D] aşaması, yalnızca bir sayfanın tüm bileşenleri dönüştürme/doğrulama aşamasını geçtikten sonra gerçekleşir. Bu aşamada, id="saisie1" bileşeninin değeri, form.saisie1 şablonuna atanır.

[C] aşaması başarısız olursa, sayfa yeniden görüntülenir ve aşağıdaki kod tekrar yürütülür:


<!-- satır 2 -->
        <h:outputText value="#{msg['saisie1.prompt']}"/>
        <h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
        <h:message for="saisie1" styleClass="error"/>
<h:outputText value="#{form.saisie1}"/>

[2]'te görüntülenen mesaj, [index.xhtml]'in 4. satırından gelmektedir. <h:message for="idComposant"/> etiketi, hata olması durumunda for özniteliğiyle belirtilen bileşene ait hata mesajını görüntüler. [2]'te görüntülenen mesaj standarttır ve [jsf-api.jar] arşivindeki [javax/faces/Messages.properties] dosyasında bulunur:

[2]'te, mesaj dosyasının çeşitli varyantlarının mevcut olduğu görülmektedir. [Messages_fr.properties] dosyasının içeriğini inceleyelim:

...
# ==============================================================================
# Bileşen Hataları
# ==============================================================================
javax.faces.component.UIInput.CONVERSION={0} : une erreur de conversion est survenue.
javax.faces.component.UIInput.REQUIRED={0} : erreur de validation. Vous devez indiquer une valeur.
javax.faces.component.UIInput.UPDATE={0} : une erreur est survenue lors du traitement des informations que vous avez soumises. 
javax.faces.component.UISelectOne.INVALID={0} : erreur de validation. La valeur est incorrecte.
javax.faces.component.UISelectMany.INVALID={0} : erreur de validation. La valeur est incorrecte.

# ==============================================================================
# Dönüştürücü Hataları
# ==============================================================================
...
javax.faces.converter.FloatConverter.FLOAT={2} : «{0 doit être un nombre composé dun ou de plusieurs chiffres.
javax.faces.converter.FloatConverter.FLOAT_detail={2} : «{0 doit être un nombre compris entre 1.4E-45 et 3.4028235E38. Exemple : {1}
javax.faces.converter.IntegerConverter.INTEGER={2} : «{0 doit être un nombre composé dun ou de plusieurs chiffres.
javax.faces.converter.IntegerConverter.INTEGER_detail={2} : «{0 doit être un nombre compris entre -2147483648 et 2147483647. Exemple : {1}
...


# ==============================================================================
# Doğrulayıcı Hataları
# ==============================================================================
javax.faces.validator.DoubleRangeValidator.MAXIMUM={1} : erreur de validation. La valeur est supérieure à la valeur maximale autorisée, "{0}".
javax.faces.validator.DoubleRangeValidator.MINIMUM={1} : erreur de validation. La valeur est inférieure à la valeur minimale autorisée, "{0}".
javax.faces.validator.DoubleRangeValidator.NOT_IN_RANGE={2} : erreur de validation. Lattribut spécifié nest pas compris entre les valeurs attendues {0} et {1}.
javax.faces.validator.DoubleRangeValidator.TYPE={0} : erreur de validation. La valeur nest pas du type correct.
...

Dosya, kategorilere ayrılmış mesajlar içeriyor:

  • bir bileşende hatalar, satır 3,
  • bir bileşen ile şablonu arasındaki dönüştürme hataları, satır 12
  • sayfada doğrulayıcılar mevcut olduğunda ortaya çıkan doğrulama hataları, satır 23.

id="saisie1" bileşeninde meydana gelen hata, String türünden Integer türüne dönüştürme hatasıdır. İlgili hata mesajı, hata mesajları dosyasının 18. satırında yer almaktadır.

javax.faces.converter.IntegerConverter.INTEGER_detail={2} : «{0}» doit être un nombre compris entre -2147483648 et 2147483647. Exemple : {1}

Görüntülenen hata mesajı aşağıda verilmiştir:

Mesajda şunlar görülmektedir:

  • {2} parametresinin, dönüştürme hatasının meydana geldiği bileşenin kimliği ile değiştirildiği,
  • {0} parametresi, [1]'te bu bileşen için girilen değerle değiştirilmiştir,
  • {1} parametresi 9346 sayısıyla değiştirilmiştir.

Bileşenlerle ilgili mesajların çoğunun iki versiyonu vardır: özet (summary) ve ayrıntılı (detail) versiyon. 16-18. satırlarda da durum böyledir:

javax.faces.converter.IntegerConverter.INTEGER={2} : «{0}» doit être un nombre composé d’un ou de plusieurs chiffres.
javax.faces.converter.IntegerConverter.INTEGER_detail={2} : «{0}» doit être un nombre compris entre -2147483648 et 2147483647. Exemple : {1}

_detail anahtarına sahip mesaj (2. satır), ayrıntılı mesaj olarak adlandırılır. Diğeri ise özet mesaj olarak adlandırılır. <h:message> etiketi varsayılan olarak ayrıntılı mesajı görüntüler. Bu davranış, showSummary ve showDetail öznitelikleriyle değiştirilebilir. "saisie2" kimliğine sahip bileşen için yapılan da budur:


        <!-- satır 3 -->
        <h:outputText value="#{msg['saisie2.prompt']}" />
        <h:inputText id="saisie2" value="#{form.saisie2}"  styleClass="saisie"/>
        <h:message for="saisie2" showSummary="true" showDetail="false" styleClass="error"/>
<h:outputText value="#{form.saisie2}"/>
  1. satırda, saisie2 bileşeni, aşağıdaki form.saisie2 alanıyla ilişkilendirilmiştir:

  private Integer saisie2 = 0;

Elde edilen sonuç şöyledir:

  • [1]'te ayrıntılı mesaj, [2]'te özet mesaj yer almaktadır.

<h:messages> etiketi, tüm bileşenlerden gelen özetlenmiş hata mesajlarının yanı sıra herhangi bir bileşenle ilişkili olmayan hata mesajlarını da bir liste halinde görüntüler. Burada da bazı öznitelikler bu varsayılan davranışı değiştirebilir:

  • showDetail: ayrıntılı mesajların görüntülenip görüntülenmeyeceğini belirlemek için true / false,
  • showSummary: özet mesajların görüntülenip görüntülenmeyeceğini belirlemek için true / false,
  • globalOnly: bileşenlerle ilişkili olmayan hata mesajlarının görüntülenip görüntülenmeyeceğini belirlemek için true / false. Bu tür bir mesaj, örneğin geliştirici tarafından oluşturulabilir.

Bir dönüştürme işlemiyle ilişkili hata mesajı çeşitli şekillerde değiştirilebilir. Öncelikle, uygulamaya başka bir mesaj dosyası kullanması talimatı verilebilir. Bu değişiklik [faces-config.xml]'te yapılır:


<faces-config ...">
  <application>
    <resource-bundle>
      <base-name>
        messages
      </base-name>
      <var>msg</var>
    </resource-bundle>
    <message-bundle>messages</message-bundle>
  </application>
...
</faces-config>

3-8. satırlar bir mesaj dosyası tanımlar, ancak <h:message> ve <h:messages> etiketleri tarafından kullanılan dosya bu değildir. Tanımlamak için 9. satırdaki <message-bundle> etiketini kullanmak gerekir. 9. satır, <h:message(s)> etiketlerine [messages.properties] dosyasının [javax.faces.Messages.properties] dosyasından önce taranması gerektiğini belirtir. Dolayısıyla, [messages_fr.properties] dosyasına aşağıdaki satırları eklersek:


# dönüştürmeler
javax.faces.converter.IntegerConverter.INTEGER=erreur
javax.faces.converter.IntegerConverter.INTEGER_detail=erreur d\u00e9taill\u00e9e

saisie1 ve saisie2 bileşenleri için döndürülen hata şu şekilde olur:

Image

Dönüştürme hata mesajını değiştirmenin diğer bir yolu, saisie3 bileşeni için aşağıda gösterildiği gibi bileşenin converterMessage özniteliğini kullanmaktır:


        <!-- satır 4 -->
        <h:outputText value="#{msg['saisie3.prompt']}" />
        <h:inputText id="saisie3" value="#{form.saisie3}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
        <h:message for="saisie3" styleClass="error"/>
<h:outputText value="#{form.saisie3}"/>

saisie3 bileşeni, aşağıdaki form.saisie3 alanıyla bağlantılıdır:


  private Integer saisie3 = 0;
  • 3. satırda, converterMessage özniteliği, dönüştürme hatası durumunda görüntülenecek mesajı açıkça belirler;
  • 3. satırda, required="true" özniteliği, girişin zorunlu olduğunu belirtir. Alan boş bırakılamaz. Bir alan, hiçbir karakter içermediği veya bir dizi boşluk içerdiği takdirde boş olarak kabul edilir. Burada da [javax.faces.Messages.properties]'te varsayılan bir mesaj bulunmaktadır:
javax.faces.component.UIInput.REQUIRED={0} : erreur de validation. Vous devez indiquer une valeur.

requiredMessage özniteliği, bu varsayılan mesajı değiştirmek için kullanılır. [messages.properties] dosyası aşağıdaki mesajları içeriyorsa:


...
data.required=Vous devez entrer une donnée
integer.required=Vous devez entrer un nombre entier

şu sonuç elde edilebilir:

ya da şunu:

Bir girdinin tam sayıya karşılık geldiğini doğrulamak her zaman yeterli olmaz. Bazen girilen sayının belirli bir aralığa ait olup olmadığını kontrol etmek gerekir. Bu durumda bir doğrulayıcı kullanılır. 4 numaralı girdi buna bir örnektir. [index.xhtml]'teki kodu şöyledir:


        <!-- satır 5 -->
        <h:outputText value="#{msg['saisie4.prompt']}" />
        <h:inputText id="saisie4" value="#{form.saisie4}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}" validatorMessage="#{msg['saisie4.error']}">
          <f:validateLongRange minimum="1" maximum="10" />
        </h:inputText>
        <h:message for="saisie4" styleClass="error"/>
<h:outputText value="#{form.saisie4}"/>
  1. satırda, saisie4 bileşeni aşağıdaki form.saisie4 şablonuna bağlıdır:

  private Integer saisie4 = 0;

3-5. satırlarda, <h:inputText> etiketinin, minimum ve maksimum adlı iki isteğe bağlı özniteliği kabul eden <f:validateLongRange> adlı bir alt etiketi vardır. "Doğrulayıcı" olarak da adlandırılan bu etiket, girilen değere bir kısıtlama eklemeye olanak tanır: değer sadece bir tamsayı olmakla kalmayıp, [minimum, maximum] aralığında bir tamsayı olmalıdır (eğer minimum ve maximum özniteliklerinin her ikisi de mevcutsa), yalnızca minimum özniteliği mevcutsa minimum'e eşit veya daha büyük olmalı, yalnızca maximum özniteliği mevcutsa maximum'e eşit veya daha küçük olmalıdır. <f:validateLongRange> doğrulayıcısının [javax.faces.Messages.properties]'te varsayılan hata mesajları vardır:

1
2
3
javax.faces.validator.LongRangeValidator.MINIMUM={1} : erreur de validation. La valeur est inférieure à la valeur minimale autorisée, "{0}".
javax.faces.validator.LongRangeValidator.NOT_IN_RANGE={2} : erreur de validation. L’attribut spécifié n’est pas compris entre les valeurs attendues {0} et {1}.
javax.faces.validator.LongRangeValidator.TYPE={0} : erreur de validation. La valeur n’est pas du type correct.

Yine, bu mesajları başkalarıyla değiştirmek mümkündür. Bileşen için özel bir mesaj tanımlamaya olanak tanıyan validatorMessage özniteliği mevcuttur. Dolayısıyla, aşağıdaki JSF kodu ile:


        <h:inputText id="saisie4" value="#{form.saisie4}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}" validatorMessage="#{msg['saisie4.error']}">
          <f:validateLongRange minimum="1" maximum="10" />
</h:inputText>

ve [messages.properties]'te şu mesaj:


saisie4.error=4-Vous devez entrer un nombre entier dans l'intervalle [1,10]

şu sonuç elde edilir:

Image

2.8.5.2. 5. ve 6. girişler: gerçek sayı girişi

Reel sayıların girilmesi, tamsayıların girilmesine benzer kurallara tabidir. 5. ve 6. girişlerin kodu XHTML şu şekildedir:


<!-- satır 6 -->
        <h:outputText value="#{msg['saisie5.prompt']}" />
        <h:inputText id="saisie5" value="#{form.saisie5}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['double.required']}"/>
        <h:message for="saisie5" styleClass="error"/>
        <h:outputText value="#{form.saisie5}"/>
        <!-- satır 7 -->
        <h:outputText value="#{msg['saisie6.prompt']}"/>
        <h:inputText id="saisie6" value="#{form.saisie6}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['double.required']}" validatorMessage="#{msg['saisie6.error']}">
          <f:validateDoubleRange minimum="0.0"/>
        </h:inputText>
        <h:message for="saisie6" styleClass="error"/>
        <h:outputText value="#{form.saisie6}"/>

[Form.java] şablonundaki, saisie5 ve saisie6 bileşenleriyle ilişkili öğeler:


  private Double saisie5 = 0.0;
  private Double saisie6 = 0.0;

[messages.properties] içindeki saisie5 ve saisie6, bileşenlerinin dönüştürücülerine ve doğrulayıcılarına ilişkin hata mesajları:


double.required=Vous devez entrer un nombre
saisie6.error=6-Vous devez entrer un nombre >=0

İşte bir yürütme örneği:

Image

2.8.5.3. Giriş 7: Boole değeri girişi

Boole değerinin girişi normalde bir onay kutusu ile yapılmalıdır. Bir giriş alanı kullanılarak yapılırsa, "true" dizesi Boole true değerine, diğer tüm dizeler ise Boole false değerine dönüştürülür.

Örnekteki XHTML kodu:


<!-- satır 8 -->
        <h:outputText value="#{msg['saisie7.prompt']}"/>
        <h:inputText id="saisie7" value="#{form.saisie7}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['double.required']}"/>
        <h:message for="saisie7" styleClass="error"/>
        <h:outputText value="#{form.saisie7}"/>

saisie7 bileşen şablonu:


  private Boolean saisie7 = true;

İşte bir giriş örneği ve buna verilen yanıt:

[1]'te girilen değer. Dönüştürme sonucunda, bu "x" dizesi boole değeri false'a dönüşür. [2] bunu göstermektedir. Şablondaki [3] değeri değişmemiştir. Bu değer, ancak sayfadaki tüm dönüştürme ve doğrulama işlemleri başarıyla tamamlandığında değişir. Bu örnekte durum böyle değildi.

2.8.5.4. Giriş 8: tarih girişi

Örnekte tarih girişi, aşağıdaki XHTML kodu ile yapılmaktadır:


<!-- satır 9 -->
        <h:outputText value="#{msg['saisie8.prompt']}"/>
        <h:inputText id="saisie8" value="#{form.saisie8}"  styleClass="saisie" required="true" requiredMessage="#{msg['date.required']}" converterMessage="#{msg['saisie8.error']}">
          <f:convertDateTime pattern="dd/MM/yyyy"/>
        </h:inputText>
        <h:message for="saisie8" styleClass="error"/>
        <h:outputText value="#{form.saisie8}">
          <f:convertDateTime pattern="dd/MM/yyyy"/>
        </h:outputText>
  1. satırdaki saisie8 bileşeni, java.lang.String <--> java.util.Date dönüştürücüsünü kullanır. saisie8 bileşeniyle ilişkili form.saisie8 şablonu şöyledir:

  private Date saisie8 = new Date();

7-9. satırlarla tanımlanan bileşen de bir dönüştürücü kullanır, ancak yalnızca java.util.Date --> java.lang.String yönünde.

<f:convertDateTime> dönüştürücüsü, dönüştürülecek karakter dizisinin biçimini veya bir tarihin görüntülenme biçimini belirleyen pattern özniteliği dahil olmak üzere çeşitli öznitelikleri destekler.

[index.xhtml] sayfasının ilk çağrıldığında, yukarıdaki 8. satır şu şekilde görüntülenir:

[1] ve [2] alanlarının her ikisi de form.saisie8 şablonunun değerini gösterir:


  private Date saisie8 = new Date();

burada saisie8, günün tarihini alır. Her iki durumda da tarihin görüntülenmesi için kullanılan dönüştürücü şöyledir:


            <f:convertDateTime pattern="dd/MM/yyyy"/>

burada dd (day) gün numarasını, MM (Month) ay numarasını ve yyyy (year) yılı ifade eder. [1]'te dönüştürücü, java.lang.String --> java.util.Date ters dönüşümü için kullanılır. Bu nedenle, girilen tarihin geçerli olabilmesi için "dd/MM/yyyy" biçimini izlemesi gerekir.

[javax.faces.Messages.properties]'te geçersiz tarihler için varsayılan mesajlar bulunmaktadır:

javax.faces.converter.DateTimeConverter.DATE={2} : «{0}» n’a pas pu être interprété en tant que date.
javax.faces.converter.DateTimeConverter.DATE_detail={2} : «{0}» n’a pas pu être interprété en tant que date. Exemple : {1} 

Bu mesajlar, kendi mesajlarınızla değiştirilebilir. Örneğin:


<h:inputText id="saisie8" value="#{form.saisie8}"  styleClass="saisie" required="true" requiredMessage="#{msg['date.required']}" converterMessage="#{msg['saisie8.error']}">
  <f:convertDateTime pattern="dd/MM/yyyy"/>
</h:inputText>

dönüştürme hatası durumunda görüntülenen mesaj, aşağıdaki saisie8.error anahtar mesajı olacaktır:


saisie8.error=8-Vous devez entrer une date valide au format jj/mm/aaaa

İşte bir örnek:

Image

2.8.5.5. Giriş 9: uzunluğu kısıtlanmış bir dizenin girilmesi

  1. örnek, girilen bir dizenin karakter sayısının belirli bir aralıkta olmasını nasıl zorunlu kılacağınızı gösterir:

<!-- satır 10 -->
        <h:outputText value="#{msg['saisie9.prompt']}"/>
        <h:inputText id="saisie9" value="#{form.saisie9}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validatorMessage="#{msg['saisie9.error']}">
          <f:validateLength minimum="4" maximum="4"/>
        </h:inputText>
        <h:message for="saisie9" styleClass="error"/>
        <h:outputText value="#{form.saisie9}"/>
  1. satırda yer alan <f:validateLength minimum="4" maximum="4"/> doğrulayıcısı, girilen dizenin tam olarak 4 karakterden oluşmasını zorunlu kılar. Bu özniteliklerden yalnızca biri kullanılabilir: minimum, minimum karakter sayısı için; maximum ise maksimum karakter sayısı için.

  2. satırdaki saisie9 bileşeninin form.saisie9 şablonu şu şekildedir:


  private String saisie9 = "";

Bu tür bir doğrulama için varsayılan hata mesajları mevcuttur:

javax.faces.validator.LengthValidator.MAXIMUM={1} : erreur de validation. La longueur est supérieure à la valeur maximale autorisée, "{0}".
javax.faces.validator.LengthValidator.MINIMUM={1} : erreur de validation. La longueur est inférieure à la valeur minimale autorisée, "{0}".

Bu mesajlar, yukarıdaki 3. satırda olduğu gibi validatorMessage özniteliği kullanılarak değiştirilebilir. saisie9.error anahtar mesajı şöyledir:


saisie9.error=9-Vous devez entrer une chaîne de 4 caractères exactement

İşte bir yürütme örneği:

Image

2.8.5.6. 9B girişi: bir şablona uyması gereken bir dizenin girişi

9B girişi, girilen bir dizenin karakter sayısının belirli bir aralıkta olmasını nasıl zorunlu kılacağını gösterir:


<!-- satır 10B -->
        <h:outputText value="#{msg['saisie9B.prompt']}"/>
        <h:inputText id="saisie9B" value="#{form.saisie9B}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validatorMessage="#{msg['saisie9B.error']}">
          <f:validateRegex pattern="^\s*\d{2}:\d{2}\s*$"/>
        </h:inputText>
        <h:message for="saisie9B" styleClass="error"/>
        <h:outputText value="#{form.saisie9B}"/>
  1. satırda, <f:validateRegex pattern="^\s*\d{2}:\d{2}\s*$"/> doğrulayıcısı, girilen dizenin bir düzenli ifade şablonuna uymasını zorunlu kılar; bu örnekte: 0 veya daha fazla boşluk, 2 rakam, : işareti, 2 rakam, 0 veya daha fazla boşluktan oluşan bir dizi.

  2. satırdaki saisie9B bileşeninin form.saisie9B şablonu şöyledir:


private String saisie9B;

Bu tür bir doğrulama için varsayılan hata mesajları mevcuttur:

1
2
3
4
5
6
javax.faces.validator.RegexValidator.PATTERN_NOT_SET=Le modèle d’expression régulière doit être défini.
javax.faces.validator.RegexValidator.PATTERN_NOT_SET_detail=La valeur définie du modèle d’expression régulière ne peut pas être vide.
javax.faces.validator.RegexValidator.NOT_MATCHED=Discordance du modèle d’expression régulière.
javax.faces.validator.RegexValidator.NOT_MATCHED_detail=Discordance du modèle d’expression régulière «{0}».
javax.faces.validator.RegexValidator.MATCH_EXCEPTION=Erreur dans l’expression régulière.
javax.faces.validator.RegexValidator.MATCH_EXCEPTION_detail=Erreur dans l’expression régulière,  «{0}»

Bu mesajlar, yukarıdaki 3. satırda olduğu gibi validatorMessage özniteliği kullanılarak değiştirilebilir. saisie9.error anahtar mesajı şöyledir:


saisie9B.error=La cha\u00eene saisie ne respecte pas le format hh:mm

İşte bir yürütme örneği:

Image

2.8.5.7. Giriş 10: Özel bir doğrulama yöntemi yazma

Özetleyelim: JSF, girilen değerler arasında sayıların (tamsayılar, gerçek sayılar) ve tarihlerin geçerliliğini, dizelerin uzunluğunu ve bir girdinin düzenli ifadeye uygunluğunu kontrol etmeyi sağlar. JSF, mevcut doğrulayıcılara ve dönüştürücülere kendi doğrulayıcılarınızı ve dönüştürücülerinizi eklemenizi sağlar. Bu konu burada ele alınmamaktadır, ancak daha ayrıntılı bilgi için [ref2]'e bakabilirsiniz.

Burada başka bir yöntem sunuyoruz: form modelindeki bir yöntemle girilen veriyi doğrulama yöntemi. Aşağıdaki örnekte bu yöntem gösterilmiştir:


<!-- satır 11 -->
        <h:outputText value="#{msg['saisie10.prompt']}"/>
        <h:inputText id="saisie10" value="#{form.saisie10}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validator="#{form.validateSaisie10}"/>
        <h:message for="saisie10" styleClass="error"/>
        <h:outputText value="#{form.saisie10}"/>
  1. satırdaki saisie10 bileşenine bağlı form.saisie10 modeli şöyledir:

  private Integer saisie10 = 0;

Girilen sayının <1 veya >7 olmasını istiyoruz. Bunu JSF'in temel doğrulayıcılarıyla kontrol edemeyiz. Bu durumda, saisie10 bileşeni için kendi doğrulama yöntemimizi yazıyoruz. Bunu, doğrulanacak bileşenin validator özniteliği ile belirtiriz:


          <h:inputText id="saisie10" value="#{form.saisie10}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validator="#{form.validateSaisie10}"/>

saisie10 bileşeni, form.validateSaisie10 yöntemi ile doğrulanır. Bu yöntem şu şekildedir:


  public void validateSaisie10(FacesContext context, UIComponent component, Object value) {
    int saisie = (Integer) value;
    if (!(saisie < 1 || saisie > 7)) {
      FacesMessage message = Messages.getMessage(null, "saisie10.incorrecte", null);
      message.setSeverity(FacesMessage.SEVERITY_ERROR);
      throw new ValidatorException(message);
    }
}

Bir doğrulama yönteminin imzası, zorunlu olarak 1. satırdakiyle aynı olmalıdır:

  • FacesContext context: sayfanın yürütme bağlamı - çeşitli bilgilere, özellikle de HttpServletRequest request ve HttpServletResponse response nesnelerine erişim sağlar,
  • UIComponent bileşen: doğrulanması gereken bileşen. <h:inputText> etiketi, UIComponent'ten türetilen UIInput türünde bir bileşenle temsil edilir. Burada ikinci parametre olarak alınan bileşen UIInput'tir,
  • Nesne değeri: Doğrulanacak girilen değer, modelinin türüne dönüştürülmüş halidir. Burada şunu anlamak önemlidir: String → model türü dönüşümü başarısız olursa, doğrulama yöntemi yürütülmez. validateSaisie10 yöntemine ulaşıldığında, bu, String → Integer dönüşümünün başarılı olduğu anlamına gelir. Bu durumda üçüncü parametre Integer türündedir.
  • 2. satır: Girilen değer, int, türüne dönüştürülür
  • 3. satır: Girilen değerin <1 veya >7 olup olmadığı kontrol edilir. Eğer öyleyse, doğrulama işlemi tamamlanır. Aksi takdirde, doğrulayıcı ValidatorException türünde bir istisna oluşturarak hatayı bildirmelidir.

ValidatorException sınıfının iki oluşturucusu vardır:

  • [1] yapıcı işlevinin parametresi, FacesMessage türünde bir hata mesajıdır. Bu tür mesaj, <h:messages> ve <h:message> etiketleri tarafından görüntülenen mesajdır;
  • [2] yapıcı işlevi ise Throwable türündeki veya bu hatadan türetilen hatanın nedenini kapsüllemeye olanak tanır.

FacesMessage türünde bir mesaj oluşturmamız gerekiyor. Bu sınıfın çeşitli oluşturucuları vardır:

[1] oluşturucu, bir FacesMessage nesnesinin özelliklerini tanımlar:

  • FacesMessage.Severity severity: Aşağıdaki sıralamadan alınan bir ciddiyet seviyesi: SEVERITY_ERROR, SEVERITY_FATAL, SEVERITY_INFO, SEVERITY_WARN,
  • Özet metni: Hata mesajının özet versiyonu - <h:message showSummary="true"> ve <h:messages> etiketleri ile görüntülenir,
  • Ayrıntılı metin: Hata mesajının ayrıntılı versiyonu - <h:message> ve <h:messages showDetail="true"> etiketleriyle görüntülenir.

Herhangi bir oluşturucu kullanılabilir; eksik parametreler daha sonra set yöntemleriyle düzeltilebilir.

[1] oluşturucusu, uluslararasılaştırılmış bir mesaj dosyasında bulunan bir mesajı belirtmeye izin vermez. Bu elbette üzücü bir durumdur. David Geary ve Cay Horstmann, "Core JavaServer Faces" adlı kitaplarında com.corejsf.util.Messages yardımcı sınıfı ile bu eksikliği gidermektedir. Java kodunun 4. satırında hata mesajını oluşturmak için kullanılan sınıf budur. Bu sınıf, 4. satırda kullanılan getMessage yöntemi dahil olmak üzere yalnızca statik yöntemler içerir:


   public static FacesMessage getMessage(String bundleName, String resourceId, Object[] params)

getMessage yöntemi üç parametre kabul eder:

  • String bundleName: .properties uzantısı olmadan, ancak paket adıyla birlikte bir mesaj dosyasının adı. Burada, [messages.properties] dosyasını belirtmek için ilk parametremiz messages olabilir. İlk parametre ile belirtilen dosyayı kullanmadan önce, getMessage, varsa uygulamanın mesaj dosyasını kullanmaya çalışır. Dolayısıyla, [faces-config.xml] dosyasında şu etiketle bir mesaj dosyası tanımlanmışsa:

  <application>
...
    <message-bundle>messages</message-bundle>
</application>

null dosyası, getMessage yöntemine ilk parametre olarak geçirilebilir. Burada da bu yapılmıştır (bkz. [web.xm], sayfa 120),

  • String resourceId: mesaj dosyasında işlenecek mesajın anahtarıdır. Bir mesajın hem özet hem de ayrıntılı bir sürümü olabileceğini görmüştük. resourceId, özet sürümün tanımlayıcısıdır. Ayrıntılı sürüm, resourceId_detail anahtarıyla otomatik olarak aranacaktır. Böylece, 10 numaralı giriş hatası için [messages.properties]'te iki mesajımız olacaktır:

saisie10.incorrecte=10-Saisie  10 incorrecte
saisie10.incorrecte_detail=10-Vous devez entrer un nombre entier <1 ou >7

Messages.getMessage yöntemi tarafından üretilen FacesMessage türündeki mesaj, bulunmuş olmaları halinde hem özet hem de ayrıntılı sürümleri içerir. Her iki sürüm de mevcut olmalıdır; aksi takdirde [NullPointerException] türünde bir istisna alınır,

  • Object[] params: Mesajın {0}, {1}, ... gibi biçimsel parametreleri varsa, bu parametrelerin gerçek değerleri. Bu biçimsel parametreler, params dizisinin elemanlarıyla değiştirilecektir.

saisie10 bileşeninin doğrulama yönteminin koduna geri dönelim:


  public void validateSaisie10(FacesContext context, UIComponent component, Object value) {
    int saisie = (Integer) value;
    if (!(saisie < 1 || saisie > 7)) {
      FacesMessage message = Messages.getMessage(null, "saisie10.incorrecte", null);
      message.setSeverity(FacesMessage.SEVERITY_ERROR);
      throw new ValidatorException(message);
    }
}
  • [4]'te, FacesMessage türündeki mesaj, Messages.getMessage statik yöntemi kullanılarak oluşturulur;
  • [5]'te mesajın ciddiyet seviyesi belirlenir,
  • [6]'te, daha önce oluşturulan mesajla ValidatorException türünde bir istisna tetiklenir. Doğrulama yöntemi, aşağıdaki XHTML kodu tarafından çağrılmıştır:

<!-- satır 11 -->
        <h:outputText value="#{msg['saisie10.prompt']}"/>
        <h:inputText id="saisie10" value="#{form.saisie10}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validator="#{form.validateSaisie10}"/>
        <h:message for="saisie10" styleClass="error"/>
<h:outputText value="#{form.saisie10}"/>
  1. satırda, saisie10 kimlikli bileşen için doğrulama yöntemi yürütülür. Dolayısıyla, validateSaisie10 yöntemi tarafından üretilen hata mesajı bu bileşene atanır ve bu nedenle 4. satırda (for="saisie10" özniteliği) tarafından görüntülenir. <h:message> etiketiyle varsayılan olarak görüntülenen, ayrıntılı sürümdür.

İşte bir yürütme örneği:

Image

2.8.5.8. Girişler 11 ve 12: Bir bileşen grubunun doğrulanması

Şimdiye kadar ele aldığımız doğrulama yöntemleri yalnızca tek bir bileşeni doğruluyordu. Peki, istenen doğrulama birden fazla bileşeni kapsıyorsa ne yapmalıyız? Şimdi bunu inceleyeceğiz. Formda:

Image

11 ve 12 numaralı girişlerin, toplamı 10'a eşit olan iki tamsayı olmasını istiyoruz.

JSF kodu şu şekilde olacaktır:


<!-- satır 12 -->
        <h:outputText value="#{msg['saisie11.prompt']}"/>
        <h:inputText id="saisie11" value="#{form.saisie11}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
        <h:panelGroup>
          <h:message for="saisie11" styleClass="error"/>
          <h:outputText value="#{form.errorSaisie11}" styleClass="error"/>
        </h:panelGroup>
        <h:outputText value="#{form.saisie11}"/>
        <!-- satır 13 -->
        <h:outputText value="#{msg['saisie12.prompt']}"/>
        <h:inputText id="saisie12" value="#{form.saisie12}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
        <h:panelGroup>
          <h:message for="saisie12" styleClass="error"/>
          <h:outputText value="#{form.errorSaisie12}" styleClass="error"/>
        </h:panelGroup>
        <h:outputText value="#{form.saisie12}"/>

ve ilgili şablon:


  private Integer saisie11 = 0;
  private Integer saisie12 = 0;
  private String errorSaisie11 = "";
private String errorSaisie12 = "";

JSF kodunun 3. satırında, saisie11 bileşeni için girilen değerin gerçekten bir tamsayı olup olmadığını doğrulamak için daha önce sunulan teknikler kullanılır. Aynı durum, 11. satırda saisie12 bileşeni için de geçerlidir. saisie11 + saisie12 = 10 olduğunu doğrulamak için özel bir doğrulama fonksiyonu oluşturulabilir. Tercih edilmesi gereken çözüm budur. Yine, bunu bulmak için [ref2] kodunu inceleyeceğiz. Burada ise farklı bir yaklaşım izliyoruz.

[index.xhtml] sayfası, [Valider] düğmesi tarafından doğrulanır; bu düğmenin kodu JSF'tir ve şu şekildedir:


<!-- kontrol düğmeleri -->
      <h:panelGrid columns="2">
        <h:commandButton value="#{msg['submit']}" action="#{form.submit}"/>
        ...
      </h:panelGrid>

burada msg['submit'] mesajı şöyledir:


submit=Valider
  1. satırda, [Valider] düğmesine yapılan tıklamayı işlemek için form.submit yönteminin çalıştırılacağı görülmektedir. Bu yöntem şöyledir:

  // eylemler
  public String submit() {
    // son onaylar
    validateForm();
    // aynı form geri gönderilir
    return null;
  }

  // genel onaylar
  private void validateForm() {
    if ((saisie11 + saisie12) != 10) {
...
}

Submit yönteminin çalıştırılması sırasında şunların gerçekleştiğini anlamak önemlidir:

  • formdaki tüm doğrulayıcılar ve dönüştürücüler çalıştırılmış ve başarılı olmuş,
  • [Form.java] modelindeki alanlar, istemci tarafından gönderilen değerleri almış olur.

Aslında, bir POST JSF'in işleme döngüsüne geri dönelim:

submit yöntemi bir olay işleyicisidir. [Valider] düğmesindeki clic olayını yönetir. Tüm olay işleyiciler gibi, bu işleyici de tüm doğrulayıcılar ve dönüştürücüler çalıştırılıp başarılı bir şekilde tamamlandıktan sonra ([C]) ve şablon gönderilen değerlerle güncellendikten sonra ([D]) [E] aşamasında çalışır. Dolayısıyla burada, daha önce yaptığımız gibi [ValidatorException] türünde istisnalar oluşturmak söz konusu değildir. Formu hata mesajlarıyla birlikte geri göndermekle yetineceğiz:

[1]'te kullanıcıyı uyaracağız; [2] ve [3]'te ise bir hata işareti koyacağız. JSF kodunda, [1] mesajı şu şekilde elde edilecektir:


<h:form id="formulaire">
      <h:messages globalOnly="true" />
      <h:panelGrid columns="4" columnClasses="col1,col2,col3,col4" border="1">
        <!-- satır 1 -->
        ...
  1. satırda, <h:messages> etiketi varsayılan olarak form bileşenlerine ilişkin tüm hatalı girişlerin hata mesajlarının özetini ve bileşenlerle ilgili olmayan tüm hata mesajlarını görüntüler. globalOnly="true" özniteliği, görüntüyü sadece bunlarla sınırlar.

[2] ve [3] mesajları, basit <h:outputText> etiketleriyle görüntülenir:


<!-- satır 12 -->
        <h:outputText value="#{msg['saisie11.prompt']}"/>
        <h:inputText id="saisie11" value="#{form.saisie11}"  styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
        <h:panelGroup>
          <h:message for="saisie11" styleClass="error"/>
          <h:outputText value="#{form.errorSaisie11}" styleClass="error"/>
        </h:panelGroup>
        <h:outputText value="#{form.saisie11}"/>
        <!-- satır 13 -->
        ...
          <h:outputText value="#{form.errorSaisie12}" styleClass="error"/>
        ...

4-7. satırlarda, saisie11 bileşeninde iki olası hata mesajı vardır:

  • hatalı bir dönüştürme veya veri eksikliğini belirten mesaj. JSF tarafından üretilen bu mesaj, FacesMessage türünde yer alacak ve 5. satırdaki <h:message> etiketiyle görüntülenecektir;
  • bu mesaj, giriş11 + giriş12'nin 10'a eşit olmaması durumunda oluşturacağımız mesajdır. Bu mesaj 6. satırda görüntülenecektir. Hata mesajı ise form.errorSaisie11 şablonunda yer alacaktır.

Bu iki mesaj, aynı anda meydana gelemeyecek hatalara karşılık gelir. "giriş11 + giriş12 = 10" kontrolü, formda hiçbir hata kalmadığında çalıştırılan submit yönteminde yapılır. Bu yöntem çalıştırıldığında, saisie11 bileşeni kontrol edilmiş olacak ve form.saise11 şablonuna değer atanmış olacaktır. 5. satırdaki mesaj artık görüntülenemeyecektir. Tersine, 5. satırdaki mesaj görüntüleniyorsa, formda en az bir hata kalmış demektir ve submit yöntemi çalıştırılmayacaktır. 6. satırdaki mesaj görüntülenmeyecektir. Olası iki hata mesajının tablonun aynı sütununda yer alması için, bunlar bir <h:panelGroup> etiketinde birleştirilmiştir (4. ve 7. satırlar).

submit yöntemi şu şekildedir:


  // eylemler
  public String submit() {
    // son onaylar
    validateForm();
    // aynı form geri gönderiliyor
    return null;
  }

  // genel onaylar
  private void validateForm() {
    if ((saisie11 + saisie12) != 10) {
      // genel mesaj
      FacesMessage message = Messages.getMessage(null, "saisies11et12.incorrectes", null);
      message.setSeverity(FacesMessage.SEVERITY_ERROR);
      FacesContext context = FacesContext.getCurrentInstance();
      context.addMessage(null, message);
      // alanlarla ilgili mesajlar
      message = Messages.getMessage(null, "error.sign", null);
      setErrorSaisie11(message.getSummary());
      setErrorSaisie12(message.getSummary());
    } else {
      setErrorSaisie11("");
      setErrorSaisie12("");
    }
}
  • 4. satır: submit yöntemi, son doğrulamaları yapmak için validateForm yöntemini çağırır,
  • 11. satır: saisie11+saisie12=10 olup olmadığı kontrol edilir,
  • değilse, 13-14. satırlarda, saisies11et12.incorrectes kimlik numaralı mesajla FacesMessage türünde bir mesaj oluşturulur. Bu mesaj şöyledir:

saisies11et12.incorrectes=La propriété saisie11+saisie12=10 n'est pas vérifiée
  • bu şekilde oluşturulan mesaj (satır 15-16), uygulamanın hata mesajları listesine eklenir. Bu mesaj belirli bir bileşene bağlı değildir. Bu, uygulamanın genel bir mesajıdır. Yukarıda sunulan <h:messages globalOnly="true"/> etiketiyle görüntülenecektir,
  • 18. satır: error.sign kimlik numaralı mesajla FacesMessage türünde yeni bir mesaj oluşturulur. Bu mesaj şöyledir:

error.sign="!"

Daha önce, [Messages.getMessage] statik yönteminin, varsa hem özet hem de ayrıntılı sürümleri içeren bir FacesMessage türünde mesaj oluşturduğunu belirtmiştik. Burada, error.sign mesajının yalnızca özet sürümü mevcuttur. m mesajının özet versiyonu, m.getSummary() işlevi ile elde edilir. 19. ve 20. satırlarda, error.sign mesajının özet versiyonu, şablonun errorSaisie11 ve errorSaisie12 alanlarına yerleştirilir. Bunlar, aşağıdaki JSF etiketleriyle görüntülenecektir:


          <h:outputText value="#{form.saisie11}"/>
          ...
          <h:outputText value="#{form.saisie12}"/>
  • 22-23. satırlar: saisie11+saisie12=10 özelliği doğrulanırsa, önceki bir hata mesajının silinmesi için şablondaki errorSaisie11 ve errorSaisie12 alanlarının ikisi de boşaltılır. Burada, şablonun istekler arasında, istemcinin oturumunda saklandığını unutmamak gerekir.

İşte bir yürütme örneği:

[1] sütununda, şablonun gönderilen değerleri aldığı görülecektir; bu da, gönderilen değerler ile şablon arasındaki tüm doğrulama ve dönüştürme işlemlerinin başarılı olduğunu gösterir. Böylece, [Valider] düğmesine yapılan tıklamayı yöneten form.submit olay işleyicisi çalıştırılabilmiştir. [2] ve [3]'te görüntülenen mesajları üreten de bu olay yöneticisidir. Form reddedilip müşteriye geri gönderilmiş olmasına rağmen modelin güncellendiği görülmektedir. Böyle bir durumda şablonun güncellenmemesi istenebilir. Nitekim, kullanıcının [Annuler] veya [4] düğmesiyle güncellemeyi iptal ettiğini varsayarsak, şablonu önceden kaydetmemişsek ilk şablona geri dönemeyiz.

2.8.5.9. Girişleri doğrulamayan bir formun POST düğmesi

Yukarıdaki formu ele alalım ve kullanıcının hatalarını fark etmeden form doldurmayı bırakmak istediğini varsayalım. Bu durumda, aşağıdaki JSF kodu tarafından oluşturulan [Annuler] düğmesini kullanacaktır:


<!-- komut düğmeleri -->
      <h:panelGrid columns="2">
        <h:commandButton value="#{msg['submit']}" action="#{form.submit}"/>
        <h:commandButton value="#{msg['cancel']}" immediate="true" action="#{form.cancel}"/>
      </h:panelGrid>
  1. satırda, msg['cancel'] mesajı şöyledir:

cancel=Annuler

form.cancel yöntemi, [Annuler] düğmesiyle ilişkilendirilmiş olup, yalnızca form geçerliyse çalıştırılacaktır. Bunu, [Valider] düğmesiyle ilişkilendirilmiş form.submit yöntemi için de göstermiştik. Kullanıcı formdaki girişlerini iptal etmek isterse, elbette girişlerinin geçerliliğini kontrol etmenin bir anlamı kalmaz. Bu sonuç, immediate="true" özniteliği ile elde edilir; bu öznitelik, JSF'e, doğrulama ve dönüştürme aşamalarına geçmeden form.cancel yöntemini çalıştırmasını söyler. POST – JSF işleme döngüsüne geri dönelim:

<h:commandButton> ve <h:commandLink> eylem bileşenlerinin immediate="true" özniteliğine sahip olayları, [C] aşamasında işlenir; ardından JSF döngüsü doğrudan yanıtın oluşturulduğu [E] aşamasına geçer.

form.cancel yöntemi şu şekildedir:


  public String cancel() {
    saisie1 = 0;
    saisie2 = 0;
    saisie3 = 0;
    saisie4 = 0;
    saisie5 = 0.0;
    saisie6 = 0.0;
    saisie7 = true;
    saisie8 = new Date();
    saisie9 = "";
    saisie10 = 0;
    return null;
}

Önceki formda [Annuler] düğmesi kullanıldığında, aşağıdaki sayfa görüntülenir:

  • Yine aynı form görüntülenir, çünkü form.cancel olay yöneticisi, null gezinme anahtarını döndürür. Dolayısıyla [index.xhtml] sayfası geri gönderilir,
  • [Form.java] şablonu, form.cancel yöntemi tarafından değiştirilmiştir. Bu durum, bu şablonu gösteren [2] sütununda yansıtılır;
  • [3] sütunu ise bileşenler için kaydedilen değeri yansıtmaktadır.

Şimdi saisie1 ve [4] bileşenlerinin JSF koduna geri dönelim;


          <!-- 1. satır -->
          <h:outputText value="#{msg['saisie1.prompt']}"/>
          <h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
          <h:message for="saisie1" styleClass="error"/>
<h:outputText value="#{form.saisie1}"/>
  1. satırda, saisie1 bileşeninin değeri, form.saisie1 şablonuyla ilişkilidir. Bu durum birkaç sonuca yol açar:
  • [index.xhtml]'ten GET'e geçiş yapıldığında, saisie1 bileşeni form.saisie1 şablonunun değerini gösterecektir,
  • [index.xhtml]'ten POST'e geçiş yapıldığında, saisie1 bileşeni için girilen değer, yalnızca formdaki tüm doğrulama ve dönüştürme işlemleri başarılı olursa form.saisie1 şablonuna atanır. Model, gönderilen değerlerle güncellenmiş olsun ya da olmasın, POST işleminin sonunda form geri gönderilirse, bileşenler kendilerine atanan modelin değerini değil, gönderilen değeri görüntüler. Yukarıdaki ekran görüntüsünde de görüldüğü gibi, [2] ve [3] sütunları aynı değerlere sahip değildir.

2.9. mv-jsf2-07 örneği: JSF bileşenlerinin durum değişikliğiyle ilgili olaylar

2.9.1. Uygulama

Uygulama, bir düğme veya bağlantı kullanılmadan gerçekleştirilen POST örneğini göstermektedir. Form şu şekildedir:

combo2 [2] listesinin içeriği, combo1 [1]'te seçilen öğeyle bağlantılıdır. [1]'teki seçim değiştirildiğinde, formda bir POST işlemi gerçekleştirilir; bu işlem sırasında combo2'in içeriği, [1]'te seçilen öğeyi yansıtacak şekilde değiştirilir ve ardından form geri gönderilir. Bu POST işlemi sırasında herhangi bir doğrulama yapılmaz.

2.9.2. NetBeans projesi

Uygulamanın NetBeans projesi şu şekildedir:

[index.xhtml] adlı tek bir form ve buna ait [Form.java] şablonu bulunmaktadır.

2.9.3. Uygulamanın ortamı

[messages_fr.properties] mesaj dosyası:


app.titre=intro-07
app.titre2=JSF - Listeners
combo1.prompt=combo1
combo2.prompt=combo2
saisie1.prompt=Nombre entier de type int
submit=Valider
raz=Raz
data.required=Donnée requise
integer.required=Entrez un nombre entier
saisie.type=Type de la saisie
saisie.champ=Champ de saisie
saisie.erreur=Erreur de saisie
bean.valeur=Valeurs du modèle du formulaire

[styles.css] stil sayfası:


.info{
   font-family: Arial,Helvetica,sans-serif;
   font-size: 14px;
   font-weight: bold
}

.col1{
   background-color: #ccccff
}

.col2{
   background-color: #ffcccc
}

.col3{
   background-color: #ffcc66
}

.col4{
   background-color: #ccffcc
}

.error{
   color: #ff0000
}

.saisie{
   background-color: #ffcccc;
   border-color: #000000;
   border-width: 5px;
   color: #cc0033;
   font-family: cursive;
   font-size: 16px
}

.combo{
  color: green;
}

.entete{
   font-family: 'Times New Roman',Times,serif;
   font-size: 14px;
   font-weight: bold
}

2.9.4. [index.xhtml] formu

[index.xhtml] formu şöyledir:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
  <h:head>
    <title>JSF</title>
    <h:outputStylesheet library="css" name="styles.css"/>
    ...
  </h:head>
  <h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
    <h2><h:outputText value="#{msg['app.titre2']}"/></h2>
    <h:form id="formulaire">
      <h:messages globalOnly="true"/>
      <h:panelGrid columns="4" border="1" columnClasses="col1,col2,col3,col4">
        <!-- başlıklar -->
        <h:outputText value="#{msg['saisie.type']}" styleClass="entete"/>
        <h:outputText value="#{msg['saisie.champ']}" styleClass="entete"/>
        <h:outputText value="#{msg['saisie.erreur']}" styleClass="entete"/>
        <h:outputText value="#{msg['bean.valeur']}" styleClass="entete"/>
        <!-- satır 1 -->
        <h:outputText value="#{msg['combo1.prompt']}"/>
        <h:selectOneMenu id="combo1" value="#{form.combo1}" immediate="true" onchange="submit();" valueChangeListener="#{form.combo1ChangeListener}" styleClass="combo">
          <f:selectItems value="#{form.combo1Items}"/>
        </h:selectOneMenu>
        <h:panelGroup></h:panelGroup>
        <h:outputText value="#{form.combo1}"/>
        <!-- satır 2 -->
        <h:outputText value="#{msg['combo2.prompt']}"/>
        <h:selectOneMenu id="combo2" value="#{form.combo2}" styleClass="combo">
          <f:selectItems value="#{form.combo2Items}"/>
        </h:selectOneMenu>
        <h:panelGroup></h:panelGroup>
        <h:outputText value="#{form.combo2}"/>
        <!-- satır 3 -->
        <h:outputText value="#{msg['saisie1.prompt']}"/>
        <h:inputText id="saisie1" value="#{form.saisie1}" required="true" requiredMessage="#{msg['data.required']}" styleClass="saisie" converterMessage="#{msg['integer.required']}"/>
        <h:message for="saisie1" styleClass="error"/>
        <h:outputText value="#{form.saisie1}"/>
      </h:panelGrid>
      <!-- komut düğmeleri -->
      <h:panelGrid columns="2" border="0">
        <h:commandButton value="#{msg['submit']}"/>
        ...
      </h:panelGrid>
    </h:form>
  </h:body>
</html>

Yenilik, combo1 listesinin 24-26. satırlarındaki kodda yer almaktadır. Yeni öznitelikler ortaya çıkmaktadır:

  • onchange: HTML özniteliği - combo1'te seçilen öğe değiştiğinde çalıştırılması gereken bir işlevi veya JavaScript kodunu bildirir. Burada, submit() JavaScript kodu, formu sunucuya gönderir,
  • valueChangeListener: JSF özniteliği – combo1'te seçilen öğe değiştiğinde sunucu tarafında yürütülecek yöntemin adını belirtir. Toplamda iki yöntem yürütülür: biri istemci tarafında, diğeri sunucu tarafında,
  • immediate=true: JSF özniteliği - sunucu tarafındaki olay işleyicisinin ne zaman çalıştırılacağını belirler: form, kullanıcının girdiği şekilde yeniden oluşturulduktan sonra, ancak girişlerin geçerlilik denetimlerinden önce. Burada, formda hatalı girişler olsa bile, combo1 listesinden seçilen öğeye göre combo2 listesini doldurmak istiyoruz. İşte bir örnek:
  • [1]'te ilk bir giriş,
  • [2]'te, combo1'te seçilen öğe A'dan B'ye aktarılır.

Elde edilen sonuç şöyledir:

POST işlemi gerçekleştirildi. combo2 ve [2]'in içeriği,combo1 ve [1]'te seçilen öğeye uyarlanmıştır; ancak [3] girişi hatalıydı. immediate=true özniteliği sayesinde, form.combo1ChangeListener yöntemi geçerlilik denetimlerinden önce yürütülmüştür. Bu öznitelik olmasaydı, [3]'teki hata nedeniyle işleme döngüsü geçerlilik denetimlerinde durmuş olacağından yöntem yürütülmezdi.

[messages.properties]'te formla ilişkili mesajlar şunlardır:


app.titre=intro-07
app.titre2=JSF - Listeners
combo1.prompt=combo1
combo2.prompt=combo2
saisie1.prompt=Nombre entier de type int
submit=Valider
raz=Raz
data.required=Donnée requise
integer.required=Entrez un nombre entier
saisie.type=Type de la saisie
saisie.champ=Champ de saisie
saisie.erreur=Erreur de saisie
bean.valeur=Valeurs du modèle du formulaire

[Form.java]'in ömrü request olarak belirlenmiştir:


package forms;

...

@ManagedBean
@RequestScoped
public class Form {
  1. satırda, bean'in kapsamı request olarak belirlenir.

2.9.5. [Form.java] şablonu

[Form.java] şablonu şu şekildedir:


package forms;

import java.util.logging.Logger;
import javax.enterprise.context.RequestScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.SelectItem;

@ManagedBean
@RequestScoped
public class Form {
  
  public Form() {
  }
  
// form alanları
  private String combo1="A";
  private String combo2="A1";
  private Integer saisie1=0;
  
  // çalışma alanları
  final private String[] combo1Labels={"A","B","C"};
  private String combo1Label="A";
  private static final Logger logger=Logger.getLogger("forms.Form");
  
  // yöntemler
  public SelectItem[] getCombo1Items(){
    // combo1'i başlat
    SelectItem[] combo1Items=new SelectItem[combo1Labels.length];
    for(int i=0;i<combo1Labels.length;i++){
      combo1Items[i]=new SelectItem(combo1Labels[i],combo1Labels[i]);
    }
    return combo1Items;
  }
  
  public SelectItem[] getCombo2Items(){
    // combo1'e göre combo2'yi başlat
    SelectItem[] combo2Items=new SelectItem[5];
    for(int i=1;i<=combo2Items.length;i++){
      combo2Items[i-1]=new SelectItem(combo1Label+i,combo1Label+i);
    }
    return combo2Items;
  }
  
  // dinleyiciler
  public void combo1ChangeListener(ValueChangeEvent event){
    // izleme
    logger.info("combo1ChangeListener");
    // combo1'den gönderilen değeri alıyoruz
    combo1Label=(String)event.getNewValue();
    // doğrulama işlemlerini atlamak istediğimiz için yanıtı gönderiyoruz
    FacesContext.getCurrentInstance().renderResponse();
  }
  
  public String raz(){
    // devam
    logger.info("raz");
    // formun sıfırlanması
    combo1Label="A";
    combo1="A";
    combo2="A1";
    saisie1=0;
    return null;
  }
  
// getter'lar - setter'lar
  ...
}

[index.xhtml] formunu [Form.java] şablonuna bağlayalım:

combo1 listesi, aşağıdaki JSF kodu tarafından oluşturulur:


        <h:selectOneMenu id="combo1" value="#{form.combo1}" immediate="true" onchange="submit();" valueChangeListener="#{form.combo1ChangeListener}" styleClass="combo">
          <f:selectItems value="#{form.combo1Items}"/>
</h:selectOneMenu>

Bu liste, öğelerini şablonundaki getCombo1Items yönteminden alır (2. satır). Bu yöntem, Java kodunun 28-35. satırlarında tanımlanmıştır. Üç öğeden oluşan {"A", "B", "C"} listesini oluşturur.

combo2 listesi, aşağıdaki JSF kodu tarafından oluşturulur:


        <h:selectOneMenu id="combo2" value="#{form.combo2}" styleClass="combo">
          <f:selectItems value="#{form.combo2Items}"/>
</h:selectOneMenu>

Öğelerini, modelindeki getCombo2Items yöntemiyle alır (2. satır). Bu yöntem, Java kodunun 37-44. satırlarında tanımlanmıştır. Beş öğeden oluşan {"X1", "X2", "X3","X4","X5"} oluşturur; burada X, 16. satırdaki combo1Label öğesidir. Dolayısıyla, formun ilk oluşturulduğu sırada, combo2 listesi şu öğeleri içerir: {"A1","A2","A3", "A4", "A5"} öğelerini içerir.

Kullanıcı, combo1 listesindeki seçili öğeyi değiştirdiğinde,

  • onchange="submit();" olayı istemci tarayıcısı tarafından işlenecektir. Böylece form sunucuya gönderilecek ve
  • sunucu tarafında JSF, combo1 bileşeninin değerinin değiştiğini algılayacaktır. 47-54. satırlardaki combo1ChangeListener yöntemi çalıştırılacaktır. ValueChangeListener türündeki bir yöntem, parametre olarak javax.faces.event.ValueChangeEvent türünde bir nesne alır. Bu nesne, değeri değişen bileşenin eski ve yeni değerlerini aşağıdaki yöntemlerle elde etmeyi sağlar:

Image

Burada bileşen, UISelectOne türündeki combo1 listesidir. Değeri String türündedir.

  • Java şablonunun 51. satırı: combo1'in yeni değeri, combo2 listesinin öğelerini oluşturmak için kullanılan combo1Label'te saklanır,
  • 53. satır: Yanıt gönderilir. Burada, combo1ChangeListener yöneticisinin immediate="true" özniteliği ile çalıştırıldığını unutmamak gerekir. Dolayısıyla, bu yönetici, sayfanın bileşen ağacının gönderilen değerlerle güncellendiği aşamadan sonra ve gönderilen değerlerin doğrulanma sürecinden önce çalıştırılır. Ancak, formda hatalı girdiler kalsa bile combo2 listesinin güncellenmesi gerektiğinden, bu doğrulama sürecini atlamak istiyoruz. Dolayısıyla, girdilerin doğrulanması aşamasına geçilmeden yanıtın hemen gönderilmesi talep ediliyor.
  • Form, girildiği haliyle geri gönderilecektir. Ancak, combo1 ve combo2 listelerindeki öğeler, gönderilen değerler değildir. Bu öğeler, getCombo1Items ve getCombo2Items yöntemleri çağrılarak yeniden oluşturulacaktır. Bu son yöntem, combo1ChangeListener tarafından belirlenen combo1Label'in yeni değerini kullanacak ve combo2 listesindeki öğeler değişecektir.

2.9.6. [Raz] düğmesi

[Raz] düğmesi ile formu aşağıda gösterildiği gibi başlangıç durumuna geri döndürmek istiyoruz:

[1]'te, [Raz] düğmesinin POST işleminden önceki hali; [2]'te ise POST işleminin sonucu yer almaktadır.

İşlevsel olarak basit olmasına rağmen, bu kullanım senaryosunun yönetimi oldukça karmaşıktır. Çeşitli çözümler denenebilir; özellikle önceki örnekte [Annuler] düğmesi için kullanılan çözüm:


       <h:commandButton value="#{msg['raz']}" immediate="true" action="#{form.raz}"/>

burada form.raz yöntemi şu şekildedir:


  public String raz(){
    // formu sıfırlama
    combo1Label="A";
    combo1="A";
    combo2="A1";
    saisie1=0;
    return null;
}

Önceki örnekteki [Raz] düğmesiyle elde edilen sonuç ise şöyledir:

[1] sütunu, form.raz yönteminin yürütüldüğünü göstermektedir. Ancak [1] sütunu, gönderilen değerleri göstermeye devam etmektedir:

  • combo1 için kaydedilen değer "B" idi. Dolayısıyla bu öğe listeden seçilmiştir,
  • combo2 için kaydedilen değer "B5" idi. form.raz'in yürütülmesi nedeniyle, combo2'in {"B1", ..., "B5"} {"A1", ..., "A5"} olarak değiştirilmiştir. "B5" öğesi artık mevcut olmadığı için seçilemiyor. Bu durumda, listenin ilk öğesi görüntüleniyor;
  • saisie1 için gönderilen değer 10'du.

Bu, immediate="true" özniteliği ile normal çalışma şeklidir. Farklı bir sonuç elde etmek için, kullanıcı başka değerler girmiş olsa bile, yeni formda görmek istediğiniz değerleri göndermelisiniz. Bunu, istemci tarafında biraz JavaScript kodu kullanarak gerçekleştirebilirsiniz. Form şu şekilde olur:


<script language="javascript">
  function raz(){
    document.forms['formulaire'].elements['formulaire:combo1'].value="A";
    document.forms['formulaire'].elements['formulaire:combo2'].value="A1";
    document.forms['formulaire'].elements['formulaire:saisie1'].value=0;
    //document.forms['formulaire'].submit();
  }
</script>
...
<h:commandButton value="#{msg['raz']}" onclick='raz()' immediate="true" action="#{form.raz}"/>
  • 10. satırda, onclick='raz()' özniteliği, kullanıcı [Raz] düğmesine tıkladığında raz JavaScript işlevinin çalıştırılmasını belirtir;
  • 3. satır: 'form:combo1' adlı HTML öğesine "A" değeri atanır. 3. satırdaki çeşitli öğeler şunlardır:
    • document: tarayıcı tarafından görüntülenen sayfa,
    • document.forms: belgedeki tüm formlar,
    • document.forms['formulaire']: name="formulaire", özniteliğine sahip form
    • documents.forms['formulaire'].elements: name="formulaire", özniteliğine sahip form öğelerinin tümü
    • document.forms['formulaire'].elements['formulaire:combo1']: name="formulaire:combo1" özniteliğine sahip form öğesi
    • document.forms['formulaire'].elements['formulaire:combo1'].value: name="formulaire:combo1". özniteliğine sahip form öğesi tarafından gönderilecek değer

Tarayıcı tarafından görüntülenen sayfanın çeşitli öğelerinin name özniteliklerini öğrenmek için, sayfanın kaynak koduna bakılabilir (aşağıda IE7 ile birlikte):

<form id="formulaire" name="formulaire" ...>
...
<select id="formulaire:combo1" name="formulaire:combo1" ...>

Bunu açıkladıktan sonra, raz işlevinin JavaScript kodunda:

  • 3. satır, combo1 bileşeni için gönderilen değerin A dizesi olmasını sağlar;
  • 4. satır, combo2 bileşeni için gönderilen değerin A1 dizesi olmasını sağlar,
  • 5. satır, saisie1 bileşeni için gönderilen değerin 0 dizesi olmasını sağlar.

Böylece, formdaki POST, <h:commandButton> türündeki herhangi bir düğmeyle (10. satır) ilişkilendirildiğinde tetiklenecektir. form.raz yöntemi çalıştırılacak ve form, gönderildiği haliyle geri döndürülecektir. Böylece şu sonuç elde edilir:

Bu sonuç pek çok şeyi gizlemektedir. combo1, combo2 ve saisie1 bileşenlerinin "A", "A1" ve "0" değerleri sunucuya gönderilir. combo1 bileşeninin önceki değerinin "B" olduğunu varsayalım. Bu durumda, combo1 bileşeninin değerinde bir değişiklik meydana gelir ve form.combo1ChangeListener yöntemi de yürütülmelidir. immediate="true" özniteliğine sahip iki olay işleyicimiz var. İkisi de çalıştırılacak mı? Eğer öyleyse, hangi sırayla? Yoksa sadece biri mi? Eğer öyleyse, hangisi?

Daha fazla bilgi edinmek için uygulamada günlükler oluşturuyoruz:


package forms;

import java.util.logging.Logger;
...
public class Form {
  
...  
// form alanları
  private String combo1="A";
  private String combo2="A1";
  private Integer saisie1=0;
  
  // çalışma alanları
  final private String[] combo1Labels={"A","B","C"};
  private String combo1Label="A";
  private static final Logger logger=Logger.getLogger("forms.Form");
  
  // dinleyici
  public void combo1ChangeListener(ValueChangeEvent event){
    // izleme
    logger.info("combo1ChangeListener");
    // combo1'den gönderilen değeri alıyoruz
    combo1Label=(String)event.getNewValue();
    // doğrulamaları atlamak istediğimiz için yanıtı gönderiyoruz
    FacesContext.getCurrentInstance().renderResponse();
  }
  
  public String raz(){
    // devam
    logger.info("raz");
    // form sıfırlanıyor
    combo1Label="A";
    combo1="A";
    combo2="A1";
    saisie1=0;
    return null;
  }
...
}
  • 16. satır: bir günlük oluşturucu oluşturulur. getLogger parametresi, günlüklerin kaynaklarını ayırt etmeyi sağlar. Burada günlük oluşturucunun adı forms.Form'tir,
  • 21. satır: combo1ChangeListener yöntemine giriş kaydedilir,
  • 30. satır: raz yöntemine giriş günlüğe kaydedilir.

[Raz] düğmesi veya combo1 değerinin değiştirilmesi hangi günlükleri üretir? Çeşitli durumları ele alalım:

  • combo1'te seçili öğe "A" iken [Raz] düğmesi kullanılır. Dolayısıyla "A", combo1 bileşeninin son değeridir. [Raz] düğmesinin, combo1 bileşeni için "A" değerini gönderen bir JavaScript işlevini çalıştırdığını görmüştük. Dolayısıyla combo1 bileşeninin değeri değişmez. Günlük kayıtları, yalnızca form.raz yönteminin yürütüldüğünü gösteriyor:
  
  • [Raz] düğmesi kullanılırken, combo1'te seçilen öğe "A" değildir. Dolayısıyla combo1 bileşeninin değeri değişir: son değeri "A" değildi ve [Raz] düğmesi ona "A" değerini gönderir. Günlükler, bu durumda iki yöntemin yürütüldüğünü gösterir. Sırasıyla: combo1ChangeListener, raz:
  
  • combo1'in değerini, [Raz] düğmesini kullanmadan değiştiriyoruz. Günlükler, yalnızca combo1ChangeListener yönteminin yürütüldüğünü gösteriyor:
  

2.10. mv-jsf2-08 örneği: <h:dataTable> etiketi

2.10.1. Uygulama

Uygulama, kişileri silme seçeneği de içeren bir kişi listesi gösterir:

  • [1]'te bir kişi listesi,
  • [2]'te ise bu kişileri silmeye yarayan bağlantılar yer almaktadır.

2.10.2. NetBeans Projesi

Uygulamanın NetBeans projesi şöyledir:

[index.xhtml] adlı tek bir form ve bunun şablonu olan [Form.java] bulunmaktadır.

2.10.3. Uygulamanın ortamı

[faces-config.xml] yapılandırma dosyası:


<?xml version='1.0' encoding='UTF-8'?>

<!-- =========== FULL CONFIGURATION FILE ================================== -->

<faces-config version="2.0"
              xmlns="http://java.sun.com/xml/ns/javaee" 
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">

  <application>
    <resource-bundle>
      <base-name>
        messages
      </base-name>
      <var>msg</var>
    </resource-bundle>
    <message-bundle>messages</message-bundle>
  </application>
</faces-config>

Mesaj dosyası [messages_fr.properties]:


app.titre=intro-08
app.titre2=JSF - DataTable
submit=Valider
personnes.headers.id=Id
personnes.headers.nom=Nom
personnes.headers.prenom=Pr\u00e9nom

[styles.css] stil sayfası:


.headers {
   text-align: center;
   font-style: italic;
   color: Snow;
   background: Teal;
}

.id {
   height: 25px;
   text-align: center;
   background: MediumTurquoise;
}

.nom {
   text-align: left;
   background: PowderBlue;
}
.prenom {
   width: 6em;
   text-align: left;
   color: Black;
   background: MediumTurquoise;
}

2.10.4. [index.xhtml] formu ve şablonu [Form.java]

[index.xhtml] sayfasına ait görünümü hatırlayalım:

  

[index.xhtml] formu şu şekildedir:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
  <h:head>
    <title>JSF</title>
    <h:outputStylesheet library="css" name="styles.css"/>
  </h:head>
  <h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
    <h2><h:outputText value="#{msg['app.titre2']}"/></h2>
    <h:form id="formulaire">
      <h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
  ........................
      </h:dataTable>
    </h:form>
  </h:body>
</html>
  1. satırda, <h:dataTable> etiketi, #{form.personnes} alanını veri kaynağı olarak kullanır. Bu alan şöyledir:

private List<Personne> personnes;

[Personne] sınıfı şu şekildedir:


package forms;

public class Personne {
  // veriler
  private int id;
  private String nom;
  private String prénom;
  
  // üreticiler
  public Personne(){
    
  }
  
  public Personne(int id, String nom, String prénom){
    this.id=id;
    this.nom=nom;
    this.prénom=prénom;
  }
  
  // toString
  public String toString(){
    return String.format("Personne[%d,%s,%s]", id,nom,prénom);
  }
  
  // getter ve setter'lar
...
}

Şimdi <h:dataTable> etiketinin içeriğine geri dönelim:


<h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
...
</h:dataTable>
  • var="personne" özniteliği, <h:datatable> etiketi içindeki geçerli kişiyi temsil eden değişkenin adını belirler,
  • headerClass="headers" özniteliği, tablonun sütun başlıklarının stilini belirler,
  • columnClasses="...." özniteliği, tablonun her bir sütununun stilini belirler.

Tablodaki sütunlardan birini inceleyelim ve nasıl oluşturulduğuna bakalım:

  

Id sütunundaki XHTML kodu şöyledir:


<h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['personnes.headers.id']}"/>
          </f:facet>
          <h:outputText value="#{personne.id}"/>
        </h:column>
        ...
      </h:dataTable>

lignes 3-5 : la balise <f:facet name="header"> définit le titre de la colonne,
ligne 4 : le titre de la colonne est pris dans le fichier des messages,
ligne 6 : personne fait référence à l'attribut var de la balise <h:dataTable ...> (ligne 1). On écrit donc l'id de la personne courante.


<h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['personnes.headers.id']}"/>
          </f:facet>
          <h:outputText value="#{personne.id}"/>
        </h:column>
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['personnes.headers.nom']}"/>
          </f:facet>
          <h:outputText value="#{personne.nom}"/>
        </h:column>
        <h:column>
          <f:facet name="header">
            <h:outputText value="#{msg['personnes.headers.prenom']}"/>
          </f:facet>
          <h:outputText value="#{personne.prénom}"/>
        </h:column>
...
      </h:dataTable>
  • 3-7. satırlar: tablonun id sütunu,
  • satır 8-13: tablonun soyadı sütunu,
  • satır 14-19: tablonun "ad" sütunu.

Şimdi, [Retirer] bağlantı sütununu inceleyelim:

Bu sütun aşağıdaki kodla oluşturulur:


<h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
...
        <h:column>
          <h:commandLink value="Retirer" action="#{form.retirerPersonne}">
            <f:setPropertyActionListener target="#{form.personneId}" value="#{personne.id}"/>
          </h:commandLink>
        </h:column>
      </h:dataTable>

[Retirer] bağlantısı, 4-6. satırlar tarafından oluşturulur. Bağlantıya tıklandığında, [Form].retirerPersonne yöntemi çalıştırılacaktır. Şimdi [Form.java] sınıfını inceleyelim:


package forms;

import java.util.ArrayList;
import java.util.List;
import javax.enterprise.context.RequestScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean
@SessionScoped
public class Form {

  // model
  private List<Personne> personnes;
  private int personneId;

  // yapıcı
  public Form() {
    // kişi listesinin başlatılması
    personnes = new ArrayList<Personne>();
    personnes.add(new Personne(1, "dupont", "jacques"));
    personnes.add(new Personne(2, "durand", "élise"));
    personnes.add(new Personne(3, "martin", "jacqueline"));
  }

  public String retirerPersonne() {
    // seçilen kişiyi arıyoruz
    int i = 0;
    for (Personne personne : personnes) {
      // geçerli kişi = seçilen kişi mi?
      if (personne.getId() == personneId) {
        // geçerli kişiyi listeden silme
        personnes.remove(i);
        // işlem tamamlandı
        break;
      } else {
        // sonraki kişi
        i++;
      }
    }
    // aynı sayfada test ediliyor
    return null;
  }
  
  // getter ve setter'lar
...
}
  • 18-24. satırlar: Oluşturucu, 14. satırdaki kişi listesini başlatır,
  • 10. satır: Bu liste istekler boyunca var olmaya devam etmesi gerektiğinden, bean'in kapsamı oturumdur.
  1. satırdaki [retirerPersonne] yöntemi çalıştırıldığında, 15. satırdaki alan, [Retirer] bağlantısı tıklanan kişinin kimliği ile başlatılmıştır:

          <h:commandLink value="Retirer" action="#{form.retirerPersonne}">
            <f:setPropertyActionListener target="#{form.personneId}" value="#{personne.id}"/>
</h:commandLink>

<f:setPropertyActionListener> etiketi, bilgilerin modele aktarılmasını sağlar. Burada value özniteliğinin değeri, target özniteliği ile tanımlanan model alanına kopyalanır. Böylece, kişi listesinden silinmesi gereken mevcut kişinin kimliği, bu alanın getter'ı aracılığıyla [Form].personneId alanına kopyalanır. Bu işlem, 1. satırdaki action özniteliği tarafından referans verilen yöntemin yürütülmesinden önce gerçekleştirilir.

26-43. satırlar: [supprimerPersonne] yöntemi, id değeri personneId'e eşit olan kişiyi listeden siler.

2.11. i mv-jsf2-09 örneği: JSF uygulamasının sayfa düzeni

2.11.1. Uygulama

Uygulama, iki görünümlü bir JSF uygulamasının sayfa düzeninin nasıl oluşturulacağını gösterir:

Uygulamanın iki görünümü vardır:

  • [1]'te 1. sayfa,
  • [2]'te ise 2. sayfa.

İki sayfa arasında geçiş yapılabilir. Burada göstermek istediğimiz şey, yukarıdaki ekran görüntülerinde de görüldüğü gibi, 1. ve 2. sayfaların ortak bir mizanpajı paylaşmasıdır.

2.11.2. NetBeans projesi

Uygulamanın NetBeans projesi şu şekildedir:

Uygulamada yalnızca XHTML sayfaları bulunmaktadır. İlgili bir Java şablonu yoktur.

2.11.3. [layout.xhtml] sayfası

[layout.xhtml] sayfası, uygulamanın sayfalarının biçimini belirler:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <h:head>
    <title>JSF</title>
    <h:outputStylesheet library="css" name="styles.css"/>
  </h:head>
  <h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
    <h:form id="formulaire">
      <table style="width: 400px">
        <tr>
          <td colspan="2" bgcolor="#ccccff">
            <ui:include src="entete.xhtml"/>
          </td>
        </tr>
        <tr style="height: 200px">
          <td bgcolor="#ffcccc">
            <ui:include src="menu.xhtml"/>
          </td>
          <td>
            <ui:insert name="contenu" >
              <h2>Contenu</h2>
            </ui:insert>
          </td>
        </tr>
        <tr bgcolor="#ffcc66">
          <td colspan="2">
            <ui:include src="basdepage.xhtml"/>
          </td>
        </tr>         
      </table>
    </h:form>
  </h:body>
</html>
  1. satırda, yeni bir ui ad alanı ortaya çıkmaktadır. Bu ad alanı, bir uygulamanın sayfalarını biçimlendirmeye olanak tanıyan etiketleri içerir. Bu ad alanındaki etiketler 17, 22, 25 ve 32. satırlarda kullanılmaktadır.

[layout.xhtml] sayfası, HTML tablosundaki bilgileri görüntüler (14. satır). Bu sayfaya bir tarayıcıdan erişilebilir:

  • [1]'te, istenen URL.

[2] alanı, aşağıdaki XHTML kodu tarafından oluşturulmuştur:


  <h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
    <h:form id="formulaire">
      <table style="width: 400px">
        <tr>
          <td colspan="2" bgcolor="#ccccff">
            <ui:include src="entete.xhtml"/>
          </td>
        </tr>
...       
      </table>
    </h:form>
</h:body>
  1. satırdaki <ui:include> etiketi, sayfaya harici bir XHTML kodunun eklenmesini sağlar. [entete.xhtml] dosyası şöyledir:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
  <body>
    <h2>entête</h2>
  </body>
</html>

3-8. satırlardaki tüm kod, [layout.xhtml] dosyasına eklenecektir. Böylece <html> ve <body> etiketleri bir <td> etiketinin içine eklenecektir. Bu işlem herhangi bir hata oluşturmaz. Dolayısıyla, <ui:include> ile eklenen sayfalar tam XHTML sayfalarıdır. Görsel açıdan bakıldığında, yalnızca 6. satır bir etki yaratacaktır. <html> ve <body> etiketleri, sözdizimsel nedenlerle bulunmaktadır.

[3] bölümü, aşağıdaki XHTML kodu tarafından oluşturulmuştur:


<h:form id="formulaire">
      <table style="width: 400px">
        <tr style="height: 200px">
          <td bgcolor="#ffcccc">
            <ui:include src="menu.xhtml"/>
          </td>
...
        </tr>
...
      </table>
    </h:form>
  1. satırdaki <ui:include> etiketi, aşağıdaki [menu.xhtml] dosyasını içerir:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
  <body>
    <h2>menu</h2>
  </body>
</html>

[4] alanı, aşağıdaki XHTML kodu tarafından oluşturulmuştur:


<h:form id="formulaire">
      <table style="width: 400px">
...
        <tr bgcolor="#ffcc66">
          <td colspan="2">
            <ui:include src="basdepage.xhtml"/>
          </td>
        </tr>         
      </table>
    </h:form>
  1. satırdaki <ui:include> etiketi, aşağıdaki [basdepage.xhtml] dosyasını içerir:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
  <body>
    <h2>bas de page</h2>
  </body>
</html>

[5] alanı, aşağıdaki XHTML kodu tarafından oluşturulmuştur:


    <h:form id="formulaire">
...
          <td>
            <ui:insert name="contenu" >
              <h2>Contenu</h2>
            </ui:insert>
          </td>
 ...
      </table>
</h:form>
  1. satırdaki <ui:insert> etiketi, içerik adlı bir alan tanımlar. Bu alan, değişken içerik alabilen bir alandır. Bunun nasıl yapıldığını göreceğiz. [layout.xhtml] sayfasını çağırdığımızda, "içerik" adlı alan için herhangi bir içerik tanımlanmamıştı. Bu durumda, 4-6. satırlardaki <ui:insert> etiketinin içeriği kullanılır. Dolayısıyla 5. satır görüntülenir.

2.11.4. [page1.xhtml] sayfası

[layout.xhtml] sayfası görüntülenmek üzere tasarlanmamıştır. Bu sayfa, [page1.xhtml] ve [page2.xhtml] sayfaları için şablon görevi görür. Buna sayfa şablonu denir. [page1.xhtml] sayfası şöyledir:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <h2>page 1</h2>
      <h:commandLink value="page 2" action="page2"/>
    </ui:define>
  </ui:composition>
</html>
  • 6. satırda ui ad alanı kullanılır,
  • 7. satırda, <ui:composition> etiketiyle sayfanın [layout.xhtml] şablonuyla ilişkilendirildiği belirtilir,
  • 8. satırda, bu ilişkilendirme sayesinde her bir <ui:define> etiketi, kullanılan şablondaki bir <ui:insert> etiketiyle ilişkilendirilir; burada bu şablon [layout.xhtml]'tir. İlişkilendirme, her iki etiketin name özniteliği aracılığıyla yapılır. Bu öznitelikler aynı olmalıdır.

Görüntülenen sayfa [layout.xhtml]'tir; burada her bir <ui:insert> etiketinin içeriği, istenen sayfanın <ui:define> etiketinin içeriğiyle değiştirilir. Burada, sanki görüntülenen sayfa şuymuş gibi işlem yapılır:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <h:head>
    <title>JSF</title>
    <h:outputStylesheet library="css" name="styles.css"/>
  </h:head>
  <h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
    <h:form id="formulaire">
      <table style="width: 400px">
        <tr>
          <td colspan="2" bgcolor="#ccccff">
            <ui:include src="entete.xhtml"/>
          </td>
        </tr>
        <tr style="height: 200px">
          <td bgcolor="#ffcccc">
            <ui:include src="menu.xhtml"/>
          </td>
          <td>
              <h2>page 1</h2>
              <h:commandLink value="page 2" action="page2"/>
          </td>
        </tr>
        <tr bgcolor="#ffcc66">
          <td colspan="2">
            <ui:include src="basdepage.xhtml"/>
          </td>
        </tr>         
      </table>
    </h:form>
  </h:body>
</html>

[page1.xhtml] sayfasının 25-26. satırları, [layout.xml] sayfasındaki <ui:insert> etiketinin yerine eklenmiştir.

[page2.xhtml] sayfası, [page1.xhtml] sayfasına benzerdir:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <h2>page 2</h2>
      <h:commandLink value="page 1" action="page1"/>
    </ui:define>
  </ui:composition>
</html>

2.12. Conclusion

JSF 2 üzerine az önce yapılan inceleme, kapsamlı olmaktan uzaktır. Ancak, takip edecek örnekleri anlamak için yeterlidir. Daha ayrıntılı bilgi için [ref2]'e bakabilirsiniz.

2.13. Eclipse ile testler

SpringSource Tool Suite ile Maven projelerinin testlerinin nasıl yapılacağını gösterelim:

  • [1]'te, [2] adlı bir Maven projesini içe aktarıyoruz ve bunu [3] düğmesiyle belirtiyoruz. Burada Eclipse için [mv-jsf2-09] adlı Maven projesini
  • [4]'te, içe aktarılan proje bir Maven projesi olarak doğru bir şekilde tanındı [5],
  • [6] olarak, proje gezginine içe aktarılan proje,
  • [7] olarak, bir Tomcat sunucusunda çalıştırılıyor [8] [9],
  • [10]'te Tomcat 7 başlatıldı,
  • [11]'te, [mv-jsf2-09] [11] projesinin ana sayfası Eclipse'in dahili tarayıcısında görüntülenir.