Skip to content

9. Applicazione di esempio 05: rdvmedecins-pfm-ejb

Ricordiamo la struttura dell’applicazione di esempio 01 JSF / EJB sviluppata per il server Glassfish:

Non apportiamo alcuna modifica a questa architettura, ad eccezione del livello web, che in questo caso verrà realizzato utilizzando JSF, Primefaces e Primefaces Mobile. Il browser di destinazione sarà quello di un dispositivo mobile.

Abbiamo sviluppato due applicazioni con Glassfish:

  • l'applicazione 01 utilizzava JSF / EJB e presentava un'interfaccia piuttosto essenziale,
  • l'applicazione 03 utilizzava PF / EJB e presentava un'interfaccia ricca.

A causa del numero limitato di componenti disponibili in PrimeFaces Mobile, torneremo all’interfaccia essenziale dell’applicazione 01. Inoltre, le viste dovranno potersi adattare alle dimensioni ridotte degli schermi dei dispositivi mobili.

9.1. Le viste

Per chiarire il concetto, riportiamo alcune schermate dell’applicazione eseguita nel simulatore di un iPhone 4:

  • in [1], la pagina iniziale. Si noti che questa volta è stato necessario specificare il nome della macchina (è possibile anche inserire il suo indirizzo IP), poiché con la macchina localhost non veniva visualizzato nulla,
  • in [2], l'elenco dei medici,
  • con [3], la data desiderata per l’appuntamento,
  • in [4], il pulsante per visualizzare l’agenda del giorno,
  • in [5], la nuova schermata mostra le fasce orarie disponibili del medico,
  • in [6], una serie di pulsanti per navigare nel calendario,
  • in [7], un messaggio che ricorda il medico e il giorno,
  • in [8], una fascia oraria da prenotare. Procediamo,
  • in [9], la schermata di selezione del cliente,
  • in [10], un messaggio che ricorda il medico, il giorno e la fascia oraria relativi all’appuntamento,
  • in [11], il menu a tendina dei clienti,
  • in [12], il pulsante di conferma,
  • in [13], la conferma ci riporta al calendario,
  • in [14], la fascia oraria occupata è ora prenotata. Ora elimineremo la prenotazione,
  • in [15], rimaniamo nella stessa schermata,
  • ma in [16], l’appuntamento è stato cancellato,

Nella pagina iniziale è possibile cambiare lingua: [17], [18], [19]:

Infine, è stata prevista una schermata degli errori:

9.2. Il progetto NetBeans

Il progetto NetBeans è il seguente:

  
  • [mv-rdvmedecins-ejb-dao-jpa]: progetto EJB dei livelli [DAO] e [JPA] dell'esempio 01,
  • [mv-rdvmedecins-ejb-metier]: progetto EJB del livello [métier] dell'esempio 01,
  • [mv-rdvmedecins-pfmobile]: progetto del livello [web] / Primefaces mobile – nuovo,
  • [mv-rdvmedecins-pfmobile-app-ear]: progetto aziendale per la distribuzione dell'applicazione sul server Glassfish – nuovo.

9.3. Il progetto aziendale

Il progetto aziendale serve esclusivamente per la distribuzione dei tre moduli [mv-rdvmedecins-ejb-dao-jpa], [mv-rdvmedecins-ejb-metier], [mv-rdvmedecins-pfmobile] sul server Glassfish. Il progetto NetBeans è il seguente:

Il progetto esiste solo per queste tre dipendenze [1] definite nel file [pom.xml] come segue:


<?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>
  • righe 6-9: l'artefatto Maven del progetto aziendale,
  • righe 14-33: le tre dipendenze del progetto. Si noti bene il tipo di queste ultime (righe 19, 25, 31).

Per eseguire l’applicazione web, sarà necessario eseguire questo progetto aziendale.

9.4. Il progetto web Primefaces mobile

Il progetto web Primefaces mobile è il seguente:

  • in [1], le pagine del progetto. La pagina [index.xhtml] è l'unica pagina del progetto. Comprende cinque viste: [vue1.xhtml], [vue2.xhtml], [vue3.xhtml], [vueErreurs.xhtml] e [config.xhtml],
  • in [2], i bean Java. Il bean [Application] con ambito application, il bean [Form] con ambito session. La classe [Erreur] incapsula un errore,
  • in [3], i file di messaggi per l’internazionalizzazione,
  • in [4], le dipendenze. Il progetto web dipende dal progetto EJB del livello [DAO], il progetto EJB del livello [métier] e Primefaces mobile per il livello [web].

