Institut Supérieur De Gestion De Tunis 3ème LFIG cours AJEE BAYOUDHI Chaouki Exemple JSF EJB et Bean géré I. Enoncé : Cet exemple est une petite application web proposant deux pages web : L’une qui affiche un formulaire afin de pouvoir ajouter un livre (AjoutLivre.xhtml), L’autre qui énumère tous les livres présents dans la base (AfficherLivres.xhtml). Ces deux pages utilisent le bean géré ControleurLivre pour stocker les propriétés nécessaires et pour la navigation. En utilisant JPA(Java Persistance API) pour la persistance et EJB pour la logique métier, tout s’emboîte : le bean géré passe tous les traitements métier à ejbLivre, qui contient deux méthodes : L’une pour stocker un livre dans une base de données (AjoutLivre()), L’autre pour récupérer tous les livres (recupererLivres()). ejbLivre est un bean de session sans état qui utilise EntityManager pour manipuler une entité Livre. Le principe de navigation entre les pages est très simple : lorsqu’un livre est ajouté, on affiche la liste. Un lien sur la page de la liste permet de revenir ensuite à la page AjoutLivre.xhtml et de créer un autre livre. Remarque : Les différents composants sont assemblés dans un fichier . war et déployés sur une instance de GlassFish et une base de données Derby. Si on utilise, par exemple le framework Maven, cette application web doit respecter la structure de répertoires de ce framework, les classes, les fichiers et les pages web doivent être placés dans les répertoires suivants : contient l’entité Livre, l’EJB ejbLivre et le bean géré ControleurLivre. src/main/resources contient le fichier persistence.xml utilisé pour associer l’entité à la base de données. src/webapp contient les deux pages web AjoutLivre.xhtml et AfficherLivres.xhtml. src/main/java src/webapp/WEB-INF contient le fichier web.xml qui déclare la servlet FacesServlet. est un fichier POM (Project Object Model) de Maven décrivant le projet, ses dépendances et ses extensions. pom.xml 1 Institut Supérieur De Gestion De Tunis 3ème LFIG cours AJEE BAYOUDHI Chaouki II. L’entité Livre : C’est la mê me ent ité vue en c ours a vec les annotations de mapping, j’ai juste ajouté deux attributs description (résumé du livre) et id (un identifiant généré automatiquement) et la requête nommée trouverTousLesLivres, qui permet de récupérer tous les livres à partir de la base de données. Entité Livre avec une requête nommée : Package coursJEE.ejbentity; @Entity @NamedQuery(name = "trouverTousLesLivres", query = "SELECT lv FROM Livre lv") public class Livre { @Id @GeneratedValue private long id; @Column(nullable = false) private String titre; private float prix; @Column(length = 2000) private String description; private String Code; private int nbrePages; // Constructeurs, getters, setters } Cette entité doit également être associée à un fichier persistence.xml dont le code est détaillé au dessous. Le fichier de configuration persistance.xml : <?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> <persistence-unit name= "ExempleUP" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider </provider> <class>coursJEE.ejbentity.Livre</class> <properties> <property name="eclipselink.target-database" value="DERBY"/> <property name="eclipselink.jdbc.driver" value= "org.apache.derby.jdbc.ClientDriver"/> <property name="eclipselink.jdbc.url" value="jdbc:derby://localhost:1527/ExempleDB"/> <property name="eclipselink.jdbc.user" value="Chaouki"/> <property name="eclipselink.jdbc.password" value="Bayoudhi"/> </properties> </persistence-unit> </persistence> Ce code permet de configurer la BD « exempleDB » gérée avec le SGBD Relationnel Derby. L’unité de persistance ExempleUP définit une connexion JDBC pour la base de 2 Institut Supérieur De Gestion De Tunis 3ème LFIG cours AJEE BAYOUDHI Chaouki données Derby nommée ExempleDB. Elle se connecte à cette base sous le compte utilisateur Chaouki avec le mot de passe Bayoudhi. Le marqueur <class> demande au fournisseur de persistance de gérer la classe Livre. Pour que ce code fonctionne, le SGBDR Derby doit s’exécuter sur le port 1527 et les classes Livre et Main doivent avoir été compilées et déployées avec ce fichier METAINF/persistence.xml. Grâce à l’API d’EntityManager, notre code manipule des objets de façon orientée objet, sans instructions SQL ni appel JDBC. III. L’EJB ejbLivre : Le code ci-dessous représente un bean de session sans état. Ce dernier obtient par injection une référence à un gestionnaire d’entités grâce auquel il peut rendre persistante une entité Livre (avec la méthode CreerLivre()) et récupérer tous les livres de la base (avec la requête nommée trouverTousLesLivres). Cet EJB n’a besoin d’aucun descripteur de déploiement. L’EJB sans état créant et récupérant des Iivres : @Stateless public class ejbLivre { @PersistenceContext(unitName = "chapter10PU") private EntityManager em; public List<Livre> recupererLivres() { //exécute la requête nommée déjà déclarée dans l’entité Query req = em.CreerNamedQuery("trouverTousLesLivres"); return req.getResultList(); } public Livre AjoutLivre(Livre unLivre) { //enregistre le livre dans la base em.persist(unLivre); return unLivre; } } IV. Le bean géré ControleurLivre : L’un des rôles d’un bean géré consiste à interagir avec les autres couches de l’application (la couche EJB, par exemple) ou à effectuer des validations. Dans le code du ControleurLivre (le bean géré) il faut ajouter l’annotation @ManagedBean. Ce bean géré contient deux attributs qui seront utilisés par les pages : est la liste des livres récupérés à partir de la base de données, qui doit s’afficher dans la page AfficherLivres.xhtml. lstLivres est l’objet qui sera associé au formulaire (dans la page AjoutLivre.xhtml) et rendu persistant. unLivre 3 Institut Supérieur De Gestion De Tunis 3ème LFIG cours AJEE BAYOUDHI Chaouki Tout le traitement métier (création et récupération des livres) s’effectue via ejbLivre (via l’instance unEjbLiv). Le bean géré obtient une référence à l’EJB par injection, via l’annotation @EJB, et dispose de deux méthodes qui seront invoquées par les pages : nouveauLiv() : Cette méthode n’effectue aucun traitement mais permet de naviguer vers AjoutLivre.xhtml. Cette méthode permet de créer un livre en invoquant l’EJB sans état et en lui passant l’attribut unLivre. Puis elle appelle à nouveau l’EJB pour obtenir tous les livres de la base et stocke la liste dans l’attribut lstLivres du bean géré. Ensuite, la méthode renvoie le nom de la page vers laquelle elle doit naviguer. Les getters et les setters, sont nécessaires pour chaque attribut (unLivre et lstLivres dans notre cas). doCreerLivre() : Le bean géré ControleurLivre qui invoque I’EJB : @ManagedBean @RequestScoped public class ControleurLivre { @EJB private ejbLivre unEjbLiv; //Les attributs du bean géré private Livre unLivre = new Livre(); private List<Livre> lstLivres; public String nouveauLiv() { return "AjoutLivre.xhtml"; } public String recupererLivre() { unLivre = unEjbLiv.CreerLivre(unLivre); lstLivres = unEjbLiv.recupererLivres(); return "AfficherLivres.xhtml"; } // Getters, setters } V. Les pages .xhtml : La page AjoutLivre.xhtml La page AjoutLivre.xhtml dont le code est ci-dessous est un formulaire permettant à l’utilisateur de saisir les informations nécessaires à la création d’un livre (Code, titre, prix, description, nombre de pages et illustrations). 4 Institut Supérieur De Gestion De Tunis 3ème LFIG cours AJEE BAYOUDHI Chaouki <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <titre>Creer un nouveau Livre</titre> </h:head> <h:body> <h1>Ajouter un Livre</h1> <hr/> <h:form> <table border="0"> <tr> <td><h:outputLabel value="Code : "/></td> <td> <h:inputText value="#{controleurLvire.unLivre.Code}"/> </td> </tr> <tr> <td><h:outputLabel value="Titre :"/></td> <td> <h:inputText value="#{controleurLvire.unLivre.titre}"/> </td> </tr> <tr> <td><h:outputLabel value="Prix : "/></td> <td> <h:inputText value="#{controleurLvire.unLivre.prix}"/> </td> </tr> <tr> <td><h:outputLabel value="Description : "/></td> <td><h:inputTextarea value="#{controleurLvire.unLivre.description}" cols="25" rows="4"/></td> </tr> <tr> <td><h:outputLabel value="Nombre de pages : "/></td> <td> <h:inputText value="#{controleurLvire.unLivre.nbrePages}"/> </td> </tr> <tr> <td><h:outputLabel value="Illustrations : "/></td> <td><h:selectBooleanCheckbox value="#{controleurLvire.unLivre.illustrations}"/> </td> <tr> </table> <h:commandButton value="Ajout Livre" action="#{controleurLvire.doCreerLivre}"/> </h:form> <hr/> </h:body> </html> 5 Institut Supérieur De Gestion De Tunis 3ème LFIG cours AJEE BAYOUDHI Chaouki Comme le montre le code précédant, la plupart des informations sont entrées dans des champs de saisie, sauf la description, qui utilise une zone de texte et les illustrations qui sont indiquées par une case à cocher. Un clic sur le bouton « Ajout Livre » provoque l’appel de la méthode doCreerLivre() du bean géré et l’EJB stocke alors le livre dans la base de données. Bien que ce code ait été simplifié, il contient l’essentiel. Il déclare d’abord l’espace de noms h pour les composants HTML de JSF : pour les utiliser, il faudra donc les préfixer par cet espace de noms (<h:body>, <h:outputText>, <h:commandButton>,...). Le langage d’expressions EL permet ensuite de lier dynamiquement la valeur du composant à la propriété correspondante du bean géré. Le code suivant, par exemple : <h:inputText value="#{controleu controleurLvire controleurLvire. rLvire.unLivre. unLivre.Code}"/> lie la valeur de l’attribut Code de unLivre avec le contenu de ce composant inputText lors de la soumission du f ormulaire. ( controleurLvire étant le nom par défaut du bean géré). Ce code est donc équivalent à celui-ci : controleurLvire.getLivre().setCode("ce qui a été saisi") La page utilise différents composants graphiques dont voici un bref résumé : permet de créer un formulaire dont les valeurs seront envoyées au serveur lorsqu’il sera soumis. <h:form> affiche un label à partir d’une chaine fixe (comme value="Code : ") ou en liant un bean à la propriété. <h:inputTextarea> affiche une zone de texte et lie sa valeur à l’attribut description du livre. <h:outputLabel> <h:selectBooleanCheckbox> affiche une case à cocher et la lie à l’attribut illustrations (un Boolean). affiche un bouton de soumission de formulaire qui lorsqu’on cliquera dessus, invoquera la méthode doCreerLivre() du bean géré (action="#{controleurLvire.doCreerLivre}"). <h:commandButton> La page AfficherLivres.xhtml La méthode doCreerLivre() du bean géré est appelée lors du clic sur le bouton de soumission de la page AjoutLivre.xhtml.Elle stocke le livre dans la base et, si aucune exception n’a été lancée, renvoie le nom de la page à afficher ensuite, AfficherLivres.xhtml, qui affiche tous les livres de la base. Un lien sur cette page permet ensuite de revenir à AjoutLivre.xhtml pour créer un autre livre. Le code de la page AfficherLivres.xhtml lister ci-sessous utilise des composants 6 Institut Supérieur De Gestion De Tunis 3ème LFIG cours AJEE BAYOUDHI Chaouki différents, mais le principe est le même que celui de la page précédente. Le composant le plus important est celui qui affiche les données sous la forme d’un tableau : <h:dataTable value="#{controleurLvire.lstLivres}" var ="liv"> L’élément <h:dataTable> est lié à l’attribut lstLivres du bean géré (une ArrayList de livres) et déclare la variable liv qui permettra de parcourir cette liste. Dans cet élément, on peut ensuite utiliser des expressions comme #{liv.Code} pour obtenir l’attribut Code d’un livre. Chaque colonne du tableau est définie par un élément <h:column>. Le marqueur <h:commandLink> en bas de la page crée un lien qui, lorsqu’on clique dessus, appelle la méthode nouveauLiv() du bean géré (celle-ci permet de revenir à la page AjoutLivre.xhtml). Code de la page AfficherLivres.xhtml <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:head> <titre>Liste des livres de la bibliothèque</titre> </h:head> <h:body> <h1>Liste Des Livres</h1> <hr/> <h:dataTable value="#{controleurLvire.lstLivres}" var="liv"> <h:column> <f:facet name="entete"> <h:outputText value="Code"/> </f:facet> <h:outputText value="#{liv.Code}"/> </h:column> <h:column> <f:facet name="entete"> <h:outputText value="Titre"/> </f:facet> <h:outputText value="#{liv.titre}"/> </h:column> <h:column> <f:facet name="entete"> <h:outputText value="Prix"/> </f:facet> <h:outputText value="#{liv.prix}"/> </h:column> <h:column> <f:facet name="entete"> 7 Institut Supérieur De Gestion De Tunis 3ème LFIG cours AJEE BAYOUDHI Chaouki <h:outputText value="Description"/> </f:facet> <h:outputText value="#{liv.description}"/> </h:column> <h:column> <f:facet name="entete"> <h:outputText value="Nombre de Pages"/> </f:facet> <h:outputText value="#{liv.nbrePages}"/> </h:column> <h:column> <f:facet name="entete"> <h:outputText value="Illustrations"/> </f:facet> <h:outputText value="#{liv.illustrations}"/> </h:column> </h:dataTable> <h:form> <h:commandLink action="#{controleurLvire.nouveauLiv}"> Ajout un nouveau Livre </h:commandLink> </h:form> <hr/> </h:body> </html> VI. Configuration avec web.xml : Les applications web sont généralement configurées à l’aide d’un descripteur de déploiement web.xml. Nous avons écrit "généralement" car ce fichier est devenu facultatif avec la nouvelle spécification Servlet 3.0. Cependant, JSF 2.0 reposant sur Servlet 2.5 (et non sur Servlet 3.0), nous devons quand même déployer notre application web avec un descripteur. Les applications JSF ont besoin d’une servlet nommée F a c e s S e r v l e t qui agit comme un contrôleur frontal pour toute l’application. Cette servlet et son association doivent être définies dans le fichier w e b . x m l . Le Fichier web.xml déclarant une FacesServlet : <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" 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_2_5.xsd"> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> 8 Institut Supérieur De Gestion De Tunis 3ème LFIG cours AJEE BAYOUDHI Chaouki <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.faces</url-pattern> </servlet-mapping> </web-app> VII. Compilation et assemblage avec Maven : L’application web doit être compilée et assemblée dans un fichier war (<packaging>war</packaging>). Le fichier pom.xml ci-dessous déclare toutes les dépendances nécessaires à la compilation du code (jsf-api, javax.ejb et javax.persistence) et précise que cette compilation utilisera la version 1.6 du JDK. Avec JSF 2.0, le fichier faces-config.xml n’est plus obligatoire mais je le donne à titre d’indication. Le Fichier pom.xml de Maven pour compiler et assembler l’application web : <?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>5.0.0</modelVersion> <groupId>coursJEE.EJB</groupId> <artifactId>Exemple</artifactId> <packaging>war</packaging> <version>1.0</version> <dependencies> <dependency> <groupId>javax.faces</groupId> <artifactId>jsf-api</artifactId> <version>2.0.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.ejb</artifactId> <version>3.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>javax.persistence</artifactId> <version>1.1.0</version> <scope>provided</scope> </dependency> </dependencies> <build> 9 Institut Supérieur De Gestion De Tunis 3ème LFIG cours AJEE BAYOUDHI Chaouki <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <inherited>true</inherited> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build> </project> Pour compiler et assembler les classes, il suffit d’ouvrir un interpréteur en ligne de commande dans le répertoire contenant le fichier pom.xml et d’entrer la commande Maven suivante : mvn package Cette commande crée le fichier Exemple-1.0.war dans le répertoire cible. Ouvrez le et vous constaterez qu’il contient l’entité Livre, le bean ejbLivre, le bean géré ControleurLivre, les deux descripteurs de déploiement (persistence.xml et web.xml) et les deux pages web (AjoutLivre.xhtml et AfficherLivres.xhtml). VIII. Déploiement dans GIassFish : L’application web assemblée doit ensuite être déployée dans GlassFish. Après avoir vérifié que Derby s’exécute et écoute sur son port par défaut, ouvrez un interpréteur en ligne de commande, placez vous dans le répertoire target contenant le fichier Exemple-1.0.war et entrez la commande suivante : asadmin deploy Exemple-1.0.war Si le déploiement réussit, la commande qui suit devrait renvoyer le nom et le type de l’application. Ici, il y a deux types : web car c’est une application web et ejb car elle contient un EJB : asadmin list-components Exemple-1.0 retourne <ejb, web> IX. Exécution de L’application : Lorsque l’application a été déployée, ouvrez votre navigateur et faite seule pointer vers l’URL suivante : http://localhost:8080/Exemple-1.0/AjoutLivre.faces 10 Institut Supérieur De Gestion De Tunis 3ème LFIG cours AJEE BAYOUDHI Chaouki Le fichier pointé est AjoutLivre.faces, pas AjoutLivre.xhtml, car avec l’extension .faces JSF sait qu’il doit traiter la page avant de l’afficher (voir l’association de .faces avec FacesServlet dans le code ci-dessous). Lorsque la page AjoutLivre s’affiche, saisissez les informations et cliquez sur le bouton d’envoi du formulaire pour être redirigé sur la page AfficherLivres. X. Configuration de FacesServlet : La FacesServlet est interne aux implémentations de JSF ; bien que vous n’ayez pas accès à son code, vous pouvez la configurer avec des métadonnées. Vous savez désormais qu’il existe deux moyens d’indiquer des métadonnées avec Java EE 6 : les annotations et les descripteurs de déploiement XML (/WEB-INF/facesconfig.xml). Avant JSF 2.0, le seul choix possible était XML mais, désormais, les beans gérés,… pouvant utiliser les annotations, les fichiers de configuration XML sont devenus facultatifs. Je vous conseille l’emploi des annotations mais, pour montrer à quoi ressemble un fichier faces-config.xml, l’extrait ci-dessous définit une locale et un ensemble de messages pour l’internationalisation et certaines règles de navigation. Extrait d’un fichier faces-config.xml : <?xml version="1.0" encoding="UTF-8"?> <faces-config 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" version="2.0"> <application> <locale-config> <default-locale>fr</default-locale> </locale-config> <resource-bundle> <base-name>messages</base-name> <var>msg</var> </resource-bundle> </application> <navigation-rule> <from-view-id>*</from-view-id> <navigation-case> <from-outcome>doCreerLivre-success</from-outcome> <to-view-id>/AfficherLivres.htm</to-view-id> </navigation-case> </navigation-rule> </faces-config> 11