Skip to content

9. Örnek uygulama 05: rdvmedecins-pfm-ejb

Glassfish sunucusu için geliştirilen örnek uygulama 01 JSF / EJB'in yapısını hatırlayalım:

Bu mimaride herhangi bir değişiklik yapmıyoruz; tek istisna, burada JSF, Primefaces ve Primefaces Mobile kullanılarak geliştirilecek web katmanıdır. Hedef tarayıcı, bir mobil cihazın tarayıcısı olacaktır.

Glassfish ile iki uygulama geliştirdik:

  • 01 numaralı uygulama JSF / EJB kullanıyordu ve oldukça basit bir arayüze sahipti,
  • 03 numaralı uygulama ise PF / EJB kullanıyordu ve zengin bir arayüze sahipti.

PrimeFaces Mobile'da mevcut bileşen sayısının az olması nedeniyle, Uygulama 01'in basit arayüzüne geri döneceğiz. Ayrıca, görünümlerin mobil ekranların küçük boyutlarına uyum sağlayabilmesi gerekecektir.

9.1. Görünümler

Fikir vermesi açısından, iPhone 4 simülatöründe çalıştırılan uygulamanın birkaç görünümünü aşağıda sunuyoruz:

  • [1]'te ana sayfa. Bu sefer makinenin adını girmemiz gerektiğine dikkat edin (IP adresini de girebiliriz), çünkü localhost makinesinde hiçbir şey görüntülenmiyordu,
  • [2] adresinde doktorlar listesi,
  • [3]'te istenen randevu tarihi,
  • [4]'te günün randevu takvimini görüntüleme düğmesi,
  • [5]'te, yeni görünümde doktorun randevu aralıkları gösteriliyor,
  • [6]'te, takvimde gezinmek için bir dizi düğme,
  • [7]'te, doktoru ve günü hatırlatan bir mesaj,
  • [8]'te, randevu almak için bir zaman aralığı. Bunu yapıyoruz,
  • [9]'te, müşterinin seçim ekranı,
  • [10]'te, randevuyla ilgili doktoru, günü ve zaman aralığını hatırlatan bir mesaj,
  • [11]'te, müşteri listesi,
  • [12]'te, onay düğmesi,
  • [13]'te, onaylama işlemiyle takvime geri dönülür,
  • [14]'te, dolu zaman aralığı artık rezerve edilmiştir. Şimdi rezervasyonu sileceğiz,
  • [15]'te aynı ekranda kalıyoruz,
  • ancak [16]'te randevu silinmiştir,

Ana sayfada dil değiştirilebilir: [17], [18], [19]:

Son olarak, bir hata görünümü eklenmiştir:

9.2. NetBeans projesi

NetBeans projesi şu şekildedir:

  
  • [mv-rdvmedecins-ejb-dao-jpa]: Örnek 01'deki [DAO] ve [JPA] katmanlarının EJB projesi,
  • [mv-rdvmedecins-ejb-metier]: Örnek 01’deki [métier] katmanının EJB projesi,
  • [mv-rdvmedecins-pfmobile]: [web] katmanına ait proje / Primefaces mobile – yeni,
  • [mv-rdvmedecins-pfmobile-app-ear]: Uygulamayı Glassfish sunucusuna dağıtmak için kurumsal proje – yeni.

9.3. Kurumsal proje

Kurumsal proje, yalnızca [mv-rdvmedecins-ejb-dao-jpa], [mv-rdvmedecins-ejb-metier] ve [mv-rdvmedecins-pfmobile] adlı üç modülün Glassfish sunucusuna dağıtılması için kullanılır. NetBeans projesi şöyledir:

Proje, [pom.xml] dosyasında aşağıdaki şekilde tanımlanan bu üç [1] bağımlılığı için mevcuttur:


<?xml version="1.0" encoding="UTF-8"?>
<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-rdvmedecins-pfmobile-app-ear</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>ear</packaging>

  <name>mv-rdvmedecins-pfmobile-app-ear</name>

...
    <dependencies>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>mv-rdvmedecins-ejb-dao-jpa</artifactId>
            <version>${project.version}</version>
            <type>ejb</type>
        </dependency>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>mv-rdvmedecins-ejb-metier</artifactId>
            <version>${project.version}</version>
            <type>ejb</type>
        </dependency>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>mv-rdvmedecins-pfmobile</artifactId>
            <version>${project.version}</version>
            <type>war</type>
        </dependency>
    </dependencies>
</project>
  • 6-9. satırlar: kurumsal projenin Maven artefaktı,
  • 14-33. satırlar: projenin üç bağımlılığı. Bunların türlerine dikkat edin (19., 25. ve 31. satırlar).

Web uygulamasını çalıştırmak için bu kurumsal projenin çalıştırılması gerekir.

9.4. Primefaces mobil web projesi

Primefaces mobil web projesi şöyledir:

  • [1] dosyasında ise projenin sayfaları yer almaktadır. [index.xhtml] sayfası, projenin tek sayfasıdır. Bu sayfa, [vue1.xhtml], [vue2.xhtml], [vue3.xhtml], [vueErreurs.xhtml] ve [config.xhtml] adlı beş görünümü içerir;
  • [2] sayfasında ise Java bean'leri bulunur. [Application] bean'inin kapsamı application, [Form] bean'inin kapsamı ise session'tir. [Erreur] sınıfı bir hatayı kapsüller,
  • [3]'te bir hatayı,
  • [4]'te ise bağımlılıklar yer alır. Web projesi, [DAO] katmanındaki EJB projesine bağımlıdır, [métier] katmanındaki EJB projesine ve [web] katmanındaki Primefaces mobile'e bağımlıdır.

9.5. Proje yapılandırması

Proje yapılandırması, daha önce incelediğimiz Primefaces veya JSF projelerindeki yapılandırmayla aynıdır. Yapılandırma dosyalarını tekrar açıklamadan listeliyoruz.

 

[web.xml]: web uygulamasını yapılandırı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.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>
  <error-page>
    <error-code>500</error-code>
    <location>/faces/exception.xhtml</location>
  </error-page>
  <error-page>
    <exception-type>Exception</exception-type>
    <location>/faces/exception.xhtml</location>
  </error-page>

</web-app>
  1. satırda, [index.xhtml] sayfasının uygulamanın ana sayfası olduğu dikkat çekmektedir.