9.5. La configurazione del progetto

La configurazione del progetto è quella dei progetti Primefaces o JSF che abbiamo esaminato. Elenchiamo i file di configurazione senza ripeterne la spiegazione.

 

[web.xml]: configura l’applicazione web.


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

Si noti, alla riga 26, che la pagina [index.xhtml] è la pagina iniziale dell’applicazione.

[faces-config.xml]: configura l'applicazione JSF


<?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]: vuota ma necessaria per l'annotazione @Named


<?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]: il file dei messaggi in francese


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

# vista1
vue1.header=Les M\u00e9decins Associ\u00e9s - R\u00e9servations

# modulo 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

# modulo 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

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

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

# configurazione
config.retour=Retour
config.titre=Configuration
config.langue=Langue
config.langue.francais=Fran\u00e7ais
config.langue.anglais=Anglais
config.valider=Valider

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

[messages_en.properties]: il file dei messaggi in inglese


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

# vista1
vue1.header=The Associated Doctors - Reservations

# modulo 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

# modulo 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

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

# errore
erreur.titre=Some error happened

# configurazione
config.retour=Back
config.titre=Configuration
config.langue=Language
config.langue.francais=French
config.langue.anglais=English
config.valider=Validate

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

9.6. La pagina [index.xhtml]

Il progetto visualizza sempre la stessa pagina, ovvero la pagina [index.xhtml]:


<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>
  • riga 8: la pagina è internazionalizzata (attributo locale),
  • riga 10: la pagina contiene cinque viste: vista1 alla riga 11, vista2 alla riga 19, vista3 alla riga 24, vueErreurs alla riga 29, config alla riga 34. In un dato momento è visibile solo una di queste viste. All'avvio dell'applicazione, viene visualizzata la vista vista1. Qui si è presentata la seguente difficoltà: se l’inizializzazione dell’applicazione è avvenuta correttamente, deve essere visualizzata [vue1.xhtml], altrimenti deve essere visualizzata [vueErreurs.xhtml]. Abbiamo risolto il problema facendo in modo che il contenuto della vista vue1 fosse gestito dal modello che assegna valori ai booleani [Form].form1rendered (riga 12) e [Form].erreurInit (riga 15) per definire il contenuto di vista1 (riga 11),

In un simulatore, la vista [vue1.xhtml] ha il rendering [1], la vista [vue2.xhtml] il rendering [2], la vista [vue3.xhtml] ha il rendering [3]:

la vista [vueErreurs.xhtml], il rendering [4]; la vista [config.xhtml], il rendering [5]:

9.7. I bean del progetto

La classe del pacchetto [utils] è già stata presentata: la classe [Messages] è una classe che facilita l'internazionalizzazione dei messaggi di un'applicazione. È stata analizzata nel paragrafo 2.8.5.7.

9.7.1. Il bean Application

Il bean [Application.java] è un bean con ambito application. Ricordiamo che questo tipo di bean serve a memorizzare dati in sola lettura e disponibili per tutti gli utenti dell’applicazione. Questo bean è il seguente:


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 {

  // livello business
  @EJB
  private IMetierLocal metier;

  public Application() {
  }
  
  // getters

  public IMetierLocal getMetier() {
    return metier;
  }
  
}
  • riga 8: si assegna al bean il nome "application",
  • riga 9: ha ambito "application",
  • righe 13-14: un riferimento all'interfaccia locale del livello [métier] gli verrà iniettato dal contenitore EJB del server dell'applicazione. Ricordiamo l'architettura dell'applicazione:

L'applicazione PFM e le applicazioni EJB e [Metier] verranno eseguite nella stessa JVM (Java Virtual Machine). Pertanto, il livello [PFM] utilizzerà l'interfaccia locale di EJB. Tutto qui. Il bean [Application] non contiene nient’altro. Per accedere al livello [métier], gli altri bean lo richiederanno a questo bean.

9.7.2. Il bean [Erreur]

La classe [Erreur] è la seguente:


package beans;

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

  // costruttore
  public Erreur(String classe, String message){
    this.setClasse(classe);
    this.message=message;
  }
  
  // getter e setter
...  
}
  • riga 9, il nome di una classe di eccezione se è stata generata un'eccezione,
  • riga 10: un messaggio di errore.

9.7.3. Il bean [Form]

Il suo codice è il seguente:


package beans;

...

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

  public Form() {
  }
  // bean dell'applicazione
  @Inject
  private Application application;
  private IMetierLocal metier;
  // cache della sessione
  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>();
  // modello
  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");
    // inizialmente nessun errore
    erreurInit = false;
    // formattazione delle date
    dateFormatter = new SimpleDateFormat(Messages.getMessage(null, "format.date", null).getSummary());
    dateFormatter.setLenient(false);
    // il giorno corrente
    strJour = dateFormatter.format(jour);
    // si recupera il livello applicativo
    metier = application.getMetier();
    // si memorizzano nella cache medici e clienti
    try {
      medecins = metier.getAllMedecins();
      clients = metier.getAllClients();
    } catch (Throwable th) {
      // si registra l'errore
      erreurInit = true;
      prepareVueErreur(th);
      return;
    }
    // verifica degli elenchi
    if (medecins.size() == 0) {
      // si registra l'errore
      erreurInit = true;
      erreurs = new ArrayList<Erreur>();
      erreurs.add(new Erreur("", "La liste des médecins est vide"));
    }
    if (clients.size() == 0) {
      // si annota l'errore
      erreurInit = true;
      erreurs = new ArrayList<Erreur>();
      erreurs.add(new Erreur("", "La liste des clients est vide"));
    }
    // errore?
    if (erreurInit) {
      // viene visualizzata la schermata degli errori
      setForms(false, false, false, true);
      return;
    }

    // i dizionari
    for (Medecin m : medecins) {
      hMedecins.put(m.getId(), m);
    }
    for (Client c : clients) {
      hClients.put(c.getId(), c);
    }
    // la vista 1
    setForms(true, false, false, false);
  }

  // visualizzazione della vista
  private void setForms(Boolean form1Rendered, Boolean form2Rendered, Boolean form3Rendered, Boolean erreurRendered) {
    this.form1Rendered = form1Rendered;
    this.form2Rendered = form2Rendered;
    this.form3Rendered = form3Rendered;
    this.erreurRendered = erreurRendered;
  }

  // preparazione vueErreur
  private void prepareVueErreur(Throwable th) {
    // si crea l'elenco degli errori
    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()));
    }
// viene visualizzata la vista degli errori
    setForms(false, false, false, true);
  }

  // getter e setter
  ..
}
  • righe 5-7: la classe [Form] è un bean denominato "form" e con ambito di sessione. Si ricorda che in questo caso la classe deve essere serializzabile,
  • righe 12-13: il bean form ha un riferimento al bean application. Questo verrà iniettato dal contenitore di servlet in cui viene eseguita l’applicazione (presenza dell’annotazione @Inject).
  • righe 24-27: controllano la visualizzazione delle viste vue1 (riga 24), vue2 (riga 25), vue3 (riga 26), vueErreurs (riga 27),
  • righe 43-44: il metodo init viene eseguito subito dopo l’istanziazione della classe (presenza dell’annotazione @PostConstruct),
  • righe 49-50: gestiscono il formato delle date. PrimeFaces Mobile non offre un calendario. Inoltre, i validatori JSF non sono utilizzabili in una pagina PFM. Dovremo quindi gestire manualmente l'inserimento della data dell'agenda,
  • riga 49: imposta il formato delle date. Questo formato è definito nei file internazionalizzati:

format.date=dd/MM/yyyy
format.date_detail=dd/MM/yyyy
  • riga 50: si specifica che non si deve utilizzare il formato "laxiste" per le date. Se non lo si fa, un valore inserito come 32/12/2011, che è errato, viene considerato come la data valida 01/01/2012,
  • riga 54: si recupera un riferimento al livello [métier] dal bean [Application],
  • righe 57-58: si richiede al livello [métier] l’elenco dei medici e dei clienti,
  • righe 66-91: se tutto è andato a buon fine, vengono creati i dizionari dei medici e dei clienti. Sono indicizzati in base al loro numero. Successivamente, verrà visualizzata la vista [vue1.xhtml] (riga 93),
  • riga 59: in caso di errore, viene generato il modello della pagina [vueErreurs.xhtml]. Questo modello corrisponde all’elenco degli errori della riga 35,
  • righe 105-115: il metodo [prepareVueErreur] genera l'elenco degli errori da visualizzare. La pagina [index.xhtml] visualizza quindi la vista [vueErreurs.xhtml] (riga 114).

La pagina [erreur.xhtml] è la seguente:


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

  <!-- Pagina degli errori -->
  <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>

Utilizza un tag <p:dataList> (righe 21-23) per visualizzare l'elenco degli errori. Il pulsante alla riga 13 consente di tornare alla vista vue1.

  • il pulsante [1] è generato dalla riga 13. L'attributo icon definisce l'icona del pulsante. Il pulsante riporta alla vista vue1 (attributo href),
  • l'intestazione [2] è generata dalle righe 11-15,
  • il titolo [3] è generato dalla riga 18,
  • il testo [4] è generato dal modello #{erreur.classe} della riga 22,
  • il testo [5] è generato dal modello #{erreur.message} della riga 22.

Definiremo ora le diverse fasi del ciclo di vita dell'applicazione. Per ogni azione dell'utente, esamineremo le viste interessate e i gestori degli eventi che vi si verificano.

9.8. Visualizzazione della pagina iniziale

Se tutto va bene, la prima vista visualizzata è [vue1.xhtml]. Il risultato è la seguente vista:

Il codice della vista [vue1.xhtml] è il seguente:


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

  <!-- Vista 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>
  • righe 11-15: generano l'intestazione [1],
  • riga 13: genera il pulsante [2]. Cliccandoci sopra si visualizza la vista di configurazione (attributo href),
  • riga 19: genera il titolo [3],
  • righe 21-26: generano il menu a tendina dei medici [4],
  • righe 27-34: generano il campo di inserimento della data [5]. L'inserimento avviene tramite un tag <p:inputText> senza validatore. La convalida della data avverrà lato server. Se la data non è corretta, il server genererà un messaggio di errore visualizzato dalle righe 30-34,
  • riga 35: il pulsante che convalida il modulo. Aggiorna tre aree: form1 (il modulo in vista1), vista2 e vueErreurs. Infatti, se la data non è valida, è form1 che deve essere aggiornato. Se la data è corretta, è vista2 che deve essere aggiornata. Infine, se si verifica un'eccezione (ad esempio, una connessione al database interrotta), deve essere visualizzata vueErreurs. Si potrebbe essere tentati di sostituire form1 con vue1 (aggiornando l'intera vista). In questo caso, l'applicazione presenta un errore.

Questa vista si basa sul seguente modello:


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

  public Form() {
  }

  // Bean dell’applicazione
  @Inject
  private Application application;
  private IMetierLocal metier;
  // cache della sessione
  private List<Medecin> medecins;
  private List<Client> clients;
  // modello
  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;
  
    // elenco dei medici
  public List<Medecin> getMedecins() {
    return medecins;
  }

  // agenda
  public String getAgenda() {
...
    }
  }
  • il campo della riga 16 alimenta in lettura e scrittura il valore dell’elenco della riga 23 della pagina. Alla visualizzazione iniziale della pagina, esso imposta il valore selezionato nel menu a discesa,
  • il metodo delle righe 27-29 genera gli elementi del menu a tendina dei medici (riga 24 della vista). Ogni opzione generata avrà come etichetta (itemLabel) il titolo, il cognome e il nome del medico e come valore (itemValue) l’ID del medico,
  • il campo della riga 18 alimenta in lettura/scrittura il campo di immissione della riga 29 della pagina,
  • righe 32-34: il metodo getAgenda gestisce il clic sul pulsante [Agenda] della riga 35 della pagina. Il suo codice è il seguente:

// agenda
  public String getAgenda() {
    try {
      // si verifica il giorno
      jour = dateFormatter.parse(strJour);
      // nessun errore
      erreurDate=false;
      msgErreurDate = "";
      // si crea l'agenda
      return getAgenda(jour);
    } catch (ParseException ex) {
      // messaggio di errore
      erreurDate=true;
      msgErreurDate = Messages.getMessage(null, "form1.date.invalide", null).getSummary();
      // vista1      
      setForms(true, false, false, false);
      return "pm:vue1";
    }
  }
  • il metodo inizia verificando la validità della data inserita dall’utente,
  • riga 5: la data è parsée secondo il formato di data inizializzato dal metodo init all'istanziazione del modello,
  • riga 11: se si verifica un'eccezione, viene generato un errore (riga 13), viene creato un messaggio di errore internazionalizzato (riga 14), viene preparata la vista vue1 (riga 16) e viene visualizzata la vista vue1 (riga 17),
  • riga 10: se la data è corretta, si esegue il seguente metodo:

  // agenda
  public String getAgenda(Date jour) {
    // nessuna fascia oraria selezionata al momento
    creneauChoisi = null;
    try {
      // si recupera il medico selezionato
      medecin = hMedecins.get(idMedecin);
      // titolo del modulo 2
      form2Titre = Messages.getMessage(null, "form2.titre", new Object[]{medecin.getTitre(), medecin.getPrenom(), medecin.getNom(), new SimpleDateFormat("dd MMM yyyy").format(jour)}).getSummary();
      // agenda del medico per un determinato giorno
      agendaMedecinJour = metier.getAgendaMedecinJour(medecin, jour);
      // viene visualizzata la vista 2
      setForms(false, true, false, false);
      return "pm:vue2";
    } catch (Throwable th) {
      System.out.println(th);
      // vista degli errori
      prepareVueErreur(th);
      return "pm:vueErreurs";
    }
}

Si tratta di un codice già incontrato in diverse occasioni. Riga 9: viene generato un messaggio internazionalizzato per la vista vue2:


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

Si noti che nel messaggio abbiamo inserito XHTML. Verrà visualizzato nel modo seguente:

9.9. Visualizzare l'agenda di un medico

L'agenda del medico viene visualizzata dalla vista [vue2.xhtml]:

Il codice della vista [vue2.xhtml] è il seguente:


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

  <!-- Vista 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>
  • riga 11: genera l'intestazione [1],
  • righe 15-20: genera il gruppo di pulsanti [2],
  • riga 21: genera il titolo [3]. Si noti il valore dell’attributo escape. È questo che permette di interpretare il codice XHTML che abbiamo inserito in form2Titre,
  • riga 24: la visualizzazione delle fasce orarie avviene tramite un dataList,
  • righe 28-33: generano il titolo della fascia oraria [4],
  • righe 34-37: visualizzano un frammento se è presente un appuntamento nella fascia oraria,
  • riga 36: visualizza l’identità del cliente che ha fissato l’appuntamento,
  • righe 41-45: visualizzano il pulsante [Supprimer] se è presente un appuntamento,
  • righe 46-50: visualizzano il pulsante [Réserver] se non c'è alcun appuntamento.

Questa vista è alimentata principalmente dal seguente modello:


private AgendaMedecinJour agendaMedecinJour;

che alimenta il campo dataList della riga 24. Questo campo è stato creato dal metodo getAgenda, quando si è passati dalla vista vue1 alla vista vue2.

9.10. Eliminazione di un appuntamento

L’eliminazione di un appuntamento corrisponde alla seguente sequenza:

La vista interessata da questa azione è la seguente:


<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>
  • riga 2: il pulsante [Supprimer] è associato al metodo [Form].action (attributo action),
  • riga 3: l’ID della fascia oraria in cui ci si trova verrà inviato al modello [Form].idCreneauChoisi,
  • riga 2: la chiamata AJAX aggiornerà i campi di form2 (modulo della vista 2) e la vista vueErreurs. Si hanno infatti due casi: se tutto va bene, verrà visualizzata nuovamente la vista vue2, altrimenti verrà visualizzata la vista vueErreurs.

Il metodo [action] è il seguente:


  // azione su RV
  public String action() {
    // si cerca la fascia oraria nell'agenda
    int i = 0;
    Boolean trouvé = false;
    while (!trouvé && i < agendaMedecinJour.getCreneauxMedecinJour().length) {
      if (agendaMedecinJour.getCreneauxMedecinJour()[i].getCreneau().getId() == idCreneauChoisi) {
        trouvé = true;
      } else {
        i++;
      }
    }
    // È stato trovato?
    if (!trouvé) {
      // È strano: si visualizza nuovamente form2
      setForms(false, true, false, false);
      return "pm:vue2";
    } else {
      creneauChoisi = agendaMedecinJour.getCreneauxMedecinJour()[i];
    }
    // Trovato
    // in base all'azione desiderata
    if (creneauChoisi.getRv() == null) {
      return reserver();
    } else {
      return supprimer();
    }
  }

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

  public String supprimer() {
    try {
      // cancellazione di un appuntamento
      metier.supprimerRv(creneauChoisi.getRv());
      // si aggiorna l'agenda
      agendaMedecinJour = metier.getAgendaMedecinJour(medecin, jour);
      // visualizza form2
      setForms(false, true, false, false);
      return "pm:vue2";
    } catch (Throwable th) {
      // visualizza errori
      prepareVueErreur(th);
      return "pm:vueErreurs";
    }
}
  • righe 3-12: si cerca la fascia oraria di cui si è ricevuto l'ID (riga 7),
  • se non lo si trova, il che è anomalo, si visualizza nuovamente vista2 (righe 16-17),
  • riga 19: se lo si trova, si memorizza l’oggetto [CreneauMedecinJour] corrispondente. È questo che ci dà accesso all’appuntamento da eliminare,
  • riga 26: lo si elimina,
  • righe 35-48: il metodo «eliminare» restituisce la vista vue2 se l’eliminazione è andata a buon fine (righe 42-43) oppure la vista vueErreurs se si è verificato un problema (righe 46-47).

9.11. Prenotazione di un appuntamento

La prenotazione di un appuntamento segue la sequenza seguente:

Si passa dalla vista vue2 alla vista vue3. Il codice relativo a questa azione è il seguente:


<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>
  • riga 2: il pulsante [Réserver] è associato al metodo [Form].action (attributo action), quindi lo stesso del pulsante [Supprimer]. La chiamata AJAX aggiorna le viste vue3 e vueErreurs a seconda che si verifichino o meno errori durante l'elaborazione della chiamata.
  • riga 3: come per il pulsante [Supprimer], l'ID della fascia oraria viene passato al modello.

Il modello che gestisce questa azione è il seguente:


// azione su RV
  public String action() {
    ...
    // a seconda dell'azione desiderata
    if (creneauChoisi.getRv() == null) {
      return reserver();
    } else {
      return supprimer();
    }
  }

  // prenotazione
  public String reserver() {
    try {
      // titolo del modulo 3
      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();
      // viene visualizzato il modulo 3
      setForms(false, false, true, false);
      return "pm:vue3";
    } catch (Throwable th) {
      // visualizzazione errori
      prepareVueErreur(th);
      return "pm:vueErreurs";
    }
  }
  • righe 2-10: il metodo action determinerà il riferimento creneauChoisi dell’oggetto [CreneauMedecinJour] oggetto della prenotazione, quindi richiamerà il metodo reserver,
  • riga 16: viene generato un messaggio internazionalizzato. È il seguente:
form3.titre={0} {1} {2}<br/>{3}<br/>{4,number,#00}h:{5,number,#00}-{6,number,#00}h:{7,number,#00}
form3.titre_detail={0} {1} {2}<br/>{3}<br/>{4,number,#00}h:{5,number,#00}-{6,number,#00}h:{7,number,#00}

Questo sarà il titolo della vista vue3. Come per la vista vue2, questo titolo contiene XML. Contiene inoltre parametri formattati per visualizzare gli orari della fascia oraria,

  • righe 19-20: viene visualizzata la vista vue3,
  • righe 23-24: viene visualizzata la vista vueErreurs se si sono verificati dei problemi.

9.12. Conferma di un appuntamento

La convalida di un appuntamento segue la sequenza seguente:

In [1], la vista vue3 e in [2], la vista vue2 dopo l’aggiunta di un appuntamento.

Il codice [vue3.xhtml] della vista3 è il seguente:


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

  <!-- Vista 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>
  • riga 16: genera il titolo della vista [3]. Si noti il valore dell’attributo escape che consente l’interpretazione dei caratteri XHTML nel titolo,
  • righe 18-23: generano il menu a tendina dei clienti [4],
  • riga 25: genera il pulsante [Valider] [5]. A questo pulsante è associato il metodo [Form].validerRv:

// conferma appuntamento
  public String validerRv() {
    try {
      // si recupera un'istanza della fascia oraria selezionata
      Creneau creneau = metier.getCreneauById(idCreneauChoisi);
      // si aggiunge l'appuntamento
      metier.ajouterRv(jour, creneau, hClients.get(idClient));
      // si aggiorna l'agenda
      agendaMedecinJour = metier.getAgendaMedecinJour(medecin, jour);
      // si visualizza form2
      setForms(false, true, false, false);
      return "pm:vue2";
    } catch (Throwable th) {
      // visualizzazione errori
      prepareVueErreur(th);
      return "pm:vueErreurs";
    }
  }

Questo codice è già stato incontrato nella versione 01. Si noti semplicemente la visualizzazione delle viste:

  • la vista vue2 (righe 11-12) se tutto è andato a buon fine,
  • la vista vueErreurs (righe 15-16) in caso contrario.

9.13. Annullamento di un appuntamento

Ciò corrisponde alla seguente sequenza:

Il pulsante [1] nella vista [vue3.xhtml] è il seguente:


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

Viene quindi richiamato il metodo [Form].showVue2, che si limita a visualizzare la vista 2:


  public String showVue2() {
    // vista2
    setForms(false, true, false, false);
    return "pm:vue2?reverse=true";
}

9.14. Navigazione nel calendario

Nella vista2, alcuni pulsanti consentono di navigare nel calendario:

Giorno precedente:

Giorno successivo:

Oggi:

Sebbene non sia visibile nelle schermate sopra riportate, l'agenda viene aggiornata e mostra gli appuntamenti del nuovo giorno selezionato.

I tag dei tre pulsanti in questione sono i seguenti in [vue2.xhtml]:


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

I metodi [Form].getPreviousAgenda, [Form].getNextAgenda, [Form].today sono stati analizzati nell'esempio 03.

9.15. Cambio della lingua di visualizzazione

Il cambio di lingua è gestito da un pulsante nella pagina iniziale:

Il codice del pulsante è il seguente:


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

Pertanto, passa alla vista di configurazione [2]. La vista [config.xhtml] è la seguente:


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

  <!-- Vista 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>
  • riga 11: visualizza [3],
  • riga 13: visualizza il pulsante [4]. Questo pulsante consente di tornare alla vista vue1,
  • riga 17: il modulo della vista,
  • riga 19: visualizza il titolo della vista [5],
  • righe 21-27: visualizzano i pulsanti di opzione. Il valore (itemValue) del pulsante di opzione selezionato verrà inviato al modello [Form].locale (attributo value della riga 23),
  • riga 28: visualizza il pulsante [Valider]. La chiamata AJAX aggiorna la vista vue1 (attributo update). Il metodo chiamato è [Form].configurer:

public String configurer(){
    // dopo la configurazione - si visualizza nuovamente la vista 1
    redirect();
    return null;
  }
  
  private void redirect() {
    // si reindirizza il client al servlet
    ExternalContext ctx = FacesContext.getCurrentInstance().getExternalContext();
    try {
      ctx.redirect(ctx.getRequestContextPath());
    } catch (IOException ex) {
      Logger.getLogger(Form.class.getName()).log(Level.SEVERE, null, ex);
    }
  }

Il metodo configurer (riga 1) si limita a reindirizzare il browser del dispositivo mobile verso la pagina URL dell’applicazione. Verrà quindi caricata la pagina [index.xhtml]:


<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>
  • riga 8: la vista utilizzerà la lingua appena modificata (attributo locale) e visualizzerà vista1 (riga 11).

9.16. Conclusion

Ricordiamo l’architettura dell’applicazione che abbiamo appena realizzato:

Il passaggio a un'interfaccia per dispositivi mobili ha richiesto la riscrittura delle pagine XHTML. Il modello, invece, è rimasto pressoché invariato. I livelli inferiori [métier], [DAO], [JPA] non hanno subito alcuna modifica.

9.17. Test con Eclipse

Come abbiamo fatto per le versioni precedenti dell’applicazione di esempio, mostriamo come testare questa versione 05 con Eclipse. Innanzitutto, importiamo in Eclipse i progetti Maven dell’esempio 05 [1]:

  • [mv-rdvmedecins-ejb-dao-jpa]: i livelli [DAO] e [JPA],
  • [mv-rdvmedecins-ejb-metier]: il livello [métier],
  • [mv-rdvmedecins-pfmobile]: il livello [web] implementato da Primefaces Mobile,
  • [mv-rdvmedecins-pfmobile-app]: il progetto padre del progetto aziendale [mv-rdvmedecins-pfmobile-app-ear]. Quando si importa il progetto padre, il progetto figlio viene importato automaticamente,
  • in [2], si esegue il progetto aziendale [mv-rdvmedecins-pfmobile-app-ear],
  • in [3], si seleziona il server Glassfish,
  • in [4], nella scheda [Servers], l'applicazione è stata distribuita. Non si avvia automaticamente. È necessario richiamarla tramite un browser o un simulatore mobile: