Java EE
Transcription
Java EE
Java EE Les applications Web JAVA Les objectifs ● Avoir une vision globale du développement d'application Java EE ● Mettre en œuvre des exemples simples ● Comprendre et éviter les pièges du débutant ● Pouvoir aller de l'avant par soi-même pour approfondir chaque brique technique Les applications multi-tiers ● ● ● Le client (Web ou Lourd) appelle un service hébergé sur un serveur ● Le serveur Web gère les parties IHM et présentation Le ou les serveurs de traitement exécute(nt) la logique métier ainsi que la synchronisation des données La base de données s'occupe de stocker les données Le jeux de piste ● ● ● ● ● ● HTML/XML/CSS JavaScript Applet Java ● ● ● Servlet JSP JSF Rest ● ● ● ● ● Navigateur JVM Tomcat GlassFish … EJB JPA (+ORM) : ● Hibernate ● EclipseLink JTA JMS JDBC JNDI JAAS Tomcat GlassFish … ● ● SQL « Proc-Stock » Oracle MySQL JavaDB Applet ● C'est quoi ? – Du code Java SE traditionnel qui s'exécute dans un contexte particulier d'accès limité aux ressources – La classe « main » de démarrage doit étendre l'une des classes suivantes : java.applet.Applet ● javax.swing.JApplet Le cycle de vie du code correspond à l'exécution des méthodes ● – init() : appelée 1 fois après son chargement ● start() : appelée lorsque l'applet est visible (votre main) ● stop() : appelée lorsque l'applet n'est plus visible ● destroy() : appelée 1 fois après sa destruction Le reste du code est le même que pour une application Java traditionnelle « awt » ou « swing » ● – Applet ● Où cela s'exécute ? – Dans une JVM démarrée par le navigateur client suite à la lecture d'une balise HTML« <applet> » – Mais attention le code n'accède pas à toutes les ressources locales (fichiers et réseaux) ● ● ● Par défaut le code s'exécute dans une SandBox Signature du code : possibilité d'accéder aux ressources locale Possibilité de gérer l'accès aux ressources avec des fichiers « policy » <applet name="FirstAnimation" codebase="class/" code="FirstAnimation.class" height="300px" width="300px" archive="plugin.jar"> <param name="message" value="Message pour les ZérOs"> </applet> grant Principal Administrateur "root" { permission java.util.PropertyPermission "java.home", "read"; permission java.util.PropertyPermission "user.home", "read"; permission java.io.FilePermission "c:\\foo.txt", "write,read"; }; grant Principal Etudiant { permission java.io.FilePermission "c:\\foo.txt", "read"; }; Servlet ● C'est quoi ? – Du code Java qui répond aux requêtes HTTP GET, POST, PUT, DELETE, HEAD, TRACE – La classe « main » de démarrage doit étendre la classe javax.servlet.http.HttpServlet nb : il peux y avoir plusieurs classes « main » – Le cycle de vie du code dépend des méthodes HTTP écoutées par la Servlet en surchargeant une ou plusieurs méthodes de la classe HttpServlet : ● doGet : point d'entrée d'une requête GET ● doPost : point d'entrée d'une requête POST ● doPut : : point d'entrée d'une requête PUT ● ● ● doDelete : point d'entrée d'une requête DELETE doHead : point d'entrée d'une requête HEAD doTrace : point d'entrée d'une requête TRACE Servlet package com.sdzee.servlets; import java.io.IOException; import import import import javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; public class Test extends HttpServlet { public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{ response.setContentType("text/html"); response.setCharacterEncoding( "UTF-8" ); PrintWriter out = response.getWriter(); out.println("<!DOCTYPE html>"); out.println("<html>"); out.println("<head>"); out.println("<meta charset=\"utf-8\" />"); out.println("<title>Test</title>"); out.println("</head>"); out.println("<body>"); out.println("<p>Ceci est une page générée depuis une servlet.</p>"); out.println("</body>"); out.println("</html>"); } } Servlet ● Où cela s'exécute ? – Sur un Conteneur Java EE WEB qui est lui même démarré dans une JVM Servlet ● Quel est le rôle du conteneur Web ? – Mettre à disposition des outils qui gèrent les flux HTTP (écoute ports système, sécurité, gestion de cache, compression, ...) – Héberger, déployer, supprimer des applications Web Java – Gérer le cycle de vie des Servlets – Diriger les requêtes HTTP vers la bonne application Servlet – Arbo livré ● Comment s'effectue la gestion de l'hébergement applicatif ? – Toute application Web Java doit être une archive « war » dont l'arborescence est normalisée ! Servlet – Arbo source ● Attention : Arborescence de livraison (ou de packaging) n'est pas arborescence des sources ! Servlet ● A quoi sert le « web.xml » ? – Ce fichier est obligatoire, il s'appelle le descripteur de déploiement – Comme son nom l'indique, il fait comprendre au conteneur Web ● ● ● Quel est le nom de l'application Où sont situées les Servlets Comment configurer leur cycle de vie : – – – – Paramètres d'exécution non passés par l'utilisateur Association entre URL et Servlet Filtres écouteurs à exécuter avant la Servlet ... Servlet <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" Version="3.0"> <!-- Description application --> <display-name>Application de test</display-name> <description>Ma première application</description> <!-- déclarations des servlets --> <servlet> <servlet-name>MaServlet</servlet-name> <servlet-class>mon.package.servlets.MaServletTest</servlet-class> <init-param> <param-name>UnParametre</param-name> <param-value>La Valeur Associé</param-value> </init-param> </servlet> <!-- correspondance servlets / URL --> <servlet-mapping> <servlet-name>MaServlet</servlet-name> <url-pattern>/MonUrlTest/*</url-pattern> </servlet-mapping> </web-app> Servlet ● Le fichier web.xml est obligatoire mais les informations de description des Servlets peuvent maintenant être remplacées par des annotations directement dans les Servlets package mon.package.servlets; ... @WebServlet(name="MaServlet"urlPatterns="/MonUrlTest/*", initParams={@WebInitParam(name="UnParametre",value="La Valeur associée")}) public class MaServlet extends HttpServlet { ... } Servlet ● Quel est le cycle de vie ? init() destroy() doGet() Client 3 Il n'y a qu'une seule instance qui répond en parallèle à toutes les requêtes ! DoGet() Client 1 doPost() Client 2 Chargement de la servlet : Démarrage du conteneur Web Ou premier appel de la servlet doGet() Client n Extinction du conteneur Web Servlet ● Javax.servlet.HttpServletRequest – getAttribute() : les attributs sont positionnés par le conteneur ou de manière programmatique – getParameter() : les paramètres sont positionnés par la requête (query string ou post paramètre) – getSession() : recupère un objet qui stocke les données de session (permet également de créer automatiquement une session) – getServletContext() : récupère un objet qui gère le contexte d'exécution de la servlet et permet de récupérer par exemple des paramètres d'initialisation – getRequestDispatcher() : récupère un objet permettant de faire suivre le traitement vers une autre Servlet Servlet ● Javax.servlet.HttpServlerResponse – getOutputStream() / getWriter() : récupère un flux pour écrire la page réponse à la requête – setContentType() : permet de positionner le type MIME de la réponse – setHeader() : permet de positionner un header particulier – setStatus() : permet de positionner un code HTTP particulier pour la réponse – addCookie() : permet d'ajouter un cookie – sendRedirect() : permet de générer une réponse de redirection Servlet ● Javax.servlet.Filter : – L'implémentation de cette interface permet de modifier la requête et / ou la réponse à la volée. – Les filtres sont gérés par le conteneur de la même manière que la Servlet et possède le même cycle de vie avec déclaration XML ou annotation. ● ● ● init() destroy() doFilter() @WebFilter(filterName="LogFilter", urlPatterns={"/*"}, initParams={@WebInitParam(name="test-param",value="MaValeur")})public class LogFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; String ipAddress = request.getRemoteAddr(); <filter> <filter-name>LogFilter</filter-name> <filter-class> net.viralpatel.servlet.filters.LogFilter </filter-class> <init-param> <param-name>test-param</param-name> <param-value>MaValeur</param-value> </init-param> </filter> <filter-mapping> <filter-name>LogFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> System.out.println("IP "+ipAddress + ", Time " + new Date().toString()); chain.doFilter(req, res); } public void init(FilterConfig config) throws ServletException { String testParam = config.getInitParameter("test-param"); System.out.println("Test Param: " + testParam); } public void destroy() { //add code to release any resource } } JavaBean ● C'est quoi ? – Une simple classe contenant des propriétés et des accesseurs, pas de code métier ! – Cela permet de stocker des informations structurées et de les transférer aux différentes classes traitant une requête – Attention JavaBean n'est pas EJB ! – On appelle également cela un POJO public class Person { private String name; private String email; private long phoneNo; public Person() { } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setEmail(String email) { this.email = email; } public String getEmail() { return email; } public void setPhoneNo(long phoneNo) { this.phoneNo = phoneNo; } public long getPhoneNo() { return phoneNo; } } public class BeanInServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { Person p = new Person(); p.setName("Sam Dalton"); p.setEmail("[email protected]"); p.setPhoneNo(1111111111); req.setAttribute("person", p); RequestDispatcher rd = req.getRequestDispatcher("/jsp/beandata.jsp"); rd.forward(req, res); } } JSP ● C'est quoi ? – Une servlet qui prend directement la forme d'une page HTML: ● ● – Dans une Servlet on écrit la page HTML dans un flux de sortie Java Dans une JSP on écrit du code Java au sein d'une page HTML Pas besoin de déclarer leur présence (que ce soit dans le web.xml ou par annotation) <%-- Ceci est un commentaire JSP --%> <%@page contentType="text/html"%> <%@page errorPage="erreur.jsp"%> <%-- Importation d'un paquetage (package) --%> <%@page import="java.util.*"%> <html> <head><title>Page JSP</title></head> <body> <%-- Déclaration d'une variable globale à la classe --%> <%! int nombreVisites = 0; %> <%-- Définition de code Java --%> <% //Il est possible d'écrire du code Java ici Date date = new Date(); // On peut incrémenter une variable globale pour compter le nombre // d'affichage, par exemple. nombreVisites++; %> <h1>Exemple de page JSP</h1> <%-- Impression de variables --%> <p>Au moment de l'exécution de ce script, nous sommes le <%= date %>.</p> <p>Cette page a été affichée <%= nombreVisites %> fois !</p> </body> </html> JSP ● Quel est le cycle de vie ? – La JSP est traduite en Servlet puis elle est compilée avant d'être chargée en mémoire et exécutée et gérée comme toutes les autres Servlets. – Si une mise à jour de la JSP est détectée alors elle est re-traduite puis re-compilée ... JSP ● Directives <%@ directive %> ● Déclarations <%! déclaration %> ● Expressions <%= expression %> ● Fragments de code/scriptlet <% fragment de code %> ● Commentaires <%-- commentaire --%> JSP ● Utiliser/Créer un JavaBean avec une portée « request », « session » ou « application » <%-- Utiliser/Créer un JavaBean placé comme attribute de session --%> <jsp:useBean id="produit" scope="session" class="ma.jsp.Produit" > <p>Nouveau <%-- Ceci est un commentaire JSP --%> produit !</p> </jsp:useBean> <%-- Afficher une propriété de ce JavaBean --%> <p>Nom: <%= produit.getNom() %></p> <%-- Afficher une propriété de ce JavaBean --%> <p>Nom: <jsp:getProperty name="produit" property="nom"/> </p> <%-- Positionner la valeur d'une propriété du JavaBean directement --%> <jsp:setProperty name="produit" property="nom" value="Voiture" /> <%-- Positionner la valeur d'une propriété du JavaBean par lecture d'un paramètre --%> <jsp:setProperty name="produit" property="prix" param="prix" /> JSP ● <jsp:forward page="URL relative"> – ● <jsp:include page="/myFooter.jsp"> – ● Définit des propriétés propres à la page mais aussi à la réponse <%@ taglib uri="http://jakarta.apache.org/taglibs/datetime" prefix="dt" %> <p>La date en millisecondes est <dt:currentTime/></p> <p>En francais<dt:format pattern="MM/dd/yyyy hh:mm"><dt:currentTime/></dt:format></p> – ● Permet d'inclure le résultat d'une Servlet ou d'une JSP dynamiquement à l'exécution de la JSP <%@ page import="java.io.*;java.sql.*" session="true" isThreadSafe="true" errorPage="bug/erreur.html" isErrorPage="false" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" %> – ● Permet de faire suivre la requête vers une autre page JSP ou HTML ou Servlet Permet d'utiliser des tags particuliers qui proviennent d'une librairie JSTL" <%@param name="monParametre" value="uneValeur" /> – Permet de passer à une page appelée par include ou forward, un paramètre complémentaire à la requête JSP ● Les objets implicitement utilisables dans une page JSP – HttpServletRequest request – HttpServletResponse response – javax.servlet.http.HttpSession session – javax.servlet.ServletContext application – javax.servlet.jsp.PageContext pageContext – javax.servlet.ServletConfig config – javax.servlet.jsp.JspWriter out JSTL ● Des librairies utilisables dans les JSP afin de standardiser et de réutiliser un ensemble de comportements d'usage courant – La taglib « core » permet de manipuler des variables de créer des comportements conditionnels, des boucles ● – La taglib Xml permet de panipuler des données XML afin de les « parser », de les « transformer » ou de les « afficher » ● – <fmt:message />, <fmt:setLocale />, <fmt:formatDate />, <fmt:formatNumber />, ... La taglib SQL permet d'effectuer des requêtes SQL, de gérer des transactions directement dans la JSP ● – <x:parse />, <x:transform />, ... La taglib FMT permet gérer des sites multi-lingue afin de faciliter le formatage chaînes et nombres en fonction de la locale ou encore l'accès aux bundles de traduction ● – <c:if />, <c:when />, <c:foreach />, ... <sql:query />, <sql:transaction />, <sql:upate />, ... La taglib FUNCTION permet de manipuler facilement des chaînes textes ● <fn:contains />, <sql:endsWidth />, <sql:indexOf />, <sql:toLowerCase />, ... <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <c:forEach var="etudiant" begin="0" items="etudiantList">< %= etudiant.nom %></c:forEach> Framework MVC – Struts ● ● Créer par C. Mc Clanahan puis devenu le projet Jakarta d'Apache en 2000 Basé sur le modèle MVC2 qui contient un contrôleur principal redirigeant les requêtes vers des contrôleurs secondaires Framework MVC – Struts <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.0//EN" "http://jakarta.apache.org/struts/dtds/strutsconfig_1_2.dtd "> <struts-config> <form-beans type="org.apache.struts.action.ActionFormBean"> <form-bean name="loginForm" type="com.jmd.test.struts.data.LoginForm" /> </form-beans> <action-mappings type="org.apache.struts.action.ActionMapping"> <action path="/login" parameter="" input="/index.jsp" scope="request" name="loginForm" type="com.jmd.test.struts.controleur.LoginAction"> <forward name="succes" path="/accueil.jsp" redirect="false" /> <forward name="echec" path="/index.jsp" redirect="false" /> </action-mappings> </struts-config> Framework MVC – Struts 2 ● Ce n'est pas une nouvelle version de Strusts 1.x mais une fusion de ce dernier avec Webwork Néanmoins les principes restent proches Framework MVC http://static.raibledesigns.com/repository/ presentations/ComparingJVMWebFramew orks-ApacheConUS2007.pdf Framework MVC – JSF ● ● ● ● Les créateurs : Sun, Oracle, Borland, BEA, IBM Oracle qui est maintenant éditeur de Java EE promeut l'utilisation de JSF comme framework de référence Version actuelle de ce framework : 2.1 Attention ce framework qui est décrit, spécifié et dont les annotations existent dans Java EE est uniquement utilisable si on met en œuvre son implémentation (il en existe donc plusieurs : MyFaces d'Apache, Mojarra d'Oracle/Sun) JSF ● Comment cela fonctionne Navigateur Conteneur Java EE Lecture du web.xml, la requette est dirigée vers la Servlet du moteur JSF javax.faces.webapp.FacesServlet Une page appelle : MaPage.jsf HTTP La vue est affiché Moteur JSF 1) Restaure ou Crée une vue demandée par le client 2) Applique les valeurs de la requête Attention : pas totalement, juste la représentation textuelle dans des composants visuels 3) Valide les valeurs appliquées 4) Applique les valeurs aux Beans Java 5) Invoque la logique applicative (la méthode appelée explicitement ou par écoute d’événement) 6)La vue est envoyé à l'utilisateur avec les paramètres qui doivent être affichées et l'état de la vue est sauvegardé Le cycle de vie détaillé JSF ● Voici un découpage « à grosse maille » des éléments constitutifs – En bleu ce qui est totalement JSF – En vert ce qui n'est pas directement JSF mais qui est fortement recommandé d'utiliser en complément VUE : XHTML CONTROLEUR : Managed Bean METIER : EJB / JTA MODELE : JPA <!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:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> </h:head> <h:body> Hello <h:outputLabel value="#{helloWorldBean.nom}" />! <h:messages /> <h:form> <h:panelGrid columns="2"> <h:outputText value="Saisir votre nom"></h:outputText> <h:inputText value="#{helloWorldBean.nom}"></h:inputText> </h:panelGrid> <h:commandButton value="Afficher nom" action="#{helloWorldBean.maFonction}" /> </h:form> <h:link value="Recharger page" action="HelloWorld.jsf" /> </h:body> </html> <?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_1.xsd" version="2.1"> <managed-bean> <managed-bean-name>helloWorldBean</managed-bean-name> <managed-bean-class>com.test.HelloWorldBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> </faces-config> package com.test; import javax.validation.constraints.Size; public class HelloWorldBean { @Size(min=2) private String nom; // Accesseur obligatoire ... public void maFonction(){ nom=nom.toUpperCase(); } } <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>MaBanque</display-name> <welcome-file-list> <welcome-file>HelloWorld.jsf</welcome-file> </welcome-file-list> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servletclass> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.jsf</url-pattern> <url-pattern>/faces/*</url-pattern> </servlet-mapping> ... </web-app> <!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:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> </h:head> <h:body> Hello <h:outputLabel value="#{helloWorldBean.nom}" />! <h:messages /> <h:form> <h:panelGrid columns="2"> <h:outputText value="Saisir votre nom"></h:outputText> <h:inputText value="#{helloWorldBean.nom}"></h:inputText> </h:panelGrid> <h:commandButton value="Afficher nom" action="#{helloWorldBean.maFonction}" /> </h:form> <h:link value="Recharger page" action="HelloWorld.jsf" /> </h:body> </html> <?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_1.xsd" version="2.1"> </faces-config> package com.test; import javax.faces.bean.ManagedBean; import javax.faces.bean.RequestScoped; import javax.validation.constraints.Size; @ManagedBean @RequestScoped public class HelloWorldBean { @Size(min=2) private String nom; // Accesseur obligatoire ... public void maFonction(){ nom=nom.toUpperCase(); } } <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>MaBanque</display-name> <welcome-file-list> <welcome-file>HelloWorld.jsf</welcome-file> </welcome-file-list> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servletclass> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.jsf</url-pattern> <url-pattern>/faces/*</url-pattern> </servlet-mapping> ... </web-app> JSF Saisir Soumettre formulaire Cliquer lien Créer un projet Eclipse JSF/JPA ● ● ● Nous allons travailler avec l'IDE Eclipse qui est l'un des deux meilleurs IDE pour faire du Java EE Nous allons également travailler avec GlassFish le serveur d'application livré avec Java EE – Conteneur Java EE Web – Conteneur EJB Nous allons également utiliser les implémentations suivantes : – JSF : Mojarra – Facelet : PrimeFaces – JPA : EclipseLink Structure du projet JSF/JPA (sous Eclipse !) web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>Test</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <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> <context-param> <description>State saving method: 'client' or 'server' (=default). See JSF Specification 2.5.2</description> <param-name>javax.faces.STATE_SAVING_METHOD</param-name> <param-value>client</param-value> </context-param> <context-param> <param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name> <param-value>resources.application</param-value> </context-param> <listener> <listener-class>com.sun.faces.config.ConfigureListener</listener-class> </listener> </web-app> face-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_1.xsd" version="2.1"> </faces-config> persistence.xml <?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="Test"> </persistence-unit> </persistence> Connecter votre projet au serveur Les vue Facelet / XTML <!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:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> </h:head> <h:body> Hello <h:outputLabel value="#{helloWorldBean.nom}" />! <h:messages /> <h:form> <h:panelGrid columns="2"> <h:outputText value="Saisir votre nom"></h:outputText> <h:inputText value="#{helloWorldBean.nom}"></h:inputText> </h:panelGrid> <h:commandButton value="Afficher nom" action="#{helloWorldBean.maFonction}" /> </h:form> <h:link value="Recharger page" action="HelloWorld.jsf" /> </h:body> </html> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> Les vue Facelet / XTML <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:p="http://primefaces.org/ui"> <ui:composition template="/template/monTemplate.xhtml"> <ui:define name="content"> <h:form id="form"> <p:dataTable id="ListeComptes" var="unCompte" value="#{compteControlerBean.listeCompte}" selectionMode="single" selection="#{ihmSelectionControlerBean.compte}" rowKey="#{unCompte.idCompte}"> <p:column> <f:facet name="header"> <h:outputText value="Libellé" /> </f:facet> <h:outputText value="#{unCompte.libelle}" /> </p:column> <p:column> <f:facet name="header"> <h:outputText value="Identifiant" /> </f:facet> <h:outputText value="#{unCompte.identifiant}" /> </p:column> <p:column> <f:facet name="header"> <h:outputText value="Valeur actuelle" /> </f:facet> <h:outputText value="#{unCompte.total}" /> </p:column> <f:facet name="footer"> <h:commandButton id="voirTransaction" value="Voir Transactions" action="#{compteControlerBean.voirTransaction}" /> <h:commandButton id="editerCompte" value="Editer Compte" action="#{compteControlerBean.editerCompte}" /> <h:commandButton id="supprimerCompte" value="Supprimer Compte" action="#{compteControlerBean.supprimerCompte}" /> <h:commandButton id="ajouterCompte" value="Ajouter Compte" action="#{compteControlerBean.ajouterCompte}" /> </f:facet> </p:dataTable> </h:form> </ui:define> </ui:composition> </html> Les balises JSF ● ● ● ● Les balises JSF « facelets » ou « ui » servent à la composition des pages et à la création des modèles de page (templates) http://javaserverfaces.java.net/nonav/docs/2.1/vdldocs/facelets/ui/tld-summary.html Les balises composant JSF « h » servent à afficher des composants graphiques HTML tels que boutons, liens, champs de saisies, formulaires, … http://javaserverfaces.java.net/nonav/docs/2.1/vdldocs/facelets/h/tld-summary.html Les balises des actions JSF « f » servent à gérer des actions tel que Ajax, ActionListener, la validation des champs saisis de manière indépendante du fournisseur de rendu http://javaserverfaces.java.net/nonav/docs/2.1/vdldocs/facelets/f/tld-summary.html Les balises Primefaces « p » servent à afficher des composants graphiques plus évolués que « h » http://primefaces.org/ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:p="http://primefaces.org/ui"> Les balises JSTL ● ● Les balises JSTL « core » « c » servent à insérer des contrôles de flux (if, forEach, otherwise, ...) http://javaserverfaces.java.net/nonav/docs/2.1/vdldocs/facelets/c/tld-summary.html Les balises JSTL « Functions » ou « fn » servent à manipuler des chaînes de caractères (indexOf, contains, trim, ...) http://javaserverfaces.java.net/nonav/docs/2.1/vdldocs/facelets/fn/tld-summary.html <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsp/jstl/core" xmlns:h="http://java.sun.com/jsp/jstl/functions"> XHTML - Template ● XHTML et Template – Permet de créer des éléments de vue réutilisables comme le bandeau et le bas de page qui sont en règle générale toujours avec les mêmes informations <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1transitional.dtd"> <h:html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"> <h:head> <title><ui:insert name="title">Default title</ui:insert></title> </h:head> <h:body> <div id="header"> <ui:insert name="header"> <ui:include src="/template/bandeau.xhtml"/> </ui:insert> </div> <div id="content"> <ui:insert name="content"> </ui:insert> </div> <div id="footer" style="width:100%;font-size:36px;lineheight:48px;background-color:navy;color:white"> <ui:insert name="footer"> Pied de page par défaut <br /> <h:graphicImage name="glassfish.png" library="images"/> <h:graphicImage url="tomcat.gif"/> </ui:insert> </div> </h:body> </h:html> <?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:p="http://primefaces.org/ui"> <h:body> <div style="width:100%;font-size:36px;line-height:48px;backgroundcolor:navy;color:white"> Ceci est un texte provenant du bandeau </div> <p:messages /> </h:body> </html> <!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:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <ui:composition template="/template/template.xhtml"> <ui:define name="title"> HelloWorld 2 avec Template </ui:define> <ui:define name="content"> Hello <h:outputLabel value="#{helloWorldBean.nom}" />! <h:messages /> <h:form> <h:panelGrid columns="2"> <h:outputText value="Saisir votre nom"></h:outputText> <h:inputText value="#{helloWorldBean.nom}"></h:inputText> </h:panelGrid> <h:commandButton value="Afficher nom" action="#{helloWorldBean.maFonction}" /> </h:form> <h:link value="Recharger page" action="HelloWorld.jsf" /> </ui:define> </ui:composition> </html> Les vue Facelet / XTML <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <head> <title>Default title</title></head><body> <div id="header"><?xml version="1.0" encoding="ISO-8859-1" ?> <!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:p="http://primefaces.org/ui"><body> <div style="width:100%;font-size:36px;line-height:48px;background-color:navy;color:white"> Ceci est un texte provenant du bandeau </div> <p:messages></p:messages></body> </html> </div> <div id="content"> Hello <label> </label>! <form id="j_idt19" name="j_idt19" method="post" action="/Test/HelloWorld2.jsf" enctype="application/x-www-form-urlencoded"> <input type="hidden" name="j_idt19" value="j_idt19" /> <table> <tbody> <tr> <td>Saisir votre nom</td> <td><input type="text" name="j_idt19:j_idt22" /></td> </tr> </tbody> </table> <input type="submit" name="j_idt19:j_idt23" value="Afficher nom" /><input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="4365122227466430620:1843683584491520865" autocomplete="off" /> </form><a href="/Test/HelloWorld2.jsf">Recharger page</a> </div> <div id="footer"> Pied de page par défaut <img src="/Test/javax.faces.resource/glassfish.png.jsf?ln=images" /><img src="tomcat.gif" /> </div></body> XHTML - Template ● ui:insert – ● ui:include – ● Sert à inclure du code XHTML en provenance d'une autre page (pareil que pour les JSP) ui:composition – ● Permet de définir un morceau du template qui sera remplacé (ou pas) par une « instance » du template Sert à définir qu'une page va utiliser un template ui:define – Sert à définir la portion du template à remplacer XHTML – Les pièges ● ● ● Toujours utiliser « h : » devant « body », « head » et « html » Depuis JSF 2.0 un répertoire « resources » créé dans le WebContent permet de stocker et d'accéder facilement à des ressources particulières comme les images, feuilles de style ou JavaScripts Attention quand on essaye de définir un lien relatif car il sera toujours relatif à la page appelé même s'il est définit dans un template ! – Hypertext links: <a href="#{request.contextPath}/…"/> – Images: <h:graphicImage name="…" library="…"/> – CSS: <h:outputStylesheet name="…" library="…"/> – Scripts: <h:outputScript name="…" library="…"/> XHTML – Les pièges ● Un certains nombre d'objets peuvent être appelés directement dans une page XHTML – application / applicationScope / session / sessionScope / view / viewScope / request / requestScope : Environnement et Attributs des différentes potées applicatives dans JSF (application, session, vue, requête) – cookie : Tous les cookies de l'application – header / headerValues : les éléments de l'entête HTTP – param / paramValues : les paramètres de la requête – initParam : les paramètres d'initialisation – facesContext : Le contexte JSF instancié pour la requête – component : Le composant courant de type UIComponent – compositeComponent : Le composite personnalisé courant XHTML et EL ● EL : Expression Language ou le moyen d'accéder à des Java Beans, leurs propriétés et leurs fonctions directement dans le code XHTML – "#{nomDeBean.nomDeProp...}" – "#{nomDeBean.nomDeFonction}" <h:body> Hello <h:outputLabel value="#{helloWorldBean.nom}" />! <h:messages /> <h:form> <h:panelGrid columns="2"> <h:outputText value="Saisir votre nom"></h:outputText> <h:inputText value="#{helloWorldBean.nom}"></h:inputText> </h:panelGrid> <h:commandButton value="Afficher nom" action="#{helloWorldBean.maFonction}" /> </h:form> <h:link value="Recharger page" action="HelloWorld.jsf" /> </h:body> </html>y XHTML et EL ● ● "#{...}" est une expression dite différée car son évaluation a lieu au moment où le rendu est effectué. Ce type d'expression est utilisé avec JSF car dans le cycle de vie le rendu est exécuté après la validation et l'invocation applicative. "${...}" est une expression dite Immédiate car son évaluation est immédiatement. Ce type d'expression est utilisé avec JSP où l'évaluation a lieu après la compilation. Avec les JSF, leur utilisation est possible. Cela peut servir à effectuer des calculs ou accéder à des valeurs en lecture seule. – ${100 div 5} – ${!empty uneVariable} – ${pageContext.request.contextPath} Les EL permettent donc à la vue de dialoguer avec quoi ? VUE : XHTML CONTROLEUR : Managed / Backing Bean METIER : EJB / JTA MODELE : JPA Backing Bean ● C'est quoi ? – Un Java Bean géré par le contexte d'exécution JSF – Il possède une portée ou « scope » (Application, Session, Vue, Requête) dans le but de sauvegarder ces attributs – Il permet de relier les valeurs portées par les composants graphiques de la vue XHTML aux attributs des « Managed Bean » – Il existe lorsqu'on utilise les EL – L'étape de validation / conversion utilise les valeurs portées par les composants pour créer des objets dans le « Managed Bean » <h:body> Hello <h:outputLabel value="#{helloWorldBean.nom}" />! <h:messages /> <h:form> <h:panelGrid columns="2"> <h:outputText value="Saisir votre nom"></h:outputText> <h:inputText value="#{helloWorldBean.nom}"></h:inputText> </h:panelGrid> <h:commandButton value="Afficher nom" action="#{helloWorldBean.maFonction}" /> </h:form> <h:link value="Recharger page" action="HelloWorld.jsf" /> </h:body> </html>y Managed Bean ● C'est quoi ? – Un Java Bean géré par le contexte d'exécution JSF – Il possède une portée ou « scope » (Application, Session, Vue, Requête) dans le but de sauvegarder ces attributs – Il possède des méthodes qui sont appelées par les vues XHTML – Il est déclaré par annotation ou par fichier xml – Il permet d'implémenter la couche contrôleur (et pas la couche métier) package com.test; import javax.faces.bean.ManagedBean; import javax.faces.bean.RequestScoped; import javax.validation.constraints.Size; @ManagedBean @RequestScoped public class HelloWorldBean { @Size(min=2) private String nom; // Accesseur obligatoire ... public void maFonction(){ nom=nom.toUpperCase(); } } Managed Bean ● ● Le nom : – Il faut toujours suffixer le nom de la classe par « Bean » – par défaut le nom d'accès au « bean » est le nom de la classe avec une minuscule en première lettre Son instanciation et son enregistrement se fera : – Au premier appel du Bean (par défaut) – Au démarrage de l'application lorsque le scope est Application (eager=true) @ManagedBean(eager=true,name="helloWorldBean") @ApplicationScoped public class HelloWorldBean { Managed Bean ● Les Scopes ou portée applicatives (usuelles) – javax.faces.bean.SessionScoped : les attributs du Bean sont conservés le temps de la session – javax.faces.bean.RequestScoped : les attributs du Bean ne sont jamais conservés entre deux appels – javax.faces.bean.ApplicationScoped : les attributs du Bean sont partagés pour tous les appels à l'application (quelque soit le client) – javax.faces.bean.ViewScoped : les attributs du Bean sont conservés pour un client tant que la vue est visible Managed Bean ● Initialisation/Destruction d'une instance – Juste après la création d'une instance, il est possible d'appeler automatiquement une méthode qui peut initialiser celle-ci avec l'annotation @PostConstruct – Le « Managed Bean » est ensuite associé à son scope – Avant d'être détruite une instance peut également appeler automatiquement une méthode qui peut libérer des ressources avec l'annotation @PreDestroy @PostConstruct public void init(){ // init variable } @PreDestroy public void destroy(){ // relache ressource } Managed Bean ● Les attributs – ● JavaBean, donc tous les attributs doivent avoir des accesseurs La validation des attributs : – Plusieurs méthodes ● ● ● ● ● – Librarie XHTML « f » des actions JSF Implémenter la méthode suivante dans un « Managed Bean » public void maMethodeValidation(FacesContext c, UIComponent u, Object value) Implémentation de l'interface « javax.faces.Validator » Implémentation de l'interface « ActionListener » Usage des tags de validation des Bean L'étape 3 du cycle de vie « Process Validation » permet de valider les champs saisis par l'utilisateur avant qu'ils soient affectés dans le « Managed Bean » ● ● ● Si une erreur de validation apparaît alors une exception est levée et un message FacesMessage est ajouté dans le FacesContexte La balise <h : messages /> permet d'afficher les messages ajoutés Le formulaire de saisie est re-affiché avec les valeurs renseignées Rappel sur le cycle de vie Managed Bean ● Validation avec une méthode particulière dans un « Managed Bean » import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; @ManagedBean() @RequestScoped public class HelloWorldBean { private String nom; ... public void maMethodeValidation(FacesContext c, UIComponent u, Object value){ String saisie = (String)value; if(saisie.length()<2){ FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "La saisie est invalide ...", null); throw new ValidatorException(message); } } } <h:panelGrid columns="2"> <h:outputText value="Saisir votre nom"></h:outputText> <h:inputText value="#{helloWorldBean.nom}" validator="#{helloWorldBean.maMethodeValidation}"> </h:inputText> </h:panelGrid> Managed Bean ● Validation avec l'implémentation de l'interface « javax.faces.Validator » import import import import import import javax.faces.application.FacesMessage; javax.faces.component.UIComponent; javax.faces.context.FacesContext; javax.faces.validator.FacesValidator; javax.faces.validator.Validator; javax.faces.validator.ValidatorException; @FacesValidator(value="monValidator") public class MonTextValidateur implements Validator{ @Override public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { String saisie = (String)value; if(saisie.equals("LaValeurSaisie")){ FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "La saisie est invalide ...", null); throw new ValidatorException(message); } } } <h:outputText value="Saisir votre nom"></h:outputText> <h:inputText value="#{helloWorldBean.nom}"> <f:validator validatorId="monValidator"/> </h:inputText> Managed Bean ● Validation avec l'usage des tags (@Email, @Size, @NotNull, @Pattern ...) – L’inconvénient est que la valeur doit déjà avoir été affectée à l'attribut ! – Les avantages sont : ● simplicité ● répond à une spécification Java (JSR-303) ● import import import import les messages par défaut peuvent être surchargés dans le fichier ValidationMessages.properties à la racine du projet javax.faces.application.FacesMessage; javax.faces.bean.ManagedBean; javax.faces.bean.RequestScoped; javax.validation.constraints.Size; @ManagedBean @RequestScoped public class HelloWorldBean { @Size(min=2) private String nom; <h:outputText value="Saisir votre nom"></h:outputText> <h:inputText value="#{helloWorldBean.nom}"></h:inputText> Managed Bean ● ● Les valeurs saisies étant des chaînes textes, elle sont nécessairement converties vers le type cible dans le « Managed Bean ». L'une des manières est l'implémentation de l'interface « javax.faces.convert.Converter» import import import import javax.faces.component.UIComponent; javax.faces.context.FacesContext; javax.faces.convert.Converter; javax.faces.convert.FacesConverter; @FacesConverter(value="monConverter") public class MonConverteur implements Converter { @Override public Object getAsObject(FacesContext context, UIComponent component, String value) { // TODO Ecrire du code qui convertie la chaîne en éntrée vers un objet return null; } @Override public String getAsString(FacesContext context, UIComponent component, Object value) { // TODO Ecrire le code de conversion inverse return null; } } <h:outputText value="Saisir votre nom"></h:outputText> <h:inputText value="#{helloWorldBean.nom}"> <f:converter converterId="monConverter"/> </h:inputText> Managed Bean ● Un autre moyen d'effectuer de la conversion est d'utiliser les convertisseurs déjà codés : – <f:convertNumber currencySymbole="$" type="currency"> – <f:convertDateTime pattern="MM/dd/yy"> <h:outputText value="Saisir votre prix"></h:outputText> <h:inputText value="#{undBean.prix}"> <f:convertNumber currencySymbole="€" type="currency"/> </h:inputText> Managed Bean ● Navigation statique entre les pages, l'attribut « action » permet de spécifier un nom de page ou un mot clé défini dans faces-config.xml // Depuis la vue login.xhtml <h:commandButton value="Submit Values" action="loginWelcome"/> // Depuis le fichier faces-config.xml <navigation-rule> <description></description> <from-view-id>/login.xhtml</from-view-id> <navigation-case> <from-outcome>loginWelcome</from-outcome> <to-view-id>/loginWelcome.xhtml</to-view-id> </navigation-case> </navigation-rule> Managed Bean ● Navigation dynamique entre les pages, l'attribut « action » permet de spécifier un appel de méthode et le retour de celle-ci va définir un nom de page destinatrice ou un mot clé défini dans faces-config.xml // Depuis la vue login.xhtml <h:commandButton value="Submit Values" action="#{monBean.nextPage}"/> // De puis le code de monBean public String nextPage() { if (username.equals("guest") && password.equals("guest")) { return "loginSuccess"; } return "loginFailure"; } <navigation-rule> <description></description> <from-view-id>/login.xhtml</from-view-id> <navigation-case> <from-outcome>loginSuccess</from-outcome> <to-view-id>/loginSuccess.xhtml</to-view-id> </navigation-case> <navigation-case> <from-outcome>loginFailure</from-outcome> <to-view-id>/loginFailure.xhtml</to-view-id> </navigation-case> </navigation-rule> Managed Bean ● Navigation & Redirection : – Par défaut lorsqu'un formulaire navigue vers une autre page alors l'URL du navigateur n'est pas modifiée. a.xhtml → b.xhtml mais le navigateur affiche toujours comme URL a.xhtml – Dans une balise <navigation-case> il est possible d'ajouter la balise <redirect/> afin que l'URL affichée dans le navigateur suive la redirection – L'autre manière d'effectuer cette redirection dans l'URL est d'utiliser un paramètre d'URL dans le retour d'une méthode Java ou de l'action du formulaire. Ex : «return "loginSuccess?faces-redirect=true" » Managed Bean ● ● ● Mais les Managed Bean sont seulement des contrôleurs et doivent donc communiquer avec des objets « métier » comme les EJB ou encore avec d'autres contrôleurs Les attributs des Managed Bean seront donc pas seulement des champs de l'IHM mais aussi de Java Bean (Entreprise ou Managed) L'initialisation des attributs « métier » est usuellement fait par le conteneur Web et EJB qui pratique l'injection de dépendances @ManagedBean @RequestScoped public class CompteControlerBean { @EJB private CompteEJB compteEjb; @ManagedProperty(value="#{ihmSelectionControlerBean}") private IhmSelectionControlerBean ihmSelectionControlerBean; Passons à la couche métier ! VUE : XHTML CONTROLEUR : Managed / Backing Bean METIER : EJB / JTA MODELE : JPA EJB : Entreprise JavaBean ● Le cycle de vie est géré par un conteneur dédié ● Ils servent à coder le métier et le contrôle de persistance ● Comme tout JavaBean, les attributs doivent avoir des accesseurs publiques ● L'accès aux EJB peut être : ● – Distant : Le conteneur EJB s'exécute sur une autre JVM que le code client – Local : Le conteneur EJB s'exécute sur la même JVM que le code client Un « lookup » JNDI permet toujours d'accéder à un EJB car le conteneur les inscrit dans l'annuaire du serveur d'application NB : depuis Java EE v6 les noms JNDI ont été standardisés et sont donc les mêmes quelque soit le serveur d'application java:global/[nom-app]/[nom-module]/[nom-bean][!nom-interface-qualifie] ● ● Par facilité nous noterons simplement EJB les EJB dit Session car nous ne verrons pas les autres Ils existent plusieurs type d'EJB – Statefull : conservent leur état entre deux appels – Stateless : ne conservent aucun état – Singleton : l'état est partagé pour tous les appels EJB accès Distant ● ● ● ● ● Il faut créer et implémenter une interface qui est annoté javax.ejb.Remote Le client doit avoir connaissance de cette interface car il utilise ce type pour accéder à l'EJB La récupération d'un EJB dans un client se fait par injection de dépendance avec l'annotation @EJB ou par « lookup » JNDI La communication entre le client et le conteneur EJB s'effectue sur le protocole RMI/IIOP L'utilisation de cette technique d'accès est généralement faite pour les client lourd (ou Riche) déployé ou pas avec JWS Mais cela peux également être deux conteneurs d'EJB distant qui communiquent entre eux EJB accès Distant Main « use » lookup JNDI « interface » MonEJBRemote « implement » MonEJB MonEJB_2 « implement » MaServlet « use » « interface » MonEJBLocal lookup JNDI « use » accès direct EJB accès Local ● Si l'EJB est uniquement accédé localement alors il n'y a aucune obligation de définir une interface, il suffit de l'annoter @LocalBean ! (Il peut par contre y avoir un besoin conceptuel ou de bonne pratique. Pour cela il y a @javax.ejb.Local) ● ● Le client (un autre EJB ou un Managed Bean ou une Servlet …) fait simplement une déclaration d'injection de dépendance avec l'annotation @EJB Pas de protocole particulier pour la communication entre le client et l'EJB puisque l'on travaille dans une même JVM EJB accès Local import import import import import import javax.ejb.LocalBean; javax.ejb.Stateless; javax.persistence.EntityManager; javax.persistence.NoResultException; javax.persistence.PersistenceContext; javax.persistence.Query; import mabanque.donnees.Compte; /** * Session Bean implementation class CompteEJB */ @Stateless @LocalBean public class CompteEJB { @PersistenceContext(unitName="MaBanque_PU") private EntityManager em; @SuppressWarnings("unchecked") public List<Compte> recupererTousLesComptes(){ Query query=em.createNamedQuery("listeCompte"); List<Compte> retour=(List<Compte>)query.getResultList(); em.flush(); for(int i=0;i<retour.size();i++) retour.get(i).setTotal(calculateTotal(retour.get(i))); em.clear(); return retour; } ...accesseurs... @ManagedBean @RequestScoped public class CompteControlerBean { @EJB private CompteEJB compteEjb; @ManagedProperty(value="#{ihmSelectionControlerBean}") private IhmSelectionControlerBean ihmSelectionControlerBean; private List<Compte> listeCompte; @PostConstruct public void initControler(){ listeCompte=compteEjb.recupererTousLesComptes(); } ...accesseurs... } EJB accès local ● ● ● @EJB(... – beanInterface : nom de l'interface désignant la vue métier que l'on souhaite – beanName : nom de référence de l'EJB (uniquement disponible si client et EJB sont sur un même environnement) – name : nom avec lequel l'EJB sera recherché – lookup : un nom JNDI portable complet @Stateless(... – name : le nom de l'EJB (par défaut le nom de la classe) – mappedName : le nom sous lequel l'EJB sera référencé par JNDI @Statefull et @Singleton – Les attributs sont les même EJB accès particulier ● ● Certains framework (Struts2) peuvent avoir besoin de créer des classes de récupération en masse et de référencement pour offrir un service d'injection d'EJB. Ce modèle de conception est souvent appelé « ServiceLocator » Il est également possible d'accéder à des EJB au sein d'un programme PHP via le « lookup » JNDI EJB : Stateless ● ● Un EJB est dit « Stateless » lorsqu'il ne conserve aucune données entre les requêtes Il faut le déclarer sur la classe EJB par l'annotation @Stateless – Généralement ces EJB sont placées dans un pool d'instances et une requête utilise un élément du pool. – Ce type d'EJB est simple et permet d'améliorer la réactivité du serveur d'application EJB : Stateless import import import import import import javax.ejb.LocalBean; javax.ejb.Stateless; javax.persistence.EntityManager; javax.persistence.NoResultException; javax.persistence.PersistenceContext; javax.persistence.Query; import mabanque.donnees.Compte; /** * Session Bean implementation class CompteEJB */ @Stateless @LocalBean public class CompteEJB { @PersistenceContext(unitName="MaBanque_PU") private EntityManager em; @SuppressWarnings("unchecked") public List<Compte> recupererTousLesComptes(){ Query query=em.createNamedQuery("listeCompte"); List<Compte> retour=(List<Compte>)query.getResultList(); em.flush(); for(int i=0;i<retour.size();i++) retour.get(i).setTotal(calculateTotal(retour.get(i))); em.clear(); return retour; } ...accesseurs... @ManagedBean @RequestScoped public class CompteControlerBean { @EJB private CompteEJB compteEjb; @ManagedProperty(value="#{ihmSelectionControlerBean}") private IhmSelectionControlerBean ihmSelectionControlerBean; private List<Compte> listeCompte; @PostConstruct public void initControler(){ listeCompte=compteEjb.recupererTousLesComptes(); } ...accesseurs... } EJB : Stateless ● Le cycle de vie et les méthodes de rappels 1.L'EJB n'existe pas 2.Un client appelle une référence sur l'EJB 3.Le conteneur créé l'instance et réalise les injections de dépendance de cet EJB 4.Le conteneur appelle les méthode annotées @PostConstruct 5.L'instance traite l'appel et reste en attente d'autres requêtes dans le pool 6.Lorsque le conteneur s'éteint, il appelle les méthodes annotées @PreDestroy EJB : Statefull ● Un EJB est dit « Statefull » lorsqu'il conserve l'état de la conversation entre chaque appel d'un même client – ● Le mécanisme traditionnel de session est utilisé pour conserver cet état Il faut le déclarer sur la classe EJB par l'annotation @Statefull – Attention ce type d'objet est plus compliqué et moins performant pour la réactivité du conteneur Web car il faut gérer sa création, sa destruction, sa persistance dans la session – De plus il est consommateur de mémoire EJB : Statefull ● Le cycle de vie et les méthodes de rappels 1.L'EJB n'existe pas 2.Un client appelle une référence sur l'EJB 3.Le conteneur créé l'instance et réalise les injections de dépendance de cet EJB 4.Le conteneur appelle les méthode annotées @PostConstruct 5.L'instance traite l'appel et reste dans en mémoire en attendant les autres requête du client 6.Si le client attend un certains temps avant de refaire une requête alors le conteneur appelle la méthode annotée @PrePassivate puis l'EJB est sérialisé sur un stockage permanent 7.Lorsque le client refait une requête alors le conteneur recharge en mémoire l'EJB et appelle la méthode annotée @PostActivate 8.Si le client invoque une méthode annotée @Remove alors le conteneur appelle également la méthode @PreDestroy puis supprimer l'EJB EJB : Singleton ● ● Un EJB est dit « Singleton » lorsqu'il conserve un état de la conversation partagé entre tous les clients de l'application Il faut le déclarer sur la classe EJB par l'annotation @Singleton – Il existe une seule instance créée dans le conteneur EJB et tous les « threads » accèdent de manière concurrente à l'EJB – La concurrence d'accès est gérée via le contexte JTA qui est initié dans le conteneur et paramétré par l'application EJB : Singleton ● Le cycle de vie et les méthodes de rappels sont identique aux Stateless 1.L'EJB n'existe pas 2.Un client appelle une référence sur l'EJB 3.Le conteneur créé l'instance et réalise les injections de dépendance de cet EJB 4.Le conteneur appel les méthode annotées @PostConstruct 5.L'instance traite l'appel et reste en attente d'autres requêtes 6.Lorsque le conteneur s'éteint il appelle les méthodes annotées @PreDestroy EJB : Singleton ● La concurrence des accès peut être gérée selon 3 types grâce à l'annotation @ConcurrencyManagement: – CMC : Container-Managed Concurrency (type par défaut) – L'annotation @Lock(LockType.WRITE) permet de spécifier qu'un seul client peut accéder à l'EJB et/ou à la méthode taguée, les autres attendent leur tour ● Alors que l'annotation @Lock(LockType.READ) autorise tous les accès en concurrence BMC : Bean-Managed Concurrency ● Le programme doit coder la gestion de la concurrence avec les instructions Java « synchronized » et « volatile » Interdite : Lève l'exception ConcurrentAccessException en cas de tentative d'accès concurrent ● – EJB - Packaging EJB - Packaging EJB – Elements particuliers ● ● L'appel de méthodes de manière asynchrone peut maintenant se faire sans utiliser JMS, en annotant simplement une méthode @Asynchronous Il est possible de définir un fichier optionnel ejb-jar.xml qui définit des ressources permettant l'initialisation d'attributs d'un EJB par exemple – L'attribut ou son « setter » est annoté @Ressource @Ressource public void setMonAttributChaine(String arg){ this.monAttributChaine=arg ; } – Le fichier ejb-jar.xml possède des balises suivantes <env-entry> <env-entry-name>monAttributChaine</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>Ma chaine par défaut</env-entry-value> </env-entry> EJB – Elements particuliers ● ● ServiceTimer : ce service a fortement été amélioré depuis la version 3.1. Il permet à un EJB Stateless d'être rappelé au moment voulu sans être sollicité par une requête (comme un cron appelle un exécutable). – @Schedules({@Schedule(hour=2 , dayOfWeek=...), …) – @Timeout Il est également possible de définir des méthodes qui seront systématiquement appelées dès qu'une autre méthode du Bean sera appelée avec l'usage de l'annotation @AroundInvoke EJB ● Il existe d'autres EJB que les EJB Session – Les EJB Entité : ils sont remplacés par l'usage de la spécification JPA – Les Message Driven Bean (MDB) qui sont utilisés pour travailler avec les files d'attente conformément à la spécification JMS EJB & JTA ● L'usage des transactions doit garantir que les manipulations des données seront – Fiable – Robuste – Conservatrice les propriétés ACID ● ● ● ● ● Atomicité : plusieurs opérations sont regroupés dans une unité de traitement tel que l'échec de l'une implique l'échec de toutes Cohérence : A la fin du traitement, les données sont dans un état cohérent Isolement : L'état intermédiaire de traitement des données n'est pas visible en dehors de l'unité de traitement Durée : Lorsque la transaction est validée alors les données sont visibles de tous L'application s'applique à gérer si on doit valider ou invalider une transaction. Toute la mécanique de gestion de la ressource vis-à-vis de l'annulation est effectuée automatiquement par le gestionnaire de transaction JTA. EJB & JTA ● Les transactions locales JTA JTA EJB & JTA ● Les transactions distribués Two phase commit : prepare & validate JTA JTA / XA JTS / OTS IIOP EJB / JTA ● ● CMT : Container Managed Transaction – C'est le conteneur qui décide quand commence et quand finit une transaction. C'est ce qu'on appelle la démarcation. – C'est la stratégie par défaut pour toute méthode appartenant à un EJB BMT : Bean Managed Transaction – C'est l'application qui décide où s'effectue la démarcation EJB / JTA ● ● CMT : Container Managed Transaction – C'est le conteneur qui gère le début et la fin d'une transaction. C'est ce qu'on appelle la démarcation. – Par défaut, toute méthode d'un EJB s'exécute dans une transaction – L'annotation @TransactionAttribute permet de spécifier au conteneur une stratégie de démarcation particulière à appliquer sur une méthode ou tout un EJB BMT : Bean Managed Transaction – C'est l'application qui décide où s'effectue la démarcation tx.begin(), tx.commit(), tx.rollback() – L'EJB ou sa méthode doit être explicitement annoté avec le tag @TransactionManagement(...) EJB / CMT REQUIRED SUPPORTED MANDATORY REQUIRES_NEW NOT_SUPPORTED NEVER EJB / CMT CLient Conteneur EJB 1 : ejb1.methodeX() EJB1 EJB2 2 : begin 4 : ejb2.methodeY() 5 : commit ou rollback TransactionAttributeType.REQUIRED Transaction EJB / CMT ● ● Quand se déclenche l'annulation d'une transaction ? – Lorsqu'une exception système est levée dans la méthode : JVM, JNDI, SGBD – Lorsqu'une exception de type « Runtime » est levée Que se passe-t-il si la méthode lève une exception applicative héritant de la classe Exception ? – L'exception est retournée mais il n'y a pas de Rollback ! Sauvegardons les données ! VUE : XHTML CONTROLEUR : Managed / Backing Bean METIER : EJB / JTA MODELE : JPA EJB & JPA ● ● Le conteneur d'EJB est capable d'initialiser une unité de persistance automatiquement et de l'injecter dans les EJB qui en ont besoin L'objectif est de laisser le conteneur gérer le cycle de vie de l'unité de persistance et de simplement se concentrer sur les services qu'elle propose <?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="MaBanque_PU"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <jta-data-source>jdbc/BanqueBdd</jta-data-source> <class>mabanque.donnees.Compte</class> <class>mabanque.donnees.Transaction</class> <properties> <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/> <property name="eclipselink.ddl-generation.output-mode" value="both"/> <property name="eclipselink.jdbc.batch-writing" value="JDBC"/> </properties> </persistence-unit> </persistence> import import import import import import javax.ejb.LocalBean; javax.ejb.Stateless; javax.persistence.EntityManager; javax.persistence.NoResultException; javax.persistence.PersistenceContext; javax.persistence.Query; import mabanque.donnees.Compte; /** * Session Bean implementation class CompteEJB */ @Stateless @LocalBean public class CompteEJB { @PersistenceContext(unitName="MaBanque_PU") private EntityManager em; JPA ● ● ● ● Cette spécification (JSR-317) a pour but de standardiser la persistance des objets Java au sein des BBD Relationnelles Actuellement nous sommes à la version 2.0 de JPA Attention cette spécification n'est pas implémentée dans Java, il faut utiliser un ORM (EclipseLink, Hibernate, …) L'utilisation des annotations a permis de faciliter l'écriture du code alors qu'avant nous étions obligé de tout écrire dans des fichiers XML et des erreurs étaient vues de manières tardives JPA JPA ● On trouve dans JPA des annotations qui permettent : – De lier une classe Java à une table du SGBD les attributs seront les colonnes de cette table ● les instances seront les tuples De lier les associations et agrégations entre classes aux relations entre tables (clés primaires, clés étrangères, table de jointure, ...) ● – – ● ● De spécifier des contraintes purement SGBD sur des artefacts Java (type des colonnes, taille, non nulle, …) et d'effectuer de la validation On trouve également dans JPA le moyen d'effectuer des requêtes : – API Criteria : La structure de la requête est construite avec des objets Java – API JPQL : ressemble à du code SQL mais permet l'usage des type Java – SQL : Requêtes de base SQL Un ORM implémente les mécanismes derrières JPA @Entity et @Table ● Permet de lier une classe à une table et lier des attributs à des colonnes – Les annotations de définition des colonnes peuvent être mises sur les attributs mais également sur les « getter » ! import import import import import javax.persistence.Entity; javax.persistence.FetchType; javax.persistence.Id; javax.persistence.Basic; javax.persistence.Table; @Entity @Table(name="PERSONNE") public class Personne { @Id @Column(name="ID_PERSONNE") private Long idPersonne; @Column(name="NOM", nullable=false, length=512) private String nomPersonne; @Basic(fetch=FetchType.LAZY,optional=true) private String prenom; public Long getIdPersonne() { return idPersonne; } ...accesseurs... import import import import import javax.persistence.Entity; javax.persistence.FetchType; javax.persistence.Id; javax.persistence.Basic; javax.persistence.Table; @Entity @Table(name="PERSONNE") public class Personne { private Long idPersonne; @Column(name="NOM", nullable=false, length=512) private String nomPersonne; @Basic(fetch=FetchType.LAZY,optional=true) private String prenom; @Id @Column(name="ID_PERSONNE") public Long getIdPersonne() { return idPersonne; } ...accesseurs... @Id - @GeneratedValue ● ● Un tuple d'une table est généralement toujours identifié par une clé primaire (not null, unique) – Clé simple : un seul et unique attribut de la table – Clé composé : un ensemble d'attributs Il est possible (et même conseillé) de laisser générer automatiquement une clé primaire simple – Le type doit être : Byte, Integer, Short, Long, Character, String, BigInteger, Date – Le moyen : @GeneratedValue(strategy=GenerationType.<type>) ● ● ● ● SEQUENCE : Demande au SGBD de créer un objet séquence qui gérera l'incrémentation automatique de la valeur IDENTITY : Créer une colonne avec un type spécifique au fournisseur qui permet l'incrémentation TABLE : L'auto-incrémentation est géré dans une table AUTO : Laisse libre choix au SGBD de choisir le gestionnaire d'incrémentation le plus adapté au type de la colonne @Entity @Table(name="PERSONNE") public class Personne { @Id @GeneratedValue(strategy=GenerationType.SEQUENCE) @Column(name="ID_PERSONNE") private Long idPersonne; @EmbeddedId ● Il est également possible d'avoir des clés composites : @EmbeddedId – Cette classe composite ne doit pas avoir de @Id – Cette classe composite doit surcharger la méthode equals() – Cette classe composite doit surcharger la méthode hashCode() @Entity @Entity @Table(name="PERSONNE") public class Personne { @EmbeddedId private PersonneId personneId; public class PersonneId { private String nom; private String prenom; public int hashCode() { return (nom + "_" + prenom).hashCode(); } public boolean equals(Object obj) { ... == ... instanceof ... } NB : Une autre annotation (moins utilisée) permet de définir une classe comme un identifiant : @ClassId @Column ● Ce tag sert à caractériser une colonne de la table : – name : le nom de la colonne – unique : true ou false, doit-elle être unique ? – nullable : true ou false, peut-elle être nulle ? – insertable : true ou false, doit-elle être prise en compte dans l'insertion/création ? – updatable : true ou false, doit-elle être prise en compte dans une mise à jour ? – columnDefinition : donne des précisions sur la création de la colonne ( – Length : s'applique uniquement sur le type chaîne et précise la longueur de la chaîne – precision/scale : s'appliquent uniquement sur des types numériques afin de définir respectivement le nombre de chiffres total qui représentent ce numérique ainsi que le nombre de chiffres total autorisés à droite de la virgule @Column(columnDefinition = "CHAR(40)") private String name; // Par défaut String = varchar2(255) @Column(name="MON_TEST", nullable=false, columnDefinition="boolean default false") public Boolean getBoleanValue() { return test; } @Basic ● Permet de préciser 2 caractéristiques basiques sur la colonne : – La première porte sur la nullité potentielle : optional = (true | false) – La seconde porte sur le chargement immédiat ou différé de l'objet fetch = ( FetchType.EAGER | FetchType.LAZY ) import import import import import javax.persistence.Entity; javax.persistence.FetchType; javax.persistence.Id; javax.persistence.Basic; javax.persistence.Table; @Entity @Table(name="PERSONNE") public class Personne { private Long idPersonne; @Column(name="NOM", nullable=false, length=512) private String nomPersonne; @Basic(fetch=FetchType.LAZY,optional=true) private String prenom; @Id @Column(name="ID_PERSONNE") public Long getIdPersonne() { return idPersonne; } ...accesseurs... @Temporal ● Ce tag permet de spécifier le contenu et le type du stockage sur la colonne ayant un type temporel : – @Temporal(TemporalType.DATE) : s'applique sur un attribut java.util.Date désignant une date dans le temps – @Temporal(TemporalType.TIME) : s'applique sur un attribut de type java.util.Date désignant un horaire – @Temporal(TemporalType.TIMESTAMP) : s'applique sur un attribut de type java.util.Date désignant une date et un horaire @Entity public class Personne { ... @Temporal(TemporalType.TIME) private Date heureDeNaissance; ... @Enumerated ● Depuis peu, il est possible de définir des colonnes ayant des valeurs déterminées à l'avance telles que sont les énumérations ! @Entity public class Personne { public enum Genre { HOMME, FEMME, INCONNU } ... @Enumerated(EnumType.STRING) private ... Genre genre; @Lob ● Permet de définir des colonnes de type « gros objet » – CLOB : Character Large Object (fichiers textes, ...) – BLOB : Binary Large Object (images, ...) @Transient ● Permet de définir un attribut qui ne sera pas représenté par une colonne – Une entité peut avoir besoin d'avoir un attribut qui ne doit pas forcément être traité par la couche persistance. – L'attribut en question sera valué de manière programmatique au bon moment @Embedded ● Lorsque qu'une classe doit être persisté dans les colonnes d'une autre entité : @Entity public class Personne { ... private String adresseMail; private String fournisseur; @Embedded private ... Mail public class Mail { eMail; ... @version ● Cette annotation permet de définir un attribut qui sera géré automatiquement par la couche de persistance afin de positionner un numéro de version à l'entité – Lors des accès concurrent à une même entité si l'on essaye de mettre à jour une entité qui n'a pas le même numéro de version dans le SGBD alors une exception est levée @Entity public class Person { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; private int salary; @Version private long version; // ...getters and setters } @OneToOne import import import import import import import import import import import javax.persistence.Column; javax.persistence.Entity; javax.persistence.FetchType; javax.persistence.Id; javax.persistence.Basic; javax.persistence.JoinColumn; javax.persistence.ManyToMany; javax.persistence.ManyToOne; javax.persistence.OneToMany; javax.persistence.OneToOne; javax.persistence.Table; @Entity @Table(name="PERSONNE") public class Personne { @Id @Column(name="ID_PERSONNE") private Long id; @OneToOne private PermisDeConduire permis; import import import import import import javax.persistence.Column; javax.persistence.Entity; javax.persistence.Id; javax.persistence.OneToOne; javax.persistence.Temporal; javax.persistence.TemporalType; @Entity public class PermisDeConduire { @Id @Column(name="ID_PERMIS") String numero; @Temporal(TemporalType.DATE) Date dateDelivrancePermisA; @Temporal(TemporalType.DATE) Date dateDelivrancePermisB; // Bidirectionnelle @OneToOne(mappedBy="permis") Personne beneficiaire; // Unidirectionnelle @OneToOne Personne delivrerar; @OneToMany @Entity @Table(name="PERSONNE") public class Personne { @Id @Column(name="ID_PERSONNE") private Long id; @OneToOne private PermisDeConduire permis; @OneToMany(mappedBy="proprietaire") private List<Voiture> mesVoitures; @ManyToMany private Set<Personne> collegues; @Entity public class Voiture { @Id @GeneratedValue(strategy=GenerationType.AUTO) @Column(name="ID_VOITURE") Long id; String marque; String modele; @Temporal(TemporalType.DATE) Date dateAchat; @ManyToOne Personne proprietaire; @ManyToOne @Entity @Table(name="PERSONNE") public class Personne { @Id @Column(name="ID_PERSONNE") private Long id; @OneToOne private PermisDeConduire permis; @OneToMany(mappedBy="proprietaire") private List<Voiture> mesVoitures; @ManyToMany private Set<Personne> collegues; @Entity public class Voiture { @Id @GeneratedValue(strategy=GenerationType.AUTO) @Column(name="ID_VOITURE") Long id; String marque; String modele; @Temporal(TemporalType.DATE) Date dateAchat; @ManyToOne Personne proprietaire; @ManyToMany @Entity @Table(name="PERSONNE") public class Personne { @Id @Column(name="ID_PERSONNE") private Long id; @OneToOne private PermisDeConduire permis; @OneToMany(mappedBy="proprietaire") private List<Voiture> mesVoitures; @ManyToMany private Set<Personne> collegues; Création automatique d'un table d'association Héritage @Entity @Table(name="PERSONNE") public class Personne { @Id @Column(name="ID_PERSONNE") private Long id; @OneToOne private PermisDeConduire permis; @OneToMany(mappedBy="proprietaire") private List<Voiture> mesVoitures; @ManyToMany private Set<Personne> collegues; @Entity public class Etudiant extends Personne { String numeroEtudiant; Héritage @Entity @Table(name="PERSONNE") @Inheritance(strategy=InheritanceType.JOINED) public class Personne { @Id @Column(name="ID_PERSONNE") private Long id; @OneToOne private PermisDeConduire permis; @OneToMany(mappedBy="proprietaire") private List<Voiture> mesVoitures; @ManyToMany private Set<Personne> collegues; @Entity public class Etudiant extends Personne { String numeroEtudiant; Dialoguer avec le SGBD ● ● Nous savons maintenant mapper les classes « entités » sur des tables de SGBD ! Mais comment accède-t-on à ces entités ? – Pour cela on utilise un EntityManager, il orchestre tous les échanges avec la ressources SGBD ● ● ● – Enregistrement Requêtes de récupération Mise à jour sur et depuis le stockage Il y a plusieurs moyen d'instancier un EntityManager (Persistence Manager) mais dans le cadre de ce cours nous allons laisser soin au conteneur EJB de gérer cela pour nous ! ● ● Nous allons simplement demander au conteneur de nous fournir un EntityManager via le nom JNDI d'une unité de persistance Puis nous allons utiliser les méthodes qui s'offrent à nous Dialoguer avec le SGBD ● Il faut d'abord créer une ressource de données dans le serveur d'application et lui donner un nom JNDI Dialoguer avec le SGBD ● Il faut ensuite lier votre projet à cette source de données avec le fichier « persistence.xml » <?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="Test"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <jta-data-source>jdbc/BanqueBdd</jta-data-source> <properties> <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/> <property name="eclipselink.ddl-generation.output-mode" value="both"/> <property name="eclipselink.jdbc.batch-writing" value="JDBC"/> </properties> </persistence-unit> </persistence> Dialoguer avec le SGBD ● Il reste juste à injecter un EntiteManager dans l'EJB @Stateless public class PersonneEJB { @PersistenceContext(unitName="Test") private EntityManager em; public void createPersonne(Personne p){ em.persist(p); } Dialoguer avec le SGBD ● Les méthodes de « javax.persistence.EntityManager » : – void persist(Object) : insère les valeurs de l'instance Java dans le SGBD – void refresh(Object) : applique à l'instance Java les valeurs stockées en bases (quelque soit les modifications effectuées sur l'instance Java) – Object find(Class, Object) : récupère un tuple du SGBD – Object merge(Object) : retourne une instance Java dont les valeurs ont été fusionnées avec celle du SGBD – remove(Object) : supprime l'entité du SGBD – clear()/detach(Object) : détache toutes les/une entité(s) de l'EM – createNameQuery : requête jPQL / SQL – createNativeQuery : requête SQL – createQuery : requête Criteria / jPQL – lock : pose un verrou dans le SGBD Dialoguer avec le SGBD ● Le cycle de vie d'une entité et ses méthodes de rappels définis par annotation @PreUpdate @PostUpdate @PostLoad @PrePersist @PostPersist @PreRemove @PostRemove @PreUpdate @PostUpdate @PostLoad Dialoguer avec le SGBD Requêtes nommées : jPQL @Entity @NamedQuery(name="findEtudiantFromNumero", query="SELEST e FROM Etudiant e WHERE e.numeroEtudiant LIKE ?1") public class Etudiant extends Personne { @Stateless public class EtudiantDAO { @PersistenceContext(unitName="Test") private EntityManager em; public EntityManager getEm() { return em; } public void setEm(EntityManager em) { this.em = em; } @SuppressWarnings("unchecked") public List<Etudiant> findEtudiantFromNumero(String debutDuNumero){ Query q=em.createNamedQuery("findEtudiantFromNumero"); q.setParameter(1,debutDuNumero+"%"); q.setMaxResults(10); return (List<Etudiant>)q.getResultList(); } Dialoguer avec le SGBD Requêtes nommées : jPQL @Entity @Table(name="PERSONNE") @NamedQueries({ @NamedQuery(name="findAllPersonnes", query="SELECT p FROM Personne P"), @NamedQuery(name="findPersonneFromPermis", query="SELECT p FROM Personne p WHERE p.permis = :permis") }) public class Personne { @Id @Column(name="ID_PERSONNE") private Long id; @Stateless public class PersonneDAO { @PersistenceContext(unitName="Test") private EntityManager em; @SuppressWarnings("unchecked") public List<Personne> findAllPersonne(){ Query query=em.createNamedQuery("findAllPersonnes"); return (List<Personne>)query.getResultList(); } @SuppressWarnings("unchecked") public List<Personne> findPersonneFromPermis(PermisDeConduire permis){ Query query=em.createNamedQuery("findPersonneFromPermis"); query.setParameter("permis", permis); return (List<Personne>)query.getResultList(); } Dialoguer avec le SGBD Requête native nommée : SQL @Entity @Table(name="PERSONNE") @NamedNativeQuery(name="findAll", query="select * from PERSONNE") public class Personne { @Id @Column(name="ID_PERSONNE") private Long id; @Stateless public class PersonneDAO { @PersistenceContext(unitName="Test") private EntityManager em; @SuppressWarnings("unchecked") public List<Personne> findAll(){ Query query=em.createNamedQuery("findAll",Personne.class); return (List<Personne>)query.getResultList(); } Dialoguer avec le SGBD ● Les requêtes dynamiques : – em.createQuety(<jPQSL String>) – em.createQuety(CriteriaQuery) – em.createNativeQuery(<SQL String>) Dialoguer avec le SGBD API Criteria @Entity @NamedQuery(name="findEtudiantFromNumero", query="SELEST e FROM Etudiant e WHERE e.numeroEtudiant LIKE ?1") public class Etudiant extends Personne { @Stateless public class EtudiantDAO { @PersistenceContext(unitName="Test") private EntityManager em; @SuppressWarnings("unchecked") public List<Etudiant> findEtudiantFromNumero(String debutDuNumero) { Query q=em.createNamedQuery("findEtudiantFromNumero"); q.setParameter(1,debutDuNumero+"%"); q.setMaxResults(10); return (List<Etudiant>)q.getResultList(); } @SuppressWarnings("unchecked") public List<Etudiant> findEtudiantFromNumeroWithCriteria(String debutDuNumero) { CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<Etudiant> query= cb.createQuery(Etudiant.class); Root<Etudiant> etudiant = query.from(Etudiant.class); ParameterExpression<String> param = cb.parameter(String.class); Expression<String> path = etudiant.get("numeroEtudiant"); query.select(etudiant).where(cb.like(path, param)); TypedQuery<Etudiant> typedQuery = em.createQuery(query); typedQuery.setParameter(param,debutDuNumero+"%"); typedQuery.setMaxResults(10); return (List<Etudiant>)typedQuery.getResultList(); } Dialoguer avec le SGBD Verrou sur les données ● Pourquoi mettre un verrou ? – ● ● ● Gérer la concurrence des accès Quelles méthodes peuvent poser un verrou – find – refresh – lock Les verrous peuvent être optimistes, la donnée est relue pour être verrouillée avant le commit : – LockModeType.OPTIMISTIC – LockModeType.OPTIMISTIC_FORCE_INCREMENT – Une entité avec un @Version utilise par défaut un verrou optimiste Les verrous peuvent être pessimiste, la donnée est verrouillée dès son accès : – LockModeType.PESSIMISTIC_READ – LockModeType.PESSIMISTIC_WRITE – LockModeType.PESSIMISTIC_FORCE_INCREMENT Le code c'est < 20 % ! ● ● Que doit-on mettre en place autour de la production de code ? – Un environnement de gestion de sources – Un environnement de développement – Un environnement d'intégration continue – Un environnement de test pour développeur – Un environnement de test unitaire – Un environnement de test fonctionnel – Un environnement de test syntaxique Oui mais il y a aussi toutes les autres étapes d'un projet – Définition du besoin – Planification – Spécification – Recette – Déploiement – Formation – Exploitation – Maintenance – Évolutions Et la mobilité ? Les références ● http://www.siteduzero.com/ ● http://www.primefaces.org/ ● http://jean-luc.massat.perso.luminy.univmed.fr/ens/jee/servlet.html ● http://www.jmdoudoux.fr/java/dej/chap-jsp.htm ● http://kmdkaci.developpez.com/ ● http://www.mkyong.com ● http://javaweb.developpez.com/faq/jsf/ ● http://blog.ippon.fr/2011/10/11/jpa-une-magie-qui-se-merite-retour-aux-sources-de-jpa/ ● ● ● ● Développement n-tiers avec Java EE Jérôme LAFOSSE Java EE6 et GlassFish 3 Antonio Goncalves