[faces-config.xml]: JSF uygulamasını yapılandırı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>
      <message-bundle>messages</message-bundle>
    <default-render-kit-id>PRIMEFACES_MOBILE</default-render-kit-id>
  </application>
</faces-config>

[beans.xml]: Boş olmakla birlikte @Named açıklaması için gereklidir


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

[messages_fr.properties]: Fransızca mesaj dosyası


# sayfa
page.titre=Les M\u00e9decins Associ\u00e9s
format.date=dd/MM/yyyy
format.date_detail=dd/MM/yyyy

# görünüm1
vue1.header=Les M\u00e9decins Associ\u00e9s - R\u00e9servations

# form 1
form1.titre=R\u00e9servations
form1.medecin=M\u00e9decin
form1.jour=Jour (jj/mm/aaaa)
form1.date.requise=La date est n\u00e9cessaire
form1.date.invalide=La date est invalide
form1.date.invalide_detail=La date est invalide
form1.agenda=Agenda
form1.options=Options

# form 2
form2.titre={0} {1} {2}<br/>{3}
form2.titre_detail={0} {1} {2}<br/>{3}
form2.retour=Retour
form2.supprimer=Supprimer
form2.reserver=R\u00e9server
form2.precedent=Jour pr\u00e9c\u00e9dent
form2.suivant=Jour suivant
form2.today=Aujourd'hui

# form 3
form3.client=Client
form3.valider=Valider
form3.titre={0} {1} {2}<br/>{3}<br/>{4,number,#00}h:{5,sayı,#00}-{6,sayı,#00}h:{7,sayı,#00}
form3.titre_detail={0} {1} {2}<br/>{3}<br/>{4,number,#00}h:{5,sayı,#00}-{6,sayı,#00}h:{7,sayı,#00}
form3.retour=Retour

# hata
erreur.titre=Une erreur s'est produite.

# yapılandırma
config.retour=Retour
config.titre=Configuration
config.langue=Langue
config.langue.francais=Fran\u00e7ais
config.langue.anglais=Anglais
config.valider=Valider

#istisna
exception.titre=Application indisponible. Veuillez recommencer ult\u00e9rieurement.

[messages_en.properties]: İngilizce mesaj dosyası


# sayfa
page.titre=The Associated Doctors
format.date=dd/MM/yyyy
format.date_detail=dd/MM/yyyy

# görünüm1
vue1.header=The Associated Doctors - Reservations

# form 1
form1.titre=Reservations
form1.medecin=Doctor
form1.jour=day (dd/mm/yyyy)
form1.date.requise=The date is necessary
form1.date.invalide=Invalid date
form1.date.invalide_detail=invalid date
form1.agenda=Diary
form1.options=Options

# form 2
form2.titre={0} {1} {2}<br/>{3}
form2.titre_detail={0} {1} {2}<br/>{3}
form2.retour=Back
form2.supprimer=Delete
form2.reserver=Reserve
form2.precedent=Previous Day
form2.suivant=Next Day
form2.today=Today

# form 3
form3.client=Patient
form3.valider=Validate
form3.titre={0} {1} {2}<br/>{3}<br/>{4,number,#00}h:{5,sayı,#00}-{6,sayı,#00}h:{7,sayı,#00}
form3.titre_detail={0} {1} {2}<br/>{3}<br/>{4,number,#00}h:{5,sayı,#00}-{6,sayı,#00}h:{7,sayı,#00}
form3.retour=Back

# hata
erreur.titre=Some error happened

# yapılandırma
config.retour=Back
config.titre=Configuration
config.langue=Language
config.langue.francais=French
config.langue.anglais=English
config.valider=Validate

#istisna
exception.titre=Application not available. Please try again later.

9.6. [index.xhtml] sayfası

Proje her zaman aynı sayfayı, yani aşağıdaki [index.xhtml] sayfasını görüntüler:


<f:view xmlns="http://www.w3.org/1999/xhtml"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:p="http://primefaces.org/ui"
        xmlns:pm="http://primefaces.org/mobile"
        contentType="text/html"
        locale="#{form.locale}">

  <pm:page title="#{msg['page.titre']}">
    <pm:view id="vue1">
      <ui:fragment rendered="#{form.form1Rendered}">
        <ui:include src="vue1.xhtml"/>
      </ui:fragment>
      <ui:fragment rendered="#{form.erreurInit}">
        <ui:include src="vueErreurs.xhtml"/>
      </ui:fragment>
    </pm:view>
    <pm:view id="vue2">
      <ui:fragment rendered="#{form.form2Rendered}">
        <ui:include src="vue2.xhtml"/>
      </ui:fragment>
    </pm:view>
    <pm:view id="vue3">
      <ui:fragment rendered="#{form.form3Rendered}">
        <ui:include src="vue3.xhtml"/>
      </ui:fragment>
    </pm:view>
    <pm:view id="vueErreurs">
      <ui:fragment rendered="#{form.erreurRendered}">
        <ui:include src="vueErreurs.xhtml"/>
      </ui:fragment>
    </pm:view>
    <pm:view id="config">
      <ui:include src="config.xhtml"/>
    </pm:view>
  </pm:page>    
</f:view>
  • 8. satır: sayfa uluslararasılaştırılmıştır (locale özniteliği),
  • 10. satır: Sayfa beş görünüm içerir: vue1 11. satır, vue2 19. satır, vue3 24. satır, vueErreurs 29. satır, config 34. satır. Belirli bir anda bu görünümlerden yalnızca biri görünür. Uygulama başlatıldığında vue1 görünümü görüntülenir. Burada şu zorlukla karşılaştık: Uygulamanın başlatılması sorunsuz geçerse [vue1.xhtml] görüntülenmeli, aksi takdirde [vueErreurs.xhtml] görüntülenmelidir. Bu sorunu, vue1 görünümünün içeriğinin, [Form].form1rendered (12. satır) ve [Form].erreurInit (satır 15) boole değişkenlerine değerler atayan model tarafından yönetilmesini sağlayarak vue1'in içeriğini (satır 11) sabitledik.

Bir simülatörde, [vue1.xhtml] görünümünün çıktısı [1], [vue2.xhtml] görünümünün çıktısı [2], [vue3.xhtml] görünümünün çıktısı [3]'tir:

[vueErreurs.xhtml] görünümü, [4] çıktısı; [config.xhtml] görünümü, [5] çıktısı:

9.7. Projenin bileşenleri

[utils] paketindeki sınıf daha önce tanıtılmıştı: [Messages] sınıfı, bir uygulamanın mesajlarının uluslararasılaştırılmasını kolaylaştıran bir sınıftır. Bu sınıf, 2.8.5.7. paragrafında incelenmiştir.

9.7.1. Application bean'i

[Application.java] bean'i, application kapsamına sahip bir bean'dir. Bu tür bean'lerin, salt okunur ve uygulamanın tüm kullanıcıları tarafından erişilebilir verileri depolamak için kullanıldığını hatırlayalım. Bu bean şu şekildedir:


package beans;

import javax.ejb.EJB;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import rdvmedecins.metier.service.IMetierLocal;

@Named(value = "application")
@ApplicationScoped
public class Application {

  // iş mantığı katmanı
  @EJB
  private IMetierLocal metier;

  public Application() {
  }
  
  // alıcılar

  public IMetierLocal getMetier() {
    return metier;
  }
  
}
  • 8. satır: bean'e application adı verilir,
  • 9. satır: uygulama kapsamındadır,
  • 13-14. satırlar: [métier] katmanının yerel arayüzüne ilişkin bir referans, uygulama sunucusundaki EJB konteyneri tarafından bu bean'e enjekte edilecektir. Uygulamanın mimarisini hatırlayalım:

PFM uygulaması ile EJB ve [Metier] uygulamaları aynı JVM (Java Sanal Makinesi) içinde çalışacaktır. Dolayısıyla [PFM] katmanı, EJB'in yerel arayüzünü kullanacaktır. Hepsi bu kadar. [Application] bean'i başka hiçbir şey içermez. [métier] katmanına erişmek için diğer bean'ler bu bean'den onu arayacaklardır.

9.7.2. [Erreur] bean'i

[Erreur] sınıfı şu şekildedir:


package beans;

public class Erreur {
  
  public Erreur() {
  }
  
  // alan
  private String classe;
  private String message;

  // yapıcı
  public Erreur(String classe, String message){
    this.setClasse(classe);
    this.message=message;
  }
  
  // alıcı ve ayarlayıcılar
...  
}
  • 9. satır, bir istisna atılmışsa istisna sınıfının adı,
  • 10. satır: bir hata mesajı.

9.7.3. [Form] bean'i

Kodu şu şekildedir:


package beans;

...

@Named(value = "form")
@SessionScoped
public class Form implements Serializable {

  public Form() {
  }
  // Uygulama bean'i
  @Inject
  private Application application;
  private IMetierLocal metier;
  // oturum önbelleği
  private List<Medecin> medecins;
  private List<Client> clients;
  private Map<Long, Medecin> hMedecins = new HashMap<Long, Medecin>();
  private Map<Long, Client> hClients = new HashMap<Long, Client>();
  // model
  private Long idMedecin;
  private Date jour = new Date();
  private String strJour;
  private Boolean form1Rendered;
  private Boolean form2Rendered;
  private Boolean form3Rendered;
  private Boolean erreurRendered;
  private String form2Titre;
  private String form3Titre;
  private AgendaMedecinJour agendaMedecinJour;
  private Long idCreneauChoisi;
  private Medecin medecin;
  private Long idClient;
  private CreneauMedecinJour creneauChoisi;
  private List<Erreur> erreurs;
  private Boolean erreurInit = false;
  private String action;
  private String locale = "fr";
  private String msgErreurDate = "";
  private SimpleDateFormat dateFormatter;
  private Boolean erreurDate;
  
  @PostConstruct
  private void init() {
    System.out.println("init");
    // başlangıçta hata yok
    erreurInit = false;
    // tarih biçimlendirme
    dateFormatter = new SimpleDateFormat(Messages.getMessage(null, "format.date", null).getSummary());
    dateFormatter.setLenient(false);
    // bugün
    strJour = dateFormatter.format(jour);
    // iş katmanını alıyoruz
    metier = application.getMetier();
    // doktorları ve müşterileri önbelleğe alıyoruz
    try {
      medecins = metier.getAllMedecins();
      clients = metier.getAllClients();
    } catch (Throwable th) {
      // hatayı not ediyoruz
      erreurInit = true;
      prepareVueErreur(th);
      return;
    }
    // listelerin doğrulanması
    if (medecins.size() == 0) {
      // hatayı not ediyoruz
      erreurInit = true;
      erreurs = new ArrayList<Erreur>();
      erreurs.add(new Erreur("", "La liste des médecins est vide"));
    }
    if (clients.size() == 0) {
      // hatayı not ediyoruz
      erreurInit = true;
      erreurs = new ArrayList<Erreur>();
      erreurs.add(new Erreur("", "La liste des clients est vide"));
    }
    // hata mı?
    if (erreurInit) {
      // hatalar sayfası görüntüleniyor
      setForms(false, false, false, true);
      return;
    }

    // sözlükler
    for (Medecin m : medecins) {
      hMedecins.put(m.getId(), m);
    }
    for (Client c : clients) {
      hClients.put(c.getId(), c);
    }
    // 1. görünüm
    setForms(true, false, false, false);
  }

  // görünüm ekranı
  private void setForms(Boolean form1Rendered, Boolean form2Rendered, Boolean form3Rendered, Boolean erreurRendered) {
    this.form1Rendered = form1Rendered;
    this.form2Rendered = form2Rendered;
    this.form3Rendered = form3Rendered;
    this.erreurRendered = erreurRendered;
  }

  // hazırlık vueErreur
  private void prepareVueErreur(Throwable th) {
    // hata listesi oluşturulur
    erreurs = new ArrayList<Erreur>();
    erreurs.add(new Erreur(th.getClass().getName(), th.getMessage()));
    while (th.getCause() != null) {
      th = th.getCause();
      erreurs.add(new Erreur(th.getClass().getName(), th.getMessage()));
    }
// hata görünümü görüntüleniyor
    setForms(false, false, false, true);
  }

  // alıcı ve ayarlayıcılar
  ..
}
  • 5-7. satırlar: [Form] sınıfı, adı "form" olan ve kapsamı oturum olan bir bean'dir. Bu durumda sınıfın serileştirilebilir olması gerektiğini hatırlatırız,
  • 12-13. satırlar: form bean'i, uygulama bean'ine bir referansa sahiptir. Bu referans, uygulamanın çalıştığı servlet konteyneri tarafından enjekte edilecektir (@Inject anotasyonunun varlığı).
  • 24-27. satırlar: vue1 (24. satır), vue2 (25. satır), vue3 (26. satır) ve vueErreurs (27. satır) görünümlerinin görüntülenmesini kontrol eder;
  • 43-44. satırlar: init yöntemi, sınıfın örneklendirilmesinden hemen sonra çalıştırılır (@PostConstruct anotasyonunun varlığı),
  • satır 49-50: tarih formatını yönetir. PrimeFaces Mobile takvim özelliği sunmaz. Ayrıca, JSF doğrulayıcıları bir PFM sayfasında kullanılamaz. Bu nedenle ajandadaki tarih girişini manuel olarak yönetmemiz gerekecektir,
  • 49. satır: tarih formatını belirler. Bu format, uluslararasılaştırılmış dosyalardan alınır:

format.date=dd/MM/yyyy
format.date_detail=dd/MM/yyyy
  • 50. satır: Tarihler konusunda “laxiste” olmaması gerektiği belirtilir. Aksi takdirde, 32/12/2011 gibi hatalı bir giriş, geçerli tarih olan 01/01/2012 olarak kabul edilir,
  • 54. satır: [métier] katmanından [Application] bean'i aracılığıyla bir referans alınır,
  • 57-58. satırlar: [métier] katmanından doktor ve müşteri listesi istenir,
  • 66-91. satırlar: Her şey yolunda giderse, doktorlar ve müşteriler için sözlükler oluşturulur. Bunlar numaralarına göre indekslenir. Ardından, [vue1.xhtml] görünümü görüntülenir (93. satır),
  • satır 59: Hata olması durumunda, [vueErreurs.xhtml] sayfa şablonu oluşturulur. Bu şablon, satır 35’teki hata listesidir,
  • 105-115. satırlar: [prepareVueErreur] yöntemi, görüntülenecek hata listesini oluşturur. Ardından [index.xhtml] sayfası, [vueErreurs.xhtml] görünümünü görüntüler (114. satır).

[erreur.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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:pm="http://primefaces.org/mobile"
      xmlns:ui="http://java.sun.com/jsf/facelets">

  <!-- Hata Görünümü -->
  <pm:header title="#{msg['page.titre']}" swatch="b">
    <f:facet name="left">
      <p:button icon="home" value=" " href="#vue1?reverse=true" />
    </f:facet>
  </pm:header>
  <pm:content>
    <div align="center">
      <h1><h:outputText value="#{msg['erreur.titre']}" style="color: blue"/></h1>
    </div>

    <p:dataList value="#{form.erreurs}" var="erreur">
      <b>#{erreur.classe}</b> : <i>#{erreur.message}</i>
    </p:dataList>
  </pm:content>
</html>

Hata listesini görüntülemek için <p:dataList> etiketini (21-23. satırlar) kullanır. 13. satırdaki düğme, vue1 görünümüne geri dönmeyi sağlar.

  • [1] düğmesi 13. satır tarafından oluşturulur. icon özniteliği, düğmenin simgesini belirler. Düğme, vue1 görünümüne geri dönülmesini sağlar (href özniteliği),
  • [2] başlığı 11-15. satırlarda oluşturulur,
  • [3] başlığı 18. satırda oluşturulur,
  • [4] metni, 22. satırdaki #{erreur.classe} şablonu tarafından oluşturulur,
  • [5] metni, 22. satırdaki #{erreur.message} şablonu tarafından oluşturulur.

Şimdi uygulamanın yaşam döngüsünün farklı aşamalarını tanımlayacağız. Kullanıcının her eylemi için, ilgili görünümleri ve bu görünümlerde meydana gelen olay işleyicilerini inceleyeceğiz.

9.8. Ana sayfanın görüntülenmesi

Her şey yolunda giderse, görüntülenen ilk görünüm [vue1.xhtml]'tir. Bu, aşağıdaki görünümü verir:

[vue1.xhtml] görünümünün kodu şö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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:pm="http://primefaces.org/mobile"
      xmlns:ui="http://java.sun.com/jsf/facelets">

  <!-- Görünüm 1 -->
  <pm:header title="#{msg['page.titre']}" swatch="b">
    <f:facet name="left">
      <p:button icon="gear" value=" "  href="#config" />
    </f:facet>
  </pm:header>
  <pm:content>
    <h:form id="form1">
      <div align="center">
        <h1><h:outputText value="#{msg['form1.titre']}" style="color: blue"/></h1>
      </div>
      <pm:field>
        <h:outputLabel value="#{msg['form1.medecin']}" for="choixMedecin"/>
        <h:selectOneMenu id="choixMedecin" value="#{form.idMedecin}">  
          <f:selectItems value="#{form.medecins}" var="medecin" itemLabel="#{medecin.titre} #{medecin.prenom} #{medecin.nom}" itemValue="#{medecin.id}"/>  
        </h:selectOneMenu>              
      </pm:field>
      <pm:field>
        <h:outputLabel value="#{msg['form1.jour']}" for="jour"/>
        <p:inputText id="jour" value="#{form.strJour}"/>
        <ui:fragment rendered="#{form.erreurDate}">
          <p:spacer width="50px"/>
          <h:outputText id="msgErreurDate" value="#{form.msgErreurDate}" style="color: red"/>
        </ui:fragment>
      </pm:field>
      <p:commandButton value="#{msg['form1.agenda']}" update=":form1, :vue2, :vueErreurs" action="#{form.getAgenda}" />
    </h:form>
  </pm:content>
</html>
  • 11-15. satırlar: [1] başlığını oluşturur,
  • 13. satır: [2] düğmesini oluşturur. Bu düğmeye tıklandığında yapılandırma görünümü (href özniteliği) görüntülenir,
  • 19. satır: [3] başlığını oluşturur,
  • 21-26. satırlar: [4] doktor listesini oluşturur,
  • satır 27-34: [5] tarih giriş alanını oluşturur. Bu giriş, doğrulayıcı içermeyen bir <p:inputText> etiketiyle yapılır. Tarihin doğrulanması sunucu tarafında yapılacaktır. Tarih yanlışsa, sunucu 30-34. satırlarda görüntülenen bir hata mesajı yerleştirecektir,
  • 35. satır: Formu onaylayan düğme. Bu düğme üç alanı günceller: form1 (vue1'deki form), vue2 ve vueErreurs. Nitekim, tarih geçersizse form1'in güncellenmesi gerekir. Tarih doğruysa vue2'nin güncellenmesi gerekir. Son olarak, bir istisna meydana gelirse (örneğin, veritabanına bağlantı kesilirse), vueErreurs görüntülenmelidir. form1 yerine vue1'i kullanmak cazip gelebilir (böylece tüm görünüm güncellenir). Ancak bu durumda uygulama hata verir.

Bu görünüm, aşağıdaki modele dayanmaktadır:


@Named(value = "form")
@SessionScoped
public class Form implements Serializable {

  public Form() {
  }

  // Uygulama bean'i
  @Inject
  private Application application;
  private IMetierLocal metier;
  // oturum önbelleği
  private List<Medecin> medecins;
  private List<Client> clients;
  // şablon
  private Long idMedecin;
  private Date jour = new Date();
  private String strJour;
  private Boolean form1Rendered;
  private Boolean form2Rendered;
  private Boolean form3Rendered;
  private Boolean erreurRendered;
  private String msgErreurDate = "";
  private Boolean erreurDate;
  
    // doktor listesi
  public List<Medecin> getMedecins() {
    return medecins;
  }

  // takvim
  public String getAgenda() {
...
    }
  }
  • 16. satırdaki alan, sayfanın 23. satırındaki listenin değerini okuma ve yazma yoluyla besler. Sayfanın ilk görüntülenişinde, açılır menüde seçilen değeri sabitler;
  • 27-29. satırlardaki yöntem, doktorlar açılır menüsünün öğelerini (görünümün 24. satırı) oluşturur. Oluşturulan her seçeneğin etiketi (itemLabel) doktorun unvanı, soyadı ve adı; değeri (itemValue) ise doktorun kimliği olacaktır,
  • 18. satırdaki alan, sayfanın 29. satırındaki giriş alanına okuma/yazma yoluyla veri sağlar,
  • 32-34. satırlar: getAgenda yöntemi, sayfanın 35. satırındaki [Agenda] düğmesine yapılan tıklamayı yönetir. Kod şu şekildedir:

// takvim
  public String getAgenda() {
    try {
      // gün kontrol ediliyor
      jour = dateFormatter.parse(strJour);
      // hata yok
      erreurDate=false;
      msgErreurDate = "";
      // takvim oluşturuluyor
      return getAgenda(jour);
    } catch (ParseException ex) {
      // hata mesajı
      erreurDate=true;
      msgErreurDate = Messages.getMessage(null, "form1.date.invalide", null).getSummary();
      // görünüm1      
      setForms(true, false, false, false);
      return "pm:vue1";
    }
  }
  • Yöntem, öncelikle kullanıcı tarafından girilen tarihin geçerliliğini kontrol eder,
  • 5. satır: Tarih, modelin oluşturulması sırasında init yöntemi tarafından başlatılan tarih biçimine göre parsée şeklindedir,
  • 11. satır: Bir istisna meydana gelirse, bir hata ayarlanır (13. satır), uluslararasılaştırılmış bir hata mesajı oluşturulur (14. satır), vue1 görünümü hazırlanır (16. satır) ve vue1 görünümü görüntülenir (17. satır),
  • 10. satır: tarih doğruysa, aşağıdaki yöntem yürütülür:

  // takvim
  public String getAgenda(Date jour) {
    // şu anda seçilmiş bir zaman aralığı yok
    creneauChoisi = null;
    try {
      // seçilen doktor alınır
      medecin = hMedecins.get(idMedecin);
      // form başlığı 2
      form2Titre = Messages.getMessage(null, "form2.titre", new Object[]{medecin.getTitre(), medecin.getPrenom(), medecin.getNom(), new SimpleDateFormat("dd MMM yyyy").format(jour)}).getSummary();
      // belirli bir gün için doktorun randevu takvimi
      agendaMedecinJour = metier.getAgendaMedecinJour(medecin, jour);
      // 2. görünüm görüntüleniyor
      setForms(false, true, false, false);
      return "pm:vue2";
    } catch (Throwable th) {
      System.out.println(th);
      // hatalar görünümü
      prepareVueErreur(th);
      return "pm:vueErreurs";
    }
}

Burada daha önce birkaç kez karşılaştığımız bir kodla karşılaşıyoruz. 9. satırda, vue2 görünümü için uluslararası bir mesaj oluşturulur:


form2.titre={0} {1} {2}<br/>{3}
form2.titre_detail={0} {1} {2}<br/>{3}

Mesaja XHTML eklediğimizi not edelim. Bu, şu şekilde görüntülenecektir:

9.9. Bir doktorun randevu takvimini görüntüleme

Doktorun randevu takvimi, [vue2.xhtml] görünümüyle görüntülenir:

[vue2.xhtml] görünümünün kodu şö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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:pm="http://primefaces.org/mobile"
      xmlns:ui="http://java.sun.com/jsf/facelets">

  <!-- Görünüm 2 -->
  <pm:header title="#{msg['page.titre']}" swatch="b"/>
  <pm:content>
    <h:form id="form2">
      <div align="center">
        <pm:buttonGroup orientation="horizontal">
          <p:commandButton inline="true" icon="back" value=" " action="#{form.showVue1}" update=":vue1"/>
          <p:commandButton inline="true" icon="minus" value=" " action="#{form.getPreviousAgenda}" update=":form2"/>
          <p:commandButton inline="true" icon="home" value=" " action="#{form.getTodayAgenda}" update=":form2"/>
          <p:commandButton inline="true" icon="plus" value=" " action="#{form.getNextAgenda}" update=":form2"/>
        </pm:buttonGroup>
        <h3><h:outputText value="#{form.form2Titre}" style="color: blue" escape="false"/></h3>
      </div>

      <p:dataList id="creneaux" type="inset" value="#{form.agendaMedecinJour.creneauxMedecinJour}" var="creneauMedecinJour">
        <p:column>
          <div align="center">
            <h2>
              <h:outputFormat value="{0,number,#00}h:{1,number,#00} - {2,number,#00}h:{3,number,#00}">
                <f:param value="#{creneauMedecinJour.creneau.hdebut}" />
                <f:param value="#{creneauMedecinJour.creneau.mdebut}" />
                <f:param value="#{creneauMedecinJour.creneau.hfin}" />
                <f:param value="#{creneauMedecinJour.creneau.mfin}" />
              </h:outputFormat>
              <ui:fragment rendered="#{creneauMedecinJour.rv!=null}">
                <br/>
                <h:outputText value="#{creneauMedecinJour.rv.client.titre} #{creneauMedecinJour.rv.client.prenom} #{creneauMedecinJour.rv.client.nom}" style="color: blue"/>
              </ui:fragment>
            </h2>
          </div>
          <div align="center">
            <ui:fragment rendered="#{creneauMedecinJour.rv!=null}">
              <p:commandButton inline="true" action="#{form.action}" value="#{msg['form2.supprimer']}" icon="minus" update=":form2, :vue3, :vueErreurs">
                <f:setPropertyActionListener value="#{creneauMedecinJour.creneau.id}" target="#{form.idCreneauChoisi}"/>
              </p:commandButton>
            </ui:fragment>
            <ui:fragment rendered="#{creneauMedecinJour.rv==null}">
              <p:commandButton inline="true" action="#{form.action}" value="#{msg['form2.reserver']}" icon="plus" update=":form2, :vue3, :vueErreurs">
                <f:setPropertyActionListener value="#{creneauMedecinJour.creneau.id}" target="#{form.idCreneauChoisi}"/>
              </p:commandButton>
            </ui:fragment>
          </div>
        </p:column>
      </p:dataList>
    </h:form>
  </pm:content>
</html>
  • 11. satır: [1] başlığını oluşturur,
  • 15-20. satırlar: [2] düğme grubunu oluşturur,
  • 21. satır: [3] başlığını oluşturur. escape özniteliğinin değerine dikkat edilmelidir. Bu, form2Titre içine yerleştirdiğimiz XHTML kodunu yorumlamamızı sağlar,
  • 24. satır: zaman aralıklarının görüntülenmesi bir dataList kullanılarak yapılır,
  • 28-33. satırlar: [4] zaman aralığının başlığını oluşturur,
  • 34-37. satırlar: zaman aralığında bir randevu varsa bir parçayı görüntüler,
  • 36. satır: randevuyu alan müşterinin kimliğini gösterir,
  • satır 41-45: randevu varsa [Supprimer] düğmesini görüntüler,
  • satır 46-50: randevu yoksa [Réserver] düğmesini görüntüler.

Bu görünüm esas olarak aşağıdaki modelden beslenir:


private AgendaMedecinJour agendaMedecinJour;

bu da 24. satırdaki dataList'e veri sağlar. Bu alan, vue1 görünümünden vue2 görünümüne geçildiğinde getAgenda yöntemi ile oluşturulmuştur.

9.10. Randevuyu silme

Bir randevunun silinmesi aşağıdaki sıraya göre gerçekleşir:

Bu işlemle ilgili görünüm şöyledir:


<ui:fragment rendered="#{creneauMedecinJour.rv!=null}">
              <p:commandButton inline="true" action="#{form.action}" value="#{msg['form2.supprimer']}" icon="minus" update=":form2, :vueErreurs">
                <f:setPropertyActionListener value="#{creneauMedecinJour.creneau.id}" target="#{form.idCreneauChoisi}"/>
              </p:commandButton>
            </ui:fragment>
  • 2. satır: [Supprimer] düğmesi, [Form].action yöntemiyle ilişkilendirilmiştir (action özniteliği),
  • 3. satır: Bulunduğumuz zaman aralığının kimliği, [Form].idCreneauChoisi modeline gönderilecektir,
  • 2. satır: AJAX çağrısı, form2 alanlarını (görünüm2 formu) ve vueErreurs görünümünü güncelleyecektir. Aslında iki durum söz konusudur: her şey yolunda giderse vue2 yeniden görüntülenecek, aksi takdirde vueErreurs görünümü görüntülenecektir.

[action] yöntemi şu şekildedir:


  // RV üzerinde işlem
  public String action() {
    // takvimde uygun zaman aralığı aranıyor
    int i = 0;
    Boolean trouvé = false;
    while (!trouvé && i < agendaMedecinJour.getCreneauxMedecinJour().length) {
      if (agendaMedecinJour.getCreneauxMedecinJour()[i].getCreneau().getId() == idCreneauChoisi) {
        trouvé = true;
      } else {
        i++;
      }
    }
    // bulundu mu?
    if (!trouvé) {
      // Bu garip - form2'yi yeniden görüntülüyoruz
      setForms(false, true, false, false);
      return "pm:vue2";
    } else {
      creneauChoisi = agendaMedecinJour.getCreneauxMedecinJour()[i];
    }
    // bulundu
    // istenen eyleme göre
    if (creneauChoisi.getRv() == null) {
      return reserver();
    } else {
      return supprimer();
    }
  }

  // rezervasyon
  public String reserver() {
 ...
  }

  public String supprimer() {
    try {
      // Randevu silme
      metier.supprimerRv(creneauChoisi.getRv());
      // takvim güncelleniyor
      agendaMedecinJour = metier.getAgendaMedecinJour(medecin, jour);
      // form2 görüntüleniyor
      setForms(false, true, false, false);
      return "pm:vue2";
    } catch (Throwable th) {
      // hatalar sayfası
      prepareVueErreur(th);
      return "pm:vueErreurs";
    }
}
  • 3-12. satırlar: ID'si alınan zaman aralığı aranır (7. satır),
  • bulunamazsa (ki bu normal değildir), vue2 yeniden görüntülenir (satır 16-17),
  • 19. satır: Bulunursa, ilgili [CreneauMedecinJour] nesnesi kaydedilir. Silinecek randevuya erişimimizi sağlayan nesne budur,
  • 26. satır: randevuyu siliyoruz,
  • satır 35-48: silme işlemi başarılı olursa (satır 42-43) "vue2" görünümünü, bir sorun olursa (satır 46-47) "vueErreurs" görünümünü döndürür.

9.11. Randevu Alma

Randevu alma işlemi aşağıdaki adımlardan oluşur:

vue2 görünümünden vue3 görünümüne geçilir. Bu eylemle ilgili kod şöyledir:


<ui:fragment rendered="#{creneauMedecinJour.rv==null}">
              <p:commandButton inline="true" action="#{form.action}" value="#{msg['form2.reserver']}" icon="plus" update=":vue3, :vueErreurs">
                <f:setPropertyActionListener value="#{creneauMedecinJour.creneau.id}" target="#{form.idCreneauChoisi}"/>
              </p:commandButton>
            </ui:fragment>
  • 2. satır: [Réserver] düğmesi, [Form].action yöntemiyle ilişkilendirilmiştir (action özniteliği); dolayısıyla bu, [Supprimer] düğmesi için olanla aynıdır. AJAX çağrısı, çağrının işlenmesi sırasında hata olup olmadığına bağlı olarak vue3 ve vueErreurs görünümlerini günceller.
  • 3. satır: [Supprimer] düğmesinde olduğu gibi, zaman aralığının kimliği modele aktarılır.

Bu eylemi işleyen şablon şöyledir:


// RV üzerinde işlem
  public String action() {
    ...
    // istenen eyleme göre
    if (creneauChoisi.getRv() == null) {
      return reserver();
    } else {
      return supprimer();
    }
  }

  // rezervasyon
  public String reserver() {
    try {
      // form 3 başlığı
      form3Titre = Messages.getMessage(null, "form3.titre", new Object[]{medecin.getTitre(), medecin.getPrenom(), medecin.getNom(), new SimpleDateFormat("dd MMM yyyy").format(jour),
                creneauChoisi.getCreneau().getHdebut(), creneauChoisi.getCreneau().getMdebut(), creneauChoisi.getCreneau().getHfin(), creneauChoisi.getCreneau().getMfin()}).getSummary();
      // 3 numaralı form görüntüleniyor
      setForms(false, false, true, false);
      return "pm:vue3";
    } catch (Throwable th) {
      // hata görünümü
      prepareVueErreur(th);
      return "pm:vueErreurs";
    }
  }
  • 2-10. satırlar: action yöntemi, rezervasyon konusu olan [CreneauMedecinJour] nesnesinin creneauChoisi referansını belirleyecek ve ardından reserver yöntemini çağıracaktır,
  • 16. satır: Uluslararasılaştırılmış bir mesaj oluşturulur. Mesaj şöyledir:
form3.titre={0} {1} {2}<br/>{3}<br/>{4,number,#00}h:{5,sayı,#00}-{6,sayı,#00}h:{7,sayı,#00}
form3.titre_detail={0} {1} {2}<br/>{3}<br/>{4,number,#00}h:{5,sayı,#00}-{6,sayı,#00}h:{7,sayı,#00}

Bu, vue3 görünümünün başlığı olacaktır. vue2 görünümünde olduğu gibi, bu başlıkta da XML yer almaktadır. Ayrıca, zaman aralığının saatlerini görüntülemek için biçimlendirilmiş parametreler de içermektedir,

  • 19-20. satırlar: vue3 görünümü görüntülenir,
  • 23-24. satırlar: Herhangi bir sorunla karşılaşılırsa vueErreurs görünümü görüntülenir.

9.12. Randevunun Onaylanması

Bir randevunun onaylanması aşağıdaki adımları içerir:

[1]'te vue3 görünümü ve [2]'te bir randevu eklendikten sonra vue2 görünümü.

vue3'ün [vue3.xhtml] kodu şö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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:pm="http://primefaces.org/mobile"
      xmlns:ui="http://java.sun.com/jsf/facelets">

  <!-- Görünüm 3 -->
  <pm:header title="#{msg['page.titre']}" swatch="b"/>
  <pm:content>
    <h:form id="form3">
      <p:commandButton inline="true" value=" " icon="back" action="#{form.showVue2}" update=":vue2"/>
      <div align="center">
        <h3><h:outputText value="#{form.form3Titre}" style="color: blue" escape="false"/></h3>
      </div>
      <pm:field>
        <h:outputLabel value="#{msg['form3.client']}" for="choixClient"/>
        <h:selectOneMenu id="choixClient" value="#{form.idClient}">
          <f:selectItems value="#{form.clients}" var="client" itemLabel="#{client.titre} #{client.prenom} #{client.nom}" itemValue="#{client.id}"/>
        </h:selectOneMenu>
      </pm:field>
      <div align="center">
        <p:commandButton inline="true" value="#{msg['form3.valider']}" action="#{form.validerRv}" update=":vue2, :vueErreurs" icon="check"/>
      </div>
    </h:form>
  </pm:content>
</html>
  • 16. satır: [3] görünümünün başlığını oluşturur. Başlıktaki XHTML karakterlerinin yorumlanmasını sağlayan escape özniteliğinin değerine dikkat edilmelidir,
  • 18-23. satırlar: [4] müşteri açılır menüsünü oluşturur,
  • 25. satır: [Valider] [5] düğmesini oluşturur. [Form].validerRv yöntemi bu düğmeyle ilişkilendirilmiştir:

// Rv onayı
  public String validerRv() {
    try {
      // seçilen zaman aralığının bir örneğini alıyoruz
      Creneau creneau = metier.getCreneauById(idCreneauChoisi);
      // Rv ekleniyor
      metier.ajouterRv(jour, creneau, hClients.get(idClient));
      // takvimi güncellenir
      agendaMedecinJour = metier.getAgendaMedecinJour(medecin, jour);
      // form2 görüntülenir
      setForms(false, true, false, false);
      return "pm:vue2";
    } catch (Throwable th) {
      // hata görünümü
      prepareVueErreur(th);
      return "pm:vueErreurs";
    }
  }

Bu kod, 01 sürümünde daha önce karşımıza çıkmıştı. Her şey yolunda gittiyse, sadece görünümlerin görüntülenmesine dikkat edelim:

  • her şey yolunda giderse vue2 görünümü (satır 11-12),
  • aksi takdirde vueErreurs görünümü (satır 15-16).

9.13. Randevu İptali

Bu, aşağıdaki sekansa karşılık gelir:

[vue3.xhtml] görünümündeki [1] düğmesi şöyledir:


      <p:commandButton inline="true" value=" " icon="back" action="#{form.showVue2}" update=":vue2"/>

Böylece [Form].showVue2 yöntemi çağrılır. Bu yöntem sadece vue2'yi görüntüler:


  public String showVue2() {
    // 2. görünüm
    setForms(false, true, false, false);
    return "pm:vue2?reverse=true";
}

9.14. Takvimde gezinme

vue2'de, takvimde gezinmeyi sağlayan düğmeler bulunur:

Önceki gün:

Sonraki gün:

Bugün:

Yukarıdaki ekran görüntülerinde gösterilmese de, takvim güncellenir ve seçilen yeni günün randevularını gösterir.

[vue2.xhtml] dosyasında ilgili üç düğmenin etiketleri şunlardır:


<pm:buttonGroup orientation="horizontal">
          <p:commandButton inline="true" icon="back" value=" " action="#{form.showVue1}" update=":vue1"/>
          <p:commandButton inline="true" icon="minus" value=" " action="#{form.getPreviousAgenda}" update=":form2"/>
          <p:commandButton inline="true" icon="home" value=" " action="#{form.getTodayAgenda}" update=":form2"/>
          <p:commandButton inline="true" icon="plus" value=" " action="#{form.getNextAgenda}" update=":form2"/>
        </pm:buttonGroup>

[Form].getPreviousAgenda, [Form].getNextAgenda, [Form].today yöntemleri örnek 03'te incelenmiştir.

9.15. Görüntüleme dilinin değiştirilmesi

Dil değişikliği, ana sayfadaki bir düğmeyle yönetilir:

Düğmenin kodu şöyledir:


      <p:button icon="gear" value=" "  href="#config" />

Bu nedenle, [2] yapılandırma görünümüne geçiyor. [config.xhtml] görünümü ş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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:pm="http://primefaces.org/mobile"
      xmlns:ui="http://java.sun.com/jsf/facelets">

  <!-- Görünüm 1 -->
  <pm:header title="#{msg['page.titre']}" swatch="b">
    <f:facet name="left">
      <p:button icon="back" value=" " href="#vue1?reverse=true" />
    </f:facet>
  </pm:header>
  <pm:content>
    <h:form id="frmConfig">
      <div align="center">
        <h3><h:outputText value="#{msg['config.titre']}" style="color: blue"/></h3>
      </div>
      <pm:field>
        <h:outputLabel value="#{msg['config.langue']}" for="langue"/>
        <h:selectOneRadio id="langue" value="#{form.locale}">
          <f:selectItem itemLabel="#{msg['config.langue.francais']}" itemValue="fr"/>
          <f:selectItem itemLabel="#{msg['config.langue.anglais']}" itemValue="en" />
        </h:selectOneRadio>
      </pm:field>
      <p:commandButton value="#{msg['config.valider']}" action="#{form.configurer}" update=":vue1"/>
    </h:form>
  </pm:content>
</html>
  • 11. satır: [3]'i görüntüler,
  • 13. satır: [4] düğmesini görüntüler. Bu düğme, vue1 görünümüne geri dönmeyi sağlar,
  • 17. satır: görünümün formunu,
  • 19. satır: [5] görünüm başlığını görüntüler,
  • 21-27. satırlar: radyo düğmelerini görüntüler. Seçilen radyo düğmesinin değeri (itemValue), [Form].locale şablonuna gönderilir (23. satırdaki value özniteliği),
  • 28. satır: [Valider] düğmesini görüntüler. AJAX çağrısı, vue1 görünümünü günceller (update özniteliği). Çağrılan yöntem [Form].configurer'dir:

public String configurer(){
    // yapılandırma sonrasında - görünüm1 yeniden görüntülenir
    redirect();
    return null;
  }
  
  private void redirect() {
    // istemci servlete yönlendirilir
    ExternalContext ctx = FacesContext.getCurrentInstance().getExternalContext();
    try {
      ctx.redirect(ctx.getRequestContextPath());
    } catch (IOException ex) {
      Logger.getLogger(Form.class.getName()).log(Level.SEVERE, null, ex);
    }
  }

configurer yöntemi (1. satır), mobil tarayıcıyı uygulamanın URL sayfasına yönlendirmekle yetinir. Dolayısıyla [index.xhtml] sayfası yüklenecektir:


<f:view xmlns="http://www.w3.org/1999/xhtml"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:p="http://primefaces.org/ui"
        xmlns:pm="http://primefaces.org/mobile"
        contentType="text/html"
        locale="#{form.locale}">

  <pm:page title="#{msg['page.titre']}">
    <pm:view id="vue1">
      ...
    </pm:view>
    ...
  </pm:page>    
</f:view>
  • 8. satır: Görünüm, az önce değiştirilen dili kullanacak (locale özniteliği) ve vue1'i (11. satır) görüntüleyecektir.

9.16. Conclusion

Az önce oluşturduğumuz uygulamanın mimarisini hatırlayalım:

Mobil arayüze geçiş, sayfaların yeniden yazılmasını gerektirdi XHTML. Buna karşın modelde pek bir değişiklik olmadı. Alt katmanlar [métier], [DAO], [JPA] ise hiç değişmedi.

9.17. Eclipse Testleri

Örnek uygulamanın önceki sürümlerinde yaptığımız gibi, bu 05 sürümünü Eclipse ile nasıl test edeceğimizi göstereceğiz. Öncelikle, örnek 05'in Maven projelerini ([1]) Eclipse'e içe aktarıyoruz:

  • [mv-rdvmedecins-ejb-dao-jpa]: [DAO] ve [JPA] katmanları,
  • [mv-rdvmedecins-ejb-metier]: [métier] katmanı,
  • [mv-rdvmedecins-pfmobile]: Primefaces Mobile tarafından uygulanan [web] katmanı,
  • [mv-rdvmedecins-pfmobile-app]: [mv-rdvmedecins-pfmobile-app-ear] kurumsal projesinin üst projesi. Üst proje içe aktarıldığında, alt proje otomatik olarak içe aktarılır,
  • [2]'te, [mv-rdvmedecins-pfmobile-app-ear] kurumsal projesi yürütülür,
  • [3]'te Glassfish sunucusu seçildi,
  • [4]'te, [Servers] sekmesinde uygulama dağıtılmıştır. Uygulama kendiliğinden çalışmaz. URL [http://localhost:8080/mv-rdvmedecins-pfmobile/] adresini bir tarayıcıda veya [5] mobil simülatöründe açmanız gerekir: