Explorer les Livres électroniques
Catégories
Explorer les Livres audio
Catégories
Explorer les Magazines
Catégories
Explorer les Documents
Catégories
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@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() doGet() destroy()
Client 3
Il n'y a qu'une seule instance qui répond en parallèle à toutes les requêtes !
<%-- 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">
– Permet de faire suivre la requête vers une autre page JSP ou HTML ou Servlet
● <jsp:include page="/myFooter.jsp">
– 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"
%>
– 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'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
● <c:if />, <c:when />, <c:foreach />, ...
– La taglib Xml permet de panipuler des données XML afin de les « parser », de les
« transformer » ou de les « afficher »
● <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
● <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
● <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 />, ...
Moteur JSF
1) Restaure ou Crée une vue demandée par le client
Une page appelle :
2) Applique les valeurs de la requête
MaPage.jsf Attention : pas totalement, juste la représentation
textuelle dans des composants visuels
HTTP 3) Valide les valeurs appliquées
4) Applique les valeurs aux Beans Java
5) Invoque la logique applicative (la méthode appelée
La vue est affiché
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
MODELE : JPA
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
package com.test;
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html" import javax.validation.constraints.Size;
xmlns:f="http://java.sun.com/jsf/core">
public class HelloWorldBean {
<h:head>
<meta http-equiv="Content-Type" @Size(min=2)
content="text/html; charset=ISO-8859-1" /> private String nom;
</h:head>
<h:body> // Accesseur obligatoire ...
Hello <h:outputLabel value="#{helloWorldBean.nom}" />!
<h:messages />
<h:form> public void maFonction(){
<h:panelGrid columns="2"> nom=nom.toUpperCase();
<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> <?xml version="1.0" encoding="UTF-8"?>
<h:link value="Recharger page" action="HelloWorld.jsf" /> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-
</h:body> instance" xmlns="http://java.sun.com/xml/ns/javaee"
</html> 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>
<?xml version="1.0" encoding="UTF-8"?> <welcome-file-list>
<welcome-file>HelloWorld.jsf</welcome-file>
<faces-config </welcome-file-list>
xmlns="http://java.sun.com/xml/ns/javaee" <servlet>
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <servlet-name>Faces Servlet</servlet-name>
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee <servlet-class>javax.faces.webapp.FacesServlet</servlet-
http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd" class>
version="2.1"> <load-on-startup>1</load-on-startup>
<managed-bean> </servlet>
<managed-bean-name>helloWorldBean</managed-bean-name> <servlet-mapping>
<managed-bean-class>com.test.HelloWorldBean</managed-bean-class> <servlet-name>Faces Servlet</servlet-name>
<managed-bean-scope>request</managed-bean-scope> <url-pattern>*.jsf</url-pattern>
</managed-bean> <url-pattern>/faces/*</url-pattern>
</faces-config> </servlet-mapping>
...
</web-app>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> package com.test;
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
<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
<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">
<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/xhtml1-
transitional.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;line- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
height:48px;background-color:navy;color:white"> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:insert name="footer">
Pied de page par défaut <html xmlns="http://www.w3.org/1999/xhtml"
<br /> xmlns:ui="http://java.sun.com/jsf/facelets"
<h:graphicImage name="glassfish.png" library="images"/> xmlns:h="http://java.sun.com/jsf/html"
<h:graphicImage url="tomcat.gif"/> xmlns:f="http://java.sun.com/jsf/core">
</ui:insert>
</div> <ui:composition template="/template/template.xhtml">
</h:body> <ui:define name="title">
</h:html> HelloWorld 2 avec Template
</ui:define>
<ui:define name="content">
<?xml version="1.0" encoding="ISO-8859-1" ?> Hello <h:outputLabel value="#{helloWorldBean.nom}" />!
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" <h:messages />
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <h:form>
<html xmlns="http://www.w3.org/1999/xhtml" <h:panelGrid columns="2">
xmlns:h="http://java.sun.com/jsf/html" <h:outputText value="Saisir votre nom"></h:outputText>
xmlns:f="http://java.sun.com/jsf/core" <h:inputText
xmlns:p="http://primefaces.org/ui"> value="#{helloWorldBean.nom}"></h:inputText>
<h:body> </h:panelGrid>
<div style="width:100%;font-size:36px;line-height:48px;background- <h:commandButton value="Afficher nom"
color:navy;color:white"> action="#{helloWorldBean.maFonction}" />
Ceci est un texte provenant du bandeau </h:form>
</div> <h:link value="Recharger page" action="HelloWorld.jsf" />
<p:messages /> </ui:define>
</h:body> </ui:composition>
</html> </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>!
<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
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;
@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
@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 {
<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 javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import 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);
}
}
import javax.faces.application.FacesMessage;
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;
@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;
}
}
<navigation-rule>
<description></description>
// De puis le code de monBean <from-view-id>/login.xhtml</from-view-id>
public String nextPage()
{ <navigation-case>
if (username.equals("guest") && <from-outcome>loginSuccess</from-outcome>
password.equals("guest")) <to-view-id>/loginSuccess.xhtml</to-view-id>
{ </navigation-case>
return "loginSuccess";
} <navigation-case>
return "loginFailure"; <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
@ManagedBean
@RequestScoped
public class CompteControlerBean {
@EJB
private CompteEJB compteEjb;
@ManagedProperty(value="#{ihmSelectionControlerBean}")
private IhmSelectionControlerBean ihmSelectionControlerBean;
Passons à la couche métier !
VUE : XHTML
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
« interface »
Main
« use » MonEJBRemote
lookup JNDI
« implement »
MonEJB MonEJB_2
« implement »
« interface »
MaServlet MonEJBLocal
« use »
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 javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query; @ManagedBean
@RequestScoped
import mabanque.donnees.Compte; public class CompteControlerBean {
/** @EJB
* Session Bean implementation class CompteEJB private CompteEJB compteEjb;
*/
@Stateless @ManagedProperty(value="#{ihmSelectionControlerBean}")
@LocalBean private IhmSelectionControlerBean ihmSelectionControlerBean;
public class CompteEJB {
private List<Compte> listeCompte;
@PersistenceContext(unitName="MaBanque_PU")
private EntityManager em; @PostConstruct
public void initControler(){
@SuppressWarnings("unchecked") listeCompte=compteEjb.recupererTousLesComptes();
public List<Compte> recupererTousLesComptes(){
Query query=em.createNamedQuery("listeCompte"); }
List<Compte> retour=(List<Compte>)query.getResultList(); ...accesseurs...
em.flush(); }
for(int i=0;i<retour.size();i++)
retour.get(i).setTotal(calculateTotal(retour.get(i)));
em.clear();
return retour;
}
...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 javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query; @ManagedBean
@RequestScoped
import mabanque.donnees.Compte; public class CompteControlerBean {
/** @EJB
* Session Bean implementation class CompteEJB private CompteEJB compteEjb;
*/
@Stateless @ManagedProperty(value="#{ihmSelectionControlerBean}")
@LocalBean private IhmSelectionControlerBean ihmSelectionControlerBean;
public class CompteEJB {
private List<Compte> listeCompte;
@PersistenceContext(unitName="MaBanque_PU")
private EntityManager em; @PostConstruct
public void initControler(){
@SuppressWarnings("unchecked") listeCompte=compteEjb.recupererTousLesComptes();
public List<Compte> recupererTousLesComptes(){
Query query=em.createNamedQuery("listeCompte"); }
List<Compte> retour=(List<Compte>)query.getResultList(); ...accesseurs...
em.flush(); }
for(int i=0;i<retour.size();i++)
retour.get(i).setTotal(calculateTotal(retour.get(i)));
em.clear();
return retour;
}
...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
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 REQUIRES_NEW
SUPPORTED NOT_SUPPORTED
MANDATORY NEVER
EJB / CMT
CLient Conteneur EJB EJB1 EJB2 Transaction
1 : ejb1.methodeX()
2 : begin
4 : ejb2.methodeY()
TransactionAttributeType.REQUIRED
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
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
– 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 javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType; import javax.persistence.FetchType;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.Basic; import javax.persistence.Basic;
import javax.persistence.Table; import javax.persistence.Table;
@Entity @Entity
@Table(name="PERSONNE") @Table(name="PERSONNE")
public class Personne { public class Personne {
@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 public class PersonneId {
@Table(name="PERSONNE")
public class Personne { private String nom;
private String prenom;
@EmbeddedId
private PersonneId personneId; public int hashCode() {
return (nom + "_" + prenom).hashCode();
}
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
@Entity
@Table(name="PERSONNE")
public class Personne {
@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 {
public class Mail {
...
private String adresseMail;
@Embedded private String fournisseur;
...
@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;
@Version
private long version;
@Id @Temporal(TemporalType.DATE)
@Column(name="ID_PERSONNE") Date dateDelivrancePermisB;
private Long id;
// Bidirectionnelle
@OneToOne @OneToOne(mappedBy="permis")
private PermisDeConduire permis; Personne beneficiaire;
// Unidirectionnelle
@OneToOne
Personne delivrerar;
@OneToMany
@Entity
@Entity
public class Voiture {
@Table(name="PERSONNE")
public class Personne {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Id
@Column(name="ID_VOITURE")
@Column(name="ID_PERSONNE")
Long id;
private Long id;
String marque;
@OneToOne
private PermisDeConduire permis;
String modele;
@OneToMany(mappedBy="proprietaire")
@Temporal(TemporalType.DATE)
private List<Voiture> mesVoitures;
Date dateAchat;
@ManyToMany
@ManyToOne
private Set<Personne> collegues;
Personne proprietaire;
@ManyToOne
@Entity
@Entity
public class Voiture {
@Table(name="PERSONNE")
public class Personne {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Id
@Column(name="ID_VOITURE")
@Column(name="ID_PERSONNE")
Long id;
private Long id;
String marque;
@OneToOne
private PermisDeConduire permis;
String modele;
@OneToMany(mappedBy="proprietaire")
@Temporal(TemporalType.DATE)
private List<Voiture> mesVoitures;
Date dateAchat;
@ManyToMany
@ManyToOne
private Set<Personne> collegues;
Personne proprietaire;
@ManyToMany
@Entity
@Table(name="PERSONNE")
public class Personne { Création automatique d'un table d'association
@Id
@Column(name="ID_PERSONNE")
private Long id;
@OneToOne
private PermisDeConduire permis;
@OneToMany(mappedBy="proprietaire")
private List<Voiture> mesVoitures;
@ManyToMany
private Set<Personne> collegues;
Héritage
@Entity
@Table(name="PERSONNE")
public class Personne {
@Id
@Column(name="ID_PERSONNE")
private Long id; @Entity
@OneToOne public class Etudiant extends Personne {
private PermisDeConduire permis;
String numeroEtudiant;
@OneToMany(mappedBy="proprietaire")
private List<Voiture> mesVoitures;
@ManyToMany
private Set<Personne> collegues;
Héritage
@Entity
@Table(name="PERSONNE")
@Inheritance(strategy=InheritanceType.JOINED)
public class Personne {
@Id
@Column(name="ID_PERSONNE")
private Long id; @Entity
public class Etudiant extends Personne {
@OneToOne
private PermisDeConduire permis; String numeroEtudiant;
@OneToMany(mappedBy="proprietaire")
private List<Voiture> mesVoitures;
@ManyToMany
private Set<Personne> collegues;
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 »
@Stateless
public class PersonneEJB {
@PersistenceContext(unitName="Test")
private EntityManager em;
@PrePersist
@PostPersist
@PreRemove
@PostRemove
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;
@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);